From 5ea21a7baf74990c5ddbf1e08cee2d78590c50e5 Mon Sep 17 00:00:00 2001 From: Thibault NORMAND Date: Sat, 6 Feb 2021 12:27:20 +0100 Subject: [PATCH 1/4] feat(patch): package removal feature. --- api/gen/go/harp/bundle/v1/patch.pb.go | 99 +++++++++++++++------------ api/proto/harp/bundle/v1/patch.proto | 2 + pkg/bundle/patch/executor.go | 15 +++- 3 files changed, 71 insertions(+), 45 deletions(-) diff --git a/api/gen/go/harp/bundle/v1/patch.pb.go b/api/gen/go/harp/bundle/v1/patch.pb.go index 20245f2c..260c8d83 100644 --- a/api/gen/go/harp/bundle/v1/patch.pb.go +++ b/api/gen/go/harp/bundle/v1/patch.pb.go @@ -474,6 +474,8 @@ type PatchPackage struct { Labels *PatchOperation `protobuf:"bytes,3,opt,name=labels,proto3" json:"labels,omitempty"` // Secret data operations. Data *PatchSecret `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` + // Flag as remove. + Remove bool `protobuf:"varint,5,opt,name=remove,proto3" json:"remove,omitempty"` } func (x *PatchPackage) Reset() { @@ -536,6 +538,13 @@ func (x *PatchPackage) GetData() *PatchSecret { return nil } +func (x *PatchPackage) GetRemove() bool { + if x != nil { + return x.Remove + } + return false +} + // PatchSecret represents secret data operations. type PatchSecret struct { state protoimpl.MessageState @@ -729,7 +738,7 @@ var file_harp_bundle_v1_patch_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x22, 0x2e, 0x0a, 0x10, 0x50, 0x61, 0x74, 0x63, 0x68, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x22, 0xef, 0x01, 0x0a, 0x0c, + 0x09, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x22, 0x87, 0x02, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x63, 0x68, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x68, 0x61, 0x72, 0x70, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, @@ -744,49 +753,51 @@ var file_harp_bundle_v1_patch_proto_rawDesc = []byte{ 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x2f, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x68, 0x61, 0x72, 0x70, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, - 0x68, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xd3, 0x01, - 0x0a, 0x0b, 0x50, 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x40, 0x0a, - 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x68, 0x61, 0x72, 0x70, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, - 0x36, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1e, 0x2e, 0x68, 0x61, 0x72, 0x70, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x12, 0x2e, 0x0a, 0x02, 0x6b, 0x76, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1e, 0x2e, 0x68, 0x61, 0x72, 0x70, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x02, 0x6b, 0x76, 0x22, 0x9a, 0x02, 0x0a, 0x0e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x03, 0x61, 0x64, 0x64, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x68, 0x61, 0x72, 0x70, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x61, 0x64, - 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x42, 0x0a, 0x06, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x68, 0x61, 0x72, 0x70, - 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, - 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x1a, 0x36, 0x0a, - 0x08, 0x41, 0x64, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x42, 0x9e, 0x01, 0x0a, 0x2a, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x65, 0x6c, 0x61, 0x73, 0x74, 0x69, 0x63, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x73, 0x65, 0x63, - 0x2e, 0x68, 0x61, 0x72, 0x70, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x42, - 0x0a, 0x50, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x6c, 0x61, 0x73, 0x74, 0x69, - 0x63, 0x2f, 0x68, 0x61, 0x72, 0x70, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, - 0x6f, 0x2f, 0x68, 0x61, 0x72, 0x70, 0x2f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2f, 0x76, 0x31, - 0x3b, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x53, 0x42, 0x58, 0xaa, - 0x02, 0x0e, 0x68, 0x61, 0x72, 0x70, 0x2e, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x56, 0x31, - 0xca, 0x02, 0x0e, 0x68, 0x61, 0x72, 0x70, 0x5c, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5c, 0x56, - 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x68, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, + 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x22, 0xd3, 0x01, 0x0a, 0x0b, 0x50, 0x61, 0x74, 0x63, 0x68, 0x53, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x40, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x68, 0x61, 0x72, + 0x70, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, + 0x68, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x36, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x68, 0x61, 0x72, 0x70, 0x2e, 0x62, + 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, + 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x2e, 0x0a, 0x02, 0x6b, + 0x76, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x68, 0x61, 0x72, 0x70, 0x2e, 0x62, + 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x02, 0x6b, 0x76, 0x22, 0x9a, 0x02, 0x0a, 0x0e, + 0x50, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, + 0x0a, 0x03, 0x61, 0x64, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x68, 0x61, + 0x72, 0x70, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, + 0x63, 0x68, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x64, 0x64, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x61, 0x64, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x12, 0x42, 0x0a, 0x06, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x68, 0x61, 0x72, 0x70, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x1a, 0x36, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, + 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x9e, 0x01, 0x0a, 0x2a, 0x63, 0x6f, 0x6d, + 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x65, 0x6c, 0x61, 0x73, 0x74, 0x69, 0x63, 0x2e, + 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x73, 0x65, 0x63, 0x2e, 0x68, 0x61, 0x72, 0x70, 0x2e, 0x62, 0x75, + 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x50, 0x61, 0x74, 0x63, 0x68, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x65, 0x6c, 0x61, 0x73, 0x74, 0x69, 0x63, 0x2f, 0x68, 0x61, 0x72, 0x70, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x68, 0x61, 0x72, 0x70, 0x2f, 0x62, + 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x76, + 0x31, 0xa2, 0x02, 0x03, 0x53, 0x42, 0x58, 0xaa, 0x02, 0x0e, 0x68, 0x61, 0x72, 0x70, 0x2e, 0x42, + 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0e, 0x68, 0x61, 0x72, 0x70, 0x5c, + 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5c, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/api/proto/harp/bundle/v1/patch.proto b/api/proto/harp/bundle/v1/patch.proto index df1c301d..e4df7cbb 100644 --- a/api/proto/harp/bundle/v1/patch.proto +++ b/api/proto/harp/bundle/v1/patch.proto @@ -99,6 +99,8 @@ message PatchPackage { PatchOperation labels = 3; // Secret data operations. PatchSecret data = 4; + // Flag as remove. + bool remove = 5; } // PatchSecret represents secret data operations. diff --git a/pkg/bundle/patch/executor.go b/pkg/bundle/patch/executor.go index e56b72dc..36b55a76 100644 --- a/pkg/bundle/patch/executor.go +++ b/pkg/bundle/patch/executor.go @@ -51,8 +51,18 @@ func executeRule(patchName string, r *bundlev1.PatchRule, b *bundlev1.Bundle, va return fmt.Errorf("unable to compile selector: %w", err) } - // Browse all packages + pkgs := []*bundlev1.Package{} + + // Process deletion first for _, p := range b.Packages { + // Package match selector specification + if s.IsSatisfiedBy(p) && !r.Package.Remove { + pkgs = append(pkgs, p) + } + } + + // Browse all packages + for _, p := range pkgs { // Package match selector specification if s.IsSatisfiedBy(p) { // Apply patch @@ -66,6 +76,9 @@ func executeRule(patchName string, r *bundlev1.PatchRule, b *bundlev1.Bundle, va } } + // Reassign packages + b.Packages = pkgs + // No error return nil } From 29532536b1739760c531efab5ab4397c9f0b93b2 Mon Sep 17 00:00:00 2001 From: Thibault NORMAND Date: Sat, 6 Feb 2021 13:51:35 +0100 Subject: [PATCH 2/4] feat(diff): patch generator. --- pkg/bundle/compare/patch.go | 98 +++++++++++++ pkg/bundle/compare/patch_test.go | 236 +++++++++++++++++++++++++++++++ 2 files changed, 334 insertions(+) create mode 100644 pkg/bundle/compare/patch.go create mode 100644 pkg/bundle/compare/patch_test.go diff --git a/pkg/bundle/compare/patch.go b/pkg/bundle/compare/patch.go new file mode 100644 index 00000000..93aa8f51 --- /dev/null +++ b/pkg/bundle/compare/patch.go @@ -0,0 +1,98 @@ +package compare + +import ( + "fmt" + "strings" + + bundlev1 "github.com/elastic/harp/api/gen/go/harp/bundle/v1" +) + +// ToPatch convert oplog to a bundle patch. +func ToPatch(oplog []DiffItem) (*bundlev1.Patch, error) { + // Check arguments + if len(oplog) == 0 { + return nil, fmt.Errorf("unable to generate a patch with an empty oplog") + } + + res := &bundlev1.Patch{ + ApiVersion: "harp.elastic.co/v1", + Kind: "BundlePatch", + Meta: &bundlev1.PatchMeta{ + Name: "autogenerated-patch", + Description: "Patch generated from oplog", + }, + Spec: &bundlev1.PatchSpec{ + Rules: []*bundlev1.PatchRule{}, + }, + } + + secretMap := map[string]*bundlev1.PatchRule{} + + // Generate patch rules + for _, op := range oplog { + if op.Type == "package" { + if op.Operation == Remove { + res.Spec.Rules = append(res.Spec.Rules, &bundlev1.PatchRule{ + Selector: &bundlev1.PatchSelector{ + MatchPath: &bundlev1.PatchSelectorMatchPath{ + Strict: op.Path, + }, + }, + Package: &bundlev1.PatchPackage{ + Remove: true, + }, + }) + } + continue + } + if op.Type == "secret" { + pathParts := strings.SplitN(op.Path, "#", 2) + pkgRule, ok := secretMap[pathParts[0]] + if !ok { + secretMap[pathParts[0]] = &bundlev1.PatchRule{ + Selector: &bundlev1.PatchSelector{ + MatchPath: &bundlev1.PatchSelectorMatchPath{ + Strict: pathParts[0], + }, + }, + Package: &bundlev1.PatchPackage{ + Data: &bundlev1.PatchSecret{ + Kv: &bundlev1.PatchOperation{}, + }, + }, + } + pkgRule = secretMap[pathParts[0]] + } + + switch op.Operation { + case Add: + if pkgRule.Package.Data.Kv.Add == nil { + pkgRule.Package.Data.Kv.Add = map[string]string{} + } + pkgRule.Package.Data.Kv.Add[pathParts[1]] = op.Value + case Replace: + if pkgRule.Package.Data.Kv.Update == nil { + pkgRule.Package.Data.Kv.Update = map[string]string{} + } + pkgRule.Package.Data.Kv.Update[pathParts[1]] = op.Value + case Remove: + if pkgRule.Package.Data.Kv.Remove == nil { + pkgRule.Package.Data.Kv.Remove = []string{} + } + pkgRule.Package.Data.Kv.Remove = append(pkgRule.Package.Data.Kv.Remove, pathParts[1]) + } + } + } + + // Add grouped secret patches + for _, r := range secretMap { + if r == nil { + continue + } + + res.Spec.Rules = append(res.Spec.Rules, r) + } + + // No error + return res, nil +} diff --git a/pkg/bundle/compare/patch_test.go b/pkg/bundle/compare/patch_test.go new file mode 100644 index 00000000..705430ef --- /dev/null +++ b/pkg/bundle/compare/patch_test.go @@ -0,0 +1,236 @@ +package compare + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + bundlev1 "github.com/elastic/harp/api/gen/go/harp/bundle/v1" +) + +var ( + ignoreOpts = []cmp.Option{ + cmpopts.IgnoreUnexported(bundlev1.Patch{}), + cmpopts.IgnoreUnexported(bundlev1.PatchMeta{}), + cmpopts.IgnoreUnexported(bundlev1.PatchSpec{}), + cmpopts.IgnoreUnexported(bundlev1.PatchRule{}), + cmpopts.IgnoreUnexported(bundlev1.PatchSecret{}), + cmpopts.IgnoreUnexported(bundlev1.PatchSelector{}), + cmpopts.IgnoreUnexported(bundlev1.PatchSelectorMatchPath{}), + cmpopts.IgnoreUnexported(bundlev1.PatchPackage{}), + cmpopts.IgnoreUnexported(bundlev1.PatchOperation{}), + } +) + +func TestToPatch(t *testing.T) { + type args struct { + oplog []DiffItem + } + tests := []struct { + name string + args args + want *bundlev1.Patch + wantErr bool + }{ + { + name: "nil", + wantErr: true, + }, + { + name: "empty", + args: args{ + oplog: []DiffItem{}, + }, + wantErr: true, + }, + { + name: "package added", + args: args{ + oplog: []DiffItem{ + {Operation: Add, Type: "package", Path: "application/test"}, + }, + }, + wantErr: false, + want: &bundlev1.Patch{ + ApiVersion: "harp.elastic.co/v1", + Kind: "BundlePatch", + Meta: &bundlev1.PatchMeta{ + Name: "autogenerated-patch", + Description: "Patch generated from oplog", + }, + Spec: &bundlev1.PatchSpec{ + Rules: []*bundlev1.PatchRule{}, + }, + }, + }, + { + name: "package removed", + args: args{ + oplog: []DiffItem{ + {Operation: Remove, Type: "package", Path: "application/test"}, + }, + }, + wantErr: false, + want: &bundlev1.Patch{ + ApiVersion: "harp.elastic.co/v1", + Kind: "BundlePatch", + Meta: &bundlev1.PatchMeta{ + Name: "autogenerated-patch", + Description: "Patch generated from oplog", + }, + Spec: &bundlev1.PatchSpec{ + Rules: []*bundlev1.PatchRule{ + { + Selector: &bundlev1.PatchSelector{ + MatchPath: &bundlev1.PatchSelectorMatchPath{ + Strict: "application/test", + }, + }, + Package: &bundlev1.PatchPackage{ + Remove: true, + }, + }, + }, + }, + }, + }, + { + name: "secret added", + args: args{ + oplog: []DiffItem{ + {Operation: Add, Type: "secret", Path: "application/test#key1", Value: "payload"}, + {Operation: Add, Type: "secret", Path: "application/test#key2", Value: "payload"}, + }, + }, + wantErr: false, + want: &bundlev1.Patch{ + ApiVersion: "harp.elastic.co/v1", + Kind: "BundlePatch", + Meta: &bundlev1.PatchMeta{ + Name: "autogenerated-patch", + Description: "Patch generated from oplog", + }, + Spec: &bundlev1.PatchSpec{ + Rules: []*bundlev1.PatchRule{ + { + Selector: &bundlev1.PatchSelector{ + MatchPath: &bundlev1.PatchSelectorMatchPath{ + Strict: "application/test", + }, + }, + Package: &bundlev1.PatchPackage{ + Data: &bundlev1.PatchSecret{ + Kv: &bundlev1.PatchOperation{ + Add: map[string]string{ + "key1": "payload", + "key2": "payload", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "secret updated", + args: args{ + oplog: []DiffItem{ + {Operation: Add, Type: "secret", Path: "application/test#key1", Value: "payload"}, + {Operation: Replace, Type: "secret", Path: "application/test#key2", Value: "payload"}, + }, + }, + wantErr: false, + want: &bundlev1.Patch{ + ApiVersion: "harp.elastic.co/v1", + Kind: "BundlePatch", + Meta: &bundlev1.PatchMeta{ + Name: "autogenerated-patch", + Description: "Patch generated from oplog", + }, + Spec: &bundlev1.PatchSpec{ + Rules: []*bundlev1.PatchRule{ + { + Selector: &bundlev1.PatchSelector{ + MatchPath: &bundlev1.PatchSelectorMatchPath{ + Strict: "application/test", + }, + }, + Package: &bundlev1.PatchPackage{ + Data: &bundlev1.PatchSecret{ + Kv: &bundlev1.PatchOperation{ + Add: map[string]string{ + "key1": "payload", + }, + Update: map[string]string{ + "key2": "payload", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "secret removed", + args: args{ + oplog: []DiffItem{ + {Operation: Add, Type: "secret", Path: "application/test#key1", Value: "payload"}, + {Operation: Replace, Type: "secret", Path: "application/test#key2", Value: "payload"}, + {Operation: Remove, Type: "secret", Path: "application/test#key3"}, + }, + }, + wantErr: false, + want: &bundlev1.Patch{ + ApiVersion: "harp.elastic.co/v1", + Kind: "BundlePatch", + Meta: &bundlev1.PatchMeta{ + Name: "autogenerated-patch", + Description: "Patch generated from oplog", + }, + Spec: &bundlev1.PatchSpec{ + Rules: []*bundlev1.PatchRule{ + { + Selector: &bundlev1.PatchSelector{ + MatchPath: &bundlev1.PatchSelectorMatchPath{ + Strict: "application/test", + }, + }, + Package: &bundlev1.PatchPackage{ + Data: &bundlev1.PatchSecret{ + Kv: &bundlev1.PatchOperation{ + Add: map[string]string{ + "key1": "payload", + }, + Update: map[string]string{ + "key2": "payload", + }, + Remove: []string{ + "key3", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ToPatch(tt.args.oplog) + if (err != nil) != tt.wantErr { + t.Errorf("ToPatch() error = %v, wantErr %v", err, tt.wantErr) + return + } + if diff := cmp.Diff(got, tt.want, ignoreOpts...); diff != "" { + t.Errorf("%q. ToPatch():\n-got/+want\ndiff %s", tt.name, diff) + } + }) + } +} From 0c861b1c24de308fa5552c01736ca55cde1d3724 Mon Sep 17 00:00:00 2001 From: Thibault NORMAND Date: Sat, 6 Feb 2021 14:46:19 +0100 Subject: [PATCH 3/4] feat(diff): oplog as patch. --- cmd/harp/internal/cmd/bundle_diff.go | 7 +++++-- pkg/bundle/codec.go | 18 ----------------- pkg/bundle/compare/patch.go | 17 ++++++++++++++++ pkg/bundle/compare/patch_test.go | 17 ++++++++++++++++ pkg/bundle/patch/executor.go | 15 +------------- pkg/bundle/patch/package.go | 17 +++++++++------- pkg/tasks/bundle/diff.go | 30 +++++++++++++++++++++++++--- pkg/tasks/bundle/patch.go | 5 +++-- 8 files changed, 80 insertions(+), 46 deletions(-) diff --git a/cmd/harp/internal/cmd/bundle_diff.go b/cmd/harp/internal/cmd/bundle_diff.go index 7a0da27f..8a9ebe7e 100644 --- a/cmd/harp/internal/cmd/bundle_diff.go +++ b/cmd/harp/internal/cmd/bundle_diff.go @@ -32,6 +32,7 @@ var bundleDiffCmd = func() *cobra.Command { var ( sourcePath string destinationPath string + generatePatch bool ) cmd := &cobra.Command{ @@ -47,6 +48,7 @@ var bundleDiffCmd = func() *cobra.Command { SourceReader: cmdutil.FileReader(sourcePath), DestinationReader: cmdutil.FileReader(destinationPath), OutputWriter: cmdutil.StdoutWriter(), + GeneratePatch: generatePatch, } // Run the task @@ -57,9 +59,10 @@ var bundleDiffCmd = func() *cobra.Command { } // Parameters - cmd.Flags().StringVar(&sourcePath, "src", "", "Container path ('-' for stdin or filename)") - cmd.Flags().StringVar(&destinationPath, "dst", "", "Container path") + cmd.Flags().StringVar(&sourcePath, "old", "", "Container path ('-' for stdin or filename)") + cmd.Flags().StringVar(&destinationPath, "new", "", "Container path ('-' for stdin or filename)") log.CheckErr("unable to mark 'dst' flag as required.", cmd.MarkFlagRequired("dst")) + cmd.Flags().BoolVar(&generatePatch, "patch", false, "Output as a bundle patch") return cmd } diff --git a/pkg/bundle/codec.go b/pkg/bundle/codec.go index fac4df96..3c5a19a8 100644 --- a/pkg/bundle/codec.go +++ b/pkg/bundle/codec.go @@ -34,7 +34,6 @@ import ( "google.golang.org/protobuf/proto" bundlev1 "github.com/elastic/harp/api/gen/go/harp/bundle/v1" - "github.com/elastic/harp/pkg/bundle/compare" "github.com/elastic/harp/pkg/bundle/secret" csov1 "github.com/elastic/harp/pkg/cso/v1" "github.com/elastic/harp/pkg/sdk/security" @@ -378,20 +377,3 @@ func JSON(w io.Writer, b *bundlev1.Bundle) error { // No error return nil } - -// Diff calculates bundle differences. -func Diff(src, dst *bundlev1.Bundle) (string, error) { - diffs, err := compare.Diff(src, dst) - if err != nil { - return "", fmt.Errorf("unable to compute bundle differences: %w", err) - } - - // Serialize as json - jsonPatch, err := json.Marshal(diffs) - if err != nil { - return "", fmt.Errorf("unable to serialize diff result: %w", err) - } - - // No error - return string(jsonPatch), nil -} diff --git a/pkg/bundle/compare/patch.go b/pkg/bundle/compare/patch.go index 93aa8f51..0a173dbf 100644 --- a/pkg/bundle/compare/patch.go +++ b/pkg/bundle/compare/patch.go @@ -1,3 +1,20 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + package compare import ( diff --git a/pkg/bundle/compare/patch_test.go b/pkg/bundle/compare/patch_test.go index 705430ef..81910b41 100644 --- a/pkg/bundle/compare/patch_test.go +++ b/pkg/bundle/compare/patch_test.go @@ -1,3 +1,20 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + package compare import ( diff --git a/pkg/bundle/patch/executor.go b/pkg/bundle/patch/executor.go index 36b55a76..e56b72dc 100644 --- a/pkg/bundle/patch/executor.go +++ b/pkg/bundle/patch/executor.go @@ -51,18 +51,8 @@ func executeRule(patchName string, r *bundlev1.PatchRule, b *bundlev1.Bundle, va return fmt.Errorf("unable to compile selector: %w", err) } - pkgs := []*bundlev1.Package{} - - // Process deletion first - for _, p := range b.Packages { - // Package match selector specification - if s.IsSatisfiedBy(p) && !r.Package.Remove { - pkgs = append(pkgs, p) - } - } - // Browse all packages - for _, p := range pkgs { + for _, p := range b.Packages { // Package match selector specification if s.IsSatisfiedBy(p) { // Apply patch @@ -76,9 +66,6 @@ func executeRule(patchName string, r *bundlev1.PatchRule, b *bundlev1.Bundle, va } } - // Reassign packages - b.Packages = pkgs - // No error return nil } diff --git a/pkg/bundle/patch/package.go b/pkg/bundle/patch/package.go index eb5de448..c35c4987 100644 --- a/pkg/bundle/patch/package.go +++ b/pkg/bundle/patch/package.go @@ -75,27 +75,30 @@ func Checksum(spec *bundlev1.Patch) (string, error) { } // Apply given patch to the given bundle. -func Apply(spec *bundlev1.Patch, b *bundlev1.Bundle, values map[string]interface{}) error { +func Apply(spec *bundlev1.Patch, b *bundlev1.Bundle, values map[string]interface{}) (*bundlev1.Bundle, error) { // Validate spec if err := Validate(spec); err != nil { - return fmt.Errorf("unable to validate spec: %w", err) + return b, fmt.Errorf("unable to validate spec: %w", err) } if b == nil { - return fmt.Errorf("cannot process nil bundle") + return b, fmt.Errorf("cannot process nil bundle") } // Prepare selectors if len(spec.Spec.Rules) == 0 { - return fmt.Errorf("empty bundle patch") + return b, fmt.Errorf("empty bundle patch") } + // Copy bundle + bCopy := proto.Clone(b).(*bundlev1.Bundle) + // Process all rules for i, r := range spec.Spec.Rules { - if err := executeRule(spec.Meta.Name, r, b, values); err != nil { - return fmt.Errorf("unable to execute rule index %d: %w", i, err) + if err := executeRule(spec.Meta.Name, r, bCopy, values); err != nil { + return b, fmt.Errorf("unable to execute rule index %d: %w", i, err) } } // No error - return nil + return bCopy, nil } diff --git a/pkg/tasks/bundle/diff.go b/pkg/tasks/bundle/diff.go index a135b306..f25f6978 100644 --- a/pkg/tasks/bundle/diff.go +++ b/pkg/tasks/bundle/diff.go @@ -19,9 +19,13 @@ package bundle import ( "context" + "encoding/json" "fmt" + "sigs.k8s.io/yaml" + "github.com/elastic/harp/pkg/bundle" + "github.com/elastic/harp/pkg/bundle/compare" "github.com/elastic/harp/pkg/tasks" ) @@ -30,6 +34,7 @@ type DiffTask struct { SourceReader tasks.ReaderProvider DestinationReader tasks.ReaderProvider OutputWriter tasks.WriterProvider + GeneratePatch bool } // Run the task. @@ -59,7 +64,7 @@ func (t *DiffTask) Run(ctx context.Context) error { } // Calculate diff - report, err := bundle.Diff(bSrc, bDst) + report, err := compare.Diff(bSrc, bDst) if err != nil { return fmt.Errorf("unable to calculate bundle difference: %w", err) } @@ -70,8 +75,27 @@ func (t *DiffTask) Run(ctx context.Context) error { return fmt.Errorf("unable to open output writer: %w", err) } - // Print report - fmt.Fprintln(writer, report) + if !t.GeneratePatch { + // Encode as JSON + if err := json.NewEncoder(writer).Encode(report); err != nil { + return fmt.Errorf("unable to marshal JSON OpLog: %w", err) + } + } else { + // Convert optlog as a patch + patch, err := compare.ToPatch(report) + if err != nil { + return fmt.Errorf("unable to convert oplog as a bundle patch: %w", err) + } + + // Marshal YAML Patch + out, err := yaml.Marshal(patch) + if err != nil { + return fmt.Errorf("unable to marshal patch as YAML: %w", err) + } + + // Write output + fmt.Fprintln(writer, string(out)) + } // No error return nil diff --git a/pkg/tasks/bundle/patch.go b/pkg/tasks/bundle/patch.go index 3538f8be..bd92f8f6 100644 --- a/pkg/tasks/bundle/patch.go +++ b/pkg/tasks/bundle/patch.go @@ -61,7 +61,8 @@ func (t *PatchTask) Run(ctx context.Context) error { } // Apply the patch speicification to generate an output bundle - if err = patch.Apply(spec, b, t.Values); err != nil { + patchedBundle, err := patch.Apply(spec, b, t.Values) + if err != nil { return fmt.Errorf("unable to generate output bundle from patch: %w", err) } @@ -72,7 +73,7 @@ func (t *PatchTask) Run(ctx context.Context) error { } // Dump all content - if err = bundle.ToContainerWriter(outputWriter, b); err != nil { + if err = bundle.ToContainerWriter(outputWriter, patchedBundle); err != nil { return fmt.Errorf("unable to dump bundle content: %w", err) } From d962d72924231c56933e91c15057269de102947a Mon Sep 17 00:00:00 2001 From: Thibault NORMAND Date: Sat, 6 Feb 2021 15:20:36 +0100 Subject: [PATCH 4/4] feat(diff): package removal feature. --- pkg/bundle/patch/executor.go | 49 +++++++++++++++++++------------ pkg/bundle/patch/executor_test.go | 42 +++++++++++--------------- pkg/bundle/patch/package.go | 16 ++++++++-- 3 files changed, 61 insertions(+), 46 deletions(-) diff --git a/pkg/bundle/patch/executor.go b/pkg/bundle/patch/executor.go index e56b72dc..cad6a964 100644 --- a/pkg/bundle/patch/executor.go +++ b/pkg/bundle/patch/executor.go @@ -31,43 +31,56 @@ import ( "github.com/elastic/harp/pkg/template/engine" ) +type ruleAction uint + +const ( + packageUnchanged ruleAction = iota + packageUpdated + packagedRemoved +) + // ----------------------------------------------------------------------------- -func executeRule(patchName string, r *bundlev1.PatchRule, b *bundlev1.Bundle, values map[string]interface{}) error { +func executeRule(patchName string, r *bundlev1.PatchRule, p *bundlev1.Package, values map[string]interface{}) (ruleAction, error) { // Check parameters if patchName == "" { - return fmt.Errorf("cannot process with blank patch name") + return packageUnchanged, fmt.Errorf("cannot process with blank patch name") } if r == nil { - return fmt.Errorf("cannot process nil rule") + return packageUnchanged, fmt.Errorf("cannot process nil rule") } - if b == nil { - return fmt.Errorf("cannot process nil bundle") + if p == nil { + return packageUnchanged, fmt.Errorf("cannot process nil package") } // Compile selector s, err := compileSelector(r.Selector, values) if err != nil { - return fmt.Errorf("unable to compile selector: %w", err) + return packageUnchanged, fmt.Errorf("unable to compile selector: %w", err) } - // Browse all packages - for _, p := range b.Packages { - // Package match selector specification - if s.IsSatisfiedBy(p) { - // Apply patch - if err := applyPackagePatch(p, r.Package, values); err != nil { - return fmt.Errorf("unable to apply patch to package `%s`: %w", p.Name, err) - } + // Package match selector specification + if s.IsSatisfiedBy(p) { + // Check removal request + if r.Package.Remove { + return packagedRemoved, nil + } - // Add annotations to mark package as patched. - bundle.Annotate(p, "patched", "true") - bundle.Annotate(p, patchName, "true") + // Apply patch + if err := applyPackagePatch(p, r.Package, values); err != nil { + return packageUnchanged, fmt.Errorf("unable to apply patch to package `%s`: %w", p.Name, err) } + + // Add annotations to mark package as patched. + bundle.Annotate(p, "patched", "true") + bundle.Annotate(p, patchName, "true") + + // No error + return packageUpdated, nil } // No error - return nil + return packageUnchanged, nil } func compileSelector(s *bundlev1.PatchSelector, values map[string]interface{}) (selector.Specification, error) { diff --git a/pkg/bundle/patch/executor_test.go b/pkg/bundle/patch/executor_test.go index 3c15a603..8a9cb954 100644 --- a/pkg/bundle/patch/executor_test.go +++ b/pkg/bundle/patch/executor_test.go @@ -46,28 +46,25 @@ func Test_executeRule_Fuzz(t *testing.T) { }, }, } - file := bundlev1.Bundle{ - Packages: []*bundlev1.Package{ - { - Name: "foo", - Secrets: &bundlev1.SecretChain{ - Data: []*bundlev1.KV{ - { - Key: "k1", - Value: []byte("v1"), - }, - }, + p := bundlev1.Package{ + Name: "foo", + Secrets: &bundlev1.SecretChain{ + Data: []*bundlev1.KV{ + { + Key: "k1", + Value: []byte("v1"), }, }, }, } + var patchName string f.Fuzz(&patchName) f.Fuzz(&spec.Spec.Rules[0]) // Execute - executeRule(patchName, spec.Spec.Rules[0], &file, values) + executeRule(patchName, spec.Spec.Rules[0], &p, values) } } @@ -123,27 +120,22 @@ func Test_applyPackagePatch_Fuzz(t *testing.T) { }, }, } - file := bundlev1.Bundle{ - Packages: []*bundlev1.Package{ - { - Name: "foo", - Secrets: &bundlev1.SecretChain{ - Data: []*bundlev1.KV{ - { - Key: "k1", - Value: []byte("v1"), - }, - }, + p := bundlev1.Package{ + Name: "foo", + Secrets: &bundlev1.SecretChain{ + Data: []*bundlev1.KV{ + { + Key: "k1", + Value: []byte("v1"), }, }, }, } - f.Fuzz(&file.Packages[0]) f.Fuzz(&spec.Spec.Rules[0].Package) // Execute - applyPackagePatch(file.Packages[0], spec.Spec.Rules[0].Package, values) + applyPackagePatch(&p, spec.Spec.Rules[0].Package, values) } } diff --git a/pkg/bundle/patch/package.go b/pkg/bundle/patch/package.go index c35c4987..92f3c7d9 100644 --- a/pkg/bundle/patch/package.go +++ b/pkg/bundle/patch/package.go @@ -75,6 +75,7 @@ func Checksum(spec *bundlev1.Patch) (string, error) { } // Apply given patch to the given bundle. +//nolint:interfacer // Explicit type restriction func Apply(spec *bundlev1.Patch, b *bundlev1.Bundle, values map[string]interface{}) (*bundlev1.Bundle, error) { // Validate spec if err := Validate(spec); err != nil { @@ -93,11 +94,20 @@ func Apply(spec *bundlev1.Patch, b *bundlev1.Bundle, values map[string]interface bCopy := proto.Clone(b).(*bundlev1.Bundle) // Process all rules - for i, r := range spec.Spec.Rules { - if err := executeRule(spec.Meta.Name, r, bCopy, values); err != nil { - return b, fmt.Errorf("unable to execute rule index %d: %w", i, err) + k := 0 + for _, p := range bCopy.Packages { + for i, r := range spec.Spec.Rules { + action, err := executeRule(spec.Meta.Name, r, p, values) + if err != nil { + return b, fmt.Errorf("unable to execute rule index %d: %w", i, err) + } + if action != packagedRemoved { + bCopy.Packages[k] = p + k++ + } } } + bCopy.Packages = bCopy.Packages[:k] // No error return bCopy, nil