From e4da5e9ab595cb16c0f7672761891924c9fc7b21 Mon Sep 17 00:00:00 2001 From: Todd Knight Date: Wed, 26 Aug 2020 11:04:26 -0700 Subject: [PATCH] add|set|remove-hosts for host-sets SDK and API (#304) * Adding ability to add host set members. * Adding SDK and API for add|set|remove hosts. --- api/hosts/host_set_test.go | 48 +++ .../handlers/host_sets/host_set_service.go | 58 +-- .../host_sets/host_set_service_test.go | 384 +++++++++++++++++- .../controller/handlers/hosts/host_service.go | 4 +- internal/types/action/action.go | 9 + 5 files changed, 450 insertions(+), 53 deletions(-) diff --git a/api/hosts/host_set_test.go b/api/hosts/host_set_test.go index a6145b8a6..7a7dfaf4f 100644 --- a/api/hosts/host_set_test.go +++ b/api/hosts/host_set_test.go @@ -14,6 +14,54 @@ import ( "github.com/stretchr/testify/require" ) +func TestCustom(t *testing.T) { + assert, require := assert.New(t), require.New(t) + amId := "ampw_1234567890" + tc := controller.NewTestController(t, &controller.TestControllerOpts{ + DisableAuthorizationFailures: true, + DefaultAuthMethodId: amId, + DefaultLoginName: "user", + DefaultPassword: "passpass", + }) + defer tc.Shutdown() + + _, proj := iam.TestScopes(t, tc.IamRepo()) + client := tc.Client().Clone() + client.SetScopeId(proj.GetPublicId()) + + hc, apiErr, err := hosts.NewHostCatalogsClient(client).Create(tc.Context(), "static") + require.NoError(err) + require.Nil(apiErr) + + hClient := hosts.NewHostsClient(client) + h1, apiErr, err := hClient.Create(tc.Context(), hc.Id, hosts.WithStaticHostAddress("someaddress")) + require.NoError(err) + require.Nil(apiErr) + h2, apiErr, err := hClient.Create(tc.Context(), hc.Id, hosts.WithStaticHostAddress("someaddress")) + require.NoError(err) + require.Nil(apiErr) + + hSetClient := hosts.NewHostSetsClient(client) + hSet, apiErr, err := hSetClient.Create(tc.Context(), hc.Id) + require.NoError(err) + require.Nil(apiErr) + + hSet, apiErr, err = hSetClient.AddHosts(tc.Context(), hc.Id, hSet.Id, hSet.Version, []string{h1.Id, h2.Id}) + require.NoError(err) + require.Nil(apiErr) + assert.Contains(hSet.HostIds, h1.Id, h2.Id) + + hSet, apiErr, err = hSetClient.SetHosts(tc.Context(), hc.Id, hSet.Id, hSet.Version, []string{h1.Id}) + require.NoError(err) + require.Nil(apiErr) + assert.ElementsMatch([]string{h1.Id}, hSet.HostIds) + + hSet, apiErr, err = hSetClient.RemoveHosts(tc.Context(), hc.Id, hSet.Id, hSet.Version, []string{h1.Id}) + require.NoError(err) + require.Nil(apiErr) + assert.Empty(hSet.HostIds) +} + func TestSet_List(t *testing.T) { assert, require := assert.New(t), require.New(t) amId := "ampw_1234567890" diff --git a/internal/servers/controller/handlers/host_sets/host_set_service.go b/internal/servers/controller/handlers/host_sets/host_set_service.go index 753381b7c..6245e71ab 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service.go @@ -184,14 +184,14 @@ func (s Service) getFromRepo(ctx context.Context, id string) (*pb.HostSet, error if err != nil { return nil, err } - h, _, err := repo.LookupSet(ctx, id) + h, m, err := repo.LookupSet(ctx, id) if err != nil { return nil, err } if h == nil { return nil, handlers.NotFoundErrorf("Host set %q doesn't exist.", id) } - return toProto(h, nil), nil + return toProto(h, m), nil } func (s Service) createInRepo(ctx context.Context, scopeId, catalogId string, item *pb.HostSet) (*pb.HostSet, error) { @@ -241,14 +241,14 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, catalogId, id string if err != nil { return nil, err } - out, _, rowsUpdated, err := repo.UpdateSet(ctx, scopeId, h, item.GetVersion(), dbMask) + out, m, rowsUpdated, err := repo.UpdateSet(ctx, scopeId, h, item.GetVersion(), dbMask) if err != nil { return nil, status.Errorf(codes.Internal, "Unable to update host set: %v.", err) } if rowsUpdated == 0 { return nil, handlers.NotFoundErrorf("Host set %q doesn't exist.", id) } - return toProto(out, nil), nil + return toProto(out, m), nil } func (s Service) deleteFromRepo(ctx context.Context, scopeId, id string) (bool, error) { @@ -284,18 +284,18 @@ func (s Service) addInRepo(ctx context.Context, scopeId, setId string, hostIds [ if err != nil { return nil, err } - // _, err = repo.AddHostSetMembers(ctx, setId, version, hostIds) - // if err != nil { - // return nil, status.Errorf(codes.Internal, "Unable to add members to group: %v.", err) - // } - out, _, err := repo.LookupSet(ctx, setId) + _, err = repo.AddSetMembers(ctx, scopeId, setId, version, hostIds) if err != nil { - return nil, status.Errorf(codes.Internal, "Unable to look up group: %v.", err) + return nil, status.Errorf(codes.Internal, "Unable to add hosts to host set: %v.", err) + } + out, m, err := repo.LookupSet(ctx, setId) + if err != nil { + return nil, status.Errorf(codes.Internal, "Unable to look up host set: %v.", err) } if out == nil { - return nil, status.Error(codes.Internal, "Unable to lookup group after adding member to it.") + return nil, status.Error(codes.Internal, "Unable to lookup host set after adding hosts to it.") } - return toProto(out, nil), nil + return toProto(out, m), nil } func (s Service) setInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*pb.HostSet, error) { @@ -303,19 +303,19 @@ func (s Service) setInRepo(ctx context.Context, scopeId, setId string, hostIds [ if err != nil { return nil, err } - // _, _, err = repo.SetHostSetMembers(ctx, groupId, version, userIds) - // if err != nil { - // return nil, status.Errorf(codes.Internal, "Unable to set members on group: %v.", err) - // } + _, _, err = repo.SetSetMembers(ctx, scopeId, setId, version, hostIds) + if err != nil { + return nil, status.Errorf(codes.Internal, "Unable to set hosts in host set: %v.", err) + } - out, _, err := repo.LookupSet(ctx, setId) + out, m, err := repo.LookupSet(ctx, setId) if err != nil { return nil, status.Errorf(codes.Internal, "Unable to look up group: %v.", err) } if out == nil { return nil, status.Error(codes.Internal, "Unable to lookup group after setting members for it.") } - return toProto(out, nil), nil + return toProto(out, m), nil } func (s Service) removeInRepo(ctx context.Context, scopeId, setId string, hostIds []string, version uint32) (*pb.HostSet, error) { @@ -323,21 +323,21 @@ func (s Service) removeInRepo(ctx context.Context, scopeId, setId string, hostId if err != nil { return nil, err } - // _, err = repo.RemoveHostSetMembers(ctx, hostSetId, version, userIds) - // if err != nil { - // return nil, status.Errorf(codes.Internal, "Unable to remove members from group: %v.", err) - // } - out, _, err := repo.LookupSet(ctx, setId) + _, err = repo.DeleteSetMembers(ctx, scopeId, setId, version, hostIds) if err != nil { - return nil, status.Errorf(codes.Internal, "Unable to look up group: %v.", err) + return nil, status.Errorf(codes.Internal, "Unable to remove hosts from host set: %v.", err) + } + out, m, err := repo.LookupSet(ctx, setId) + if err != nil { + return nil, status.Errorf(codes.Internal, "Unable to look up host set: %v.", err) } if out == nil { - return nil, status.Error(codes.Internal, "Unable to lookup group after removing members from it.") + return nil, status.Error(codes.Internal, "Unable to lookup host set after removing hosts from it.") } - return toProto(out, nil), nil + return toProto(out, m), nil } -func toProto(in *static.HostSet, members []*static.HostSetMember) *pb.HostSet { +func toProto(in *static.HostSet, hs []*static.Host) *pb.HostSet { out := pb.HostSet{ Id: in.GetPublicId(), HostCatalogId: in.GetCatalogId(), @@ -352,8 +352,8 @@ func toProto(in *static.HostSet, members []*static.HostSetMember) *pb.HostSet { if in.GetName() != "" { out.Name = wrapperspb.String(in.GetName()) } - for _, m := range members { - out.HostIds = append(out.HostIds, m.GetHostId()) + for _, h := range hs { + out.HostIds = append(out.HostIds, h.GetPublicId()) } return &out } diff --git a/internal/servers/controller/handlers/host_sets/host_set_service_test.go b/internal/servers/controller/handlers/host_sets/host_set_service_test.go index de97aff1b..c1f05bee2 100644 --- a/internal/servers/controller/handlers/host_sets/host_set_service_test.go +++ b/internal/servers/controller/handlers/host_sets/host_set_service_test.go @@ -42,7 +42,11 @@ func TestGet(t *testing.T) { return static.NewRepository(rw, rw, kms) } hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] - h := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] + hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] + + h := static.TestHosts(t, conn, hc.GetPublicId(), 2) + static.TestSetMembers(t, conn, hs.GetPublicId(), h) + hIds := []string{h[0].GetPublicId(), h[1].GetPublicId()} toMerge := &pbs.GetHostSetRequest{ HostCatalogId: hc.GetPublicId(), @@ -50,11 +54,12 @@ func TestGet(t *testing.T) { pHost := &pb.HostSet{ HostCatalogId: hc.GetPublicId(), - Id: h.GetPublicId(), - CreatedTime: h.CreateTime.GetTimestamp(), - UpdatedTime: h.UpdateTime.GetTimestamp(), + Id: hs.GetPublicId(), + CreatedTime: hs.CreateTime.GetTimestamp(), + UpdatedTime: hs.UpdateTime.GetTimestamp(), Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()}, Type: "static", + HostIds: hIds, } cases := []struct { @@ -65,7 +70,7 @@ func TestGet(t *testing.T) { }{ { name: "Get an Existing Host", - req: &pbs.GetHostSetRequest{Id: h.GetPublicId()}, + req: &pbs.GetHostSetRequest{Id: hs.GetPublicId()}, res: &pbs.GetHostSetResponse{Item: pHost}, errCode: codes.OK, }, @@ -430,25 +435,30 @@ func TestUpdate(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] - h, err := static.NewHostSet(hc.GetPublicId(), static.WithName("default"), static.WithDescription("default")) + h := static.TestHosts(t, conn, hc.GetPublicId(), 2) + hIds := []string{h[0].GetPublicId(), h[1].GetPublicId()} + + hs, err := static.NewHostSet(hc.GetPublicId(), static.WithName("default"), static.WithDescription("default")) require.NoError(t, err) - h, err = repo.CreateSet(context.Background(), proj.GetPublicId(), h) + hs, err = repo.CreateSet(context.Background(), proj.GetPublicId(), hs) require.NoError(t, err) + static.TestSetMembers(t, conn, hs.GetPublicId(), h) + var version uint32 = 1 resetHostSet := func() { version++ - _, _, _, err = repo.UpdateSet(context.Background(), proj.GetPublicId(), h, version, []string{"Name", "Description"}) + _, _, _, err = repo.UpdateSet(context.Background(), proj.GetPublicId(), hs, version, []string{"Name", "Description"}) require.NoError(t, err, "Failed to reset host.") version++ } - hCreated, err := ptypes.Timestamp(h.GetCreateTime().GetTimestamp()) + hCreated, err := ptypes.Timestamp(hs.GetCreateTime().GetTimestamp()) require.NoError(t, err, "Failed to convert proto to timestamp") toMerge := &pbs.UpdateHostSetRequest{ HostCatalogId: hc.GetPublicId(), - Id: h.GetPublicId(), + Id: hs.GetPublicId(), } tested, err := host_sets.NewService(repoFn) @@ -474,12 +484,13 @@ func TestUpdate(t *testing.T) { res: &pbs.UpdateHostSetResponse{ Item: &pb.HostSet{ HostCatalogId: hc.GetPublicId(), - Id: h.GetPublicId(), + Id: hs.GetPublicId(), Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()}, Name: &wrappers.StringValue{Value: "new"}, Description: &wrappers.StringValue{Value: "desc"}, - CreatedTime: h.GetCreateTime().GetTimestamp(), + CreatedTime: hs.GetCreateTime().GetTimestamp(), Type: "static", + HostIds: hIds, }, }, errCode: codes.OK, @@ -498,12 +509,13 @@ func TestUpdate(t *testing.T) { res: &pbs.UpdateHostSetResponse{ Item: &pb.HostSet{ HostCatalogId: hc.GetPublicId(), - Id: h.GetPublicId(), + Id: hs.GetPublicId(), Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()}, Name: &wrappers.StringValue{Value: "new"}, Description: &wrappers.StringValue{Value: "desc"}, - CreatedTime: h.GetCreateTime().GetTimestamp(), + CreatedTime: hs.GetCreateTime().GetTimestamp(), Type: "static", + HostIds: hIds, }, }, errCode: codes.OK, @@ -553,11 +565,12 @@ func TestUpdate(t *testing.T) { res: &pbs.UpdateHostSetResponse{ Item: &pb.HostSet{ HostCatalogId: hc.GetPublicId(), - Id: h.GetPublicId(), + Id: hs.GetPublicId(), Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()}, Description: &wrappers.StringValue{Value: "default"}, - CreatedTime: h.GetCreateTime().GetTimestamp(), + CreatedTime: hs.GetCreateTime().GetTimestamp(), Type: "static", + HostIds: hIds, }, }, errCode: codes.OK, @@ -575,11 +588,12 @@ func TestUpdate(t *testing.T) { res: &pbs.UpdateHostSetResponse{ Item: &pb.HostSet{ HostCatalogId: hc.GetPublicId(), - Id: h.GetPublicId(), + Id: hs.GetPublicId(), Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()}, Name: &wrappers.StringValue{Value: "default"}, - CreatedTime: h.GetCreateTime().GetTimestamp(), + CreatedTime: hs.GetCreateTime().GetTimestamp(), Type: "static", + HostIds: hIds, }, }, errCode: codes.OK, @@ -598,12 +612,13 @@ func TestUpdate(t *testing.T) { res: &pbs.UpdateHostSetResponse{ Item: &pb.HostSet{ HostCatalogId: hc.GetPublicId(), - Id: h.GetPublicId(), + Id: hs.GetPublicId(), Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()}, Name: &wrappers.StringValue{Value: "updated"}, Description: &wrappers.StringValue{Value: "default"}, - CreatedTime: h.GetCreateTime().GetTimestamp(), + CreatedTime: hs.GetCreateTime().GetTimestamp(), Type: "static", + HostIds: hIds, }, }, errCode: codes.OK, @@ -622,12 +637,13 @@ func TestUpdate(t *testing.T) { res: &pbs.UpdateHostSetResponse{ Item: &pb.HostSet{ HostCatalogId: hc.GetPublicId(), - Id: h.GetPublicId(), + Id: hs.GetPublicId(), Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String()}, Name: &wrappers.StringValue{Value: "default"}, Description: &wrappers.StringValue{Value: "notignored"}, - CreatedTime: h.GetCreateTime().GetTimestamp(), + CreatedTime: hs.GetCreateTime().GetTimestamp(), Type: "static", + HostIds: hIds, }, }, errCode: codes.OK, @@ -745,3 +761,327 @@ func TestUpdate(t *testing.T) { }) } } + +func TestAddHostSetHosts(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + + rw := db.New(conn) + repoFn := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + s, err := host_sets.NewService(repoFn) + require.NoError(t, err, "Error when getting new host set service.") + + hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] + hs := static.TestHosts(t, conn, hc.GetPublicId(), 4) + + addCases := []struct { + name string + setup func(*static.HostSet) + addHosts []string + resultHosts []string + }{ + { + name: "Add host on empty set", + setup: func(g *static.HostSet) {}, + addHosts: []string{hs[1].GetPublicId()}, + resultHosts: []string{hs[1].GetPublicId()}, + }, + { + name: "Add host on populated set", + setup: func(g *static.HostSet) { + static.TestSetMembers(t, conn, g.GetPublicId(), hs[:1]) + }, + addHosts: []string{hs[1].GetPublicId()}, + resultHosts: []string{hs[0].GetPublicId(), hs[1].GetPublicId()}, + }, + } + + for _, tc := range addCases { + t.Run(tc.name, func(t *testing.T) { + ss := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] + tc.setup(ss) + req := &pbs.AddHostSetHostsRequest{ + HostCatalogId: hc.GetPublicId(), + Id: ss.GetPublicId(), + Version: ss.GetVersion(), + HostIds: tc.addHosts, + } + + got, err := s.AddHostSetHosts(auth.DisabledAuthTestContext(auth.WithScopeId(proj.GetPublicId())), req) + s, ok := status.FromError(err) + require.True(t, ok) + require.NoError(t, err, "Got error: %v", s) + + assert.ElementsMatch(t, tc.resultHosts, got.GetItem().GetHostIds()) + }) + } + + ss := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] + + failCases := []struct { + name string + req *pbs.AddHostSetHostsRequest + errCode codes.Code + }{ + { + name: "Bad Set Id", + req: &pbs.AddHostSetHostsRequest{ + HostCatalogId: hc.GetPublicId(), + Id: "bad id", + Version: ss.GetVersion(), + HostIds: []string{hs[0].GetPublicId()}, + }, + errCode: codes.InvalidArgument, + }, + { + name: "Bad Catalog Id", + req: &pbs.AddHostSetHostsRequest{ + HostCatalogId: "bad catalog id", + Id: ss.GetPublicId(), + Version: ss.GetVersion(), + HostIds: []string{hs[0].GetPublicId()}, + }, + errCode: codes.InvalidArgument, + }, + { + name: "Empty host list", + req: &pbs.AddHostSetHostsRequest{ + HostCatalogId: hc.GetPublicId(), + Id: ss.GetPublicId(), + Version: ss.GetVersion(), + }, + errCode: codes.InvalidArgument, + }, + } + for _, tc := range failCases { + t.Run(tc.name, func(t *testing.T) { + assert := assert.New(t) + _, gErr := s.AddHostSetHosts(auth.DisabledAuthTestContext(auth.WithScopeId(proj.GetPublicId())), tc.req) + assert.Equal(tc.errCode, status.Code(gErr), "AddHostSetHosts(%+v) got error %v, wanted %v", tc.req, gErr, tc.errCode) + }) + } +} + +func TestSetHostSetHosts(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + + rw := db.New(conn) + repoFn := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + s, err := host_sets.NewService(repoFn) + require.NoError(t, err, "Error when getting new host set service.") + + hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] + hs := static.TestHosts(t, conn, hc.GetPublicId(), 4) + + setCases := []struct { + name string + setup func(*static.HostSet) + setHosts []string + resultHosts []string + }{ + { + name: "Set host on empty set", + setup: func(r *static.HostSet) {}, + setHosts: []string{hs[1].GetPublicId()}, + resultHosts: []string{hs[1].GetPublicId()}, + }, + { + name: "Set host on populated set", + setup: func(r *static.HostSet) { + static.TestSetMembers(t, conn, r.GetPublicId(), hs[:1]) + }, + setHosts: []string{hs[1].GetPublicId()}, + resultHosts: []string{hs[1].GetPublicId()}, + }, + { + name: "Set empty on populated set", + setup: func(r *static.HostSet) { + static.TestSetMembers(t, conn, r.GetPublicId(), hs[:2]) + }, + setHosts: []string{}, + resultHosts: nil, + }, + } + for _, tc := range setCases { + t.Run(tc.name, func(t *testing.T) { + ss := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] + tc.setup(ss) + req := &pbs.SetHostSetHostsRequest{ + HostCatalogId: hc.GetPublicId(), + Id: ss.GetPublicId(), + Version: ss.GetVersion(), + HostIds: tc.setHosts, + } + + got, err := s.SetHostSetHosts(auth.DisabledAuthTestContext(auth.WithScopeId(proj.GetPublicId())), req) + require.NoError(t, err, "Got error: %v", s) + assert.ElementsMatch(t, tc.resultHosts, got.GetItem().GetHostIds()) + }) + } + + ss := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] + + failCases := []struct { + name string + req *pbs.SetHostSetHostsRequest + errCode codes.Code + }{ + { + name: "Bad Set Id", + req: &pbs.SetHostSetHostsRequest{ + HostCatalogId: hc.GetPublicId(), + Id: "bad id", + Version: ss.GetVersion(), + HostIds: []string{hs[0].GetPublicId()}, + }, + errCode: codes.InvalidArgument, + }, + { + name: "Bad Catalog Id", + req: &pbs.SetHostSetHostsRequest{ + HostCatalogId: "bad catalog id", + Id: ss.GetPublicId(), + Version: ss.GetVersion(), + HostIds: []string{hs[0].GetPublicId()}, + }, + errCode: codes.InvalidArgument, + }, + } + for _, tc := range failCases { + t.Run(tc.name, func(t *testing.T) { + assert := assert.New(t) + _, gErr := s.SetHostSetHosts(auth.DisabledAuthTestContext(auth.WithScopeId(proj.GetPublicId())), tc.req) + assert.Equal(tc.errCode, status.Code(gErr), "SetHostSetHosts(%+v) got error %v, wanted %v", tc.req, gErr, tc.errCode) + }) + } +} + +func TestRemoveHostSetHosts(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + kms := kms.TestKms(t, conn, wrapper) + + _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + + rw := db.New(conn) + repoFn := func() (*static.Repository, error) { + return static.NewRepository(rw, rw, kms) + } + s, err := host_sets.NewService(repoFn) + require.NoError(t, err, "Error when getting new host set service.") + + hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] + hs := static.TestHosts(t, conn, hc.GetPublicId(), 4) + + removeCases := []struct { + name string + setup func(*static.HostSet) + removeHosts []string + resultHosts []string + wantErr bool + }{ + { + name: "Remove host on empty set", + setup: func(r *static.HostSet) {}, + removeHosts: []string{hs[1].GetPublicId()}, + wantErr: true, + }, + { + name: "Remove 1 of 2 hosts from set", + setup: func(r *static.HostSet) { + static.TestSetMembers(t, conn, r.GetPublicId(), hs[:2]) + }, + removeHosts: []string{hs[1].GetPublicId()}, + resultHosts: []string{hs[0].GetPublicId()}, + }, + { + name: "Remove all hosts from set", + setup: func(r *static.HostSet) { + static.TestSetMembers(t, conn, r.GetPublicId(), hs[:2]) + }, + removeHosts: []string{hs[0].GetPublicId(), hs[1].GetPublicId()}, + resultHosts: []string{}, + }, + } + + for _, tc := range removeCases { + t.Run(tc.name, func(t *testing.T) { + ss := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] + tc.setup(ss) + req := &pbs.RemoveHostSetHostsRequest{ + HostCatalogId: hc.GetPublicId(), + Id: ss.GetPublicId(), + Version: ss.GetVersion(), + HostIds: tc.removeHosts, + } + + got, err := s.RemoveHostSetHosts(auth.DisabledAuthTestContext(auth.WithScopeId(proj.GetPublicId())), req) + if tc.wantErr { + assert.Error(t, err) + return + } + s, ok := status.FromError(err) + require.True(t, ok) + require.NoError(t, err, "Got error: %v", s) + + assert.ElementsMatch(t, tc.resultHosts, got.GetItem().GetHostIds()) + }) + } + + ss := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] + + failCases := []struct { + name string + req *pbs.RemoveHostSetHostsRequest + errCode codes.Code + }{ + { + name: "Bad catalog Id", + req: &pbs.RemoveHostSetHostsRequest{ + HostCatalogId: "bad id", + Id: ss.GetPublicId(), + Version: ss.GetVersion(), + HostIds: []string{hs[0].GetPublicId()}, + }, + errCode: codes.InvalidArgument, + }, + { + name: "Bad set Id", + req: &pbs.RemoveHostSetHostsRequest{ + HostCatalogId: hc.GetPublicId(), + Id: "bad id", + Version: ss.GetVersion(), + HostIds: []string{hs[0].GetPublicId()}, + }, + errCode: codes.InvalidArgument, + }, + { + name: "empty hosts", + req: &pbs.RemoveHostSetHostsRequest{ + HostCatalogId: hc.GetPublicId(), + Id: "bad id", + Version: ss.GetVersion(), + HostIds: []string{}, + }, + errCode: codes.InvalidArgument, + }, + } + for _, tc := range failCases { + t.Run(tc.name, func(t *testing.T) { + assert := assert.New(t) + _, gErr := s.RemoveHostSetHosts(auth.DisabledAuthTestContext(auth.WithScopeId(proj.GetPublicId())), tc.req) + assert.Equal(tc.errCode, status.Code(gErr), "RemoveHostSetHosts(%+v) got error %v, wanted %v", tc.req, gErr, tc.errCode) + }) + } +} diff --git a/internal/servers/controller/handlers/hosts/host_service.go b/internal/servers/controller/handlers/hosts/host_service.go index b12b1b0d2..89859ed75 100644 --- a/internal/servers/controller/handlers/hosts/host_service.go +++ b/internal/servers/controller/handlers/hosts/host_service.go @@ -260,7 +260,7 @@ func (s Service) listFromRepo(ctx context.Context, catalogId string) ([]*pb.Host return outHl, nil } -func toProto(in *static.Host, members []*static.HostSetMember) (*pb.Host, error) { +func toProto(in *static.Host, members []*static.HostSet) (*pb.Host, error) { out := pb.Host{ Id: in.GetPublicId(), HostCatalogId: in.GetCatalogId(), @@ -276,7 +276,7 @@ func toProto(in *static.Host, members []*static.HostSetMember) (*pb.Host, error) out.Name = wrapperspb.String(in.GetName()) } for _, m := range members { - out.HostSetIds = append(out.HostSetIds, m.GetSetId()) + out.HostSetIds = append(out.HostSetIds, m.GetPublicId()) } st, err := handlers.ProtoToStruct(&pb.StaticHostAttributes{Address: wrapperspb.String(in.GetAddress())}) if err != nil { diff --git a/internal/types/action/action.go b/internal/types/action/action.go index bf8b54545..483d1f76c 100644 --- a/internal/types/action/action.go +++ b/internal/types/action/action.go @@ -27,6 +27,9 @@ const ( RemoveMembers Type = 18 SetPassword Type = 19 ChangePassword Type = 20 + AddHosts Type = 21 + SetHosts Type = 22 + RemoveHosts Type = 23 ) var Map = map[string]Type{ @@ -50,6 +53,9 @@ var Map = map[string]Type{ RemoveMembers.String(): RemoveMembers, SetPassword.String(): SetPassword, ChangePassword.String(): ChangePassword, + AddHosts.String(): AddHosts, + SetHosts.String(): SetHosts, + RemoveHosts.String(): RemoveHosts, } func (a Type) String() string { @@ -75,5 +81,8 @@ func (a Type) String() string { "remove-members", "set-password", "change-password", + "add-hosts", + "set-hosts", + "remove-hosts", }[a] }