mirror of https://github.com/hashicorp/consul
resource: owner references must include a uid (#17169)
parent
e02ef16f02
commit
eff5dd1812
|
@ -119,6 +119,31 @@ func (s *Server) Write(ctx context.Context, req *pbresource.WriteRequest) (*pbre
|
||||||
return errUseWriteStatus
|
return errUseWriteStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generally, we expect resources with owners to be created by controllers,
|
||||||
|
// and they should provide the Uid. In cases where no Uid is given (e.g. the
|
||||||
|
// owner is specified in the resource HCL) we'll look up whatever the current
|
||||||
|
// Uid is and use that.
|
||||||
|
//
|
||||||
|
// An important note on consistency:
|
||||||
|
//
|
||||||
|
// We read the owner with StrongConsistency here to reduce the likelihood of
|
||||||
|
// creating a resource pointing to the wrong "incarnation" of the owner in
|
||||||
|
// cases where the owner is deleted and re-created in quick succession.
|
||||||
|
//
|
||||||
|
// That said, there is still a chance that the owner has been deleted by the
|
||||||
|
// time we write this resource. This is not a relational database and we do
|
||||||
|
// not support ACID transactions or real foreign key constraints.
|
||||||
|
if input.Owner != nil && input.Owner.Uid == "" {
|
||||||
|
owner, err := s.Backend.Read(ctx, storage.StrongConsistency, input.Owner)
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, storage.ErrNotFound):
|
||||||
|
return status.Error(codes.InvalidArgument, "resource.owner does not exist")
|
||||||
|
case err != nil:
|
||||||
|
return status.Errorf(codes.Internal, "failed to resolve owner: %v", err)
|
||||||
|
}
|
||||||
|
input.Owner = owner.Id
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(spatel): Revisit owner<->resource tenancy rules post-1.16
|
// TODO(spatel): Revisit owner<->resource tenancy rules post-1.16
|
||||||
|
|
||||||
// Update path.
|
// Update path.
|
||||||
|
|
|
@ -491,6 +491,57 @@ func TestWrite_Owner_Immutable(t *testing.T) {
|
||||||
require.ErrorContains(t, err, "owner cannot be changed")
|
require.ErrorContains(t, err, "owner cannot be changed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWrite_Owner_Uid(t *testing.T) {
|
||||||
|
server := testServer(t)
|
||||||
|
client := testClient(t, server)
|
||||||
|
|
||||||
|
demo.RegisterTypes(server.Registry)
|
||||||
|
|
||||||
|
t.Run("uid given", func(t *testing.T) {
|
||||||
|
artist, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
album, err := demo.GenerateV2Album(artist.Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
album.Owner.Uid = ulid.Make().String()
|
||||||
|
|
||||||
|
_, err = client.Write(testContext(t), &pbresource.WriteRequest{Resource: album})
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("no uid - owner not found", func(t *testing.T) {
|
||||||
|
artist, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
album, err := demo.GenerateV2Album(artist.Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = client.Write(testContext(t), &pbresource.WriteRequest{Resource: album})
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("no uid - automatically resolved", func(t *testing.T) {
|
||||||
|
artist, err := demo.GenerateV2Artist()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
rsp1, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: artist})
|
||||||
|
require.NoError(t, err)
|
||||||
|
artist = rsp1.Resource
|
||||||
|
|
||||||
|
album, err := demo.GenerateV2Album(clone(artist.Id))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Blank out the owner Uid to check it gets automatically filled in.
|
||||||
|
album.Owner.Uid = ""
|
||||||
|
|
||||||
|
rsp2, err := client.Write(testContext(t), &pbresource.WriteRequest{Resource: album})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, rsp2.Resource.Owner.Uid)
|
||||||
|
require.Equal(t, artist.Id.Uid, rsp2.Resource.Owner.Uid)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type blockOnceBackend struct {
|
type blockOnceBackend struct {
|
||||||
storage.Backend
|
storage.Backend
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue