Compare commits

...

15 Commits

Author SHA1 Message Date
c41345d393 *: bump to v2.3.3 2016-04-29 12:38:04 -07:00
506ef9fe8d etcdserver/auth: check empty password in merge
Fix https://github.com/coreos/etcd/issues/5182.
2016-04-29 12:37:24 -07:00
49141d5916 *: bump to v2.3.2+git 2016-04-21 12:57:57 -07:00
ce63f10738 *: bump to v2.3.2 2016-04-21 11:26:37 -07:00
31bd750141 etcdserver: close response body when getting cluster information 2016-04-21 10:58:58 -07:00
37510d0306 e2e: test etcdtl user list on root user 2016-04-21 10:58:46 -07:00
d7da3787bc client: accept roles in response for ListUser
Fixes #5046
2016-04-21 10:58:40 -07:00
54d0f1d43b e2e: test etcdctl v2 double user grant
Crashes in 2.3.1
2016-04-21 10:58:30 -07:00
20db10f6f7 etcdctl: print grant/revoke error instead of scanning roles for changes
Fixes #5045
2016-04-21 10:58:17 -07:00
11c09373e1 etcdmain: start on unsupported arch when ETCD_UNSUPPORTED_ARCH is set 2016-04-21 10:42:45 -07:00
96f412e4d7 *: bump to v2.3.1+git 2016-04-01 14:56:48 -07:00
2b67f5256a *: bump to v2.3.1 2016-04-01 14:32:28 -07:00
6aa8b631e6 client: return original ctx error
Fix https://github.com/coreos/etcd/issues/3209.
2016-04-01 14:30:31 -07:00
72dea51e6a rafthttp: do not block on proposal 2016-04-01 14:28:50 -07:00
74fa0270a4 *: bump to v2.3.0+git 2016-03-18 10:23:04 -07:00
13 changed files with 147 additions and 87 deletions

View File

@ -137,12 +137,15 @@ curl -L http://127.0.0.1:2379/version
The `v2` API responses should not change after the 2.0.0 release but new features will be added over time.
#### 32-bit systems
#### 32-bit and other unsupported systems
etcd has known issues on 32-bit systems due to a bug in the Go runtime. See #[358][358] for more information.
To avoid inadvertantly producing an unstable etcd server, 32-bit builds emit an `etcd` that prints
a warning message and immediately exits.
To avoid inadvertantly running a possibly unstable etcd server, `etcd` on unsupported architectures will print
a warning message and immediately exit if the environment variable `ETCD_UNSUPPORTED_ARCH` is not set to
the target architecture.
Currently only the amd64 architecture is officially supported by `etcd`.
[358]: https://github.com/coreos/etcd/issues/358

View File

@ -36,6 +36,12 @@ type User struct {
Revoke []string `json:"revoke,omitempty"`
}
// userListEntry is the user representation given by the server for ListUsers
type userListEntry struct {
User string `json:"user"`
Roles []Role `json:"roles"`
}
type UserRoles struct {
User string `json:"user"`
Roles []Role `json:"roles"`
@ -198,7 +204,7 @@ func (u *httpAuthUserAPI) ListUsers(ctx context.Context) ([]string, error) {
}
var userList struct {
Users []User `json:"users"`
Users []userListEntry `json:"users"`
}
if err = json.Unmarshal(body, &userList); err != nil {

View File

@ -342,7 +342,9 @@ func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Respo
resp, body, err = hc.Do(ctx, action)
if err != nil {
cerr.Errors = append(cerr.Errors, err)
// mask previous errors with context error, which is controlled by user
if err == ctx.Err() {
return nil, nil, ctx.Err()
}
if err == context.Canceled || err == context.DeadlineExceeded {
return nil, nil, err
}

View File

@ -295,7 +295,7 @@ func TestSimpleHTTPClientDoHeaderTimeout(t *testing.T) {
t.Fatalf("expected non-nil error, got nil")
}
case <-time.After(time.Second):
t.Fatalf("unexpected timeout when waitting for the test to finish")
t.Fatalf("unexpected timeout when waiting for the test to finish")
}
}
@ -444,7 +444,51 @@ func TestHTTPClusterClientDoDeadlineExceedContext(t *testing.T) {
t.Errorf("err = %+v, want %+v", err, context.DeadlineExceeded)
}
case <-time.After(time.Second):
t.Fatalf("unexpected timeout when waitting for request to deadline exceed")
t.Fatalf("unexpected timeout when waiting for request to deadline exceed")
}
}
type fakeCancelContext struct{}
var fakeCancelContextError = errors.New("fake context canceled")
func (f fakeCancelContext) Deadline() (time.Time, bool) { return time.Time{}, false }
func (f fakeCancelContext) Done() <-chan struct{} {
d := make(chan struct{}, 1)
d <- struct{}{}
return d
}
func (f fakeCancelContext) Err() error { return fakeCancelContextError }
func (f fakeCancelContext) Value(key interface{}) interface{} { return 1 }
func withTimeout(parent context.Context, timeout time.Duration) (context.Context, context.CancelFunc) {
return parent, func() { parent = nil }
}
func TestHTTPClusterClientDoCanceledContext(t *testing.T) {
fakeURL := url.URL{}
tr := newFakeTransport()
tr.finishCancel <- struct{}{}
c := &httpClusterClient{
clientFactory: newHTTPClientFactory(tr, DefaultCheckRedirect, 0),
endpoints: []url.URL{fakeURL},
}
errc := make(chan error)
go func() {
ctx, cancel := withTimeout(fakeCancelContext{}, time.Millisecond)
cancel()
_, _, err := c.Do(ctx, &fakeAction{})
errc <- err
}()
select {
case err := <-errc:
if err != fakeCancelContextError {
t.Errorf("err = %+v, want %+v", err, fakeCancelContextError)
}
case <-time.After(time.Second):
t.Fatalf("unexpected timeout when waiting for request to fake context canceled")
}
}

View File

@ -185,9 +185,18 @@ func testCtlV2GetRoleUser(t *testing.T, cfg *etcdProcessClusterConfig) {
if err := etcdctlUserGet(epc, "username"); err != nil {
t.Fatalf("failed to get user (%v)", err)
}
// ensure double grant gives an error; was crashing in 2.3.1
regrantArgs := etcdctlPrefixArgs(epc)
regrantArgs = append(regrantArgs, "user", "grant", "--roles", "foo", "username")
if err := spawnWithExpect(regrantArgs, "duplicate"); err != nil {
t.Fatalf("missing duplicate error on double grant role (%v)", err)
}
}
func TestCtlV2UserList(t *testing.T) {
func TestCtlV2UserListUsername(t *testing.T) { testCtlV2UserList(t, "username") }
func TestCtlV2UserListRoot(t *testing.T) { testCtlV2UserList(t, "root") }
func testCtlV2UserList(t *testing.T, username string) {
defer testutil.AfterTest(t)
epc := setupEtcdctlTest(t, &configWithProxy, false)
@ -197,10 +206,10 @@ func TestCtlV2UserList(t *testing.T) {
}
}()
if err := etcdctlUserAdd(epc, "username", "password"); err != nil {
if err := etcdctlUserAdd(epc, username, "password"); err != nil {
t.Fatalf("failed to add user (%v)", err)
}
if err := etcdctlUserList(epc, "username"); err != nil {
if err := etcdctlUserList(epc, username); err != nil {
t.Fatalf("failed to list users (%v)", err)
}
}

View File

@ -17,8 +17,6 @@ package command
import (
"fmt"
"os"
"reflect"
"sort"
"strings"
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/bgentry/speakeasy"
@ -195,21 +193,12 @@ func userGrantRevoke(c *cli.Context, grant bool) {
os.Exit(1)
}
var newUser *client.User
if grant {
newUser, err = api.GrantUser(ctx, user, roles)
_, err = api.GrantUser(ctx, user, roles)
} else {
newUser, err = api.RevokeUser(ctx, user, roles)
}
sort.Strings(newUser.Roles)
sort.Strings(currentUser.Roles)
if reflect.DeepEqual(newUser.Roles, currentUser.Roles) {
if grant {
fmt.Printf("User unchanged; roles already granted")
} else {
fmt.Printf("User unchanged; roles already revoked")
}
_, err = api.RevokeUser(ctx, user, roles)
}
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)

View File

@ -12,9 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// TODO: support arm64
// +build amd64
package etcdmain
import (
@ -79,6 +76,8 @@ var (
)
func Main() {
checkSupportArch()
cfg := NewConfig()
err := cfg.Parse(os.Args[1:])
if err != nil {
@ -554,3 +553,16 @@ func setupLogging(cfg *config) {
repoLog.SetLogLevel(settings)
}
}
func checkSupportArch() {
// TODO qualify arm64
if runtime.GOARCH == "amd64" {
return
}
if env, ok := os.LookupEnv("ETCD_UNSUPPORTED_ARCH"); ok && env == runtime.GOARCH {
plog.Warningf("running etcd on unsupported architecture %q since ETCD_UNSUPPORTED_ARCH is set", env)
return
}
plog.Errorf("etcd on unsupported platform without ETCD_UNSUPPORTED_ARCH=%s set.", runtime.GOARCH)
os.Exit(1)
}

View File

@ -1,31 +0,0 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !amd64
package etcdmain
import (
"fmt"
"os"
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
)
var plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "etcdmain")
func Main() {
fmt.Println("unsupported architecture; unreliable, unstable")
os.Exit(-1)
}

View File

@ -281,13 +281,7 @@ func (s *store) UpdateUser(user User) (User, error) {
return old, err
}
hash, err := s.HashPassword(user.Password)
if err != nil {
return old, err
}
user.Password = hash
newUser, err := old.merge(user)
newUser, err := old.merge(user, s.PasswordStore)
if err != nil {
return old, err
}
@ -452,29 +446,33 @@ func (s *store) DisableAuth() error {
// is called and returns a new User with these modifications applied. Think of
// all Users as immutable sets of data. Merge allows you to perform the set
// operations (desired grants and revokes) atomically
func (u User) merge(n User) (User, error) {
func (ou User) merge(nu User, s PasswordStore) (User, error) {
var out User
if u.User != n.User {
return out, authErr(http.StatusConflict, "Merging user data with conflicting usernames: %s %s", u.User, n.User)
if ou.User != nu.User {
return out, authErr(http.StatusConflict, "Merging user data with conflicting usernames: %s %s", ou.User, nu.User)
}
out.User = u.User
if n.Password != "" {
out.Password = n.Password
out.User = ou.User
if nu.Password != "" {
hash, err := s.HashPassword(nu.Password)
if err != nil {
return ou, err
}
out.Password = hash
} else {
out.Password = u.Password
out.Password = ou.Password
}
currentRoles := types.NewUnsafeSet(u.Roles...)
for _, g := range n.Grant {
currentRoles := types.NewUnsafeSet(ou.Roles...)
for _, g := range nu.Grant {
if currentRoles.Contains(g) {
plog.Noticef("granting duplicate role %s for user %s", g, n.User)
return User{}, authErr(http.StatusConflict, fmt.Sprintf("Granting duplicate role %s for user %s", g, n.User))
plog.Noticef("granting duplicate role %s for user %s", g, nu.User)
return User{}, authErr(http.StatusConflict, fmt.Sprintf("Granting duplicate role %s for user %s", g, nu.User))
}
currentRoles.Add(g)
}
for _, r := range n.Revoke {
for _, r := range nu.Revoke {
if !currentRoles.Contains(r) {
plog.Noticef("revoking ungranted role %s for user %s", r, n.User)
return User{}, authErr(http.StatusConflict, fmt.Sprintf("Revoking ungranted role %s for user %s", r, n.User))
plog.Noticef("revoking ungranted role %s for user %s", r, nu.User)
return User{}, authErr(http.StatusConflict, fmt.Sprintf("Revoking ungranted role %s for user %s", r, nu.User))
}
currentRoles.Remove(r)
}

View File

@ -26,6 +26,21 @@ import (
etcdstore "github.com/coreos/etcd/store"
)
type fakeDoer struct{}
func (_ fakeDoer) Do(context.Context, etcdserverpb.Request) (etcdserver.Response, error) {
return etcdserver.Response{}, nil
}
func TestCheckPassword(t *testing.T) {
st := NewStore(fakeDoer{}, 5*time.Second)
u := User{Password: "$2a$10$I3iddh1D..EIOXXQtsra4u8AjOtgEa2ERxVvYGfXFBJDo1omXwP.q"}
matched := st.CheckPassword(u, "foo")
if matched {
t.Fatalf("expected false, got %v", matched)
}
}
const testTimeout = time.Millisecond
func TestMergeUser(t *testing.T) {
@ -71,16 +86,16 @@ func TestMergeUser(t *testing.T) {
User{User: "foo", Roles: []string{"role1", "role2"}},
false,
},
{
User{User: "foo"},
User{User: "foo", Password: "$2a$10$aUPOdbOGNawaVSusg3g2wuC3AH6XxIr9/Ms4VgDvzrAVOJPYzZILa"},
User{User: "foo", Roles: []string{}, Password: "$2a$10$aUPOdbOGNawaVSusg3g2wuC3AH6XxIr9/Ms4VgDvzrAVOJPYzZILa"},
{ // empty password will not overwrite the previous password
User{User: "foo", Password: "foo", Roles: []string{}},
User{User: "foo", Password: ""},
User{User: "foo", Password: "foo", Roles: []string{}},
false,
},
}
for i, tt := range tbl {
out, err := tt.input.merge(tt.merge)
out, err := tt.input.merge(tt.merge, passwordStore{})
if err != nil && !tt.iserr {
t.Fatalf("Got unexpected error on item %d", i)
}

View File

@ -71,6 +71,7 @@ func getClusterFromRemotePeers(urls []string, timeout time.Duration, logerr bool
continue
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
if logerr {
plog.Warningf("could not read the body of cluster response: %v", err)

View File

@ -142,11 +142,23 @@ func startPeer(transport *Transport, urls types.URLs, local, to, cid types.ID, r
go func() {
for {
select {
case mm := <-p.propc:
case mm := <-p.recvc:
if err := r.Process(ctx, mm); err != nil {
plog.Warningf("failed to process raft message (%v)", err)
}
case mm := <-p.recvc:
case <-p.stopc:
return
}
}
}()
// r.Process might block for processing proposal when there is no leader.
// Thus propc must be put into a separate routine with recvc to avoid blocking
// processing other raft messages.
go func() {
for {
select {
case mm := <-p.propc:
if err := r.Process(ctx, mm); err != nil {
plog.Warningf("failed to process raft message (%v)", err)
}

View File

@ -29,7 +29,7 @@ import (
var (
// MinClusterVersion is the min cluster version this etcd binary is compatible with.
MinClusterVersion = "2.2.0"
Version = "2.3.0"
Version = "2.3.3"
// Git SHA Value will be set during build
GitSHA = "Not provided (use ./build instead of go build)"