From 54e5dec8f24b996a448a97e04f538c574ec9a7a2 Mon Sep 17 00:00:00 2001 From: Allison Richardet Date: Tue, 4 Oct 2016 21:47:29 +0000 Subject: [PATCH 1/7] Add ability to modify a user --- resource/group/preparer.go | 1 + resource/user/preparer.go | 21 +- resource/user/preparer_test.go | 14 + resource/user/user.go | 353 +++++++---- resource/user/user_default.go | 5 + resource/user/user_linux.go | 30 + resource/user/user_test.go | 1041 +++++++++++++++++++------------- 7 files changed, 916 insertions(+), 549 deletions(-) diff --git a/resource/group/preparer.go b/resource/group/preparer.go index fc2c45c2e..7d54e327b 100644 --- a/resource/group/preparer.go +++ b/resource/group/preparer.go @@ -37,6 +37,7 @@ type Preparer struct { NewName string `hcl:"new_name"` // State is whether the group should be present. + // The default value is present. State State `hcl:"state" valid_values:"present,absent"` } diff --git a/resource/user/preparer.go b/resource/user/preparer.go index d0cac1d53..432699ec9 100644 --- a/resource/user/preparer.go +++ b/resource/user/preparer.go @@ -29,6 +29,12 @@ type Preparer struct { // Username is the user login name. Username string `hcl:"username" required:"true"` + // NewUsername is used when modifying a user. + // Username will be changed to NewUsername. No changes to the home directory + // name or location of the contents will be made. This can be done using + // HomeDir and MoveDir options. + NewUsername string `hcl:"new_username"` + // UID is the user ID. UID *uint32 `hcl:"uid"` @@ -41,13 +47,20 @@ type Preparer struct { GID *uint32 `hcl:"gid" mutually_exclusive:"gid,groupname"` // Name is the user description. + // This field can be indicated when adding or modifying a user. Name string `hcl:"name"` - // HomeDir is the user's login directory. By default, the login + // HomeDir is the user's login directory. By default, the login // name is appended to the home directory. + // This field can be indicated when adding or modifying a user. HomeDir string `hcl:"home_dir"` + // MoveDir is used to move the contents of HomeDir when modifying a user. + // HomeDir must also be indicated if MoveDir is set to true. + MoveDir bool `hcl:"move_dir"` + // State is whether the user should be present. + // The default value is present. State State `hcl:"state" valid_values:"present,absent"` } @@ -63,15 +76,21 @@ func (p *Preparer) Prepare(render resource.Renderer) (resource.Task, error) { return nil, fmt.Errorf("user \"gid\" parameter out of range") } + if p.MoveDir && p.HomeDir == "" { + return nil, fmt.Errorf("user \"home_dir\" parameter required with \"move_dir\" parameter") + } + if p.State == "" { p.State = StatePresent } usr := NewUser(new(System)) usr.Username = p.Username + usr.NewUsername = p.NewUsername usr.GroupName = p.GroupName usr.Name = p.Name usr.HomeDir = p.HomeDir + usr.MoveDir = p.MoveDir usr.State = p.State if p.UID != nil { diff --git a/resource/user/preparer_test.go b/resource/user/preparer_test.go index eb5cd113d..416f21683 100644 --- a/resource/user/preparer_test.go +++ b/resource/user/preparer_test.go @@ -119,6 +119,13 @@ func TestPrepare(t *testing.T) { assert.NoError(t, err) }) + + t.Run("home_dir and move_dir parameters", func(t *testing.T) { + p := user.Preparer{Username: "test", MoveDir: true, HomeDir: "tmp"} + _, err := p.Prepare(&fr) + + assert.NoError(t, err) + }) }) t.Run("invalid", func(t *testing.T) { @@ -135,5 +142,12 @@ func TestPrepare(t *testing.T) { assert.EqualError(t, err, fmt.Sprintf("user \"gid\" parameter out of range")) }) + + t.Run("no home_dir with move_dir", func(t *testing.T) { + p := user.Preparer{Username: "test", MoveDir: true} + _, err := p.Prepare(&fr) + + assert.EqualError(t, err, fmt.Sprintf("user \"home_dir\" parameter required with \"move_dir\" parameter")) + }) }) } diff --git a/resource/user/user.go b/resource/user/user.go index 525ca9248..6aa712b95 100644 --- a/resource/user/user.go +++ b/resource/user/user.go @@ -35,14 +35,18 @@ const ( // User manages user users type User struct { - Username string - UID string - GroupName string - GID string - Name string - HomeDir string - State State - system SystemUtils + Username string + NewUsername string + UID string + GroupName string + GID string + Name string + HomeDir string + MoveDir bool + State State + system SystemUtils + + *resource.Status } // AddUserOptions are the options specified in the configuration to be used @@ -54,10 +58,22 @@ type AddUserOptions struct { Directory string } +// ModUserOptions are the options specified in the configuration to be used +// when modifying a user +type ModUserOptions struct { + Username string + UID string + Group string + Comment string + Directory string + MoveDir bool +} + // SystemUtils provides system utilities for user type SystemUtils interface { AddUser(userName string, options *AddUserOptions) error DelUser(userName string) error + ModUser(userName string, options *ModUserOptions) error Lookup(userName string) (*user.User, error) LookupID(userID string) (*user.User, error) LookupGroup(groupName string) (*user.Group, error) @@ -90,57 +106,35 @@ func (u *User) Check(resource.Renderer) (resource.TaskStatus, error) { userByID, uidErr = u.system.LookupID(u.UID) } - status := &resource.Status{} + u.Status = resource.NewStatus() if nameErr == ErrUnsupported { - status.RaiseLevel(resource.StatusFatal) - return status, ErrUnsupported + u.Status.RaiseLevel(resource.StatusFatal) + return u, ErrUnsupported } switch u.State { case StatePresent: - switch { - case u.UID == "": - _, nameNotFound := nameErr.(user.UnknownUserError) + _, nameNotFound := nameErr.(user.UnknownUserError) - switch { - case userByName != nil: - status.AddMessage(fmt.Sprintf("user %s already exists", u.Username)) - case nameNotFound: - _, err := SetAddUserOptions(u) - if err != nil { - status.RaiseLevel(resource.StatusCantChange) - return status, errors.Wrapf(err, "cannot add user %s", u.Username) - } - status.RaiseLevel(resource.StatusWillChange) - status.AddMessage("user does not exist") - status.AddDifference("user", string(StateAbsent), fmt.Sprintf("user %s", u.Username), "") + switch { + case nameNotFound: + _, err := u.DiffAdd(u.Status) + if err != nil { + return u, errors.Wrapf(err, "cannot add user %s", u.Username) } - case u.UID != "": - _, nameNotFound := nameErr.(user.UnknownUserError) - _, uidNotFound := uidErr.(user.UnknownUserIdError) - - switch { - case nameNotFound && uidNotFound: - _, err := SetAddUserOptions(u) - if err != nil { - status.RaiseLevel(resource.StatusCantChange) - return status, errors.Wrapf(err, "cannot add user %s with uid %s", u.Username, u.UID) - } - status.RaiseLevel(resource.StatusWillChange) - status.AddMessage("user name and uid do not exist") - status.AddDifference("user", string(StateAbsent), fmt.Sprintf("user %s with uid %s", u.Username, u.UID), "") - case nameNotFound: - status.RaiseLevel(resource.StatusCantChange) - return status, fmt.Errorf("cannot add user %s with uid %s: user uid already exists", u.Username, u.UID) - case uidNotFound: - status.RaiseLevel(resource.StatusCantChange) - return status, fmt.Errorf("cannot add user %s with uid %s: user already exists", u.Username, u.UID) - case userByName != nil && userByID != nil && userByName.Name != userByID.Name || userByName.Uid != userByID.Uid: - status.RaiseLevel(resource.StatusCantChange) - return status, fmt.Errorf("cannot add user %s with uid %s: user and uid belong to different users", u.Username, u.UID) - case userByName != nil && userByID != nil && *userByName == *userByID: - status.AddMessage("user %s with uid %s already exists", u.Username, u.UID) + if resource.AnyChanges(u.Status.Differences) { + u.Status.AddMessage("add user") + } + case userByName != nil: + _, err := u.DiffMod(u.Status, userByName) + if err != nil { + return u, errors.Wrapf(err, "cannot modify user %s", u.Username) + } + if resource.AnyChanges(u.Status.Differences) { + u.Status.AddMessage("modify user") + } else { + u.Status.AddMessage(fmt.Sprintf("no modifications indicated for user %s", u.Username)) } } case StateAbsent: @@ -150,10 +144,10 @@ func (u *User) Check(resource.Renderer) (resource.TaskStatus, error) { switch { case nameNotFound: - status.AddMessage(fmt.Sprintf("user %s does not exist", u.Username)) + u.Status.AddMessage(fmt.Sprintf("user %s does not exist", u.Username)) case userByName != nil: - status.RaiseLevel(resource.StatusWillChange) - status.AddDifference("user", fmt.Sprintf("user %s", u.Username), string(StateAbsent), "") + u.Status.RaiseLevel(resource.StatusWillChange) + u.Status.AddDifference("user", fmt.Sprintf("user %s", u.Username), fmt.Sprintf("<%s>", string(StateAbsent)), "") } case u.UID != "": _, nameNotFound := nameErr.(user.UnknownUserError) @@ -161,27 +155,27 @@ func (u *User) Check(resource.Renderer) (resource.TaskStatus, error) { switch { case nameNotFound && uidNotFound: - status.AddMessage(fmt.Sprintf("user %s and uid %s do not exist", u.Username, u.UID)) + u.Status.AddMessage(fmt.Sprintf("user %s and uid %s do not exist", u.Username, u.UID)) case nameNotFound: - status.RaiseLevel(resource.StatusCantChange) - return status, fmt.Errorf("cannot delete user %s with uid %s: user does not exist", u.Username, u.UID) + u.Status.RaiseLevel(resource.StatusCantChange) + return u, fmt.Errorf("cannot delete user %s with uid %s: user does not exist", u.Username, u.UID) case uidNotFound: - status.RaiseLevel(resource.StatusCantChange) - return status, fmt.Errorf("cannot delete user %s with uid %s: uid does not exist", u.Username, u.UID) + u.Status.RaiseLevel(resource.StatusCantChange) + return u, fmt.Errorf("cannot delete user %s with uid %s: uid does not exist", u.Username, u.UID) case userByName != nil && userByID != nil && userByName.Name != userByID.Name || userByName.Uid != userByID.Uid: - status.RaiseLevel(resource.StatusCantChange) - return status, fmt.Errorf("cannot delete user %s with uid %s: user and uid belong to different users", u.Username, u.UID) + u.Status.RaiseLevel(resource.StatusCantChange) + return u, fmt.Errorf("cannot delete user %s with uid %s: user and uid belong to different users", u.Username, u.UID) case userByName != nil && userByID != nil && *userByName == *userByID: - status.RaiseLevel(resource.StatusWillChange) - status.AddDifference("user", fmt.Sprintf("user %s with uid %s", u.Username, u.UID), string(StateAbsent), "") + u.Status.RaiseLevel(resource.StatusWillChange) + u.Status.AddDifference("user", fmt.Sprintf("user %s with uid %s", u.Username, u.UID), fmt.Sprintf("<%s>", string(StateAbsent)), "") } } default: - status.RaiseLevel(resource.StatusFatal) - return status, fmt.Errorf("user: unrecognized state %v", u.State) + u.Status.RaiseLevel(resource.StatusFatal) + return u, fmt.Errorf("user: unrecognized state %v", u.State) } - return status, nil + return u.Status, nil } // Apply changes for user @@ -200,58 +194,45 @@ func (u *User) Apply() (resource.TaskStatus, error) { userByID, uidErr = u.system.LookupID(u.UID) } - status := &resource.Status{} + u.Status = resource.NewStatus() if nameErr == ErrUnsupported { - status.RaiseLevel(resource.StatusFatal) - return status, ErrUnsupported + u.Status.RaiseLevel(resource.StatusFatal) + return u, ErrUnsupported } switch u.State { case StatePresent: - switch { - case u.UID == "": - _, nameNotFound := nameErr.(user.UnknownUserError) + _, nameNotFound := nameErr.(user.UnknownUserError) - switch { - case nameNotFound: - options, err := SetAddUserOptions(u) - if err != nil { - status.RaiseLevel(resource.StatusCantChange) - return status, errors.Wrapf(err, "will not attempt to add user %s", u.Username) - } + switch { + case nameNotFound: + options, err := u.DiffAdd(u.Status) + if err != nil { + return u, errors.Wrapf(err, "will not attempt to add user %s", u.Username) + } + if resource.AnyChanges(u.Status.Differences) { err = u.system.AddUser(u.Username, options) if err != nil { - status.RaiseLevel(resource.StatusFatal) - status.AddMessage(fmt.Sprintf("error adding user %s", u.Username)) - return status, errors.Wrap(err, "user add") + u.Status.RaiseLevel(resource.StatusFatal) + u.Status.AddMessage(fmt.Sprintf("error adding user %s", u.Username)) + return u, errors.Wrap(err, "user add") } - status.AddMessage(fmt.Sprintf("added user %s", u.Username)) - default: - status.RaiseLevel(resource.StatusCantChange) - return status, fmt.Errorf("will not attempt to add user %s", u.Username) + u.Status.AddMessage(fmt.Sprintf("added user %s", u.Username)) } - case u.UID != "": - _, nameNotFound := nameErr.(user.UnknownUserError) - _, uidNotFound := uidErr.(user.UnknownUserIdError) - - switch { - case nameNotFound && uidNotFound: - options, err := SetAddUserOptions(u) - if err != nil { - status.RaiseLevel(resource.StatusCantChange) - return status, errors.Wrapf(err, "will not attempt to add user %s with uid %s", u.Username, u.UID) - } - err = u.system.AddUser(u.Username, options) + case userByName != nil: + options, err := u.DiffMod(u.Status, userByName) + if err != nil { + return u, errors.Wrapf(err, "will not attempt to modify user %s", u.Username) + } + if resource.AnyChanges(u.Status.Differences) { + err = u.system.ModUser(u.Username, options) if err != nil { - status.RaiseLevel(resource.StatusFatal) - status.AddMessage(fmt.Sprintf("error adding user %s with uid %s", u.Username, u.UID)) - return status, errors.Wrap(err, "user add") + u.Status.RaiseLevel(resource.StatusFatal) + u.Status.AddMessage(fmt.Sprintf("error modifying user %s", u.Username)) + return u, errors.Wrap(err, "user modify") } - status.AddMessage(fmt.Sprintf("added user %s with uid %s", u.Username, u.UID)) - default: - status.RaiseLevel(resource.StatusCantChange) - return status, fmt.Errorf("will not attempt to add user %s with uid %s", u.Username, u.UID) + u.Status.AddMessage(fmt.Sprintf("modified user %s", u.Username)) } } case StateAbsent: @@ -263,14 +244,14 @@ func (u *User) Apply() (resource.TaskStatus, error) { case !nameNotFound && userByName != nil: err := u.system.DelUser(u.Username) if err != nil { - status.RaiseLevel(resource.StatusFatal) - status.AddMessage(fmt.Sprintf("error deleting user %s", u.Username)) - return status, errors.Wrap(err, "user delete") + u.Status.RaiseLevel(resource.StatusFatal) + u.Status.AddMessage(fmt.Sprintf("error deleting user %s", u.Username)) + return u, errors.Wrap(err, "user delete") } - status.AddMessage(fmt.Sprintf("deleted user %s", u.Username)) + u.Status.AddMessage(fmt.Sprintf("deleted user %s", u.Username)) default: - status.RaiseLevel(resource.StatusCantChange) - return status, fmt.Errorf("will not attempt to delete user %s", u.Username) + u.Status.RaiseLevel(resource.StatusCantChange) + return u, fmt.Errorf("will not attempt to delete user %s", u.Username) } case u.UID != "": _, nameNotFound := nameErr.(user.UnknownUserError) @@ -280,56 +261,168 @@ func (u *User) Apply() (resource.TaskStatus, error) { case !nameNotFound && !uidNotFound && userByName != nil && userByID != nil && *userByName == *userByID: err := u.system.DelUser(u.Username) if err != nil { - status.RaiseLevel(resource.StatusFatal) - status.AddMessage(fmt.Sprintf("error deleting user %s with uid %s", u.Username, u.UID)) - return status, errors.Wrap(err, "user delete") + u.Status.RaiseLevel(resource.StatusFatal) + u.Status.AddMessage(fmt.Sprintf("error deleting user %s with uid %s", u.Username, u.UID)) + return u, errors.Wrap(err, "user delete") } - status.AddMessage(fmt.Sprintf("deleted user %s with uid %s", u.Username, u.UID)) + u.Status.AddMessage(fmt.Sprintf("deleted user %s with uid %s", u.Username, u.UID)) default: - status.RaiseLevel(resource.StatusCantChange) - return status, fmt.Errorf("will not attempt to delete user %s with uid %s", u.Username, u.UID) + u.Status.RaiseLevel(resource.StatusCantChange) + return u, fmt.Errorf("will not attempt to delete user %s with uid %s", u.Username, u.UID) } } default: - status.RaiseLevel(resource.StatusFatal) - return status, fmt.Errorf("user: unrecognized state %s", u.State) + u.Status.RaiseLevel(resource.StatusFatal) + return u, fmt.Errorf("user: unrecognized state %s", u.State) } - return status, nil + return u, nil } -// SetAddUserOptions returns a AddUserOptions struct with the options -// specified in the configuration for adding a user -// If group information is provided and the group is not found, nil and an -// error indicating the group name or gid is not found is returned -func SetAddUserOptions(u *User) (*AddUserOptions, error) { +func noOptionsSet(u *User) bool { + switch { + case u.UID != "", u.GroupName != "", u.GID != "", u.Name != "", u.HomeDir != "": + return false + } + return true +} + +// DiffAdd checks for differences between the current and desired state for the +// user to be added indicated by the User fields. The options to be used for the +// add command are set. +func (u *User) DiffAdd(status *resource.Status) (*AddUserOptions, error) { options := new(AddUserOptions) + u.Status.AddDifference("username", fmt.Sprintf("<%s>", string(StateAbsent)), u.Username, "") + if u.UID != "" { - options.UID = u.UID + usr, err := user.LookupId(u.UID) + _, uidNotFound := err.(user.UnknownUserIdError) + + if uidNotFound { + options.UID = u.UID + status.AddDifference("uid", fmt.Sprintf("<%s>", string(StateAbsent)), u.UID, "") + } else if usr != nil { + u.Status.RaiseLevel(resource.StatusCantChange) + return nil, fmt.Errorf("uid %s already exists", u.UID) + } } switch { case u.GroupName != "": - _, err := user.LookupGroup(u.GroupName) + grp, err := user.LookupGroup(u.GroupName) if err != nil { + u.Status.RaiseLevel(resource.StatusCantChange) return nil, fmt.Errorf("group %s does not exist", u.GroupName) + } else if grp != nil { + options.Group = u.GroupName + status.AddDifference("group", fmt.Sprintf("<%s>", string(StateAbsent)), u.GroupName, "") } - options.Group = u.GroupName case u.GID != "": - _, err := user.LookupGroupId(u.GID) + grp, err := user.LookupGroupId(u.GID) if err != nil { + u.Status.RaiseLevel(resource.StatusCantChange) return nil, fmt.Errorf("group gid %s does not exist", u.GID) + } else if grp != nil { + options.Group = u.GID + status.AddDifference("gid", fmt.Sprintf("<%s>", string(StateAbsent)), u.GID, "") } - options.Group = u.GID } if u.Name != "" { options.Comment = u.Name + status.AddDifference("comment", fmt.Sprintf("<%s>", string(StateAbsent)), u.Name, "") } if u.HomeDir != "" { options.Directory = u.HomeDir + status.AddDifference("home_dir", fmt.Sprintf("<%s>", string(StateAbsent)), u.HomeDir, "") + } + + if resource.AnyChanges(u.Status.Differences) { + u.Status.RaiseLevel(resource.StatusWillChange) + } + + return options, nil +} + +// DiffMod checks for differences between the user associated with u.Username +// and the desired modifications of that user indicated by the other User +// fields. The options to be used for the modify command are set. +func (u *User) DiffMod(status *resource.Status, currUser *user.User) (*ModUserOptions, error) { + options := new(ModUserOptions) + + // Check for differences between currUser and the desired modifications + if u.NewUsername != "" { + usr, _ := user.Lookup(u.NewUsername) + if usr != nil { + u.Status.RaiseLevel(resource.StatusCantChange) + return nil, fmt.Errorf("user %s already exists", u.NewUsername) + } else { + options.Username = u.NewUsername + status.AddDifference("username", u.Username, u.NewUsername, "") + } + } + + if u.UID != "" { + usr, err := user.LookupId(u.UID) + _, uidNotFound := err.(user.UnknownUserIdError) + + if uidNotFound { + options.UID = u.UID + status.AddDifference("uid", currUser.Uid, u.UID, "") + } else if usr != nil && currUser.Uid != u.UID { + u.Status.RaiseLevel(resource.StatusCantChange) + return nil, fmt.Errorf("uid %s already exists", u.UID) + } + } + + switch { + case u.GroupName != "": + grp, err := user.LookupGroup(u.GroupName) + if err != nil { + u.Status.RaiseLevel(resource.StatusCantChange) + return nil, fmt.Errorf("group %s does not exist", u.GroupName) + } else if grp != nil && currUser.Gid != grp.Gid { + currGroup, err := user.LookupGroupId(currUser.Gid) + if err != nil { + u.Status.RaiseLevel(resource.StatusCantChange) + return nil, fmt.Errorf("group gid %s does not exist", currUser.Gid) + } + options.Group = u.GroupName + status.AddDifference("group", currGroup.Name, u.GroupName, "") + } + case u.GID != "": + grp, err := user.LookupGroupId(u.GID) + if err != nil { + u.Status.RaiseLevel(resource.StatusCantChange) + return nil, fmt.Errorf("group gid %s does not exist", u.GID) + } else if grp != nil && currUser.Gid != u.GID { + options.Group = u.GID + status.AddDifference("gid", currUser.Gid, u.GID, "") + } + } + + if u.Name != "" { + if currUser.Name != u.Name { + options.Comment = u.Name + status.AddDifference("comment", currUser.Name, u.Name, "") + } + } + + if u.HomeDir != "" { + if currUser.HomeDir != u.HomeDir { + options.Directory = u.HomeDir + status.AddDifference("home_dir", currUser.HomeDir, u.HomeDir, "") + if u.MoveDir { + options.MoveDir = true + status.AddDifference("home_dir contents", currUser.HomeDir, u.HomeDir, "") + } + } + } + + if resource.AnyChanges(u.Status.Differences) { + u.Status.RaiseLevel(resource.StatusWillChange) } return options, nil diff --git a/resource/user/user_default.go b/resource/user/user_default.go index fa23de6bd..c44f1d511 100644 --- a/resource/user/user_default.go +++ b/resource/user/user_default.go @@ -33,6 +33,11 @@ func (s *System) DelUser(userName string) error { return ErrUnsupported } +// ModUser implementation for systems which are not supported +func (s *System) ModUser(userName string, options *ModUserOptions) error { + return ErrUnsupported +} + // Lookup implementation for systems which are not supported func (s *System) Lookup(userName string) (*user.User, error) { return nil, ErrUnsupported diff --git a/resource/user/user_linux.go b/resource/user/user_linux.go index b2202d6aa..3ead8a422 100644 --- a/resource/user/user_linux.go +++ b/resource/user/user_linux.go @@ -59,6 +59,36 @@ func (s *System) DelUser(userName string) error { return nil } +// ModUser modifies a user +func (s *System) ModUser(userName string, options *ModUserOptions) error { + args := []string{userName} + if options.Username != "" { + args = append(args, "-l", options.Username) + } + if options.UID != "" { + args = append(args, "-u", options.UID) + } + if options.Group != "" { + args = append(args, "-g", options.Group) + } + if options.Comment != "" { + args = append(args, "-c", options.Comment) + } + if options.Directory != "" { + args = append(args, "-d", options.Directory) + if options.MoveDir { + args = append(args, "-m") + } + } + + cmd := exec.Command("usermod", args...) + err := cmd.Run() + if err != nil { + return fmt.Errorf("usermod: %s", err) + } + return nil +} + // Lookup looks up a user by name // If the user cannot be found an error is returned func (s *System) Lookup(userName string) (*user.User, error) { diff --git a/resource/user/user_test.go b/resource/user/user_test.go index 493f585bc..4431a847d 100644 --- a/resource/user/user_test.go +++ b/resource/user/user_test.go @@ -32,22 +32,24 @@ import ( ) var ( - currUser *os.User - currUsername string - currUID string - currGroup *os.Group - currGroupName string - currGID string - userErr error - groupErr error - tempUsername []string - fakeUsername string - fakeUID string - tempGroupName []string - fakeGroupName string - fakeGID string - gidErr error - uidErr error + currUser *os.User + currUsername string + currUID string + currGroup *os.Group + currGroupName string + currGID string + existingGroup *os.Group + existingGroupName string + existingGID string + existingUser *os.User + existingUID string + tempUsername []string + fakeUsername string + fakeUID string + tempGroupName []string + fakeGroupName string + fakeGID string + err error ) const ( @@ -69,34 +71,53 @@ const ( ) func init() { - currUser, userErr = os.Current() - if userErr != nil { - panic(userErr) + currUser, err = os.Current() + if err != nil { + panic(err) } currUsername = currUser.Username currUID = currUser.Uid currGID = currUser.Gid - currGroup, groupErr = os.LookupGroupId(currGID) - if groupErr != nil { - panic(groupErr) + currGroup, err = os.LookupGroupId(currGID) + if err != nil { + panic(err) } + currGroupName = currGroup.Name - fakeUID, uidErr = setFakeUid() - if uidErr != nil { - panic(uidErr) + fakeUID, err = setFakeUid() + if err != nil { + panic(err) } - fakeGID, gidErr = setFakeGid() - if gidErr != nil { - panic(gidErr) + fakeGID, err = setFakeGid() + if err != nil { + panic(err) } - currUsername = currUser.Username tempUsername = strings.Split(uuid.NewV4().String(), "-") fakeUsername = strings.Join(tempUsername[0:], "") tempGroupName = strings.Split(uuid.NewV4().String(), "-") fakeGroupName = strings.Join(tempUsername[0:], "") + + existingGID, err = setGid() + if err != nil { + panic(err) + } + existingGroup, err = os.LookupGroupId(existingGID) + if err != nil { + panic(err) + } + existingGroupName = existingGroup.Name + + existingUID, err = setUid() + if err != nil { + panic(err) + } + existingUser, err = os.LookupId(existingUID) + if err != nil { + panic(err) + } } // TestUserInterface tests that User is properly implemented @@ -114,213 +135,61 @@ func TestCheck(t *testing.T) { u := user.NewUser(new(user.System)) u.State = user.StatePresent - t.Run("uid not provided", func(t *testing.T) { - t.Run("no add-user already exists", func(t *testing.T) { - u.Username = currUsername - status, err := u.Check(fakerenderer.New()) - - if runtime.GOOS == "linux" { - assert.NoError(t, err) - assert.Equal(t, resource.StatusNoChange, status.StatusCode()) - assert.Equal(t, fmt.Sprintf("user %s already exists", u.Username), status.Messages()[0]) - assert.False(t, status.HasChanges()) - } else { - assert.EqualError(t, err, "user: not supported on this system") - } - }) - + t.Run("add tests", func(t *testing.T) { t.Run("add user", func(t *testing.T) { u.Username = fakeUsername status, err := u.Check(fakerenderer.New()) if runtime.GOOS == "linux" { assert.NoError(t, err) - assert.Equal(t, "user does not exist", status.Messages()[0]) + assert.Equal(t, "add user", status.Messages()[0]) assert.Equal(t, resource.StatusWillChange, status.StatusCode()) - assert.Equal(t, string(user.StateAbsent), status.Diffs()["user"].Original()) - assert.Equal(t, fmt.Sprintf("user %s", u.Username), status.Diffs()["user"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, status.Diffs()["username"].Current()) assert.True(t, status.HasChanges()) } else { assert.EqualError(t, err, "user: not supported on this system") } }) - t.Run("group provided", func(t *testing.T) { - t.Run("no add-group name does not exist", func(t *testing.T) { - u.Username = fakeUsername - u.GroupName = fakeGroupName - status, err := u.Check(fakerenderer.New()) - - if runtime.GOOS == "linux" { - assert.EqualError(t, err, fmt.Sprintf("cannot add user %s: group %s does not exist", u.Username, u.GroupName)) - assert.Equal(t, resource.StatusCantChange, status.StatusCode()) - assert.True(t, status.HasChanges()) - } else { - assert.EqualError(t, err, "user: not supported on this system") - } - }) - - t.Run("add user with group name", func(t *testing.T) { - u.Username = fakeUsername - u.GroupName = currGroupName - status, err := u.Check(fakerenderer.New()) - - if runtime.GOOS == "linux" { - assert.NoError(t, err) - assert.Equal(t, "user does not exist", status.Messages()[0]) - assert.Equal(t, resource.StatusWillChange, status.StatusCode()) - assert.Equal(t, string(user.StateAbsent), status.Diffs()["user"].Original()) - assert.Equal(t, fmt.Sprintf("user %s", u.Username), status.Diffs()["user"].Current()) - assert.True(t, status.HasChanges()) - } else { - assert.EqualError(t, err, "user: not supported on this system") - } - }) - - t.Run("no add-group gid does not exist", func(t *testing.T) { - u.Username = fakeUsername - u.GID = fakeGID - status, err := u.Check(fakerenderer.New()) - - if runtime.GOOS == "linux" { - assert.EqualError(t, err, fmt.Sprintf("cannot add user %s: group gid %s does not exist", u.Username, u.GID)) - assert.Equal(t, resource.StatusCantChange, status.StatusCode()) - assert.True(t, status.HasChanges()) - } else { - assert.EqualError(t, err, "user: not supported on this system") - } - }) - - t.Run("add user with group gid", func(t *testing.T) { - u.Username = fakeUsername - u.GID = currGID - status, err := u.Check(fakerenderer.New()) - - if runtime.GOOS == "linux" { - assert.NoError(t, err) - assert.Equal(t, "user does not exist", status.Messages()[0]) - assert.Equal(t, resource.StatusWillChange, status.StatusCode()) - assert.Equal(t, string(user.StateAbsent), status.Diffs()["user"].Original()) - assert.Equal(t, fmt.Sprintf("user %s", u.Username), status.Diffs()["user"].Current()) - assert.True(t, status.HasChanges()) - } else { - assert.EqualError(t, err, "user: not supported on this system") - } - }) - }) - }) - - t.Run("uid provided", func(t *testing.T) { - t.Run("add user with uid", func(t *testing.T) { + t.Run("cannot add user", func(t *testing.T) { u.Username = fakeUsername - u.UID = fakeUID + u.GroupName = fakeGroupName status, err := u.Check(fakerenderer.New()) if runtime.GOOS == "linux" { - assert.NoError(t, err) - assert.Equal(t, resource.StatusWillChange, status.StatusCode()) - assert.Equal(t, fmt.Sprintf("user name and uid do not exist"), status.Messages()[0]) - assert.Equal(t, string(user.StateAbsent), status.Diffs()["user"].Original()) - assert.Equal(t, fmt.Sprintf("user %s with uid %s", u.Username, u.UID), status.Diffs()["user"].Current()) + assert.EqualError(t, err, fmt.Sprintf("cannot add user %s: group %s does not exist", u.Username, u.GroupName)) + assert.Equal(t, resource.StatusCantChange, status.StatusCode()) assert.True(t, status.HasChanges()) } else { assert.EqualError(t, err, "user: not supported on this system") } }) + }) - t.Run("group provided", func(t *testing.T) { - t.Run("no add-group name does not exist", func(t *testing.T) { - u.Username = fakeUsername - u.UID = fakeUID - u.GroupName = fakeGroupName - status, err := u.Check(fakerenderer.New()) - - if runtime.GOOS == "linux" { - assert.EqualError(t, err, fmt.Sprintf("cannot add user %s with uid %s: group %s does not exist", u.Username, u.UID, u.GroupName)) - assert.Equal(t, resource.StatusCantChange, status.StatusCode()) - assert.True(t, status.HasChanges()) - } else { - assert.EqualError(t, err, "user: not supported on this system") - } - }) - - t.Run("add user with group name", func(t *testing.T) { - u.Username = fakeUsername - u.UID = fakeUID - u.GroupName = currGroupName - status, err := u.Check(fakerenderer.New()) - - if runtime.GOOS == "linux" { - assert.NoError(t, err) - assert.Equal(t, resource.StatusWillChange, status.StatusCode()) - assert.Equal(t, fmt.Sprintf("user name and uid do not exist"), status.Messages()[0]) - assert.Equal(t, string(user.StateAbsent), status.Diffs()["user"].Original()) - assert.Equal(t, fmt.Sprintf("user %s with uid %s", u.Username, u.UID), status.Diffs()["user"].Current()) - assert.True(t, status.HasChanges()) - } else { - assert.EqualError(t, err, "user: not supported on this system") - } - }) - - t.Run("no add-group gid does not exist", func(t *testing.T) { - u.Username = fakeUsername - u.UID = fakeUID - u.GID = fakeGID - status, err := u.Check(fakerenderer.New()) - - if runtime.GOOS == "linux" { - assert.EqualError(t, err, fmt.Sprintf("cannot add user %s with uid %s: group gid %s does not exist", u.Username, u.UID, u.GID)) - assert.Equal(t, resource.StatusCantChange, status.StatusCode()) - assert.True(t, status.HasChanges()) - } else { - assert.EqualError(t, err, "user: not supported on this system") - } - }) - - t.Run("add user with group gid", func(t *testing.T) { - gid, err := setGid() - if err != nil { - panic(err) - } - u.Username = fakeUsername - u.UID = fakeUID - u.GID = gid - status, err := u.Check(fakerenderer.New()) - - if runtime.GOOS == "linux" { - assert.NoError(t, err) - assert.Equal(t, resource.StatusWillChange, status.StatusCode()) - assert.Equal(t, fmt.Sprintf("user name and uid do not exist"), status.Messages()[0]) - assert.Equal(t, string(user.StateAbsent), status.Diffs()["user"].Original()) - assert.Equal(t, fmt.Sprintf("user %s with uid %s", u.Username, u.UID), status.Diffs()["user"].Current()) - assert.True(t, status.HasChanges()) - } else { - assert.EqualError(t, err, "user: not supported on this system") - } - }) - }) - - t.Run("no add-user uid already exists", func(t *testing.T) { - u.Username = fakeUsername - u.UID = currUID + t.Run("modify tests", func(t *testing.T) { + t.Run("no modifications", func(t *testing.T) { + u.Username = currUsername + u.GroupName = "" // clear this field set from previous t.Run status, err := u.Check(fakerenderer.New()) if runtime.GOOS == "linux" { - assert.EqualError(t, err, fmt.Sprintf("cannot add user %s with uid %s: user uid already exists", u.Username, u.UID)) - assert.Equal(t, resource.StatusCantChange, status.StatusCode()) - assert.True(t, status.HasChanges()) + assert.NoError(t, err) + assert.Equal(t, resource.StatusNoChange, status.StatusCode()) + assert.Equal(t, fmt.Sprintf("no modifications indicated for user %s", u.Username), status.Messages()[0]) + assert.False(t, status.HasChanges()) } else { assert.EqualError(t, err, "user: not supported on this system") } }) - t.Run("no add-user name already exists", func(t *testing.T) { + t.Run("cannot modify user", func(t *testing.T) { u.Username = currUsername - u.UID = fakeUID + u.GroupName = fakeGroupName status, err := u.Check(fakerenderer.New()) if runtime.GOOS == "linux" { - assert.EqualError(t, err, fmt.Sprintf("cannot add user %s with uid %s: user already exists", u.Username, u.UID)) + assert.EqualError(t, err, fmt.Sprintf("cannot modify user %s: group %s does not exist", u.Username, u.GroupName)) assert.Equal(t, resource.StatusCantChange, status.StatusCode()) assert.True(t, status.HasChanges()) } else { @@ -328,18 +197,18 @@ func TestCheck(t *testing.T) { } }) - t.Run("no add-user name and uid belong to different users", func(t *testing.T) { - uid, err := setUid() - if err != nil { - panic(err) - } + t.Run("modify user", func(t *testing.T) { u.Username = currUsername - u.UID = uid + u.NewUsername = fakeUsername + u.GroupName = "" // clear this field set from previous t.Run status, err := u.Check(fakerenderer.New()) if runtime.GOOS == "linux" { - assert.EqualError(t, err, fmt.Sprintf("cannot add user %s with uid %s: user and uid belong to different users", u.Username, u.UID)) - assert.Equal(t, resource.StatusCantChange, status.StatusCode()) + assert.NoError(t, err) + assert.Equal(t, resource.StatusWillChange, status.StatusCode()) + assert.Equal(t, "modify user", status.Messages()[0]) + assert.Equal(t, u.Username, status.Diffs()["username"].Original()) + assert.Equal(t, u.NewUsername, status.Diffs()["username"].Current()) assert.True(t, status.HasChanges()) } else { assert.EqualError(t, err, "user: not supported on this system") @@ -375,7 +244,7 @@ func TestCheck(t *testing.T) { assert.NoError(t, err) assert.Equal(t, resource.StatusWillChange, status.StatusCode()) assert.Equal(t, fmt.Sprintf("user %s", u.Username), status.Diffs()["user"].Original()) - assert.Equal(t, string(user.StateAbsent), status.Diffs()["user"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), status.Diffs()["user"].Current()) assert.True(t, status.HasChanges()) } else { assert.EqualError(t, err, "user: not supported on this system") @@ -428,12 +297,8 @@ func TestCheck(t *testing.T) { }) t.Run("no delete-user name and uid belong to different users", func(t *testing.T) { - uid, err := setUid() - if err != nil { - panic(err) - } u.Username = currUsername - u.UID = uid + u.UID = existingUID status, err := u.Check(fakerenderer.New()) if runtime.GOOS == "linux" { @@ -454,7 +319,7 @@ func TestCheck(t *testing.T) { assert.NoError(t, err) assert.Equal(t, resource.StatusWillChange, status.StatusCode()) assert.Equal(t, fmt.Sprintf("user %s with uid %s", u.Username, u.UID), status.Diffs()["user"].Original()) - assert.Equal(t, string(user.StateAbsent), status.Diffs()["user"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), status.Diffs()["user"].Current()) assert.True(t, status.HasChanges()) } else { assert.EqualError(t, err, "user: not supported on this system") @@ -485,20 +350,20 @@ func TestApply(t *testing.T) { t.Parallel() t.Run("state=present", func(t *testing.T) { - t.Run("uid not provided", func(t *testing.T) { + t.Run("add tests", func(t *testing.T) { t.Run("add user", func(t *testing.T) { usr := &os.User{ Username: fakeUsername, } m := &MockSystem{} - o := &MockOptions{} + d := &MockDiff{} u := user.NewUser(m) u.Username = usr.Username u.State = user.StatePresent options := user.AddUserOptions{} m.On("Lookup", u.Username).Return(usr, os.UnknownUserError("")) - o.On("SetAddUserOptions", u).Return(options, nil) + d.On("DiffAdd", u.Status).Return(options, nil) m.On("AddUser", u.Username, &options).Return(nil) status, err := u.Apply() @@ -507,7 +372,7 @@ func TestApply(t *testing.T) { assert.Equal(t, fmt.Sprintf("added user %s", u.Username), status.Messages()[0]) }) - t.Run("no add-group name does not exist", func(t *testing.T) { + t.Run("will not attempt to add", func(t *testing.T) { usr := &os.User{ Username: fakeUsername, } @@ -515,7 +380,7 @@ func TestApply(t *testing.T) { Name: fakeGroupName, } m := &MockSystem{} - o := &MockOptions{} + d := &MockDiff{} u := user.NewUser(m) u.Username = usr.Username u.GroupName = grp.Name @@ -525,34 +390,7 @@ func TestApply(t *testing.T) { m.On("Lookup", u.Username).Return(usr, os.UnknownUserError("")) m.On("LookupGroup", u.GroupName).Return(grp, os.UnknownGroupError("")) - o.On("SetAddUserOptions", u).Return(nil, optErr) - m.On("AddUser", u.Username, &options).Return(nil) - status, err := u.Apply() - - m.AssertNotCalled(t, "AddUser", u.Username, &options) - assert.EqualError(t, err, fmt.Sprintf("will not attempt to add user %s: %s", u.Username, optErr)) - assert.Equal(t, resource.StatusCantChange, status.StatusCode()) - }) - - t.Run("no add-group gid does not exist", func(t *testing.T) { - usr := &os.User{ - Username: fakeUsername, - } - grp := &os.Group{ - Gid: fakeGID, - } - m := &MockSystem{} - o := &MockOptions{} - u := user.NewUser(m) - u.Username = usr.Username - u.GID = grp.Gid - u.State = user.StatePresent - options := user.AddUserOptions{} - optErr := fmt.Sprintf("group gid %s does not exist", u.GID) - - m.On("Lookup", u.Username).Return(usr, os.UnknownUserError("")) - m.On("LookupGroupID", u.GID).Return(grp, os.UnknownGroupError("")) - o.On("SetAddUserOptions", u).Return(nil, optErr) + d.On("DiffAdd", u.Status).Return(nil, optErr) m.On("AddUser", u.Username, &options).Return(nil) status, err := u.Apply() @@ -561,180 +399,100 @@ func TestApply(t *testing.T) { assert.Equal(t, resource.StatusCantChange, status.StatusCode()) }) - t.Run("no add-error adding user", func(t *testing.T) { + t.Run("error adding user", func(t *testing.T) { usr := &os.User{ Username: fakeUsername, } m := &MockSystem{} - o := &MockOptions{} + d := &MockDiff{} u := user.NewUser(m) u.Username = usr.Username u.State = user.StatePresent options := user.AddUserOptions{} m.On("Lookup", u.Username).Return(usr, os.UnknownUserError("")) - o.On("SetAddUserOptions", u).Return(options, nil) + d.On("DiffAdd", u.Status).Return(options, nil) m.On("AddUser", u.Username, &options).Return(fmt.Errorf("")) status, err := u.Apply() m.AssertCalled(t, "AddUser", u.Username, &options) - assert.EqualError(t, err, fmt.Sprintf("user add: ")) + assert.EqualError(t, err, "user add: ") assert.Equal(t, resource.StatusFatal, status.StatusCode()) assert.Equal(t, fmt.Sprintf("error adding user %s", u.Username), status.Messages()[0]) }) - - t.Run("no add-will not attempt add", func(t *testing.T) { - usr := &os.User{ - Username: fakeUsername, - } - m := &MockSystem{} - o := &MockOptions{} - u := user.NewUser(m) - u.Username = usr.Username - u.State = user.StatePresent - options := user.AddUserOptions{} - - m.On("Lookup", u.Username).Return(usr, nil) - o.On("SetAddUserOptions", u).Return(options, nil) - m.On("AddUser", u.Username, &options).Return(nil) - status, err := u.Apply() - - m.AssertNotCalled(t, "AddUser", u.Username, &options) - assert.EqualError(t, err, fmt.Sprintf("will not attempt to add user %s", u.Username)) - assert.Equal(t, resource.StatusCantChange, status.StatusCode()) - }) }) - t.Run("uid provided", func(t *testing.T) { - t.Run("add user with uid", func(t *testing.T) { + t.Run("modify tests", func(t *testing.T) { + t.Run("modify user", func(t *testing.T) { usr := &os.User{ - Username: fakeUsername, - Uid: fakeUID, + Username: currUsername, } m := &MockSystem{} - o := &MockOptions{} + d := &MockDiff{} u := user.NewUser(m) u.Username = usr.Username - u.UID = usr.Uid + u.Name = "test" u.State = user.StatePresent - options := user.AddUserOptions{UID: u.UID} + options := user.ModUserOptions{Comment: u.Name} - m.On("Lookup", u.Username).Return(usr, os.UnknownUserError("")) - m.On("LookupID", u.UID).Return(usr, os.UnknownUserIdError(1)) - o.On("SetAddUserOptions", u).Return(options, nil) - m.On("AddUser", u.Username, &options).Return(nil) + m.On("Lookup", u.Username).Return(usr, nil) + d.On("DiffMod", u.Status, currUser).Return(options, nil) + m.On("ModUser", u.Username, &options).Return(nil) status, err := u.Apply() - m.AssertCalled(t, "AddUser", u.Username, &options) + m.AssertCalled(t, "ModUser", u.Username, &options) assert.NoError(t, err) - assert.Equal(t, fmt.Sprintf("added user %s with uid %s", u.Username, u.UID), status.Messages()[0]) + assert.Equal(t, fmt.Sprintf("modified user %s", u.Username), status.Messages()[0]) }) - t.Run("no add-group name does not exist", func(t *testing.T) { + t.Run("will not attempt to modify", func(t *testing.T) { usr := &os.User{ - Username: fakeUsername, - Uid: fakeUID, + Username: currUsername, } grp := &os.Group{ Name: fakeGroupName, } m := &MockSystem{} - o := &MockOptions{} + d := &MockDiff{} u := user.NewUser(m) u.Username = usr.Username u.GroupName = grp.Name u.State = user.StatePresent - options := user.AddUserOptions{} + options := user.ModUserOptions{} optErr := fmt.Sprintf("group %s does not exist", u.GroupName) - m.On("Lookup", u.Username).Return(usr, os.UnknownUserError("")) - m.On("LookupID", u.UID).Return(usr, os.UnknownUserIdError(1)) + m.On("Lookup", u.Username).Return(usr, nil) m.On("LookupGroup", u.GroupName).Return(grp, os.UnknownGroupError("")) - o.On("SetAddUserOptions", u).Return(nil, optErr) - m.On("AddUser", u.Username, &options).Return(nil) - status, err := u.Apply() - - m.AssertNotCalled(t, "AddUser", u.Username, &options) - assert.EqualError(t, err, fmt.Sprintf("will not attempt to add user %s: %s", u.Username, optErr)) - assert.Equal(t, resource.StatusCantChange, status.StatusCode()) - }) - - t.Run("no add-group gid does not exist", func(t *testing.T) { - usr := &os.User{ - Username: fakeUsername, - Uid: fakeUID, - } - grp := &os.Group{ - Gid: fakeGID, - } - m := &MockSystem{} - o := &MockOptions{} - u := user.NewUser(m) - u.Username = usr.Username - u.GID = grp.Gid - u.State = user.StatePresent - options := user.AddUserOptions{} - optErr := fmt.Sprintf("group gid %s does not exist", u.GID) - - m.On("Lookup", u.Username).Return(usr, os.UnknownUserError("")) - m.On("LookupID", u.UID).Return(usr, os.UnknownUserIdError(1)) - m.On("LookupGroupID", u.GID).Return(grp, os.UnknownGroupError("")) - o.On("SetAddUserOptions", u).Return(nil, optErr) - m.On("AddUser", u.Username, &options).Return(nil) + d.On("DiffMod", u.Status, currUser).Return(nil, optErr) + m.On("ModUser", u.Username, &options).Return(nil) status, err := u.Apply() - m.AssertNotCalled(t, "AddUser", u.Username, &options) - assert.EqualError(t, err, fmt.Sprintf("will not attempt to add user %s: %s", u.Username, optErr)) + m.AssertNotCalled(t, "ModUser", u.Username, &options) + assert.EqualError(t, err, fmt.Sprintf("will not attempt to modify user %s: %s", u.Username, optErr)) assert.Equal(t, resource.StatusCantChange, status.StatusCode()) }) - t.Run("no add-error adding user", func(t *testing.T) { - usr := &os.User{ - Username: fakeUsername, - Uid: fakeUID, - } - m := &MockSystem{} - o := &MockOptions{} - u := user.NewUser(m) - u.Username = usr.Username - u.UID = usr.Uid - u.State = user.StatePresent - options := user.AddUserOptions{UID: u.UID} - - m.On("Lookup", u.Username).Return(usr, os.UnknownUserError("")) - m.On("LookupID", u.UID).Return(usr, os.UnknownUserIdError(1)) - o.On("SetAddUserOptions", u).Return(options, nil) - m.On("AddUser", u.Username, &options).Return(fmt.Errorf("")) - status, err := u.Apply() - - m.AssertCalled(t, "AddUser", u.Username, &options) - assert.EqualError(t, err, fmt.Sprintf("user add: ")) - assert.Equal(t, resource.StatusFatal, status.StatusCode()) - assert.Equal(t, fmt.Sprintf("error adding user %s with uid %s", u.Username, u.UID), status.Messages()[0]) - }) - - t.Run("no add-will not attempt add", func(t *testing.T) { + t.Run("error modifying user", func(t *testing.T) { usr := &os.User{ - Username: fakeUsername, - Uid: fakeUID, + Username: currUsername, } m := &MockSystem{} - o := &MockOptions{} + d := &MockDiff{} u := user.NewUser(m) u.Username = usr.Username - u.UID = usr.Uid + u.Name = "test" u.State = user.StatePresent - options := user.AddUserOptions{UID: u.UID} + options := user.ModUserOptions{Comment: u.Name} m.On("Lookup", u.Username).Return(usr, nil) - m.On("LookupID", u.UID).Return(usr, nil) - o.On("SetAddUserOptions", u).Return(options, nil) - m.On("AddUser", u.Username, &options).Return(nil) + d.On("DiffMod", u.Status, currUser).Return(options, nil) + m.On("ModUser", u.Username, &options).Return(fmt.Errorf("")) status, err := u.Apply() - m.AssertNotCalled(t, "AddUser", u.Username, &options) - assert.EqualError(t, err, fmt.Sprintf("will not attempt to add user %s with uid %s", u.Username, u.UID)) - assert.Equal(t, resource.StatusCantChange, status.StatusCode()) + m.AssertCalled(t, "ModUser", u.Username, &options) + assert.EqualError(t, err, "user modify: ") + assert.Equal(t, resource.StatusFatal, status.StatusCode()) + assert.Equal(t, fmt.Sprintf("error modifying user %s", u.Username), status.Messages()[0]) }) }) }) @@ -773,7 +531,7 @@ func TestApply(t *testing.T) { status, err := u.Apply() m.AssertCalled(t, "DelUser", u.Username) - assert.EqualError(t, err, fmt.Sprintf("user delete: ")) + assert.EqualError(t, err, "user delete: ") assert.Equal(t, resource.StatusFatal, status.StatusCode()) assert.Equal(t, fmt.Sprintf("error deleting user %s", u.Username), status.Messages()[0]) }) @@ -836,7 +594,7 @@ func TestApply(t *testing.T) { status, err := u.Apply() m.AssertCalled(t, "DelUser", u.Username) - assert.EqualError(t, err, fmt.Sprintf("user delete: ")) + assert.EqualError(t, err, "user delete: ") assert.Equal(t, resource.StatusFatal, status.StatusCode()) assert.Equal(t, fmt.Sprintf("error deleting user %s with uid %s", u.Username, u.UID), status.Messages()[0]) }) @@ -870,7 +628,7 @@ func TestApply(t *testing.T) { Uid: fakeUID, } m := &MockSystem{} - o := &MockOptions{} + d := &MockDiff{} u := user.NewUser(m) u.Username = usr.Username u.UID = usr.Uid @@ -879,12 +637,12 @@ func TestApply(t *testing.T) { m.On("Lookup", u.Username).Return(usr, nil) m.On("LookupID", u.UID).Return(usr, nil) - o.On("SetAddUserOptions", u).Return(options, nil) + d.On("DiffAdd", u.Status).Return(options, nil) m.On("AddUser", u.Username, &options).Return(nil) m.On("DelUser", u.Username).Return(nil) status, err := u.Apply() - o.AssertNotCalled(t, "SetAddUserOptions", u) + d.AssertNotCalled(t, "DiffAdd", u) m.AssertNotCalled(t, "AddUser", u.Username, &options) m.AssertNotCalled(t, "DelUser", u.Username) assert.EqualError(t, err, fmt.Sprintf("user: unrecognized state %s", u.State)) @@ -892,105 +650,539 @@ func TestApply(t *testing.T) { }) } -// TestSetAddUserOptions tests options provided for adding a user -// are properly set -func TestSetAddUserOptions(t *testing.T) { +// TestDiffAdd tests DiffAdd for user +func TestDiffAdd(t *testing.T) { t.Parallel() - gid, err := setGid() - if err != nil { - panic(err) - } - grp, err := os.LookupGroupId(gid) - if err != nil { - panic(err) - } - t.Run("all options", func(t *testing.T) { + t.Run("set all options", func(t *testing.T) { u := user.NewUser(new(user.System)) u.Username = fakeUsername u.UID = fakeUID - u.GID = gid + u.GroupName = existingGroupName u.Name = "test" - u.HomeDir = "testDir" + u.HomeDir = "/tmp/test" + u.Status = resource.NewStatus() + + expected := &user.AddUserOptions{ + UID: u.UID, + Group: u.GroupName, + Comment: u.Name, + Directory: u.HomeDir, + } - options, err := user.SetAddUserOptions(u) + options, err := u.DiffAdd(u.Status) assert.NoError(t, err) - assert.Equal(t, u.UID, options.UID) - assert.Equal(t, u.GID, options.Group) - assert.Equal(t, u.Name, options.Comment) - assert.Equal(t, u.HomeDir, options.Directory) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["group"].Original()) + assert.Equal(t, u.GroupName, u.Status.Diffs()["group"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["uid"].Original()) + assert.Equal(t, u.UID, u.Status.Diffs()["uid"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["comment"].Original()) + assert.Equal(t, u.Name, u.Status.Diffs()["comment"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["home_dir"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir"].Current()) }) - t.Run("group options", func(t *testing.T) { - t.Run("group name and gid", func(t *testing.T) { + t.Run("uid", func(t *testing.T) { + t.Run("uid not found", func(t *testing.T) { u := user.NewUser(new(user.System)) u.Username = fakeUsername - u.GroupName = grp.Name - u.GID = gid + u.UID = fakeUID + u.Status = resource.NewStatus() + + expected := &user.AddUserOptions{ + UID: u.UID, + } - options, err := user.SetAddUserOptions(u) + options, err := u.DiffAdd(u.Status) assert.NoError(t, err) - assert.Equal(t, u.GroupName, options.Group) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["uid"].Original()) + assert.Equal(t, u.UID, u.Status.Diffs()["uid"].Current()) }) - t.Run("with group name", func(t *testing.T) { + t.Run("error-uid found", func(t *testing.T) { u := user.NewUser(new(user.System)) u.Username = fakeUsername - u.GroupName = grp.Name + u.UID = currUID + u.Status = resource.NewStatus() + + options, err := u.DiffAdd(u.Status) + + assert.EqualError(t, err, fmt.Sprintf("uid %s already exists", u.UID)) + assert.Nil(t, options) + assert.Equal(t, resource.StatusCantChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + }) + }) + + t.Run("group", func(t *testing.T) { + t.Run("with groupname", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = fakeUsername + u.GroupName = existingGroupName + u.Status = resource.NewStatus() + + expected := &user.AddUserOptions{ + Group: u.GroupName, + } - options, err := user.SetAddUserOptions(u) + options, err := u.DiffAdd(u.Status) assert.NoError(t, err) - assert.Equal(t, u.GroupName, options.Group) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["group"].Original()) + assert.Equal(t, u.GroupName, u.Status.Diffs()["group"].Current()) }) - t.Run("group name not found", func(t *testing.T) { + t.Run("error-groupname not found", func(t *testing.T) { u := user.NewUser(new(user.System)) u.Username = fakeUsername u.GroupName = fakeGroupName + u.Status = resource.NewStatus() - options, err := user.SetAddUserOptions(u) + options, err := u.DiffAdd(u.Status) assert.EqualError(t, err, fmt.Sprintf("group %s does not exist", u.GroupName)) assert.Nil(t, options) + assert.Equal(t, resource.StatusCantChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) }) - t.Run("with group gid", func(t *testing.T) { + t.Run("with gid", func(t *testing.T) { u := user.NewUser(new(user.System)) u.Username = fakeUsername - u.GID = gid + u.GID = existingGID + u.Status = resource.NewStatus() - options, err := user.SetAddUserOptions(u) + expected := &user.AddUserOptions{ + Group: u.GID, + } + + options, err := u.DiffAdd(u.Status) assert.NoError(t, err) - assert.Equal(t, u.GID, options.Group) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["gid"].Original()) + assert.Equal(t, u.GID, u.Status.Diffs()["gid"].Current()) }) - t.Run("group gid not found", func(t *testing.T) { + t.Run("error-gid not found", func(t *testing.T) { u := user.NewUser(new(user.System)) u.Username = fakeUsername u.GID = fakeGID + u.Status = resource.NewStatus() - options, err := user.SetAddUserOptions(u) + options, err := u.DiffAdd(u.Status) assert.EqualError(t, err, fmt.Sprintf("group gid %s does not exist", u.GID)) assert.Nil(t, options) + assert.Equal(t, resource.StatusCantChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + }) + + t.Run("user with groupname and gid", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = fakeUsername + u.GroupName = existingGroupName + u.GID = existingGID + u.Status = resource.NewStatus() + + expected := &user.AddUserOptions{ + Group: u.GroupName, + } + + options, err := u.DiffAdd(u.Status) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["group"].Original()) + assert.Equal(t, u.GroupName, u.Status.Diffs()["group"].Current()) }) }) + t.Run("comment", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = fakeUsername + u.Name = "test" + u.Status = resource.NewStatus() + + expected := &user.AddUserOptions{ + Comment: u.Name, + } + + options, err := u.DiffAdd(u.Status) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["comment"].Original()) + assert.Equal(t, u.Name, u.Status.Diffs()["comment"].Current()) + }) + + t.Run("directory", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = fakeUsername + u.HomeDir = "/tmp/test" + u.Status = resource.NewStatus() + + expected := &user.AddUserOptions{ + Directory: u.HomeDir, + } + + options, err := u.DiffAdd(u.Status) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["home_dir"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir"].Current()) + }) + t.Run("no options", func(t *testing.T) { u := user.NewUser(new(user.System)) u.Username = fakeUsername + u.Status = resource.NewStatus() + + expected := &user.AddUserOptions{} - options, err := user.SetAddUserOptions(u) + options, err := u.DiffAdd(u.Status) assert.NoError(t, err) - assert.Equal(t, "", options.UID) - assert.Equal(t, "", options.Group) - assert.Equal(t, "", options.Comment) - assert.Equal(t, "", options.Directory) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + }) +} + +// TestDiffMod tests DiffMod for user +func TestDiffMod(t *testing.T) { + t.Parallel() + + t.Run("set all options", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.NewUsername = fakeUsername + u.UID = fakeUID + u.GID = existingGID + u.Name = "test" + u.HomeDir = "/tmp/test" + u.MoveDir = true + u.Status = resource.NewStatus() + + expected := &user.ModUserOptions{ + Username: u.NewUsername, + UID: u.UID, + Group: u.GID, + Comment: u.Name, + Directory: u.HomeDir, + MoveDir: true, + } + + options, err := u.DiffMod(u.Status, currUser) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, currUser.Username, u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.NewUsername, u.Status.Diffs()["username"].Current()) + assert.Equal(t, currGID, u.Status.Diffs()["gid"].Original()) + assert.Equal(t, u.GID, u.Status.Diffs()["gid"].Current()) + assert.Equal(t, currUID, u.Status.Diffs()["uid"].Original()) + assert.Equal(t, u.UID, u.Status.Diffs()["uid"].Current()) + assert.Equal(t, currUser.Name, u.Status.Diffs()["comment"].Original()) + assert.Equal(t, u.Name, u.Status.Diffs()["comment"].Current()) + assert.Equal(t, currUser.HomeDir, u.Status.Diffs()["home_dir"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir"].Current()) + assert.Equal(t, currUser.HomeDir, u.Status.Diffs()["home_dir contents"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir contents"].Current()) + }) + + t.Run("username", func(t *testing.T) { + t.Run("user not found", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.NewUsername = fakeUsername + u.Status = resource.NewStatus() + + expected := &user.ModUserOptions{ + Username: u.NewUsername, + } + + options, err := u.DiffMod(u.Status, currUser) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, currUser.Username, u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.NewUsername, u.Status.Diffs()["username"].Current()) + }) + + t.Run("error-user found", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.NewUsername = existingUser.Username + u.Status = resource.NewStatus() + + options, err := u.DiffMod(u.Status, currUser) + + assert.EqualError(t, err, fmt.Sprintf("user %s already exists", u.NewUsername)) + assert.Nil(t, options) + assert.Equal(t, resource.StatusCantChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + }) + }) + + t.Run("uid", func(t *testing.T) { + t.Run("uid not found", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.UID = fakeUID + u.Status = resource.NewStatus() + + expected := &user.ModUserOptions{ + UID: u.UID, + } + + options, err := u.DiffMod(u.Status, currUser) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, currUID, u.Status.Diffs()["uid"].Original()) + assert.Equal(t, u.UID, u.Status.Diffs()["uid"].Current()) + }) + + t.Run("error-uid found", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.UID = existingUID + u.Status = resource.NewStatus() + + options, err := u.DiffMod(u.Status, currUser) + + assert.EqualError(t, err, fmt.Sprintf("uid %s already exists", u.UID)) + assert.Nil(t, options) + assert.Equal(t, resource.StatusCantChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + }) + + t.Run("current uid-other options", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.UID = currUID + u.Name = "test" + u.Status = resource.NewStatus() + + expected := &user.ModUserOptions{ + Comment: "test", + } + + options, err := u.DiffMod(u.Status, currUser) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, currUser.Name, u.Status.Diffs()["comment"].Original()) + assert.Equal(t, u.Name, u.Status.Diffs()["comment"].Current()) + assert.Equal(t, expected.UID, "") + }) + + t.Run("current uid-no other options", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.UID = currUID + u.Status = resource.NewStatus() + + expected := &user.ModUserOptions{} + + options, err := u.DiffMod(u.Status, currUser) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusNoChange, u.Status.StatusCode()) + assert.False(t, u.Status.HasChanges()) + }) + }) + + t.Run("group", func(t *testing.T) { + t.Run("with groupname", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.GroupName = existingGroupName + u.Status = resource.NewStatus() + + expected := &user.ModUserOptions{ + Group: u.GroupName, + } + + options, err := u.DiffMod(u.Status, currUser) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, currGroupName, u.Status.Diffs()["group"].Original()) + assert.Equal(t, u.GroupName, u.Status.Diffs()["group"].Current()) + }) + + t.Run("error-groupname not found", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.GroupName = fakeGroupName + u.Status = resource.NewStatus() + + options, err := u.DiffMod(u.Status, currUser) + + assert.EqualError(t, err, fmt.Sprintf("group %s does not exist", u.GroupName)) + assert.Nil(t, options) + assert.Equal(t, resource.StatusCantChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + }) + + t.Run("with gid", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.GID = existingGID + u.Status = resource.NewStatus() + + expected := &user.ModUserOptions{ + Group: u.GID, + } + + options, err := u.DiffMod(u.Status, currUser) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, currGID, u.Status.Diffs()["gid"].Original()) + assert.Equal(t, u.GID, u.Status.Diffs()["gid"].Current()) + }) + + t.Run("error-gid not found", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.GID = fakeGID + u.Status = resource.NewStatus() + + options, err := u.DiffMod(u.Status, currUser) + + assert.EqualError(t, err, fmt.Sprintf("group gid %s does not exist", u.GID)) + assert.Nil(t, options) + assert.Equal(t, resource.StatusCantChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + }) + + t.Run("user with groupname and gid", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.GroupName = existingGroupName + u.GID = existingGID + u.Status = resource.NewStatus() + + expected := &user.ModUserOptions{ + Group: u.GroupName, + } + + options, err := u.DiffMod(u.Status, currUser) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, currGroupName, u.Status.Diffs()["group"].Original()) + assert.Equal(t, u.GroupName, u.Status.Diffs()["group"].Current()) + }) + }) + + t.Run("comment", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.Name = "test" + u.Status = resource.NewStatus() + + expected := &user.ModUserOptions{ + Comment: u.Name, + } + + options, err := u.DiffMod(u.Status, currUser) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, currUser.Name, u.Status.Diffs()["comment"].Original()) + assert.Equal(t, u.Name, u.Status.Diffs()["comment"].Current()) + }) + + t.Run("directory", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.HomeDir = "/tmp/test" + u.Status = resource.NewStatus() + + expected := &user.ModUserOptions{ + Directory: u.HomeDir, + } + + options, err := u.DiffMod(u.Status, currUser) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, currUser.HomeDir, u.Status.Diffs()["home_dir"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir"].Current()) + }) + + t.Run("no options", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = currUsername + u.Status = resource.NewStatus() + + expected := &user.ModUserOptions{} + + options, err := u.DiffMod(u.Status, currUser) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusNoChange, u.Status.StatusCode()) + assert.False(t, u.Status.HasChanges()) }) } @@ -1019,12 +1211,13 @@ func setFakeUid() (string, error) { return "", fmt.Errorf("setFakeUid: could not set uid") } -// setGid is used to find a gid that exists. +// setGid is used to find a gid that exists, but is not +// the gid for the current user. func setGid() (string, error) { for i := 0; i <= maxGID; i++ { gid := strconv.Itoa(i) _, err := os.LookupGroupId(gid) - if err == nil { + if err == nil && gid != currGID { return gid, nil } } @@ -1043,17 +1236,23 @@ func setFakeGid() (string, error) { return "", fmt.Errorf("setFakeGid: could not set gid") } -// MockOptions is a mock implementation for setting user options -type MockOptions struct { +// MockDiff is a mock implementation for user diffs +type MockDiff struct { mock.Mock } -// SetAddUserOptions sets the options for adding a user -func (m *MockOptions) SetAddUserOptions(u *user.User) (*user.AddUserOptions, error) { - args := m.Called(u) +// DiffAdd sets the diffs and options for adding a user +func (m *MockDiff) DiffAdd(r resource.Status) (*user.AddUserOptions, error) { + args := m.Called(r) return args.Get(0).(*user.AddUserOptions), args.Error(1) } +// DiffMod sets the diffs and options for modifying a user +func (m *MockDiff) DiffMod(r resource.Status, u *user.User) (*user.ModUserOptions, error) { + args := m.Called(r, u) + return args.Get(0).(*user.ModUserOptions), args.Error(1) +} + // MockSystem is a mock implementation of user.System type MockSystem struct { mock.Mock @@ -1071,6 +1270,12 @@ func (m *MockSystem) DelUser(name string) error { return args.Error(0) } +// ModUser modifies a user +func (m *MockSystem) ModUser(name string, options *user.ModUserOptions) error { + args := m.Called(name, options) + return args.Error(0) +} + // Lookup looks up a user by name func (m *MockSystem) Lookup(name string) (*os.User, error) { args := m.Called(name) From 6d686bea0dba568c383c0597923b62e90a84ad52 Mon Sep 17 00:00:00 2001 From: Allison Richardet Date: Tue, 25 Oct 2016 21:06:48 +0000 Subject: [PATCH 2/7] Address adding a user if a group by the same name exists --- resource/user/user.go | 11 ++++++++++- resource/user/user_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/resource/user/user.go b/resource/user/user.go index 6aa712b95..566460f97 100644 --- a/resource/user/user.go +++ b/resource/user/user.go @@ -293,7 +293,16 @@ func noOptionsSet(u *User) bool { func (u *User) DiffAdd(status *resource.Status) (*AddUserOptions, error) { options := new(AddUserOptions) - u.Status.AddDifference("username", fmt.Sprintf("<%s>", string(StateAbsent)), u.Username, "") + // if a group exists with the same name as the user being added, a groupname + // must also be indicated so the user may be added to that group + grp, _ := user.LookupGroup(u.Username) + if grp != nil && grp.Name == u.Username && u.GroupName == "" { + u.Status.RaiseLevel(resource.StatusCantChange) + u.Status.AddMessage("if you want to add this user to that group, use the groupname field") + return nil, fmt.Errorf("group %s exists", u.Username) + } else { + u.Status.AddDifference("username", fmt.Sprintf("<%s>", string(StateAbsent)), u.Username, "") + } if u.UID != "" { usr, err := user.LookupId(u.UID) diff --git a/resource/user/user_test.go b/resource/user/user_test.go index 4431a847d..4d66160da 100644 --- a/resource/user/user_test.go +++ b/resource/user/user_test.go @@ -688,6 +688,44 @@ func TestDiffAdd(t *testing.T) { assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir"].Current()) }) + t.Run("username", func(t *testing.T) { + t.Run("group exists-provide groupname", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = existingGroupName + u.GroupName = existingGroupName + u.Status = resource.NewStatus() + + expected := &user.AddUserOptions{ + Group: u.GroupName, + } + + options, err := u.DiffAdd(u.Status) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["group"].Original()) + assert.Equal(t, u.GroupName, u.Status.Diffs()["group"].Current()) + }) + + t.Run("error-group exists", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = existingGroupName + u.Status = resource.NewStatus() + + options, err := u.DiffAdd(u.Status) + + assert.EqualError(t, err, fmt.Sprintf("group %s exists", u.Username)) + assert.Nil(t, options) + assert.Equal(t, resource.StatusCantChange, u.Status.StatusCode()) + assert.Equal(t, "if you want to add this user to that group, use the groupname field", u.Status.Messages()[0]) + assert.True(t, u.Status.HasChanges()) + }) + }) + t.Run("uid", func(t *testing.T) { t.Run("uid not found", func(t *testing.T) { u := user.NewUser(new(user.System)) From a87d9cb8de1df1cbb5c6fde725803779572787d2 Mon Sep 17 00:00:00 2001 From: Allison Richardet Date: Tue, 25 Oct 2016 21:37:41 +0000 Subject: [PATCH 3/7] Address lint errors --- resource/user/user.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/resource/user/user.go b/resource/user/user.go index 566460f97..1ed57785e 100644 --- a/resource/user/user.go +++ b/resource/user/user.go @@ -300,9 +300,8 @@ func (u *User) DiffAdd(status *resource.Status) (*AddUserOptions, error) { u.Status.RaiseLevel(resource.StatusCantChange) u.Status.AddMessage("if you want to add this user to that group, use the groupname field") return nil, fmt.Errorf("group %s exists", u.Username) - } else { - u.Status.AddDifference("username", fmt.Sprintf("<%s>", string(StateAbsent)), u.Username, "") } + u.Status.AddDifference("username", fmt.Sprintf("<%s>", string(StateAbsent)), u.Username, "") if u.UID != "" { usr, err := user.LookupId(u.UID) @@ -367,10 +366,9 @@ func (u *User) DiffMod(status *resource.Status, currUser *user.User) (*ModUserOp if usr != nil { u.Status.RaiseLevel(resource.StatusCantChange) return nil, fmt.Errorf("user %s already exists", u.NewUsername) - } else { - options.Username = u.NewUsername - status.AddDifference("username", u.Username, u.NewUsername, "") } + options.Username = u.NewUsername + status.AddDifference("username", u.Username, u.NewUsername, "") } if u.UID != "" { From 664604fa1f57b881c9b58db851fe986d07916969 Mon Sep 17 00:00:00 2001 From: Allison Richardet Date: Wed, 26 Oct 2016 14:28:25 +0000 Subject: [PATCH 4/7] Remove message indicating there are no modifications to a user --- resource/user/user.go | 2 -- resource/user/user_test.go | 1 - 2 files changed, 3 deletions(-) diff --git a/resource/user/user.go b/resource/user/user.go index 1ed57785e..026d49a46 100644 --- a/resource/user/user.go +++ b/resource/user/user.go @@ -133,8 +133,6 @@ func (u *User) Check(resource.Renderer) (resource.TaskStatus, error) { } if resource.AnyChanges(u.Status.Differences) { u.Status.AddMessage("modify user") - } else { - u.Status.AddMessage(fmt.Sprintf("no modifications indicated for user %s", u.Username)) } } case StateAbsent: diff --git a/resource/user/user_test.go b/resource/user/user_test.go index 4d66160da..400fea0b9 100644 --- a/resource/user/user_test.go +++ b/resource/user/user_test.go @@ -176,7 +176,6 @@ func TestCheck(t *testing.T) { if runtime.GOOS == "linux" { assert.NoError(t, err) assert.Equal(t, resource.StatusNoChange, status.StatusCode()) - assert.Equal(t, fmt.Sprintf("no modifications indicated for user %s", u.Username), status.Messages()[0]) assert.False(t, status.HasChanges()) } else { assert.EqualError(t, err, "user: not supported on this system") From 998d841480ef15d529bf137f02ea32b590347313 Mon Sep 17 00:00:00 2001 From: Allison Richardet Date: Thu, 27 Oct 2016 02:57:51 +0000 Subject: [PATCH 5/7] Add options for adding a user related to home directory --- resource/user/preparer.go | 17 +++++ resource/user/preparer_test.go | 21 ++++++ resource/user/user.go | 21 ++++-- resource/user/user_linux.go | 6 ++ resource/user/user_test.go | 116 +++++++++++++++++++++++++++------ 5 files changed, 157 insertions(+), 24 deletions(-) diff --git a/resource/user/preparer.go b/resource/user/preparer.go index 432699ec9..f20088f38 100644 --- a/resource/user/preparer.go +++ b/resource/user/preparer.go @@ -50,6 +50,17 @@ type Preparer struct { // This field can be indicated when adding or modifying a user. Name string `hcl:"name"` + // CreateHome when set to true will create the home directory for the user. + // The files and directories contained in the skeleton directory (which can be + // defined with the SkelDir option) will be copied to the home directory. + CreateHome bool `hcl:"create_home"` + + // SkelDir contains files and directories to be copied in the user's home + // directory when adding a user. If not set, the skeleton directory is defined + // by the SKEL variable in /etc/default/useradd or, by default, /etc/skel. + // SkelDir is only valid is CreatHome is specified. + SkelDir string `hcl:"skel_dir"` + // HomeDir is the user's login directory. By default, the login // name is appended to the home directory. // This field can be indicated when adding or modifying a user. @@ -76,6 +87,10 @@ func (p *Preparer) Prepare(render resource.Renderer) (resource.Task, error) { return nil, fmt.Errorf("user \"gid\" parameter out of range") } + if p.SkelDir != "" && !p.CreateHome { + return nil, fmt.Errorf("user \"create_home\" parameter required with \"skel_dir\" parameter") + } + if p.MoveDir && p.HomeDir == "" { return nil, fmt.Errorf("user \"home_dir\" parameter required with \"move_dir\" parameter") } @@ -89,6 +104,8 @@ func (p *Preparer) Prepare(render resource.Renderer) (resource.Task, error) { usr.NewUsername = p.NewUsername usr.GroupName = p.GroupName usr.Name = p.Name + usr.CreateHome = p.CreateHome + usr.SkelDir = p.SkelDir usr.HomeDir = p.HomeDir usr.MoveDir = p.MoveDir usr.State = p.State diff --git a/resource/user/preparer_test.go b/resource/user/preparer_test.go index 416f21683..b279943da 100644 --- a/resource/user/preparer_test.go +++ b/resource/user/preparer_test.go @@ -99,6 +99,20 @@ func TestPrepare(t *testing.T) { assert.NoError(t, err) }) + t.Run("create_home and skel_dir parameters", func(t *testing.T) { + p := user.Preparer{UID: &testID, GID: &testID, Username: "test", CreateHome: true, SkelDir: "/etc/skel", HomeDir: "tmp", State: user.StateAbsent} + _, err := p.Prepare(&fr) + + assert.NoError(t, err) + }) + + t.Run("create_home parameter", func(t *testing.T) { + p := user.Preparer{UID: &testID, GID: &testID, Username: "test", CreateHome: true, HomeDir: "tmp", State: user.StateAbsent} + _, err := p.Prepare(&fr) + + assert.NoError(t, err) + }) + t.Run("no name parameter", func(t *testing.T) { p := user.Preparer{UID: &testID, GID: &testID, Username: "test", HomeDir: "tmp", State: user.StateAbsent} _, err := p.Prepare(&fr) @@ -143,6 +157,13 @@ func TestPrepare(t *testing.T) { assert.EqualError(t, err, fmt.Sprintf("user \"gid\" parameter out of range")) }) + t.Run("no create_home with skel_dir", func(t *testing.T) { + p := user.Preparer{UID: &testID, GID: &testID, Username: "test", SkelDir: "/etc/skel", HomeDir: "tmp", State: user.StateAbsent} + _, err := p.Prepare(&fr) + + assert.EqualError(t, err, fmt.Sprintf("user \"create_home\" parameter required with \"skel_dir\" parameter")) + }) + t.Run("no home_dir with move_dir", func(t *testing.T) { p := user.Preparer{Username: "test", MoveDir: true} _, err := p.Prepare(&fr) diff --git a/resource/user/user.go b/resource/user/user.go index 026d49a46..710f2971f 100644 --- a/resource/user/user.go +++ b/resource/user/user.go @@ -41,6 +41,8 @@ type User struct { GroupName string GID string Name string + CreateHome bool + SkelDir string HomeDir string MoveDir bool State State @@ -52,10 +54,12 @@ type User struct { // AddUserOptions are the options specified in the configuration to be used // when adding a user type AddUserOptions struct { - UID string - Group string - Comment string - Directory string + UID string + Group string + Comment string + CreateHome bool + SkelDir string + Directory string } // ModUserOptions are the options specified in the configuration to be used @@ -340,6 +344,15 @@ func (u *User) DiffAdd(status *resource.Status) (*AddUserOptions, error) { status.AddDifference("comment", fmt.Sprintf("<%s>", string(StateAbsent)), u.Name, "") } + if u.CreateHome { + options.CreateHome = true + status.AddDifference("create_home", fmt.Sprintf("<%s>", string(StateAbsent)), u.HomeDir, "") + if u.SkelDir != "" { + options.SkelDir = u.SkelDir + status.AddDifference("skel_dir contents", u.SkelDir, u.HomeDir, "") + } + } + if u.HomeDir != "" { options.Directory = u.HomeDir status.AddDifference("home_dir", fmt.Sprintf("<%s>", string(StateAbsent)), u.HomeDir, "") diff --git a/resource/user/user_linux.go b/resource/user/user_linux.go index 3ead8a422..4b88c27b8 100644 --- a/resource/user/user_linux.go +++ b/resource/user/user_linux.go @@ -37,6 +37,12 @@ func (s *System) AddUser(userName string, options *AddUserOptions) error { if options.Comment != "" { args = append(args, "-c", options.Comment) } + if options.CreateHome { + args = append(args, "-m") + if options.SkelDir != "" { + args = append(args, "-k", options.SkelDir) + } + } if options.Directory != "" { args = append(args, "-d", options.Directory) } diff --git a/resource/user/user_test.go b/resource/user/user_test.go index 400fea0b9..af859c167 100644 --- a/resource/user/user_test.go +++ b/resource/user/user_test.go @@ -659,14 +659,18 @@ func TestDiffAdd(t *testing.T) { u.UID = fakeUID u.GroupName = existingGroupName u.Name = "test" + u.CreateHome = true + u.SkelDir = "/tmp/skel" u.HomeDir = "/tmp/test" u.Status = resource.NewStatus() expected := &user.AddUserOptions{ - UID: u.UID, - Group: u.GroupName, - Comment: u.Name, - Directory: u.HomeDir, + UID: u.UID, + Group: u.GroupName, + Comment: u.Name, + CreateHome: u.CreateHome, + SkelDir: u.SkelDir, + Directory: u.HomeDir, } options, err := u.DiffAdd(u.Status) @@ -683,6 +687,10 @@ func TestDiffAdd(t *testing.T) { assert.Equal(t, u.UID, u.Status.Diffs()["uid"].Current()) assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["comment"].Original()) assert.Equal(t, u.Name, u.Status.Diffs()["comment"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["create_home"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["create_home"].Current()) + assert.Equal(t, u.SkelDir, u.Status.Diffs()["skel_dir contents"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["skel_dir contents"].Current()) assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["home_dir"].Original()) assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir"].Current()) }) @@ -883,25 +891,93 @@ func TestDiffAdd(t *testing.T) { }) t.Run("directory", func(t *testing.T) { - u := user.NewUser(new(user.System)) - u.Username = fakeUsername - u.HomeDir = "/tmp/test" - u.Status = resource.NewStatus() + t.Run("home_dir name", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = fakeUsername + u.HomeDir = "/tmp/test" + u.Status = resource.NewStatus() - expected := &user.AddUserOptions{ - Directory: u.HomeDir, - } + expected := &user.AddUserOptions{ + Directory: u.HomeDir, + } - options, err := u.DiffAdd(u.Status) + options, err := u.DiffAdd(u.Status) - assert.NoError(t, err) - assert.Equal(t, expected, options) - assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) - assert.True(t, u.Status.HasChanges()) - assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) - assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) - assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["home_dir"].Original()) - assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir"].Current()) + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["home_dir"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir"].Current()) + }) + + t.Run("create_home", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = fakeUsername + u.CreateHome = true + u.Status = resource.NewStatus() + + expected := &user.AddUserOptions{ + CreateHome: u.CreateHome, + } + + options, err := u.DiffAdd(u.Status) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["create_home"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["create_home"].Current()) + }) + + t.Run("create_home with skel_dir", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = fakeUsername + u.CreateHome = true + u.SkelDir = "/tmp/skel" + u.Status = resource.NewStatus() + + expected := &user.AddUserOptions{ + CreateHome: u.CreateHome, + SkelDir: u.SkelDir, + } + + options, err := u.DiffAdd(u.Status) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["create_home"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["create_home"].Current()) + assert.Equal(t, u.SkelDir, u.Status.Diffs()["skel_dir contents"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["skel_dir contents"].Current()) + }) + + t.Run("no create_home and skel_dir", func(t *testing.T) { + u := user.NewUser(new(user.System)) + u.Username = fakeUsername + u.SkelDir = "/tmp/skel" + u.Status = resource.NewStatus() + + expected := &user.AddUserOptions{} + + options, err := u.DiffAdd(u.Status) + + assert.NoError(t, err) + assert.Equal(t, expected, options) + assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) + assert.True(t, u.Status.HasChanges()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) + assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + }) }) t.Run("no options", func(t *testing.T) { From dc8ba679a86fad817e74db36dd801fa6a8e88501 Mon Sep 17 00:00:00 2001 From: Allison Richardet Date: Thu, 27 Oct 2016 19:46:21 +0000 Subject: [PATCH 6/7] Modify diff output for user directory fields --- resource/user/preparer.go | 5 ++-- resource/user/user.go | 12 +++++--- resource/user/user_test.go | 57 ++++++++++++++++++++++---------------- 3 files changed, 44 insertions(+), 30 deletions(-) diff --git a/resource/user/preparer.go b/resource/user/preparer.go index f20088f38..2ae4bcced 100644 --- a/resource/user/preparer.go +++ b/resource/user/preparer.go @@ -61,8 +61,9 @@ type Preparer struct { // SkelDir is only valid is CreatHome is specified. SkelDir string `hcl:"skel_dir"` - // HomeDir is the user's login directory. By default, the login - // name is appended to the home directory. + // HomeDir is the name of the user's login directory. If not set, the home + // directory is defined by appending the value of Username to the HOME + // variable in /etc/default/useradd, resulting in /HOME/Username. // This field can be indicated when adding or modifying a user. HomeDir string `hcl:"home_dir"` diff --git a/resource/user/user.go b/resource/user/user.go index 710f2971f..867d24662 100644 --- a/resource/user/user.go +++ b/resource/user/user.go @@ -345,17 +345,21 @@ func (u *User) DiffAdd(status *resource.Status) (*AddUserOptions, error) { } if u.CreateHome { + dirDiff := u.HomeDir + if u.HomeDir == "" { + dirDiff = "" + } options.CreateHome = true - status.AddDifference("create_home", fmt.Sprintf("<%s>", string(StateAbsent)), u.HomeDir, "") + status.AddDifference("create_home", fmt.Sprintf("<%s>", string(StateAbsent)), dirDiff, "") if u.SkelDir != "" { options.SkelDir = u.SkelDir - status.AddDifference("skel_dir contents", u.SkelDir, u.HomeDir, "") + status.AddDifference("skel_dir contents", u.SkelDir, dirDiff, "") } } if u.HomeDir != "" { options.Directory = u.HomeDir - status.AddDifference("home_dir", fmt.Sprintf("<%s>", string(StateAbsent)), u.HomeDir, "") + status.AddDifference("home_dir name", "", u.HomeDir, "") } if resource.AnyChanges(u.Status.Differences) { @@ -431,7 +435,7 @@ func (u *User) DiffMod(status *resource.Status, currUser *user.User) (*ModUserOp if u.HomeDir != "" { if currUser.HomeDir != u.HomeDir { options.Directory = u.HomeDir - status.AddDifference("home_dir", currUser.HomeDir, u.HomeDir, "") + status.AddDifference("home_dir name", currUser.HomeDir, u.HomeDir, "") if u.MoveDir { options.MoveDir = true status.AddDifference("home_dir contents", currUser.HomeDir, u.HomeDir, "") diff --git a/resource/user/user_test.go b/resource/user/user_test.go index af859c167..8b5921dde 100644 --- a/resource/user/user_test.go +++ b/resource/user/user_test.go @@ -691,8 +691,8 @@ func TestDiffAdd(t *testing.T) { assert.Equal(t, u.HomeDir, u.Status.Diffs()["create_home"].Current()) assert.Equal(t, u.SkelDir, u.Status.Diffs()["skel_dir contents"].Original()) assert.Equal(t, u.HomeDir, u.Status.Diffs()["skel_dir contents"].Current()) - assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["home_dir"].Original()) - assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir"].Current()) + assert.Equal(t, "", u.Status.Diffs()["home_dir name"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir name"].Current()) }) t.Run("username", func(t *testing.T) { @@ -891,14 +891,18 @@ func TestDiffAdd(t *testing.T) { }) t.Run("directory", func(t *testing.T) { - t.Run("home_dir name", func(t *testing.T) { + t.Run("create_home with home_dir", func(t *testing.T) { u := user.NewUser(new(user.System)) u.Username = fakeUsername + u.CreateHome = true u.HomeDir = "/tmp/test" + u.SkelDir = "/tmp/skel" u.Status = resource.NewStatus() expected := &user.AddUserOptions{ - Directory: u.HomeDir, + CreateHome: u.CreateHome, + SkelDir: u.SkelDir, + Directory: u.HomeDir, } options, err := u.DiffAdd(u.Status) @@ -909,18 +913,24 @@ func TestDiffAdd(t *testing.T) { assert.True(t, u.Status.HasChanges()) assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) - assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["home_dir"].Original()) - assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir"].Current()) + assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["create_home"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["create_home"].Current()) + assert.Equal(t, "", u.Status.Diffs()["home_dir name"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir name"].Current()) + assert.Equal(t, u.SkelDir, u.Status.Diffs()["skel_dir contents"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["skel_dir contents"].Current()) }) - t.Run("create_home", func(t *testing.T) { + t.Run("create_home with default home", func(t *testing.T) { u := user.NewUser(new(user.System)) u.Username = fakeUsername u.CreateHome = true + u.SkelDir = "/tmp/skel" u.Status = resource.NewStatus() expected := &user.AddUserOptions{ CreateHome: u.CreateHome, + SkelDir: u.SkelDir, } options, err := u.DiffAdd(u.Status) @@ -932,20 +942,18 @@ func TestDiffAdd(t *testing.T) { assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["create_home"].Original()) - assert.Equal(t, u.HomeDir, u.Status.Diffs()["create_home"].Current()) + assert.Equal(t, "", u.Status.Diffs()["create_home"].Current()) + assert.Equal(t, u.SkelDir, u.Status.Diffs()["skel_dir contents"].Original()) + assert.Equal(t, "", u.Status.Diffs()["skel_dir contents"].Current()) }) - t.Run("create_home with skel_dir", func(t *testing.T) { + t.Run("default home without create_home", func(t *testing.T) { u := user.NewUser(new(user.System)) u.Username = fakeUsername - u.CreateHome = true u.SkelDir = "/tmp/skel" u.Status = resource.NewStatus() - expected := &user.AddUserOptions{ - CreateHome: u.CreateHome, - SkelDir: u.SkelDir, - } + expected := &user.AddUserOptions{} options, err := u.DiffAdd(u.Status) @@ -955,19 +963,18 @@ func TestDiffAdd(t *testing.T) { assert.True(t, u.Status.HasChanges()) assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) - assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["create_home"].Original()) - assert.Equal(t, u.HomeDir, u.Status.Diffs()["create_home"].Current()) - assert.Equal(t, u.SkelDir, u.Status.Diffs()["skel_dir contents"].Original()) - assert.Equal(t, u.HomeDir, u.Status.Diffs()["skel_dir contents"].Current()) }) - t.Run("no create_home and skel_dir", func(t *testing.T) { + t.Run("home_dir without create_home", func(t *testing.T) { u := user.NewUser(new(user.System)) u.Username = fakeUsername + u.HomeDir = "/tmp/test" u.SkelDir = "/tmp/skel" u.Status = resource.NewStatus() - expected := &user.AddUserOptions{} + expected := &user.AddUserOptions{ + Directory: u.HomeDir, + } options, err := u.DiffAdd(u.Status) @@ -977,6 +984,8 @@ func TestDiffAdd(t *testing.T) { assert.True(t, u.Status.HasChanges()) assert.Equal(t, fmt.Sprintf("<%s>", string(user.StateAbsent)), u.Status.Diffs()["username"].Original()) assert.Equal(t, u.Username, u.Status.Diffs()["username"].Current()) + assert.Equal(t, "", u.Status.Diffs()["home_dir name"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir name"].Current()) }) }) @@ -1036,8 +1045,8 @@ func TestDiffMod(t *testing.T) { assert.Equal(t, u.UID, u.Status.Diffs()["uid"].Current()) assert.Equal(t, currUser.Name, u.Status.Diffs()["comment"].Original()) assert.Equal(t, u.Name, u.Status.Diffs()["comment"].Current()) - assert.Equal(t, currUser.HomeDir, u.Status.Diffs()["home_dir"].Original()) - assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir"].Current()) + assert.Equal(t, currUser.HomeDir, u.Status.Diffs()["home_dir name"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir name"].Current()) assert.Equal(t, currUser.HomeDir, u.Status.Diffs()["home_dir contents"].Original()) assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir contents"].Current()) }) @@ -1279,8 +1288,8 @@ func TestDiffMod(t *testing.T) { assert.Equal(t, expected, options) assert.Equal(t, resource.StatusWillChange, u.Status.StatusCode()) assert.True(t, u.Status.HasChanges()) - assert.Equal(t, currUser.HomeDir, u.Status.Diffs()["home_dir"].Original()) - assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir"].Current()) + assert.Equal(t, currUser.HomeDir, u.Status.Diffs()["home_dir name"].Original()) + assert.Equal(t, u.HomeDir, u.Status.Diffs()["home_dir name"].Current()) }) t.Run("no options", func(t *testing.T) { From 1a1a4788f7dde5830f913ad0b902aa10cc4b6bf7 Mon Sep 17 00:00:00 2001 From: Allison Richardet Date: Fri, 28 Oct 2016 08:47:51 -0500 Subject: [PATCH 7/7] Update docs from source --- docs/content/resources/user.group.md | 3 ++- docs/content/resources/user.user.md | 35 +++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/docs/content/resources/user.group.md b/docs/content/resources/user.group.md index 5839312e9..bc763f058 100644 --- a/docs/content/resources/user.group.md +++ b/docs/content/resources/user.group.md @@ -1,7 +1,7 @@ --- title: "user.group" slug: "user-group" -date: "2016-10-04T13:01:50-05:00" +date: "2016-10-28T08:47:08-05:00" menu: main: parent: resources @@ -43,5 +43,6 @@ The group Name will be changed to NewName. Valid values: `present` and `absent` State is whether the group should be present. +The default value is present. diff --git a/docs/content/resources/user.user.md b/docs/content/resources/user.user.md index 78afcd9fe..db186ebfb 100644 --- a/docs/content/resources/user.user.md +++ b/docs/content/resources/user.user.md @@ -1,7 +1,7 @@ --- title: "user.user" slug: "user-user" -date: "2016-10-04T13:01:50-05:00" +date: "2016-10-28T08:47:08-05:00" menu: main: parent: resources @@ -28,6 +28,13 @@ user.user "user" { Username is the user login name. +- `new_username` (string) + + NewUsername is used when modifying a user. +Username will be changed to NewUsername. No changes to the home directory +name or location of the contents will be made. This can be done using +HomeDir and MoveDir options. + - `uid` (optional uint32) UID is the user ID. @@ -51,11 +58,32 @@ Only one of GID or Groupname may be indicated. - `name` (string) Name is the user description. +This field can be indicated when adding or modifying a user. + +- `create_home` (bool) + + CreateHome when set to true will create the home directory for the user. +The files and directories contained in the skeleton directory (which can be +defined with the SkelDir option) will be copied to the home directory. + +- `skel_dir` (string) + + SkelDir contains files and directories to be copied in the user's home +directory when adding a user. If not set, the skeleton directory is defined +by the SKEL variable in /etc/default/useradd or, by default, /etc/skel. +SkelDir is only valid is CreatHome is specified. - `home_dir` (string) - HomeDir is the user's login directory. By default, the login -name is appended to the home directory. + HomeDir is the name of the user's login directory. If not set, the home +directory is defined by appending the value of Username to the HOME +variable in /etc/default/useradd, resulting in /HOME/Username. +This field can be indicated when adding or modifying a user. + +- `move_dir` (bool) + + MoveDir is used to move the contents of HomeDir when modifying a user. +HomeDir must also be indicated if MoveDir is set to true. - `state` (State) @@ -63,5 +91,6 @@ name is appended to the home directory. Valid values: `present` and `absent` State is whether the user should be present. +The default value is present.