Browse Source

etcdctl: use user specified timeout value for entire command execution

etcdctl should be capable to use a user specified timeout value for
total command execution, not only per request timeout. This commit
adds a new option --total-timeout to the command. The value passed via
this option is used as a timeout value of entire command execution.

Fixes coreos#3517
Hitoshi Mitake 10 years ago
parent
commit
8ebc933111

+ 1 - 2
etcdctl/command/auth_commands.go

@@ -20,7 +20,6 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
 	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
-	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
 	"github.com/coreos/etcd/client"
 	"github.com/coreos/etcd/client"
 )
 )
 
 
@@ -67,7 +66,7 @@ func authEnableDisable(c *cli.Context, enable bool) {
 		os.Exit(1)
 		os.Exit(1)
 	}
 	}
 	s := mustNewAuthAPI(c)
 	s := mustNewAuthAPI(c)
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
 	var err error
 	var err error
 	if enable {
 	if enable {
 		err = s.Enable(ctx)
 		err = s.Enable(ctx)

+ 10 - 14
etcdctl/command/member_commands.go

@@ -20,8 +20,6 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
 	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
-	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
-	"github.com/coreos/etcd/client"
 )
 )
 
 
 func NewMemberCommand() cli.Command {
 func NewMemberCommand() cli.Command {
@@ -59,9 +57,9 @@ func actionMemberList(c *cli.Context) {
 		os.Exit(1)
 		os.Exit(1)
 	}
 	}
 	mAPI := mustNewMembersAPI(c)
 	mAPI := mustNewMembersAPI(c)
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
+	defer cancel()
 	members, err := mAPI.List(ctx)
 	members, err := mAPI.List(ctx)
-	cancel()
 	if err != nil {
 	if err != nil {
 		fmt.Fprintln(os.Stderr, err.Error())
 		fmt.Fprintln(os.Stderr, err.Error())
 		os.Exit(1)
 		os.Exit(1)
@@ -86,9 +84,10 @@ func actionMemberAdd(c *cli.Context) {
 	mAPI := mustNewMembersAPI(c)
 	mAPI := mustNewMembersAPI(c)
 
 
 	url := args[1]
 	url := args[1]
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
+	defer cancel()
+
 	m, err := mAPI.Add(ctx, url)
 	m, err := mAPI.Add(ctx, url)
-	cancel()
 	if err != nil {
 	if err != nil {
 		fmt.Fprintln(os.Stderr, err.Error())
 		fmt.Fprintln(os.Stderr, err.Error())
 		os.Exit(1)
 		os.Exit(1)
@@ -98,9 +97,7 @@ func actionMemberAdd(c *cli.Context) {
 	newName := args[0]
 	newName := args[0]
 	fmt.Printf("Added member named %s with ID %s to cluster\n", newName, newID)
 	fmt.Printf("Added member named %s with ID %s to cluster\n", newName, newID)
 
 
-	ctx, cancel = context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
 	members, err := mAPI.List(ctx)
 	members, err := mAPI.List(ctx)
-	cancel()
 	if err != nil {
 	if err != nil {
 		fmt.Fprintln(os.Stderr, err.Error())
 		fmt.Fprintln(os.Stderr, err.Error())
 		os.Exit(1)
 		os.Exit(1)
@@ -132,10 +129,11 @@ func actionMemberRemove(c *cli.Context) {
 	removalID := args[0]
 	removalID := args[0]
 
 
 	mAPI := mustNewMembersAPI(c)
 	mAPI := mustNewMembersAPI(c)
+
+	ctx, cancel := contextWithTotalTimeout(c)
+	defer cancel()
 	// Get the list of members.
 	// Get the list of members.
-	listctx, listCancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
-	members, err := mAPI.List(listctx)
-	listCancel()
+	members, err := mAPI.List(ctx)
 	if err != nil {
 	if err != nil {
 		fmt.Fprintln(os.Stderr, "Error while verifying ID against known members:", err.Error())
 		fmt.Fprintln(os.Stderr, "Error while verifying ID against known members:", err.Error())
 		os.Exit(1)
 		os.Exit(1)
@@ -158,9 +156,7 @@ func actionMemberRemove(c *cli.Context) {
 	}
 	}
 
 
 	// Actually attempt to remove the member.
 	// Actually attempt to remove the member.
-	ctx, removeCancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
 	err = mAPI.Remove(ctx, removalID)
 	err = mAPI.Remove(ctx, removalID)
-	removeCancel()
 	if err != nil {
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Received an error trying to remove member %s: %s", removalID, err.Error())
 		fmt.Fprintf(os.Stderr, "Received an error trying to remove member %s: %s", removalID, err.Error())
 		os.Exit(1)
 		os.Exit(1)
@@ -180,7 +176,7 @@ func actionMemberUpdate(c *cli.Context) {
 
 
 	mid := args[0]
 	mid := args[0]
 	urls := args[1]
 	urls := args[1]
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
 	err := mAPI.Update(ctx, mid, strings.Split(urls, ","))
 	err := mAPI.Update(ctx, mid, strings.Split(urls, ","))
 	cancel()
 	cancel()
 	if err != nil {
 	if err != nil {

+ 8 - 12
etcdctl/command/role_commands.go

@@ -21,7 +21,6 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
 	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
-	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
 	"github.com/coreos/etcd/client"
 	"github.com/coreos/etcd/client"
 	"github.com/coreos/etcd/pkg/pathutil"
 	"github.com/coreos/etcd/pkg/pathutil"
 )
 )
@@ -93,7 +92,7 @@ func actionRoleList(c *cli.Context) {
 		os.Exit(1)
 		os.Exit(1)
 	}
 	}
 	r := mustNewAuthRoleAPI(c)
 	r := mustNewAuthRoleAPI(c)
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
 	roles, err := r.ListRoles(ctx)
 	roles, err := r.ListRoles(ctx)
 	cancel()
 	cancel()
 	if err != nil {
 	if err != nil {
@@ -108,16 +107,15 @@ func actionRoleList(c *cli.Context) {
 
 
 func actionRoleAdd(c *cli.Context) {
 func actionRoleAdd(c *cli.Context) {
 	api, role := mustRoleAPIAndName(c)
 	api, role := mustRoleAPIAndName(c)
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
+	defer cancel()
 	currentRole, err := api.GetRole(ctx, role)
 	currentRole, err := api.GetRole(ctx, role)
-	cancel()
 	if currentRole != nil {
 	if currentRole != nil {
 		fmt.Fprintf(os.Stderr, "Role %s already exists\n", role)
 		fmt.Fprintf(os.Stderr, "Role %s already exists\n", role)
 		os.Exit(1)
 		os.Exit(1)
 	}
 	}
-	ctx, cancel = context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+
 	err = api.AddRole(ctx, role)
 	err = api.AddRole(ctx, role)
-	cancel()
 	if err != nil {
 	if err != nil {
 		fmt.Fprintln(os.Stderr, err.Error())
 		fmt.Fprintln(os.Stderr, err.Error())
 		os.Exit(1)
 		os.Exit(1)
@@ -128,7 +126,7 @@ func actionRoleAdd(c *cli.Context) {
 
 
 func actionRoleRemove(c *cli.Context) {
 func actionRoleRemove(c *cli.Context) {
 	api, role := mustRoleAPIAndName(c)
 	api, role := mustRoleAPIAndName(c)
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
 	err := api.RemoveRole(ctx, role)
 	err := api.RemoveRole(ctx, role)
 	cancel()
 	cancel()
 	if err != nil {
 	if err != nil {
@@ -182,21 +180,19 @@ func roleGrantRevoke(c *cli.Context, grant bool) {
 	}
 	}
 
 
 	api, role := mustRoleAPIAndName(c)
 	api, role := mustRoleAPIAndName(c)
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
+	defer cancel()
 	currentRole, err := api.GetRole(ctx, role)
 	currentRole, err := api.GetRole(ctx, role)
-	cancel()
 	if err != nil {
 	if err != nil {
 		fmt.Fprintln(os.Stderr, err.Error())
 		fmt.Fprintln(os.Stderr, err.Error())
 		os.Exit(1)
 		os.Exit(1)
 	}
 	}
-	ctx, cancel = context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
 	var newRole *client.Role
 	var newRole *client.Role
 	if grant {
 	if grant {
 		newRole, err = api.GrantRoleKV(ctx, role, []string{path}, permType)
 		newRole, err = api.GrantRoleKV(ctx, role, []string{path}, permType)
 	} else {
 	} else {
 		newRole, err = api.RevokeRoleKV(ctx, role, []string{path}, permType)
 		newRole, err = api.RevokeRoleKV(ctx, role, []string{path}, permType)
 	}
 	}
-	cancel()
 	if err != nil {
 	if err != nil {
 		fmt.Fprintln(os.Stderr, err.Error())
 		fmt.Fprintln(os.Stderr, err.Error())
 		os.Exit(1)
 		os.Exit(1)
@@ -215,7 +211,7 @@ func roleGrantRevoke(c *cli.Context, grant bool) {
 func actionRoleGet(c *cli.Context) {
 func actionRoleGet(c *cli.Context) {
 	api, rolename := mustRoleAPIAndName(c)
 	api, rolename := mustRoleAPIAndName(c)
 
 
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
 	role, err := api.GetRole(ctx, rolename)
 	role, err := api.GetRole(ctx, rolename)
 	cancel()
 	cancel()
 	if err != nil {
 	if err != nil {

+ 10 - 16
etcdctl/command/user_commands.go

@@ -23,7 +23,6 @@ import (
 
 
 	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/bgentry/speakeasy"
 	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/bgentry/speakeasy"
 	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
 	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
-	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
 	"github.com/coreos/etcd/client"
 	"github.com/coreos/etcd/client"
 )
 )
 
 
@@ -89,7 +88,7 @@ func actionUserList(c *cli.Context) {
 		os.Exit(1)
 		os.Exit(1)
 	}
 	}
 	u := mustNewAuthUserAPI(c)
 	u := mustNewAuthUserAPI(c)
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
 	users, err := u.ListUsers(ctx)
 	users, err := u.ListUsers(ctx)
 	cancel()
 	cancel()
 	if err != nil {
 	if err != nil {
@@ -104,9 +103,9 @@ func actionUserList(c *cli.Context) {
 
 
 func actionUserAdd(c *cli.Context) {
 func actionUserAdd(c *cli.Context) {
 	api, user := mustUserAPIAndName(c)
 	api, user := mustUserAPIAndName(c)
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
+	defer cancel()
 	currentUser, err := api.GetUser(ctx, user)
 	currentUser, err := api.GetUser(ctx, user)
-	cancel()
 	if currentUser != nil {
 	if currentUser != nil {
 		fmt.Fprintf(os.Stderr, "User %s already exists\n", user)
 		fmt.Fprintf(os.Stderr, "User %s already exists\n", user)
 		os.Exit(1)
 		os.Exit(1)
@@ -116,9 +115,7 @@ func actionUserAdd(c *cli.Context) {
 		fmt.Fprintln(os.Stderr, "Error reading password:", err)
 		fmt.Fprintln(os.Stderr, "Error reading password:", err)
 		os.Exit(1)
 		os.Exit(1)
 	}
 	}
-	ctx, cancel = context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
 	err = api.AddUser(ctx, user, pass)
 	err = api.AddUser(ctx, user, pass)
-	cancel()
 	if err != nil {
 	if err != nil {
 		fmt.Fprintln(os.Stderr, err.Error())
 		fmt.Fprintln(os.Stderr, err.Error())
 		os.Exit(1)
 		os.Exit(1)
@@ -129,7 +126,7 @@ func actionUserAdd(c *cli.Context) {
 
 
 func actionUserRemove(c *cli.Context) {
 func actionUserRemove(c *cli.Context) {
 	api, user := mustUserAPIAndName(c)
 	api, user := mustUserAPIAndName(c)
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
 	err := api.RemoveUser(ctx, user)
 	err := api.RemoveUser(ctx, user)
 	cancel()
 	cancel()
 	if err != nil {
 	if err != nil {
@@ -142,9 +139,9 @@ func actionUserRemove(c *cli.Context) {
 
 
 func actionUserPasswd(c *cli.Context) {
 func actionUserPasswd(c *cli.Context) {
 	api, user := mustUserAPIAndName(c)
 	api, user := mustUserAPIAndName(c)
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
+	defer cancel()
 	currentUser, err := api.GetUser(ctx, user)
 	currentUser, err := api.GetUser(ctx, user)
-	cancel()
 	if currentUser == nil {
 	if currentUser == nil {
 		fmt.Fprintln(os.Stderr, err.Error())
 		fmt.Fprintln(os.Stderr, err.Error())
 		os.Exit(1)
 		os.Exit(1)
@@ -155,9 +152,7 @@ func actionUserPasswd(c *cli.Context) {
 		os.Exit(1)
 		os.Exit(1)
 	}
 	}
 
 
-	ctx, cancel = context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
 	_, err = api.ChangePassword(ctx, user, pass)
 	_, err = api.ChangePassword(ctx, user, pass)
-	cancel()
 	if err != nil {
 	if err != nil {
 		fmt.Fprintln(os.Stderr, err.Error())
 		fmt.Fprintln(os.Stderr, err.Error())
 		os.Exit(1)
 		os.Exit(1)
@@ -181,23 +176,22 @@ func userGrantRevoke(c *cli.Context, grant bool) {
 		os.Exit(1)
 		os.Exit(1)
 	}
 	}
 
 
+	ctx, cancel := contextWithTotalTimeout(c)
+	defer cancel()
+
 	api, user := mustUserAPIAndName(c)
 	api, user := mustUserAPIAndName(c)
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
 	currentUser, err := api.GetUser(ctx, user)
 	currentUser, err := api.GetUser(ctx, user)
-	cancel()
 	if currentUser == nil {
 	if currentUser == nil {
 		fmt.Fprintln(os.Stderr, err.Error())
 		fmt.Fprintln(os.Stderr, err.Error())
 		os.Exit(1)
 		os.Exit(1)
 	}
 	}
 
 
-	ctx, cancel = context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
 	var newUser *client.User
 	var newUser *client.User
 	if grant {
 	if grant {
 		newUser, err = api.GrantUser(ctx, user, roles)
 		newUser, err = api.GrantUser(ctx, user, roles)
 	} else {
 	} else {
 		newUser, err = api.RevokeUser(ctx, user, roles)
 		newUser, err = api.RevokeUser(ctx, user, roles)
 	}
 	}
-	cancel()
 	sort.Strings(newUser.Roles)
 	sort.Strings(newUser.Roles)
 	sort.Strings(currentUser.Roles)
 	sort.Strings(currentUser.Roles)
 	if reflect.DeepEqual(newUser.Roles, currentUser.Roles) {
 	if reflect.DeepEqual(newUser.Roles, currentUser.Roles) {
@@ -217,7 +211,7 @@ func userGrantRevoke(c *cli.Context, grant bool) {
 
 
 func actionUserGet(c *cli.Context) {
 func actionUserGet(c *cli.Context) {
 	api, username := mustUserAPIAndName(c)
 	api, username := mustUserAPIAndName(c)
-	ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+	ctx, cancel := contextWithTotalTimeout(c)
 	user, err := api.GetUser(ctx, username)
 	user, err := api.GetUser(ctx, username)
 	cancel()
 	cancel()
 	if err != nil {
 	if err != nil {

+ 4 - 0
etcdctl/command/util.go

@@ -263,3 +263,7 @@ func newClient(c *cli.Context) (client.Client, error) {
 
 
 	return client.New(cfg)
 	return client.New(cfg)
 }
 }
+
+func contextWithTotalTimeout(c *cli.Context) (context.Context, context.CancelFunc) {
+	return context.WithTimeout(context.Background(), c.GlobalDuration("total-timeout"))
+}

+ 1 - 0
etcdctl/main.go

@@ -40,6 +40,7 @@ func main() {
 		cli.StringFlag{Name: "ca-file", Value: "", Usage: "verify certificates of HTTPS-enabled servers using this CA bundle"},
 		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.StringFlag{Name: "username, u", Value: "", Usage: "provide username[:password] and prompt if password is not supplied."},
 		cli.DurationFlag{Name: "timeout", Value: time.Second, Usage: "connection timeout per request"},
 		cli.DurationFlag{Name: "timeout", Value: 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{
 	app.Commands = []cli.Command{
 		command.NewBackupCommand(),
 		command.NewBackupCommand(),