func TestCollaborationOperationsUnshareVM(t *testing.T) { r := runner.New("collaboration-UnshareVM-tests") err := r.Init() if err != nil { panic(err) } defer r.Close() appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() // init with defaults mongoCache := cache.NewMongoCacheWithTTL(modelhelper.Mongo.Session) defer mongoCache.StopGC() handler := New(r.Log, mongoCache, appConfig, r.Kite) Convey("while testing UnshareVM", t, func() { Convey("should be able to create the channel and workspace first", func() { Convey("should be able to UnshareVM", func() { creator, err := socialapimodels.CreateAccountInBothDbs() // init account So(err, ShouldBeNil) participant1, err := socialapimodels.CreateAccountInBothDbs() So(err, ShouldBeNil) participant2, err := socialapimodels.CreateAccountInBothDbs() So(err, ShouldBeNil) m1, m1ws1 := prepareSingleWorkspace(creator, participant1, participant2) channelId, err := strconv.ParseInt(m1ws1.ChannelId, 10, 64) So(err, ShouldBeNil) req1 := &models.Ping{ AccountId: creator.Id, FileId: fmt.Sprintf("%d", rand.Int63()), ChannelId: channelId, } toBeRemovedUsers, err := handler.findToBeRemovedUsers(req1) So(err, ShouldBeNil) So(toBeRemovedUsers, ShouldNotBeNil) err = handler.UnshareVM(req1, toBeRemovedUsers) So(err, ShouldBeNil) err = handler.EndPrivateMessage(req1) So(err, ShouldBeNil) Convey("remove users should not be in the machine", func() { mm1, err := modelhelper.GetMachineByUid(m1.Uid) So(err, ShouldBeNil) So(mm1, ShouldNotBeNil) So(len(mm1.Users), ShouldEqual, 1) ownerUser, err := modelhelper.GetUserByAccountId(creator.OldId) So(err, ShouldBeNil) So(mm1.Users[0].Id.Hex(), ShouldEqual, ownerUser.ObjectId.Hex()) }) }) Convey("if participant and owner shares multiple workspaces", func() { creator, err := socialapimodels.CreateAccountInBothDbs() // init account So(err, ShouldBeNil) participant1, err := socialapimodels.CreateAccountInBothDbs() So(err, ShouldBeNil) participant2, err := socialapimodels.CreateAccountInBothDbs() So(err, ShouldBeNil) participant3, err := socialapimodels.CreateAccountInBothDbs() So(err, ShouldBeNil) _, _, m2, m2ws1, m2ws2 := prepareWorkspace(creator, participant1, participant2, participant3) Convey("remove from first workspace", func() { channelId, err := strconv.ParseInt(m2ws1.ChannelId, 10, 64) So(err, ShouldBeNil) req := &models.Ping{ AccountId: creator.Id, FileId: fmt.Sprintf("%d", rand.Int63()), ChannelId: channelId, } toBeRemovedUsers, err := handler.findToBeRemovedUsers(req) So(err, ShouldBeNil) So(toBeRemovedUsers, ShouldNotBeNil) err = handler.UnshareVM(req, toBeRemovedUsers) So(err, ShouldBeNil) err = handler.EndPrivateMessage(req) So(err, ShouldBeNil) Convey("participants should still be in the second machine", func() { mm2, err := modelhelper.GetMachineByUid(m2.Uid) So(err, ShouldBeNil) So(mm2, ShouldNotBeNil) So(len(mm2.Users), ShouldEqual, 3) // participant1 is not in the second WS, so it should be removed from the machine ownerUser, err := modelhelper.GetUserByAccountId(creator.OldId) So(err, ShouldBeNil) So(mm2.Users[0].Id.Hex(), ShouldEqual, ownerUser.ObjectId.Hex()) participant2User, err := modelhelper.GetUserByAccountId(participant2.OldId) So(err, ShouldBeNil) So(mm2.Users[1].Id.Hex(), ShouldEqual, participant2User.ObjectId.Hex()) participant3User, err := modelhelper.GetUserByAccountId(participant3.OldId) So(err, ShouldBeNil) So(mm2.Users[2].Id.Hex(), ShouldEqual, participant3User.ObjectId.Hex()) Convey("after removing from second WS", func() { // remove from second WS too channelId, err := strconv.ParseInt(m2ws2.ChannelId, 10, 64) So(err, ShouldBeNil) req := &models.Ping{ AccountId: creator.Id, FileId: fmt.Sprintf("%d", rand.Int63()), ChannelId: channelId, } toBeRemovedUsers, err := handler.findToBeRemovedUsers(req) So(err, ShouldBeNil) So(toBeRemovedUsers, ShouldNotBeNil) err = handler.UnshareVM(req, toBeRemovedUsers) So(err, ShouldBeNil) err = handler.EndPrivateMessage(req) So(err, ShouldBeNil) Convey("owner and permanent should still stay", func() { mm2, err := modelhelper.GetMachineByUid(m2.Uid) So(err, ShouldBeNil) So(mm2, ShouldNotBeNil) So(len(mm2.Users), ShouldEqual, 2) ownerUser, err := modelhelper.GetUserByAccountId(creator.OldId) So(err, ShouldBeNil) So(mm2.Users[0].Id.Hex(), ShouldEqual, ownerUser.ObjectId.Hex()) participant1User, err := modelhelper.GetUserByAccountId(participant3.OldId) So(err, ShouldBeNil) So(mm2.Users[1].Id.Hex(), ShouldEqual, participant1User.ObjectId.Hex()) }) }) }) }) }) }) }) }
func prepareSingleWorkspace(creator, participant1, participant2 *socialapimodels.Account) ( *mongomodels.Machine, // m1 *mongomodels.Workspace, // m1 ws1 ) { ownerUser, err := modelhelper.GetUserByAccountId(creator.OldId) So(err, ShouldBeNil) participant1User, err := modelhelper.GetUserByAccountId(participant1.OldId) So(err, ShouldBeNil) participant2User, err := modelhelper.GetUserByAccountId(participant2.OldId) So(err, ShouldBeNil) // sample machine struct m1 := &mongomodels.Machine{ ObjectId: bson.NewObjectId(), Uid: bson.NewObjectId().Hex(), Users: []mongomodels.MachineUser{ { // real owner Id: ownerUser.ObjectId, Sudo: true, Owner: true, }, { // secondary owner Id: participant1User.ObjectId, Sudo: false, Owner: true, Permanent: false, }, { // random Id: participant2User.ObjectId, Sudo: false, Owner: true, Permanent: false, }, }, CreatedAt: time.Now().UTC(), Status: mongomodels.MachineStatus{ State: "running", ModifiedAt: time.Now().UTC(), }, Assignee: mongomodels.MachineAssignee{}, UserDeleted: false, } So(modelhelper.CreateMachine(m1), ShouldBeNil) // // create the first channel // c1 := socialapimodels.NewChannel() // init channel c1.CreatorId = creator.Id // set Creator id c1.TypeConstant = socialapimodels.Channel_TYPE_COLLABORATION So(c1.Create(), ShouldBeNil) c1p1, err := c1.AddParticipant(creator.Id) So(err, ShouldBeNil) So(c1p1, ShouldNotBeNil) c1p2, err := c1.AddParticipant(participant1.Id) So(err, ShouldBeNil) So(c1p2, ShouldNotBeNil) c1p3, err := c1.AddParticipant(participant2.Id) So(err, ShouldBeNil) So(c1p3, ShouldNotBeNil) m1ws1 := &mongomodels.Workspace{ ObjectId: bson.NewObjectId(), OriginId: bson.ObjectIdHex(creator.OldId), Name: "My Workspace", Slug: "m1ws1", ChannelId: strconv.FormatInt(c1.Id, 10), MachineUID: m1.Uid, MachineLabel: "koding-vm-0", Owner: "cihangir", RootPath: "/home/cihangir", IsDefault: true, } So(modelhelper.CreateWorkspace(m1ws1), ShouldBeNil) return m1, m1ws1 }
func (c *Controller) findToBeRemovedUsers(ping *models.Ping) ([]bson.ObjectId, error) { ws, err := modelhelper.GetWorkspaceByChannelId( strconv.FormatInt(ping.ChannelId, 10), ) if err != nil { return nil, filterErr(err) } machine, err := modelhelper.GetMachineByUid(ws.MachineUID) if err != nil { return nil, filterErr(err) } ownerMachineUser := machine.Owner() if ownerMachineUser == nil { c.log.Critical("owner couldnt found %+v", ping) return nil, nil // if we cant find the owner, we cant process the users } ownerAccount, err := modelhelper.GetAccountByUserId(ownerMachineUser.Id) if err != nil { return nil, filterErr(err) } // get workspaces of the owner ownersWorkspaces, err := modelhelper.GetWorkspaces(ownerAccount.Id) if err != nil { return nil, filterErr(err) } users := partitionUsers(ownerAccount, ownersWorkspaces, ws) var toBeRemovedUsers []bson.ObjectId for accountID, count := range users { // if we count the user more than once, that means user is in another // workspace too if count > 1 { continue } u, err := modelhelper.GetUserByAccountId(accountID) if err != nil { return nil, err } toBeRemovedUsers = append(toBeRemovedUsers, u.ObjectId) } var filteredUsers []bson.ObjectId permanentUsers := make(map[string]struct{}) for _, user := range machine.Users { if user.Permanent { permanentUsers[user.Id.Hex()] = struct{}{} } } for _, toBeRemovedUser := range toBeRemovedUsers { if _, ok := permanentUsers[toBeRemovedUser.Hex()]; !ok { filteredUsers = append(filteredUsers, toBeRemovedUser) } } return filteredUsers, nil }