Step 31: Proto + sync update for targeting.
Added only/never repeated string fields to ManifestEntry proto. Updated convert.go for round-trip. Targeting test in convert_test.go. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,8 @@ message ManifestEntry {
|
|||||||
string plaintext_hash = 7; // SHA-256 of plaintext (encrypted entries only)
|
string plaintext_hash = 7; // SHA-256 of plaintext (encrypted entries only)
|
||||||
bool encrypted = 8;
|
bool encrypted = 8;
|
||||||
bool locked = 9; // repo-authoritative; restore always overwrites
|
bool locked = 9; // repo-authoritative; restore always overwrites
|
||||||
|
repeated string only = 10; // per-machine targeting: only apply on matching
|
||||||
|
repeated string never = 11; // per-machine targeting: never apply on matching
|
||||||
}
|
}
|
||||||
|
|
||||||
// KekSlot describes a single KEK source for unwrapping the DEK.
|
// KekSlot describes a single KEK source for unwrapping the DEK.
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ func EntryToProto(e manifest.Entry) *sgardpb.ManifestEntry {
|
|||||||
PlaintextHash: e.PlaintextHash,
|
PlaintextHash: e.PlaintextHash,
|
||||||
Encrypted: e.Encrypted,
|
Encrypted: e.Encrypted,
|
||||||
Locked: e.Locked,
|
Locked: e.Locked,
|
||||||
|
Only: e.Only,
|
||||||
|
Never: e.Never,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +74,8 @@ func ProtoToEntry(p *sgardpb.ManifestEntry) manifest.Entry {
|
|||||||
PlaintextHash: p.GetPlaintextHash(),
|
PlaintextHash: p.GetPlaintextHash(),
|
||||||
Encrypted: p.GetEncrypted(),
|
Encrypted: p.GetEncrypted(),
|
||||||
Locked: p.GetLocked(),
|
Locked: p.GetLocked(),
|
||||||
|
Only: p.GetOnly(),
|
||||||
|
Never: p.GetNever(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,46 @@ func TestEmptyManifestRoundTrip(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTargetingRoundTrip(t *testing.T) {
|
||||||
|
now := time.Date(2026, 3, 24, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
onlyEntry := manifest.Entry{
|
||||||
|
Path: "~/.bashrc.linux",
|
||||||
|
Type: "file",
|
||||||
|
Hash: "abcd",
|
||||||
|
Only: []string{"os:linux", "tag:work"},
|
||||||
|
Updated: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
proto := EntryToProto(onlyEntry)
|
||||||
|
back := ProtoToEntry(proto)
|
||||||
|
|
||||||
|
if len(back.Only) != 2 || back.Only[0] != "os:linux" || back.Only[1] != "tag:work" {
|
||||||
|
t.Errorf("Only round-trip: got %v, want [os:linux tag:work]", back.Only)
|
||||||
|
}
|
||||||
|
if len(back.Never) != 0 {
|
||||||
|
t.Errorf("Never should be empty, got %v", back.Never)
|
||||||
|
}
|
||||||
|
|
||||||
|
neverEntry := manifest.Entry{
|
||||||
|
Path: "~/.config/heavy",
|
||||||
|
Type: "file",
|
||||||
|
Hash: "efgh",
|
||||||
|
Never: []string{"arch:arm64"},
|
||||||
|
Updated: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
proto2 := EntryToProto(neverEntry)
|
||||||
|
back2 := ProtoToEntry(proto2)
|
||||||
|
|
||||||
|
if len(back2.Never) != 1 || back2.Never[0] != "arch:arm64" {
|
||||||
|
t.Errorf("Never round-trip: got %v, want [arch:arm64]", back2.Never)
|
||||||
|
}
|
||||||
|
if len(back2.Only) != 0 {
|
||||||
|
t.Errorf("Only should be empty, got %v", back2.Only)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEntryEmptyOptionalFieldsRoundTrip(t *testing.T) {
|
func TestEntryEmptyOptionalFieldsRoundTrip(t *testing.T) {
|
||||||
now := time.Date(2026, 3, 1, 0, 0, 0, 0, time.UTC)
|
now := time.Date(2026, 3, 1, 0, 0, 0, 0, time.UTC)
|
||||||
e := manifest.Entry{
|
e := manifest.Entry{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.36.10
|
// protoc-gen-go v1.36.11
|
||||||
// protoc v6.32.1
|
// protoc v7.34.0
|
||||||
// source: sgard/v1/sgard.proto
|
// source: sgard/v1/sgard.proto
|
||||||
|
|
||||||
package sgardpb
|
package sgardpb
|
||||||
@@ -86,6 +86,8 @@ type ManifestEntry struct {
|
|||||||
PlaintextHash string `protobuf:"bytes,7,opt,name=plaintext_hash,json=plaintextHash,proto3" json:"plaintext_hash,omitempty"` // SHA-256 of plaintext (encrypted entries only)
|
PlaintextHash string `protobuf:"bytes,7,opt,name=plaintext_hash,json=plaintextHash,proto3" json:"plaintext_hash,omitempty"` // SHA-256 of plaintext (encrypted entries only)
|
||||||
Encrypted bool `protobuf:"varint,8,opt,name=encrypted,proto3" json:"encrypted,omitempty"`
|
Encrypted bool `protobuf:"varint,8,opt,name=encrypted,proto3" json:"encrypted,omitempty"`
|
||||||
Locked bool `protobuf:"varint,9,opt,name=locked,proto3" json:"locked,omitempty"` // repo-authoritative; restore always overwrites
|
Locked bool `protobuf:"varint,9,opt,name=locked,proto3" json:"locked,omitempty"` // repo-authoritative; restore always overwrites
|
||||||
|
Only []string `protobuf:"bytes,10,rep,name=only,proto3" json:"only,omitempty"` // per-machine targeting: only apply on matching
|
||||||
|
Never []string `protobuf:"bytes,11,rep,name=never,proto3" json:"never,omitempty"` // per-machine targeting: never apply on matching
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -183,6 +185,20 @@ func (x *ManifestEntry) GetLocked() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *ManifestEntry) GetOnly() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Only
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ManifestEntry) GetNever() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Never
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// KekSlot describes a single KEK source for unwrapping the DEK.
|
// KekSlot describes a single KEK source for unwrapping the DEK.
|
||||||
type KekSlot struct {
|
type KekSlot struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
@@ -1079,7 +1095,7 @@ var File_sgard_v1_sgard_proto protoreflect.FileDescriptor
|
|||||||
|
|
||||||
const file_sgard_v1_sgard_proto_rawDesc = "" +
|
const file_sgard_v1_sgard_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\x14sgard/v1/sgard.proto\x12\bsgard.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"\x8a\x02\n" +
|
"\x14sgard/v1/sgard.proto\x12\bsgard.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"\xb4\x02\n" +
|
||||||
"\rManifestEntry\x12\x12\n" +
|
"\rManifestEntry\x12\x12\n" +
|
||||||
"\x04path\x18\x01 \x01(\tR\x04path\x12\x12\n" +
|
"\x04path\x18\x01 \x01(\tR\x04path\x12\x12\n" +
|
||||||
"\x04hash\x18\x02 \x01(\tR\x04hash\x12\x12\n" +
|
"\x04hash\x18\x02 \x01(\tR\x04hash\x12\x12\n" +
|
||||||
@@ -1089,7 +1105,10 @@ const file_sgard_v1_sgard_proto_rawDesc = "" +
|
|||||||
"\aupdated\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\aupdated\x12%\n" +
|
"\aupdated\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\aupdated\x12%\n" +
|
||||||
"\x0eplaintext_hash\x18\a \x01(\tR\rplaintextHash\x12\x1c\n" +
|
"\x0eplaintext_hash\x18\a \x01(\tR\rplaintextHash\x12\x1c\n" +
|
||||||
"\tencrypted\x18\b \x01(\bR\tencrypted\x12\x16\n" +
|
"\tencrypted\x18\b \x01(\bR\tencrypted\x12\x16\n" +
|
||||||
"\x06locked\x18\t \x01(\bR\x06locked\"\xe4\x01\n" +
|
"\x06locked\x18\t \x01(\bR\x06locked\x12\x12\n" +
|
||||||
|
"\x04only\x18\n" +
|
||||||
|
" \x03(\tR\x04only\x12\x14\n" +
|
||||||
|
"\x05never\x18\v \x03(\tR\x05never\"\xe4\x01\n" +
|
||||||
"\aKekSlot\x12\x12\n" +
|
"\aKekSlot\x12\x12\n" +
|
||||||
"\x04type\x18\x01 \x01(\tR\x04type\x12\x1f\n" +
|
"\x04type\x18\x01 \x01(\tR\x04type\x12\x1f\n" +
|
||||||
"\vargon2_time\x18\x02 \x01(\x05R\n" +
|
"\vargon2_time\x18\x02 \x01(\x05R\n" +
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// - protoc-gen-go-grpc v1.5.1
|
// - protoc-gen-go-grpc v1.6.1
|
||||||
// - protoc v6.32.1
|
// - protoc v7.34.0
|
||||||
// source: sgard/v1/sgard.proto
|
// source: sgard/v1/sgard.proto
|
||||||
|
|
||||||
package sgardpb
|
package sgardpb
|
||||||
@@ -152,22 +152,22 @@ type GardenSyncServer interface {
|
|||||||
type UnimplementedGardenSyncServer struct{}
|
type UnimplementedGardenSyncServer struct{}
|
||||||
|
|
||||||
func (UnimplementedGardenSyncServer) Authenticate(context.Context, *AuthenticateRequest) (*AuthenticateResponse, error) {
|
func (UnimplementedGardenSyncServer) Authenticate(context.Context, *AuthenticateRequest) (*AuthenticateResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Authenticate not implemented")
|
return nil, status.Error(codes.Unimplemented, "method Authenticate not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedGardenSyncServer) PushManifest(context.Context, *PushManifestRequest) (*PushManifestResponse, error) {
|
func (UnimplementedGardenSyncServer) PushManifest(context.Context, *PushManifestRequest) (*PushManifestResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method PushManifest not implemented")
|
return nil, status.Error(codes.Unimplemented, "method PushManifest not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedGardenSyncServer) PushBlobs(grpc.ClientStreamingServer[PushBlobsRequest, PushBlobsResponse]) error {
|
func (UnimplementedGardenSyncServer) PushBlobs(grpc.ClientStreamingServer[PushBlobsRequest, PushBlobsResponse]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method PushBlobs not implemented")
|
return status.Error(codes.Unimplemented, "method PushBlobs not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedGardenSyncServer) PullManifest(context.Context, *PullManifestRequest) (*PullManifestResponse, error) {
|
func (UnimplementedGardenSyncServer) PullManifest(context.Context, *PullManifestRequest) (*PullManifestResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method PullManifest not implemented")
|
return nil, status.Error(codes.Unimplemented, "method PullManifest not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedGardenSyncServer) PullBlobs(*PullBlobsRequest, grpc.ServerStreamingServer[PullBlobsResponse]) error {
|
func (UnimplementedGardenSyncServer) PullBlobs(*PullBlobsRequest, grpc.ServerStreamingServer[PullBlobsResponse]) error {
|
||||||
return status.Errorf(codes.Unimplemented, "method PullBlobs not implemented")
|
return status.Error(codes.Unimplemented, "method PullBlobs not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedGardenSyncServer) Prune(context.Context, *PruneRequest) (*PruneResponse, error) {
|
func (UnimplementedGardenSyncServer) Prune(context.Context, *PruneRequest) (*PruneResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Prune not implemented")
|
return nil, status.Error(codes.Unimplemented, "method Prune not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedGardenSyncServer) mustEmbedUnimplementedGardenSyncServer() {}
|
func (UnimplementedGardenSyncServer) mustEmbedUnimplementedGardenSyncServer() {}
|
||||||
func (UnimplementedGardenSyncServer) testEmbeddedByValue() {}
|
func (UnimplementedGardenSyncServer) testEmbeddedByValue() {}
|
||||||
@@ -180,7 +180,7 @@ type UnsafeGardenSyncServer interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RegisterGardenSyncServer(s grpc.ServiceRegistrar, srv GardenSyncServer) {
|
func RegisterGardenSyncServer(s grpc.ServiceRegistrar, srv GardenSyncServer) {
|
||||||
// If the following call pancis, it indicates UnimplementedGardenSyncServer was
|
// If the following call panics, it indicates UnimplementedGardenSyncServer was
|
||||||
// embedded by pointer and is nil. This will cause panics if an
|
// embedded by pointer and is nil. This will cause panics if an
|
||||||
// unimplemented method is ever invoked, so we test this at initialization
|
// unimplemented method is ever invoked, so we test this at initialization
|
||||||
// time to prevent it from happening at runtime later due to I/O.
|
// time to prevent it from happening at runtime later due to I/O.
|
||||||
|
|||||||
Reference in New Issue
Block a user