diff --git a/generate/zz_filesystem_generated.go b/generate/zz_filesystem_generated.go index 8155971d63..bda98b8358 100644 --- a/generate/zz_filesystem_generated.go +++ b/generate/zz_filesystem_generated.go @@ -7089,39 +7089,39 @@ var TemplatesZip = []byte{ 0xc4, 0x93, 0x44, 0x83, 0x7f, 0x47, 0x67, 0x8e, 0xf4, 0x61, 0x02, 0x75, 0x2c, 0x4f, 0xbb, 0x76, 0xe7, 0xf4, 0xfc, 0x70, 0xf3, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x68, 0x33, 0xee, 0xa3, 0xf6, 0x0a, 0x00, 0x00, 0xa7, 0x1d, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x6f, 0x6d, - 0x2e, 0x78, 0x6d, 0x6c, 0xbc, 0x57, 0x51, 0x6f, 0xdc, 0x28, 0x10, 0x7e, 0xdf, 0x5f, 0x61, 0x45, 0x79, 0x35, 0x64, 0x2f, 0x77, 0x6a, 0x15, 0x71, 0x54, 0x7d, 0xb8, 0xd3, 0x45, 0x4a, 0xda, 0x48, - 0xc9, 0x55, 0xf7, 0x4a, 0xf0, 0xd8, 0x4b, 0x62, 0xc0, 0x05, 0xbc, 0xd9, 0x55, 0xb4, 0xff, 0xfd, 0x04, 0xd8, 0x5e, 0xdb, 0x6b, 0x27, 0x69, 0xb4, 0xed, 0x9b, 0x99, 0xf9, 0x66, 0x18, 0xbe, 0x19, - 0x0f, 0x03, 0xf9, 0xb4, 0x91, 0x65, 0xb2, 0x06, 0x63, 0x85, 0x56, 0x7f, 0x9e, 0x2c, 0xd1, 0xd9, 0xc9, 0x27, 0xba, 0x20, 0x95, 0xd1, 0x0f, 0xc0, 0x5d, 0xb2, 0xb1, 0xe2, 0xc2, 0xf2, 0x15, 0x48, - 0x76, 0xa5, 0x39, 0x73, 0x01, 0xb3, 0x72, 0xae, 0xba, 0xc0, 0x58, 0xb2, 0x35, 0x28, 0xc4, 0x2a, 0xc6, 0x57, 0x80, 0xb4, 0x29, 0xf0, 0xcd, 0xd7, 0x6b, 0xfc, 0x3b, 0x3a, 0x43, 0x67, 0x89, 0x47, + 0x2e, 0x78, 0x6d, 0x6c, 0xbc, 0x57, 0x51, 0x6f, 0xe4, 0x26, 0x10, 0x7e, 0xdf, 0x5f, 0x61, 0x45, 0x79, 0x35, 0x64, 0x9b, 0x56, 0xbd, 0x46, 0x94, 0xd3, 0x3d, 0xb4, 0x6a, 0xa4, 0xe4, 0x2e, 0x52, + 0xd2, 0x53, 0x5f, 0x09, 0x1e, 0x7b, 0x49, 0x0c, 0xf8, 0x00, 0x6f, 0x76, 0x15, 0xed, 0x7f, 0xaf, 0x00, 0xdb, 0x6b, 0x7b, 0xed, 0x24, 0x17, 0xed, 0xdd, 0x9b, 0x99, 0xf9, 0x66, 0x18, 0xbe, 0x19, + 0x0f, 0x03, 0xf9, 0xb8, 0x91, 0x65, 0xb2, 0x06, 0x63, 0x85, 0x56, 0x7f, 0x9e, 0x2c, 0xd1, 0xd9, 0xc9, 0x47, 0xba, 0x20, 0x95, 0xd1, 0x0f, 0xc0, 0x5d, 0xb2, 0xb1, 0xe2, 0xc2, 0xf2, 0x15, 0x48, + 0x76, 0xa5, 0x39, 0x73, 0x01, 0xb3, 0x72, 0xae, 0xba, 0xc0, 0x58, 0xb2, 0x35, 0x28, 0xc4, 0x2a, 0xc6, 0x57, 0x80, 0xb4, 0x29, 0xf0, 0xcd, 0x97, 0x6b, 0xfc, 0x2b, 0x3a, 0x43, 0x67, 0x89, 0x47, 0xd8, 0x29, 0xc8, 0xc6, 0x66, 0x51, 0x98, 0x06, 0x20, 0xda, 0xd8, 0xec, 0x24, 0xd9, 0xc8, 0x52, 0xd9, 0x37, 0xb8, 0x3d, 0x59, 0x24, 0x49, 0x12, 0xd1, 0x17, 0x1b, 0x2b, 0x3a, 0x8b, 0xa7, 0xa7, - 0x27, 0xf4, 0x74, 0x1e, 0xb0, 0xbf, 0x9d, 0x9d, 0x2d, 0xf1, 0x7f, 0xd7, 0x57, 0xb7, 0x21, 0xe4, 0x54, 0x28, 0xeb, 0x98, 0xe2, 0x70, 0x42, 0x17, 0x49, 0x42, 0xa4, 0xce, 0xa0, 0xfc, 0x16, 0x4f, + 0x27, 0xf4, 0x74, 0x1e, 0xb0, 0xbf, 0x9c, 0x9d, 0x2d, 0xf1, 0x7f, 0xd7, 0x57, 0xb7, 0x21, 0xe4, 0x54, 0x28, 0xeb, 0x98, 0xe2, 0x70, 0x42, 0x17, 0x49, 0x42, 0xa4, 0xce, 0xa0, 0xfc, 0x1a, 0x4f, 0x4a, 0x83, 0x47, 0x82, 0x07, 0x32, 0x0f, 0x2a, 0x8c, 0xae, 0xab, 0xcb, 0x8c, 0x6a, 0x53, 0x20, 0xc6, 0x25, 0x10, 0xdc, 0x4a, 0xbc, 0x96, 0x19, 0x27, 0x72, 0xc6, 0xdd, 0x65, 0x46, 0xf3, 0x5a, - 0x71, 0xcf, 0x07, 0xc1, 0x3d, 0xa1, 0xc7, 0x34, 0x5c, 0xd2, 0xa5, 0xdf, 0x21, 0xbd, 0xfd, 0xf2, 0xf9, 0xe6, 0xf6, 0x9f, 0xaf, 0x77, 0x04, 0xaf, 0x7b, 0xbb, 0x54, 0x46, 0x57, 0x60, 0x9c, 0x00, - 0x4b, 0xc3, 0xa1, 0x08, 0xd7, 0xb2, 0x12, 0x25, 0x98, 0xb4, 0x2a, 0xeb, 0x42, 0x28, 0xd4, 0x82, 0xcf, 0xd1, 0x47, 0xb4, 0x24, 0x78, 0x4e, 0x1d, 0x8d, 0x23, 0x65, 0x2d, 0x06, 0x19, 0x28, 0x81, - 0x59, 0xa0, 0xcb, 0x25, 0xc1, 0x33, 0xaa, 0x68, 0xd7, 0x24, 0x19, 0xdd, 0xd7, 0xa2, 0xcc, 0x90, 0xd5, 0xb5, 0xe1, 0xf0, 0x97, 0xe2, 0x3a, 0x13, 0xaa, 0xa0, 0xff, 0xde, 0xfd, 0x9d, 0x7e, 0x24, - 0xf8, 0x45, 0xcc, 0xd0, 0x8d, 0x81, 0x4a, 0x1b, 0x27, 0x54, 0x81, 0x74, 0xed, 0xaa, 0xda, 0xcd, 0xb9, 0x9a, 0xc5, 0x45, 0x77, 0xdf, 0x6b, 0x66, 0x1e, 0x6b, 0x8b, 0xaa, 0x92, 0xb9, 0x5c, 0x1b, + 0x71, 0xcf, 0x07, 0xc1, 0x3d, 0xa1, 0xc7, 0x34, 0x5c, 0xd2, 0xa5, 0xdf, 0x21, 0xbd, 0xfd, 0xfc, 0xe9, 0xe6, 0xf6, 0x9f, 0x2f, 0x77, 0x04, 0xaf, 0x7b, 0xbb, 0x54, 0x46, 0x57, 0x60, 0x9c, 0x00, + 0x4b, 0xc3, 0xa1, 0x08, 0xd7, 0xb2, 0x12, 0x25, 0x98, 0xb4, 0x2a, 0xeb, 0x42, 0x28, 0xd4, 0x82, 0xcf, 0xd1, 0x07, 0xb4, 0x24, 0x78, 0x4e, 0x1d, 0x8d, 0x23, 0x65, 0x2d, 0x06, 0x19, 0x28, 0x81, + 0x59, 0xa0, 0xcb, 0x25, 0xc1, 0x33, 0xaa, 0x68, 0xd7, 0x24, 0x19, 0xdd, 0xd7, 0xa2, 0xcc, 0x90, 0xd5, 0xb5, 0xe1, 0xf0, 0x97, 0xe2, 0x3a, 0x13, 0xaa, 0xa0, 0xff, 0xde, 0xfd, 0x9d, 0x7e, 0x20, + 0xf8, 0x45, 0xcc, 0xd0, 0x8d, 0x81, 0x4a, 0x1b, 0x27, 0x54, 0x81, 0x74, 0xed, 0xaa, 0xda, 0xcd, 0xb9, 0x9a, 0xc5, 0x45, 0x77, 0xdf, 0x6a, 0x66, 0x1e, 0x6b, 0x8b, 0xaa, 0x92, 0xb9, 0x5c, 0x1b, 0x89, 0x5a, 0x7e, 0x53, 0x91, 0xd1, 0x46, 0x99, 0xde, 0x6b, 0x49, 0xf0, 0x8b, 0xc8, 0x19, 0x67, 0x21, 0x9b, 0x5e, 0x2f, 0x34, 0x1a, 0x2b, 0x27, 0x3c, 0x76, 0xf0, 0x19, 0x77, 0xfb, 0x3c, 0x9d, - 0x7f, 0xf0, 0x89, 0x9a, 0x05, 0x44, 0x7b, 0xfb, 0x28, 0xaa, 0xcb, 0x3b, 0x4b, 0x9d, 0xa9, 0x81, 0xe0, 0x76, 0xd5, 0xe8, 0x6a, 0x03, 0xb9, 0x30, 0x70, 0x58, 0x02, 0xbe, 0x8e, 0xae, 0x3f, 0x10, - 0x3c, 0x87, 0xf0, 0x15, 0x85, 0x87, 0x25, 0x45, 0x32, 0xa8, 0x40, 0x65, 0xa0, 0xf8, 0xf6, 0x9a, 0x29, 0x56, 0x80, 0x04, 0xe5, 0x9a, 0x8d, 0x3a, 0x55, 0x57, 0x7e, 0x03, 0x7c, 0x2b, 0xea, 0xfd, - 0x0c, 0xa7, 0xcf, 0xb3, 0xc4, 0xec, 0x06, 0x3f, 0x48, 0x63, 0xd7, 0xfb, 0x23, 0x26, 0x4c, 0x7b, 0x59, 0xda, 0x8d, 0xff, 0x9e, 0xc6, 0x41, 0x7b, 0xb2, 0x09, 0xeb, 0x46, 0xb5, 0x1b, 0xfc, 0x50, - 0x8d, 0x99, 0xdb, 0x56, 0x40, 0x2b, 0x5f, 0x1a, 0xe1, 0x6b, 0xaf, 0xb0, 0x5c, 0x57, 0x40, 0x85, 0xf4, 0x85, 0x47, 0x70, 0x5c, 0xb5, 0x27, 0xc7, 0xe3, 0xa3, 0xf7, 0x24, 0x2d, 0x9b, 0x78, 0x8e, - 0xce, 0x09, 0x32, 0x27, 0xa8, 0xec, 0x88, 0xdc, 0x17, 0xdd, 0x01, 0x6d, 0x7d, 0xd2, 0xda, 0x2a, 0xcf, 0x6b, 0xf5, 0x7d, 0x9b, 0x3e, 0x2a, 0xe6, 0xc4, 0x1a, 0x52, 0x58, 0x83, 0x72, 0xf6, 0x90, - 0xb1, 0x89, 0x13, 0x1c, 0x2f, 0x04, 0x2b, 0x59, 0x59, 0x9a, 0x2d, 0xa4, 0x2b, 0x60, 0xa5, 0x5b, 0xfd, 0xe2, 0xdd, 0x99, 0xe1, 0xbf, 0x78, 0xc7, 0x87, 0x5a, 0x09, 0xf7, 0xc7, 0x54, 0x59, 0x36, - 0x45, 0xe4, 0xc0, 0x0e, 0x4b, 0xe8, 0x87, 0xc3, 0x31, 0x60, 0x5d, 0xca, 0xac, 0xff, 0x9b, 0xb3, 0x17, 0x63, 0x1a, 0x02, 0xdf, 0x1f, 0xd1, 0x44, 0x41, 0x87, 0x76, 0xde, 0xa0, 0x63, 0x37, 0xd9, - 0xb7, 0x82, 0xb8, 0xa6, 0x47, 0x6d, 0x03, 0x2d, 0xbd, 0xf1, 0xf2, 0x8f, 0x3b, 0x1c, 0xf7, 0xdf, 0x87, 0x8d, 0x03, 0xe5, 0x65, 0x6d, 0x77, 0xed, 0x09, 0xfa, 0x28, 0xe0, 0xb5, 0x1b, 0x0a, 0xfb, - 0xe2, 0xbe, 0xd4, 0x9f, 0x5c, 0xb3, 0xd2, 0x0e, 0x65, 0x8d, 0x94, 0x06, 0x06, 0x09, 0x0e, 0xdf, 0x93, 0x80, 0x02, 0x14, 0x18, 0xe6, 0x20, 0xe5, 0x3a, 0x83, 0x37, 0x03, 0x53, 0x9f, 0x4d, 0x3b, - 0x05, 0x8f, 0xb2, 0x61, 0xdc, 0x78, 0x22, 0xf0, 0x9e, 0x70, 0x9f, 0x53, 0x3c, 0x4c, 0xea, 0x61, 0x8e, 0x7b, 0xa9, 0x88, 0x39, 0x1a, 0x8d, 0x1c, 0xaf, 0x26, 0x6b, 0x66, 0x44, 0x99, 0xcc, 0x15, - 0xd7, 0x2a, 0x17, 0x45, 0x6d, 0xd8, 0x98, 0xf2, 0x6e, 0x0e, 0xfa, 0x6c, 0x8a, 0x11, 0xef, 0x84, 0x99, 0x82, 0xa6, 0x15, 0x33, 0x4c, 0x82, 0x03, 0x13, 0xfa, 0x60, 0x31, 0x24, 0x63, 0xda, 0xd8, - 0xcb, 0x27, 0xb6, 0x7b, 0x07, 0x25, 0xa3, 0xfb, 0xf7, 0x55, 0x4a, 0x66, 0xee, 0xeb, 0x1f, 0xa4, 0xc4, 0x6e, 0xad, 0x03, 0x79, 0x13, 0xaf, 0xf7, 0xed, 0x37, 0x66, 0x04, 0xbb, 0x2f, 0x61, 0xcc, - 0xce, 0x03, 0x5b, 0x33, 0x54, 0x3b, 0x51, 0xa2, 0x52, 0x17, 0x85, 0x1f, 0xae, 0x64, 0xb8, 0xa8, 0x4c, 0x98, 0x64, 0x1f, 0xee, 0xb5, 0xb5, 0x5e, 0xd3, 0x08, 0xd1, 0x95, 0x2e, 0xe2, 0x45, 0x66, - 0x08, 0x9e, 0x37, 0x1d, 0x6e, 0x11, 0x67, 0xc9, 0x95, 0x96, 0x40, 0x4f, 0x9f, 0xf7, 0x8b, 0x5d, 0x3b, 0x65, 0x06, 0xcd, 0x20, 0x21, 0xaf, 0x86, 0x7e, 0xbc, 0xdc, 0xe4, 0x4c, 0x94, 0x96, 0xe5, - 0x3f, 0x25, 0x37, 0xc7, 0x6b, 0x1a, 0x42, 0x39, 0x28, 0xe2, 0x59, 0xd3, 0xd8, 0xb6, 0xe7, 0xdb, 0xc2, 0x1a, 0x8c, 0xc8, 0xb7, 0x6f, 0xec, 0x04, 0x2f, 0x55, 0x50, 0xf2, 0xe6, 0x2a, 0x0a, 0xc8, - 0x38, 0x6d, 0x20, 0x21, 0x59, 0x01, 0xa8, 0x62, 0x6e, 0x45, 0x4f, 0x9f, 0x87, 0xaf, 0x80, 0x4c, 0x18, 0xe0, 0x4e, 0x9b, 0xed, 0x0e, 0x8f, 0x55, 0xb9, 0x50, 0xac, 0xfc, 0xc2, 0x24, 0xec, 0x52, - 0x53, 0x2b, 0xe5, 0xcb, 0xeb, 0xd0, 0xdf, 0xe1, 0x9e, 0x3f, 0xbf, 0x7a, 0x93, 0xf7, 0x54, 0x70, 0xf2, 0xb6, 0x2a, 0x4e, 0xe6, 0x2b, 0x39, 0xaa, 0xde, 0xd5, 0xa5, 0xdb, 0x55, 0x33, 0x81, 0x76, - 0x37, 0xb6, 0x7f, 0x6f, 0xe5, 0xa2, 0x0b, 0xa0, 0x5d, 0x76, 0x4e, 0x44, 0x46, 0x23, 0xe5, 0x04, 0x8b, 0xde, 0x58, 0xc1, 0x9d, 0x58, 0x8f, 0x42, 0x6b, 0x9f, 0xa1, 0xdb, 0x41, 0xb4, 0x8a, 0x49, - 0xe8, 0x3c, 0x84, 0x45, 0x2f, 0xe8, 0xb1, 0x05, 0xc1, 0x87, 0x8e, 0x0f, 0x5e, 0xb7, 0x41, 0xd8, 0x3e, 0x76, 0x72, 0x56, 0xda, 0xf1, 0xdb, 0x27, 0x00, 0xba, 0xdb, 0x9e, 0xf1, 0x47, 0x5f, 0x2a, - 0x61, 0x84, 0x6f, 0xe3, 0x98, 0x54, 0x2e, 0x46, 0x51, 0xed, 0x47, 0x70, 0xdc, 0x23, 0xa5, 0x5b, 0x58, 0xba, 0xe8, 0x1e, 0xa1, 0x74, 0xf1, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, - 0x08, 0x97, 0x9b, 0xfc, 0xbe, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xe1, 0x13, 0x35, 0x0b, 0x88, 0xf6, 0xf6, 0x51, 0x54, 0x97, 0x77, 0x96, 0x3a, 0x53, 0x03, 0xc1, 0xed, 0xaa, 0xd1, 0xd5, 0x06, 0x72, 0x61, 0xe0, 0xb0, 0x04, 0x7c, 0x1d, 0x5d, 0xff, 0x4e, + 0xf0, 0x1c, 0xc2, 0x57, 0x14, 0x1e, 0x96, 0x14, 0xc9, 0xa0, 0x02, 0x95, 0x81, 0xe2, 0xdb, 0x6b, 0xa6, 0x58, 0x01, 0x12, 0x94, 0x6b, 0x36, 0xea, 0x54, 0x5d, 0xf9, 0x0d, 0xf0, 0xad, 0xa8, 0xf7, + 0x33, 0x9c, 0x3e, 0xcf, 0x12, 0xb3, 0x1b, 0xfc, 0x20, 0x8d, 0x5d, 0xef, 0x8f, 0x98, 0x30, 0xed, 0x65, 0x69, 0x37, 0xfe, 0x7b, 0x1a, 0x07, 0xed, 0xc9, 0x26, 0xac, 0x1b, 0xd5, 0x6e, 0xf0, 0x43, + 0x35, 0x66, 0x6e, 0x5b, 0x01, 0xad, 0x7c, 0x69, 0x84, 0xaf, 0xbd, 0xc2, 0x72, 0x5d, 0x01, 0x15, 0xd2, 0x17, 0x1e, 0xc1, 0x71, 0xd5, 0x9e, 0x1c, 0x8f, 0x8f, 0xde, 0x93, 0xb4, 0x6c, 0xe2, 0x39, + 0x3a, 0x27, 0xc8, 0x9c, 0xa0, 0xb2, 0x23, 0x72, 0x5f, 0x74, 0x07, 0xb4, 0xf5, 0x49, 0x6b, 0xab, 0x3c, 0xaf, 0xd5, 0xb7, 0x6d, 0xfa, 0xa8, 0x98, 0x13, 0x6b, 0x48, 0x61, 0x0d, 0xca, 0xd9, 0x43, + 0xc6, 0x26, 0x4e, 0x70, 0xbc, 0x10, 0xac, 0x64, 0x65, 0x69, 0xb6, 0x90, 0xae, 0x80, 0x95, 0x6e, 0xf5, 0x93, 0x77, 0x67, 0x86, 0xff, 0xe4, 0x1d, 0x1f, 0x6a, 0x25, 0xdc, 0x6f, 0x53, 0x65, 0xd9, + 0x14, 0x91, 0x03, 0x3b, 0x2c, 0xa1, 0xef, 0x0e, 0xc7, 0x80, 0x75, 0x29, 0xb3, 0xfe, 0x6f, 0xce, 0x5e, 0x8c, 0x69, 0x08, 0x7c, 0x7f, 0x44, 0x13, 0x05, 0x1d, 0xda, 0x79, 0x83, 0x8e, 0xdd, 0x64, + 0xdf, 0x0a, 0xe2, 0x9a, 0x1e, 0xb5, 0x0d, 0xb4, 0xf4, 0xc6, 0xcb, 0x3f, 0xee, 0x70, 0xdc, 0x7f, 0x1f, 0x36, 0x0e, 0x94, 0x97, 0xb5, 0xdd, 0xb5, 0x27, 0xe8, 0xa3, 0x80, 0xd7, 0x6e, 0x28, 0xec, + 0x8b, 0xfb, 0x52, 0x7f, 0x72, 0xcd, 0x4a, 0x3b, 0x94, 0x35, 0x52, 0x1a, 0x18, 0x24, 0x38, 0x7c, 0x4f, 0x02, 0x0a, 0x50, 0x60, 0x98, 0x83, 0x94, 0xeb, 0x0c, 0xde, 0x0c, 0x4c, 0x7d, 0x36, 0xed, + 0x14, 0x3c, 0xca, 0x86, 0x71, 0xe3, 0x89, 0xc0, 0x7b, 0xc2, 0x7d, 0x4e, 0xf1, 0x30, 0xa9, 0x87, 0x39, 0xee, 0xa5, 0x22, 0xe6, 0x68, 0x34, 0x72, 0xbc, 0x9a, 0xac, 0x99, 0x11, 0x65, 0x32, 0x57, + 0x5c, 0xab, 0x5c, 0x14, 0xb5, 0x61, 0x63, 0xca, 0xbb, 0x39, 0xe8, 0x93, 0x29, 0x46, 0xbc, 0x13, 0x66, 0x0a, 0x9a, 0x56, 0xcc, 0x30, 0x09, 0x0e, 0x4c, 0xe8, 0x83, 0xc5, 0x90, 0x8c, 0x69, 0x63, + 0x2f, 0x9f, 0xd8, 0xee, 0x1d, 0x94, 0x8c, 0xee, 0xdf, 0x57, 0x29, 0x99, 0xb9, 0xaf, 0xbf, 0x93, 0x12, 0xbb, 0xb5, 0x0e, 0xe4, 0x4d, 0xbc, 0xde, 0xb7, 0x5f, 0x99, 0x11, 0xec, 0xbe, 0x84, 0x31, + 0x3b, 0x0f, 0x6c, 0xcd, 0x50, 0xed, 0x44, 0x89, 0x4a, 0x5d, 0x14, 0x7e, 0xb8, 0x92, 0xe1, 0xa2, 0x32, 0x61, 0x92, 0x7d, 0xb8, 0xd7, 0xd6, 0x7a, 0x4d, 0x23, 0x44, 0x57, 0xba, 0x88, 0x17, 0x99, + 0x21, 0x78, 0xde, 0x74, 0xb8, 0x45, 0x9c, 0x25, 0x57, 0x5a, 0x02, 0x3d, 0x7d, 0xde, 0x2f, 0x76, 0xed, 0x94, 0x19, 0x34, 0x83, 0x84, 0xbc, 0x1a, 0xfa, 0xf1, 0x72, 0x93, 0x33, 0x51, 0x5a, 0x96, + 0xff, 0x90, 0xdc, 0x1c, 0xaf, 0x69, 0x08, 0xe5, 0xa0, 0x88, 0x67, 0x4d, 0x63, 0xdb, 0x9e, 0x6f, 0x0b, 0x6b, 0x30, 0x22, 0xdf, 0xbe, 0xb1, 0x13, 0xbc, 0x54, 0x41, 0xc9, 0x9b, 0xab, 0x28, 0x20, + 0xe3, 0xb4, 0x81, 0x84, 0x64, 0x05, 0xa0, 0x8a, 0xb9, 0x15, 0x3d, 0x7d, 0x1e, 0xbe, 0x02, 0x32, 0x61, 0x80, 0x3b, 0x6d, 0xb6, 0x3b, 0x3c, 0x56, 0xe5, 0x42, 0xb1, 0xf2, 0x33, 0x93, 0xb0, 0x4b, + 0x4d, 0xad, 0x94, 0x2f, 0xaf, 0x43, 0x7f, 0x87, 0x7b, 0xfe, 0xf8, 0xea, 0x4d, 0xde, 0x53, 0xc1, 0xc9, 0xdb, 0xaa, 0x38, 0x99, 0xaf, 0xe4, 0xa8, 0x7a, 0x57, 0x97, 0x6e, 0x57, 0xcd, 0x04, 0xda, + 0xdd, 0xd8, 0xfe, 0xbd, 0x95, 0x8b, 0x2e, 0x80, 0x76, 0xd9, 0x39, 0x11, 0x19, 0x8d, 0x94, 0x13, 0x2c, 0x7a, 0x63, 0x05, 0x77, 0x62, 0x3d, 0x0a, 0xad, 0x7d, 0x86, 0x6e, 0x07, 0xd1, 0x2a, 0x26, + 0xa1, 0xf3, 0x10, 0x16, 0xbd, 0xa0, 0xc7, 0x16, 0x04, 0x1f, 0x3a, 0x3e, 0x78, 0xdd, 0x06, 0x61, 0xfb, 0xd8, 0xc9, 0x59, 0x69, 0xc7, 0x6f, 0x9f, 0x00, 0xe8, 0x6e, 0x7b, 0xc6, 0x1f, 0x7d, 0xa9, + 0x84, 0x11, 0xbe, 0x8d, 0x63, 0x52, 0xb9, 0x18, 0x45, 0xb5, 0x1f, 0xc1, 0x71, 0x8f, 0x94, 0x6e, 0x61, 0xe9, 0xa2, 0x7b, 0x84, 0xd2, 0xc5, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, + 0x08, 0x7b, 0x3e, 0x7e, 0x8a, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x6d, 0x61, 0x69, 0x6e, 0x2f, 0x50, 0x4b, @@ -7517,38 +7517,38 @@ var TemplatesZip = []byte{ 0xdd, 0x1f, 0xc4, 0x93, 0x44, 0x83, 0x7f, 0x47, 0x67, 0x8e, 0xf4, 0x61, 0x02, 0x75, 0x2c, 0x4f, 0xbb, 0x76, 0xe7, 0xf4, 0xfc, 0x70, 0xf3, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x68, 0x33, 0xee, 0xa3, 0xf6, 0x0a, 0x00, 0x00, 0xa7, 0x1d, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0xbc, - 0x57, 0x51, 0x6f, 0xdc, 0x28, 0x10, 0x7e, 0xdf, 0x5f, 0x61, 0x45, 0x79, 0x35, 0x64, 0x2f, 0x77, 0x6a, 0x15, 0x71, 0x54, 0x7d, 0xb8, 0xd3, 0x45, 0x4a, 0xda, 0x48, 0xc9, 0x55, 0xf7, 0x4a, 0xf0, - 0xd8, 0x4b, 0x62, 0xc0, 0x05, 0xbc, 0xd9, 0x55, 0xb4, 0xff, 0xfd, 0x04, 0xd8, 0x5e, 0xdb, 0x6b, 0x27, 0x69, 0xb4, 0xed, 0x9b, 0x99, 0xf9, 0x66, 0x18, 0xbe, 0x19, 0x0f, 0x03, 0xf9, 0xb4, 0x91, - 0x65, 0xb2, 0x06, 0x63, 0x85, 0x56, 0x7f, 0x9e, 0x2c, 0xd1, 0xd9, 0xc9, 0x27, 0xba, 0x20, 0x95, 0xd1, 0x0f, 0xc0, 0x5d, 0xb2, 0xb1, 0xe2, 0xc2, 0xf2, 0x15, 0x48, 0x76, 0xa5, 0x39, 0x73, 0x01, - 0xb3, 0x72, 0xae, 0xba, 0xc0, 0x58, 0xb2, 0x35, 0x28, 0xc4, 0x2a, 0xc6, 0x57, 0x80, 0xb4, 0x29, 0xf0, 0xcd, 0xd7, 0x6b, 0xfc, 0x3b, 0x3a, 0x43, 0x67, 0x89, 0x47, 0xd8, 0x29, 0xc8, 0xc6, 0x66, + 0x57, 0x51, 0x6f, 0xe4, 0x26, 0x10, 0x7e, 0xdf, 0x5f, 0x61, 0x45, 0x79, 0x35, 0x64, 0x9b, 0x56, 0xbd, 0x46, 0x94, 0xd3, 0x3d, 0xb4, 0x6a, 0xa4, 0xe4, 0x2e, 0x52, 0xd2, 0x53, 0x5f, 0x09, 0x1e, + 0x7b, 0x49, 0x0c, 0xf8, 0x00, 0x6f, 0x76, 0x15, 0xed, 0x7f, 0xaf, 0x00, 0xdb, 0x6b, 0x7b, 0xed, 0x24, 0x17, 0xed, 0xdd, 0x9b, 0x99, 0xf9, 0x66, 0x18, 0xbe, 0x19, 0x0f, 0x03, 0xf9, 0xb8, 0x91, + 0x65, 0xb2, 0x06, 0x63, 0x85, 0x56, 0x7f, 0x9e, 0x2c, 0xd1, 0xd9, 0xc9, 0x47, 0xba, 0x20, 0x95, 0xd1, 0x0f, 0xc0, 0x5d, 0xb2, 0xb1, 0xe2, 0xc2, 0xf2, 0x15, 0x48, 0x76, 0xa5, 0x39, 0x73, 0x01, + 0xb3, 0x72, 0xae, 0xba, 0xc0, 0x58, 0xb2, 0x35, 0x28, 0xc4, 0x2a, 0xc6, 0x57, 0x80, 0xb4, 0x29, 0xf0, 0xcd, 0x97, 0x6b, 0xfc, 0x2b, 0x3a, 0x43, 0x67, 0x89, 0x47, 0xd8, 0x29, 0xc8, 0xc6, 0x66, 0x51, 0x98, 0x06, 0x20, 0xda, 0xd8, 0xec, 0x24, 0xd9, 0xc8, 0x52, 0xd9, 0x37, 0xb8, 0x3d, 0x59, 0x24, 0x49, 0x12, 0xd1, 0x17, 0x1b, 0x2b, 0x3a, 0x8b, 0xa7, 0xa7, 0x27, 0xf4, 0x74, 0x1e, 0xb0, - 0xbf, 0x9d, 0x9d, 0x2d, 0xf1, 0x7f, 0xd7, 0x57, 0xb7, 0x21, 0xe4, 0x54, 0x28, 0xeb, 0x98, 0xe2, 0x70, 0x42, 0x17, 0x49, 0x42, 0xa4, 0xce, 0xa0, 0xfc, 0x16, 0x4f, 0x4a, 0x83, 0x47, 0x82, 0x07, + 0xbf, 0x9c, 0x9d, 0x2d, 0xf1, 0x7f, 0xd7, 0x57, 0xb7, 0x21, 0xe4, 0x54, 0x28, 0xeb, 0x98, 0xe2, 0x70, 0x42, 0x17, 0x49, 0x42, 0xa4, 0xce, 0xa0, 0xfc, 0x1a, 0x4f, 0x4a, 0x83, 0x47, 0x82, 0x07, 0x32, 0x0f, 0x2a, 0x8c, 0xae, 0xab, 0xcb, 0x8c, 0x6a, 0x53, 0x20, 0xc6, 0x25, 0x10, 0xdc, 0x4a, 0xbc, 0x96, 0x19, 0x27, 0x72, 0xc6, 0xdd, 0x65, 0x46, 0xf3, 0x5a, 0x71, 0xcf, 0x07, 0xc1, 0x3d, - 0xa1, 0xc7, 0x34, 0x5c, 0xd2, 0xa5, 0xdf, 0x21, 0xbd, 0xfd, 0xf2, 0xf9, 0xe6, 0xf6, 0x9f, 0xaf, 0x77, 0x04, 0xaf, 0x7b, 0xbb, 0x54, 0x46, 0x57, 0x60, 0x9c, 0x00, 0x4b, 0xc3, 0xa1, 0x08, 0xd7, - 0xb2, 0x12, 0x25, 0x98, 0xb4, 0x2a, 0xeb, 0x42, 0x28, 0xd4, 0x82, 0xcf, 0xd1, 0x47, 0xb4, 0x24, 0x78, 0x4e, 0x1d, 0x8d, 0x23, 0x65, 0x2d, 0x06, 0x19, 0x28, 0x81, 0x59, 0xa0, 0xcb, 0x25, 0xc1, - 0x33, 0xaa, 0x68, 0xd7, 0x24, 0x19, 0xdd, 0xd7, 0xa2, 0xcc, 0x90, 0xd5, 0xb5, 0xe1, 0xf0, 0x97, 0xe2, 0x3a, 0x13, 0xaa, 0xa0, 0xff, 0xde, 0xfd, 0x9d, 0x7e, 0x24, 0xf8, 0x45, 0xcc, 0xd0, 0x8d, - 0x81, 0x4a, 0x1b, 0x27, 0x54, 0x81, 0x74, 0xed, 0xaa, 0xda, 0xcd, 0xb9, 0x9a, 0xc5, 0x45, 0x77, 0xdf, 0x6b, 0x66, 0x1e, 0x6b, 0x8b, 0xaa, 0x92, 0xb9, 0x5c, 0x1b, 0x89, 0x5a, 0x7e, 0x53, 0x91, - 0xd1, 0x46, 0x99, 0xde, 0x6b, 0x49, 0xf0, 0x8b, 0xc8, 0x19, 0x67, 0x21, 0x9b, 0x5e, 0x2f, 0x34, 0x1a, 0x2b, 0x27, 0x3c, 0x76, 0xf0, 0x19, 0x77, 0xfb, 0x3c, 0x9d, 0x7f, 0xf0, 0x89, 0x9a, 0x05, - 0x44, 0x7b, 0xfb, 0x28, 0xaa, 0xcb, 0x3b, 0x4b, 0x9d, 0xa9, 0x81, 0xe0, 0x76, 0xd5, 0xe8, 0x6a, 0x03, 0xb9, 0x30, 0x70, 0x58, 0x02, 0xbe, 0x8e, 0xae, 0x3f, 0x10, 0x3c, 0x87, 0xf0, 0x15, 0x85, - 0x87, 0x25, 0x45, 0x32, 0xa8, 0x40, 0x65, 0xa0, 0xf8, 0xf6, 0x9a, 0x29, 0x56, 0x80, 0x04, 0xe5, 0x9a, 0x8d, 0x3a, 0x55, 0x57, 0x7e, 0x03, 0x7c, 0x2b, 0xea, 0xfd, 0x0c, 0xa7, 0xcf, 0xb3, 0xc4, - 0xec, 0x06, 0x3f, 0x48, 0x63, 0xd7, 0xfb, 0x23, 0x26, 0x4c, 0x7b, 0x59, 0xda, 0x8d, 0xff, 0x9e, 0xc6, 0x41, 0x7b, 0xb2, 0x09, 0xeb, 0x46, 0xb5, 0x1b, 0xfc, 0x50, 0x8d, 0x99, 0xdb, 0x56, 0x40, - 0x2b, 0x5f, 0x1a, 0xe1, 0x6b, 0xaf, 0xb0, 0x5c, 0x57, 0x40, 0x85, 0xf4, 0x85, 0x47, 0x70, 0x5c, 0xb5, 0x27, 0xc7, 0xe3, 0xa3, 0xf7, 0x24, 0x2d, 0x9b, 0x78, 0x8e, 0xce, 0x09, 0x32, 0x27, 0xa8, - 0xec, 0x88, 0xdc, 0x17, 0xdd, 0x01, 0x6d, 0x7d, 0xd2, 0xda, 0x2a, 0xcf, 0x6b, 0xf5, 0x7d, 0x9b, 0x3e, 0x2a, 0xe6, 0xc4, 0x1a, 0x52, 0x58, 0x83, 0x72, 0xf6, 0x90, 0xb1, 0x89, 0x13, 0x1c, 0x2f, - 0x04, 0x2b, 0x59, 0x59, 0x9a, 0x2d, 0xa4, 0x2b, 0x60, 0xa5, 0x5b, 0xfd, 0xe2, 0xdd, 0x99, 0xe1, 0xbf, 0x78, 0xc7, 0x87, 0x5a, 0x09, 0xf7, 0xc7, 0x54, 0x59, 0x36, 0x45, 0xe4, 0xc0, 0x0e, 0x4b, - 0xe8, 0x87, 0xc3, 0x31, 0x60, 0x5d, 0xca, 0xac, 0xff, 0x9b, 0xb3, 0x17, 0x63, 0x1a, 0x02, 0xdf, 0x1f, 0xd1, 0x44, 0x41, 0x87, 0x76, 0xde, 0xa0, 0x63, 0x37, 0xd9, 0xb7, 0x82, 0xb8, 0xa6, 0x47, - 0x6d, 0x03, 0x2d, 0xbd, 0xf1, 0xf2, 0x8f, 0x3b, 0x1c, 0xf7, 0xdf, 0x87, 0x8d, 0x03, 0xe5, 0x65, 0x6d, 0x77, 0xed, 0x09, 0xfa, 0x28, 0xe0, 0xb5, 0x1b, 0x0a, 0xfb, 0xe2, 0xbe, 0xd4, 0x9f, 0x5c, - 0xb3, 0xd2, 0x0e, 0x65, 0x8d, 0x94, 0x06, 0x06, 0x09, 0x0e, 0xdf, 0x93, 0x80, 0x02, 0x14, 0x18, 0xe6, 0x20, 0xe5, 0x3a, 0x83, 0x37, 0x03, 0x53, 0x9f, 0x4d, 0x3b, 0x05, 0x8f, 0xb2, 0x61, 0xdc, - 0x78, 0x22, 0xf0, 0x9e, 0x70, 0x9f, 0x53, 0x3c, 0x4c, 0xea, 0x61, 0x8e, 0x7b, 0xa9, 0x88, 0x39, 0x1a, 0x8d, 0x1c, 0xaf, 0x26, 0x6b, 0x66, 0x44, 0x99, 0xcc, 0x15, 0xd7, 0x2a, 0x17, 0x45, 0x6d, - 0xd8, 0x98, 0xf2, 0x6e, 0x0e, 0xfa, 0x6c, 0x8a, 0x11, 0xef, 0x84, 0x99, 0x82, 0xa6, 0x15, 0x33, 0x4c, 0x82, 0x03, 0x13, 0xfa, 0x60, 0x31, 0x24, 0x63, 0xda, 0xd8, 0xcb, 0x27, 0xb6, 0x7b, 0x07, - 0x25, 0xa3, 0xfb, 0xf7, 0x55, 0x4a, 0x66, 0xee, 0xeb, 0x1f, 0xa4, 0xc4, 0x6e, 0xad, 0x03, 0x79, 0x13, 0xaf, 0xf7, 0xed, 0x37, 0x66, 0x04, 0xbb, 0x2f, 0x61, 0xcc, 0xce, 0x03, 0x5b, 0x33, 0x54, - 0x3b, 0x51, 0xa2, 0x52, 0x17, 0x85, 0x1f, 0xae, 0x64, 0xb8, 0xa8, 0x4c, 0x98, 0x64, 0x1f, 0xee, 0xb5, 0xb5, 0x5e, 0xd3, 0x08, 0xd1, 0x95, 0x2e, 0xe2, 0x45, 0x66, 0x08, 0x9e, 0x37, 0x1d, 0x6e, - 0x11, 0x67, 0xc9, 0x95, 0x96, 0x40, 0x4f, 0x9f, 0xf7, 0x8b, 0x5d, 0x3b, 0x65, 0x06, 0xcd, 0x20, 0x21, 0xaf, 0x86, 0x7e, 0xbc, 0xdc, 0xe4, 0x4c, 0x94, 0x96, 0xe5, 0x3f, 0x25, 0x37, 0xc7, 0x6b, - 0x1a, 0x42, 0x39, 0x28, 0xe2, 0x59, 0xd3, 0xd8, 0xb6, 0xe7, 0xdb, 0xc2, 0x1a, 0x8c, 0xc8, 0xb7, 0x6f, 0xec, 0x04, 0x2f, 0x55, 0x50, 0xf2, 0xe6, 0x2a, 0x0a, 0xc8, 0x38, 0x6d, 0x20, 0x21, 0x59, - 0x01, 0xa8, 0x62, 0x6e, 0x45, 0x4f, 0x9f, 0x87, 0xaf, 0x80, 0x4c, 0x18, 0xe0, 0x4e, 0x9b, 0xed, 0x0e, 0x8f, 0x55, 0xb9, 0x50, 0xac, 0xfc, 0xc2, 0x24, 0xec, 0x52, 0x53, 0x2b, 0xe5, 0xcb, 0xeb, - 0xd0, 0xdf, 0xe1, 0x9e, 0x3f, 0xbf, 0x7a, 0x93, 0xf7, 0x54, 0x70, 0xf2, 0xb6, 0x2a, 0x4e, 0xe6, 0x2b, 0x39, 0xaa, 0xde, 0xd5, 0xa5, 0xdb, 0x55, 0x33, 0x81, 0x76, 0x37, 0xb6, 0x7f, 0x6f, 0xe5, - 0xa2, 0x0b, 0xa0, 0x5d, 0x76, 0x4e, 0x44, 0x46, 0x23, 0xe5, 0x04, 0x8b, 0xde, 0x58, 0xc1, 0x9d, 0x58, 0x8f, 0x42, 0x6b, 0x9f, 0xa1, 0xdb, 0x41, 0xb4, 0x8a, 0x49, 0xe8, 0x3c, 0x84, 0x45, 0x2f, - 0xe8, 0xb1, 0x05, 0xc1, 0x87, 0x8e, 0x0f, 0x5e, 0xb7, 0x41, 0xd8, 0x3e, 0x76, 0x72, 0x56, 0xda, 0xf1, 0xdb, 0x27, 0x00, 0xba, 0xdb, 0x9e, 0xf1, 0x47, 0x5f, 0x2a, 0x61, 0x84, 0x6f, 0xe3, 0x98, - 0x54, 0x2e, 0x46, 0x51, 0xed, 0x47, 0x70, 0xdc, 0x23, 0xa5, 0x5b, 0x58, 0xba, 0xe8, 0x1e, 0xa1, 0x74, 0xf1, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x97, 0x9b, 0xfc, 0xbe, + 0xa1, 0xc7, 0x34, 0x5c, 0xd2, 0xa5, 0xdf, 0x21, 0xbd, 0xfd, 0xfc, 0xe9, 0xe6, 0xf6, 0x9f, 0x2f, 0x77, 0x04, 0xaf, 0x7b, 0xbb, 0x54, 0x46, 0x57, 0x60, 0x9c, 0x00, 0x4b, 0xc3, 0xa1, 0x08, 0xd7, + 0xb2, 0x12, 0x25, 0x98, 0xb4, 0x2a, 0xeb, 0x42, 0x28, 0xd4, 0x82, 0xcf, 0xd1, 0x07, 0xb4, 0x24, 0x78, 0x4e, 0x1d, 0x8d, 0x23, 0x65, 0x2d, 0x06, 0x19, 0x28, 0x81, 0x59, 0xa0, 0xcb, 0x25, 0xc1, + 0x33, 0xaa, 0x68, 0xd7, 0x24, 0x19, 0xdd, 0xd7, 0xa2, 0xcc, 0x90, 0xd5, 0xb5, 0xe1, 0xf0, 0x97, 0xe2, 0x3a, 0x13, 0xaa, 0xa0, 0xff, 0xde, 0xfd, 0x9d, 0x7e, 0x20, 0xf8, 0x45, 0xcc, 0xd0, 0x8d, + 0x81, 0x4a, 0x1b, 0x27, 0x54, 0x81, 0x74, 0xed, 0xaa, 0xda, 0xcd, 0xb9, 0x9a, 0xc5, 0x45, 0x77, 0xdf, 0x6a, 0x66, 0x1e, 0x6b, 0x8b, 0xaa, 0x92, 0xb9, 0x5c, 0x1b, 0x89, 0x5a, 0x7e, 0x53, 0x91, + 0xd1, 0x46, 0x99, 0xde, 0x6b, 0x49, 0xf0, 0x8b, 0xc8, 0x19, 0x67, 0x21, 0x9b, 0x5e, 0x2f, 0x34, 0x1a, 0x2b, 0x27, 0x3c, 0x76, 0xf0, 0x19, 0x77, 0xfb, 0x3c, 0x9d, 0xff, 0xe1, 0x13, 0x35, 0x0b, + 0x88, 0xf6, 0xf6, 0x51, 0x54, 0x97, 0x77, 0x96, 0x3a, 0x53, 0x03, 0xc1, 0xed, 0xaa, 0xd1, 0xd5, 0x06, 0x72, 0x61, 0xe0, 0xb0, 0x04, 0x7c, 0x1d, 0x5d, 0xff, 0x4e, 0xf0, 0x1c, 0xc2, 0x57, 0x14, + 0x1e, 0x96, 0x14, 0xc9, 0xa0, 0x02, 0x95, 0x81, 0xe2, 0xdb, 0x6b, 0xa6, 0x58, 0x01, 0x12, 0x94, 0x6b, 0x36, 0xea, 0x54, 0x5d, 0xf9, 0x0d, 0xf0, 0xad, 0xa8, 0xf7, 0x33, 0x9c, 0x3e, 0xcf, 0x12, + 0xb3, 0x1b, 0xfc, 0x20, 0x8d, 0x5d, 0xef, 0x8f, 0x98, 0x30, 0xed, 0x65, 0x69, 0x37, 0xfe, 0x7b, 0x1a, 0x07, 0xed, 0xc9, 0x26, 0xac, 0x1b, 0xd5, 0x6e, 0xf0, 0x43, 0x35, 0x66, 0x6e, 0x5b, 0x01, + 0xad, 0x7c, 0x69, 0x84, 0xaf, 0xbd, 0xc2, 0x72, 0x5d, 0x01, 0x15, 0xd2, 0x17, 0x1e, 0xc1, 0x71, 0xd5, 0x9e, 0x1c, 0x8f, 0x8f, 0xde, 0x93, 0xb4, 0x6c, 0xe2, 0x39, 0x3a, 0x27, 0xc8, 0x9c, 0xa0, + 0xb2, 0x23, 0x72, 0x5f, 0x74, 0x07, 0xb4, 0xf5, 0x49, 0x6b, 0xab, 0x3c, 0xaf, 0xd5, 0xb7, 0x6d, 0xfa, 0xa8, 0x98, 0x13, 0x6b, 0x48, 0x61, 0x0d, 0xca, 0xd9, 0x43, 0xc6, 0x26, 0x4e, 0x70, 0xbc, + 0x10, 0xac, 0x64, 0x65, 0x69, 0xb6, 0x90, 0xae, 0x80, 0x95, 0x6e, 0xf5, 0x93, 0x77, 0x67, 0x86, 0xff, 0xe4, 0x1d, 0x1f, 0x6a, 0x25, 0xdc, 0x6f, 0x53, 0x65, 0xd9, 0x14, 0x91, 0x03, 0x3b, 0x2c, + 0xa1, 0xef, 0x0e, 0xc7, 0x80, 0x75, 0x29, 0xb3, 0xfe, 0x6f, 0xce, 0x5e, 0x8c, 0x69, 0x08, 0x7c, 0x7f, 0x44, 0x13, 0x05, 0x1d, 0xda, 0x79, 0x83, 0x8e, 0xdd, 0x64, 0xdf, 0x0a, 0xe2, 0x9a, 0x1e, + 0xb5, 0x0d, 0xb4, 0xf4, 0xc6, 0xcb, 0x3f, 0xee, 0x70, 0xdc, 0x7f, 0x1f, 0x36, 0x0e, 0x94, 0x97, 0xb5, 0xdd, 0xb5, 0x27, 0xe8, 0xa3, 0x80, 0xd7, 0x6e, 0x28, 0xec, 0x8b, 0xfb, 0x52, 0x7f, 0x72, + 0xcd, 0x4a, 0x3b, 0x94, 0x35, 0x52, 0x1a, 0x18, 0x24, 0x38, 0x7c, 0x4f, 0x02, 0x0a, 0x50, 0x60, 0x98, 0x83, 0x94, 0xeb, 0x0c, 0xde, 0x0c, 0x4c, 0x7d, 0x36, 0xed, 0x14, 0x3c, 0xca, 0x86, 0x71, + 0xe3, 0x89, 0xc0, 0x7b, 0xc2, 0x7d, 0x4e, 0xf1, 0x30, 0xa9, 0x87, 0x39, 0xee, 0xa5, 0x22, 0xe6, 0x68, 0x34, 0x72, 0xbc, 0x9a, 0xac, 0x99, 0x11, 0x65, 0x32, 0x57, 0x5c, 0xab, 0x5c, 0x14, 0xb5, + 0x61, 0x63, 0xca, 0xbb, 0x39, 0xe8, 0x93, 0x29, 0x46, 0xbc, 0x13, 0x66, 0x0a, 0x9a, 0x56, 0xcc, 0x30, 0x09, 0x0e, 0x4c, 0xe8, 0x83, 0xc5, 0x90, 0x8c, 0x69, 0x63, 0x2f, 0x9f, 0xd8, 0xee, 0x1d, + 0x94, 0x8c, 0xee, 0xdf, 0x57, 0x29, 0x99, 0xb9, 0xaf, 0xbf, 0x93, 0x12, 0xbb, 0xb5, 0x0e, 0xe4, 0x4d, 0xbc, 0xde, 0xb7, 0x5f, 0x99, 0x11, 0xec, 0xbe, 0x84, 0x31, 0x3b, 0x0f, 0x6c, 0xcd, 0x50, + 0xed, 0x44, 0x89, 0x4a, 0x5d, 0x14, 0x7e, 0xb8, 0x92, 0xe1, 0xa2, 0x32, 0x61, 0x92, 0x7d, 0xb8, 0xd7, 0xd6, 0x7a, 0x4d, 0x23, 0x44, 0x57, 0xba, 0x88, 0x17, 0x99, 0x21, 0x78, 0xde, 0x74, 0xb8, + 0x45, 0x9c, 0x25, 0x57, 0x5a, 0x02, 0x3d, 0x7d, 0xde, 0x2f, 0x76, 0xed, 0x94, 0x19, 0x34, 0x83, 0x84, 0xbc, 0x1a, 0xfa, 0xf1, 0x72, 0x93, 0x33, 0x51, 0x5a, 0x96, 0xff, 0x90, 0xdc, 0x1c, 0xaf, + 0x69, 0x08, 0xe5, 0xa0, 0x88, 0x67, 0x4d, 0x63, 0xdb, 0x9e, 0x6f, 0x0b, 0x6b, 0x30, 0x22, 0xdf, 0xbe, 0xb1, 0x13, 0xbc, 0x54, 0x41, 0xc9, 0x9b, 0xab, 0x28, 0x20, 0xe3, 0xb4, 0x81, 0x84, 0x64, + 0x05, 0xa0, 0x8a, 0xb9, 0x15, 0x3d, 0x7d, 0x1e, 0xbe, 0x02, 0x32, 0x61, 0x80, 0x3b, 0x6d, 0xb6, 0x3b, 0x3c, 0x56, 0xe5, 0x42, 0xb1, 0xf2, 0x33, 0x93, 0xb0, 0x4b, 0x4d, 0xad, 0x94, 0x2f, 0xaf, + 0x43, 0x7f, 0x87, 0x7b, 0xfe, 0xf8, 0xea, 0x4d, 0xde, 0x53, 0xc1, 0xc9, 0xdb, 0xaa, 0x38, 0x99, 0xaf, 0xe4, 0xa8, 0x7a, 0x57, 0x97, 0x6e, 0x57, 0xcd, 0x04, 0xda, 0xdd, 0xd8, 0xfe, 0xbd, 0x95, + 0x8b, 0x2e, 0x80, 0x76, 0xd9, 0x39, 0x11, 0x19, 0x8d, 0x94, 0x13, 0x2c, 0x7a, 0x63, 0x05, 0x77, 0x62, 0x3d, 0x0a, 0xad, 0x7d, 0x86, 0x6e, 0x07, 0xd1, 0x2a, 0x26, 0xa1, 0xf3, 0x10, 0x16, 0xbd, + 0xa0, 0xc7, 0x16, 0x04, 0x1f, 0x3a, 0x3e, 0x78, 0xdd, 0x06, 0x61, 0xfb, 0xd8, 0xc9, 0x59, 0x69, 0xc7, 0x6f, 0x9f, 0x00, 0xe8, 0x6e, 0x7b, 0xc6, 0x1f, 0x7d, 0xa9, 0x84, 0x11, 0xbe, 0x8d, 0x63, + 0x52, 0xb9, 0x18, 0x45, 0xb5, 0x1f, 0xc1, 0x71, 0x8f, 0x94, 0x6e, 0x61, 0xe9, 0xa2, 0x7b, 0x84, 0xd2, 0xc5, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x50, 0x4b, 0x07, 0x08, 0x7b, 0x3e, 0x7e, 0x8a, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, @@ -13660,7 +13660,7 @@ var TemplatesZip = []byte{ 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x33, 0xee, 0xa3, 0xf6, 0x0a, 0x00, 0x00, 0xa7, 0x1d, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0x6b, 0x6a, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x2e, 0x63, 0x6d, 0x64, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, - 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x9b, 0xfc, 0xbe, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x3e, 0x7e, 0x8a, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0xab, 0x75, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0xed, 0x79, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x65, 0x76, @@ -13722,8 +13722,8 @@ var TemplatesZip = []byte{ 0x61, 0x0e, 0x00, 0x00, 0x19, 0x2c, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0x54, 0x91, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x33, 0xee, 0xa3, 0xf6, 0x0a, 0x00, 0x00, 0xa7, 0x1d, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0xf4, 0x9f, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, - 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x2e, 0x63, 0x6d, 0x64, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x9b, - 0xfc, 0xbe, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x2d, 0xab, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, + 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x6d, 0x76, 0x6e, 0x77, 0x2e, 0x63, 0x6d, 0x64, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x3e, + 0x7e, 0x8a, 0xf9, 0x03, 0x00, 0x00, 0xf9, 0x10, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x2d, 0xab, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x70, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x81, 0x68, 0xaf, 0x03, 0x00, 0x71, 0x75, 0x61, 0x72, 0x6b, 0x75, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/go.mod b/go.mod index 8259fac0b5..86fd9ac31e 100644 --- a/go.mod +++ b/go.mod @@ -65,16 +65,16 @@ require ( gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools/v3 v3.5.2 - k8s.io/api v0.35.7 - k8s.io/apimachinery v0.35.7 - k8s.io/client-go v0.35.7 + k8s.io/api v0.35.8 + k8s.io/apimachinery v0.35.8 + k8s.io/client-go v0.35.8 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 knative.dev/client/pkg v0.0.0-20260824022027-f22c1f6498a3 - knative.dev/eventing v0.50.1-0.20260824121526-ec5c1d17105c + knative.dev/eventing v0.50.1-0.20260826203415-973bbbc1f791 knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b - knative.dev/pkg v0.0.0-20260820190123-c9015f8bfdea - knative.dev/serving v0.50.1-0.20260821122824-98e5ef90aa70 + knative.dev/pkg v0.0.0-20260825072334-d2a153acc00c + knative.dev/serving v0.50.1-0.20260825144834-618b0c275af1 sigs.k8s.io/controller-runtime v0.23.3 sigs.k8s.io/yaml v1.6.0 ) @@ -315,8 +315,8 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/apiextensions-apiserver v0.35.7 // indirect - k8s.io/apiserver v0.35.7 // indirect + k8s.io/apiextensions-apiserver v0.35.8 // indirect + k8s.io/apiserver v0.35.8 // indirect k8s.io/cli-runtime v0.34.1 // indirect k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect knative.dev/networking v0.0.0-20260821014922-17b28c62e1cb // indirect diff --git a/go.sum b/go.sum index f1f8805bea..996597e4cc 100644 --- a/go.sum +++ b/go.sum @@ -1833,26 +1833,26 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= k8s.io/api v0.27.7/go.mod h1:ZNExI/Lhrs9YrLgVWx6jjHZdoWCTXfBXuFjt1X6olro= k8s.io/api v0.27.10/go.mod h1:cDmAF4GtSVRO0+5hOY/Vo3lLCQMOp6FfrXZ94/gQwC0= -k8s.io/api v0.35.7 h1:qzjBIl6HRlZ4TXvVELrWC50d30RZu/uLJFE9efpLgxk= -k8s.io/api v0.35.7/go.mod h1:rXqZY94EWBj+9wVgdVOVS46MtnAEF09khzjCR4EOqiE= +k8s.io/api v0.35.8 h1:hxpmPYdneQPKNh0cZyB09Hwd3vgXzdcJs5R3toDXsvU= +k8s.io/api v0.35.8/go.mod h1:I5gVNknFd4hfVVcMCixrenD7V38JUY78q3jtpGyC19c= k8s.io/apiextensions-apiserver v0.27.7/go.mod h1:x0p+b5a955lfPz9gaDeBy43obM12s+N9dNHK6+dUL+g= -k8s.io/apiextensions-apiserver v0.35.7 h1:0Fj7U6mSPEJKfW2GnjOF1zthG2pIpdL2mj14mUJ3E4w= -k8s.io/apiextensions-apiserver v0.35.7/go.mod h1:sh7EoBfvntnXL6GwaLMhfvlhJpGzTFHSI7AyPQ0YQ1g= +k8s.io/apiextensions-apiserver v0.35.8 h1:2lvyZ28M01/1uOPftaPXIbsOaCslzKHTEcdCjCI0VRE= +k8s.io/apiextensions-apiserver v0.35.8/go.mod h1:/ZbM1upeajFY5yHqGMKEzWTLb5Tx3kU2SkxTCtqB4Uw= k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= k8s.io/apimachinery v0.27.7/go.mod h1:jBGQgTjkw99ef6q5hv1YurDd3BqKDk9YRxmX0Ozo0i8= k8s.io/apimachinery v0.27.10/go.mod h1:IHu2ovJ60RqxyPSLmTel7KDLdOCRbpOxwtUBmwBnT/E= -k8s.io/apimachinery v0.35.7 h1:dIMi/gFnUDiASbeBqx1wqLAB8jlNo4kysncMZceV0OM= -k8s.io/apimachinery v0.35.7/go.mod h1:1I6w9iM6v//hXKgxe1g3ybZgcOaJrcYg9sQGtndFrmc= +k8s.io/apimachinery v0.35.8 h1:piOyQQgse1sGztJVfy3B8f11YpT+KwK5KkD5Jie1EK0= +k8s.io/apimachinery v0.35.8/go.mod h1:z9Vq5oR1X38pkhh0wV531iKSeqmOVjqgHdYMjvzq2+o= k8s.io/apiserver v0.27.7/go.mod h1:OrLG9RwCOerutAlo8QJW5EHzUG9Dad7k6rgcDUNSO/w= -k8s.io/apiserver v0.35.7 h1:Z9RShd9T1nSEua/j0BQcL4JTP09jZfddR/gNr9AFSO0= -k8s.io/apiserver v0.35.7/go.mod h1:zswr0siixtL3+h07x6Ec/8CUcAcLQ4yUwT6COvKoOYQ= +k8s.io/apiserver v0.35.8 h1:DuwdXkMrrNi6ovUaQk9EZhIgP7cDaWOvNP/CvrLJNfM= +k8s.io/apiserver v0.35.8/go.mod h1:97kTXpbFyeZwLcrK5hRYfc9ZfM3T7D4oYC0WXsJoXXM= k8s.io/cli-runtime v0.34.1 h1:btlgAgTrYd4sk8vJTRG6zVtqBKt9ZMDeQZo2PIzbL7M= k8s.io/cli-runtime v0.34.1/go.mod h1:aVA65c+f0MZiMUPbseU/M9l1Wo2byeaGwUuQEQVVveE= k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= k8s.io/client-go v0.27.7/go.mod h1:dZ2kqcalYp5YZ2EV12XIMc77G6PxHWOJp/kclZr4+5Q= k8s.io/client-go v0.27.10/go.mod h1:PhrjLdIJNy7L8liOPEzm6wNlMjhIRJeVbfvksTxKNqI= -k8s.io/client-go v0.35.7 h1:UWxdVZcqqCdeip50BKoIcoGryX7WfTE1oQQVolP8HJs= -k8s.io/client-go v0.35.7/go.mod h1:uksk4ZVg/i4PrgsMsludkEe7Cp6v8hJ9wvje+2Ekn+c= +k8s.io/client-go v0.35.8 h1:tIW2sirCQMiGoCSvtOYqS059CDQ5n1nrDQa+PVt4nqY= +k8s.io/client-go v0.35.8/go.mod h1:fT8dATMU8FHMq4hlOudbsxihQ1LIQfDaLNDXBnIk6OQ= k8s.io/code-generator v0.27.7/go.mod h1:w1YF/xQcTg+d9Ag+04xuRqER+q8rDnJ70ynLql8/RLA= k8s.io/component-base v0.27.7/go.mod h1:YGjlCVL1oeKvG3HSciyPHFh+LCjIEqsxz4BDR3cfHRs= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -1878,16 +1878,16 @@ k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0x k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= knative.dev/client/pkg v0.0.0-20260824022027-f22c1f6498a3 h1:yDau9zgPC4EPkkFKsXiKslX+A4u3erge5PArBXOk+0g= knative.dev/client/pkg v0.0.0-20260824022027-f22c1f6498a3/go.mod h1:uA+bOCkcmaU8gbYLQ/6GrUXD9hmfV0tsXUoYUC/d3cg= -knative.dev/eventing v0.50.1-0.20260824121526-ec5c1d17105c h1:GfZm9tjdDAuRMtPOrAM5340nj/VHiKEpkIuxHwPYkWI= -knative.dev/eventing v0.50.1-0.20260824121526-ec5c1d17105c/go.mod h1:se5U7KcgxQ/VaAIjvWnGjYqXDQhfKwfTgB+7cf2mY1A= +knative.dev/eventing v0.50.1-0.20260826203415-973bbbc1f791 h1:LBlXWXWxKbRV5mTFsmbrTp8itbTqyILFvBVozpM0KUQ= +knative.dev/eventing v0.50.1-0.20260826203415-973bbbc1f791/go.mod h1:se5U7KcgxQ/VaAIjvWnGjYqXDQhfKwfTgB+7cf2mY1A= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b h1:MvbV2F2BdI8qKrYYUhDwbUZbX0BAYRSIpXM2TOtTvs0= knative.dev/hack v0.0.0-20260428014158-b2a37f1b6e7b/go.mod h1:L5RzHgbvam0u8QFHfzCX6MKxu/a/gIGEdaRBqNiVbl0= knative.dev/networking v0.0.0-20260821014922-17b28c62e1cb h1:ukTLqsBsPfPsvEvB7c8DsVoKpDGi4tMsHCDzL8y8ycM= knative.dev/networking v0.0.0-20260821014922-17b28c62e1cb/go.mod h1:LcjiQvD89e7hdND0fI4O4sbWt7L8fEnb+9xPi5BEa5Y= -knative.dev/pkg v0.0.0-20260820190123-c9015f8bfdea h1:vNg/ixrFn2UMDAh727Ag26G3jZAQGskbn5VOwZvKoOM= -knative.dev/pkg v0.0.0-20260820190123-c9015f8bfdea/go.mod h1:azi5R3dOIcS9Pp8+QAZR9Yp7e/eoD7rNVFIefkZr8N8= -knative.dev/serving v0.50.1-0.20260821122824-98e5ef90aa70 h1:15SH9Ne0DBpYFNLuCnF4UQJ+MNi3WopTWmSZPNMfX40= -knative.dev/serving v0.50.1-0.20260821122824-98e5ef90aa70/go.mod h1:MP+yKsx/gLczU+agah1HgRrpU6KT0SV96a7bD+v5PKo= +knative.dev/pkg v0.0.0-20260825072334-d2a153acc00c h1:x99AILfR+9/dMMSXTpD5akQkCxfqreR+Py40i4pUrmA= +knative.dev/pkg v0.0.0-20260825072334-d2a153acc00c/go.mod h1:79LFLJ+XR5kxkPWy4KBBJkdTrk00t7zynpgOpP8uKrs= +knative.dev/serving v0.50.1-0.20260825144834-618b0c275af1 h1:U7ixUp+rikVPkEEsYzn/PkL8t6OSDMfAFZfVxPb7JKQ= +knative.dev/serving v0.50.1-0.20260825144834-618b0c275af1/go.mod h1:TSVJNoOBCimZFxyVSe2+8CYuNLSlkMn4RxRSZDHditE= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/pkg/mcp/instructions.md b/pkg/mcp/instructions.md index 90ad89b37f..ec05ee4cd7 100644 --- a/pkg/mcp/instructions.md +++ b/pkg/mcp/instructions.md @@ -42,7 +42,7 @@ This is essential because: **Exceptions:** - The `list` tool operates on the cluster, not local files, so it does NOT use a path parameter (it uses namespace instead) -- The `delete` and `describe` tools each require exactly one of `path` or `name`; they do NOT support a no-argument CWD mode (the MCP server process has its own working directory unrelated to the Function being managed) +- The `delete`, `describe` and `logs` tools each require exactly one of `path` or `name`; they do NOT support a no-argument CWD mode (the MCP server process has its own working directory unrelated to the Function being managed) ## Deployment Behavior @@ -63,6 +63,7 @@ This is essential because: - Before 'invoke' → Read `func://help/invoke` - Before 'list' → Read `func://help/list` - Before 'describe' → Read `func://help/describe` +- Before 'logs' → Read `func://help/logs` - Before 'delete' → Read `func://help/delete` - Before 'run' → Read `func://help/run` @@ -168,6 +169,24 @@ A first-time deploy can be detected by checking the func.yaml for a value in the - 'namespace' is only valid together with 'name'; providing both 'path' and 'namespace' is rejected, since path mode already determines the namespace from the Function's own deploy identity - Read-only; does not modify local files or cluster resources +### logs + +- **FIRST:** Read `func://help/logs` for authoritative usage information +- Exactly one of `path` or `name` is required (same shape as `delete` / `describe`) — never both, and never neither +- `path` must be an absolute path to the Function project directory (reads func.yaml); `name` is the deployed Function name on the cluster +- The Function must already be **deployed** — in path mode the tool still talks to the cluster via the project's deploy identity, so calling it before `deploy` is a usage error, not a tool bug +- Returns a **finite snapshot** of recent logs (it prints and exits; it is not a live stream); the default is the most recent 1000 lines per pod, so the output is bounded even when you pass neither `since` nor `tail` +- Narrow it further with `since` (time window, e.g. `30s`, `5m`, `2h`) and/or a smaller `tail` (most recent lines per pod); a negative `tail` means unlimited, and is the only way to ask for every retained line +- If the payload exceeds the tool's size limit, only the most recent lines are returned and `truncated` is set to true; re-run with `tail` or `since` rather than assuming the output is complete +- **Always read `warnings` before concluding a Function produced no output.** Empty or partial `logs` with a zero exit is normal and explained there: the Function may have scaled to zero (no pods, therefore no logs), or the logs of only some of its pods could be read +- `namespace` applies when identifying the Function by `name`. In `path` mode the namespace is read from the Function's own deploy identity in func.yaml and `namespace` has no effect — the same behavior as `func logs`, which this tool does not add rules on top of +- When more than one pod serves the Function, each line is prefixed with `[pod/] ` +- `follow`/streaming is deliberately not exposed: it never terminates, so it is unusable from an agent. Use `since`/`tail` and call again to observe new output +- Only Functions deployed with the default **knative** deployer are supported; for other deployers the CLI returns an error directing you to `kubectl logs` +- `verbose` output is written to the same stream the notices come back on, so enabling it makes `warnings` considerably noisier; the returned notices are bounded and older ones are dropped when they overflow +- This tool is **read-only** — it never modifies any state +- Use logs to diagnose a deployed Function after `deploy`, especially when combined with `invoke` to trigger the Function and observe its output + ### delete - **FIRST:** Read `func://help/delete` for authoritative usage information diff --git a/pkg/mcp/mcp.go b/pkg/mcp/mcp.go index 5f439027c0..0a910dc1c2 100644 --- a/pkg/mcp/mcp.go +++ b/pkg/mcp/mcp.go @@ -137,6 +137,7 @@ func New(options ...Option) *Server { mcp.AddTool(i, invokeTool, s.invokeHandler) mcp.AddTool(i, listTool, s.listHandler) mcp.AddTool(i, describeTool, s.describeHandler) + mcp.AddTool(i, logsTool, s.logsHandler) mcp.AddTool(i, deleteTool, s.deleteHandler) mcp.AddTool(i, runTool, s.runHandler) mcp.AddTool(i, runStopTool, s.runStopHandler) @@ -179,6 +180,7 @@ func New(options ...Option) *Server { i.AddResource(newHelpResource(s, "Invoke Help", "help for 'invoke'", "invoke")) i.AddResource(newHelpResource(s, "List Help", "help for 'list'", "list")) i.AddResource(newHelpResource(s, "Describe Help", "help for 'describe'", "describe")) + i.AddResource(newHelpResource(s, "Logs Help", "help for 'logs'", "logs")) i.AddResource(newHelpResource(s, "Delete Help", "help for delete", "delete")) i.AddResource(newHelpResource(s, "Volumes Help", "general help for volumes", "config", "volumes")) diff --git a/pkg/mcp/tools_logs.go b/pkg/mcp/tools_logs.go new file mode 100644 index 0000000000..988e1017ba --- /dev/null +++ b/pkg/mcp/tools_logs.go @@ -0,0 +1,186 @@ +package mcp + +import ( + "context" + "fmt" + "strconv" + "strings" + "unicode/utf8" + + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +// defaultTailLines bounds at the source how much log output the CLI is asked +// to gather when the caller has not bounded it themselves. 'func logs' +// defaults to every line the Function's pods have retained, and the executor +// buffers that in full before this package sees any of it, so a default is +// applied here rather than gathering everything and discarding most of it +// after the fact. It is declared in the tool's input schema, and a negative +// 'tail' still means unlimited, exactly as it does in the CLI. +const defaultTailLines = 1000 + +// maxLogBytes is a backstop on the log payload returned to the caller, for +// the cases defaultTailLines does not bound: an explicitly large (or +// unlimited) 'tail', or a Function served by many pods, each contributing its +// own tail. The most recent lines are kept, and the fact that older lines +// were dropped is reported via LogsOutput.Truncated rather than being silent. +const maxLogBytes = 256 * 1024 + +// maxWarningBytes bounds the notices returned alongside the logs. stderr is +// not necessarily a line or two: 'verbose' writes its output there, as does +// one warning per pod whose logs could not be read, so it needs a bound of +// its own rather than being returned whole. +const maxWarningBytes = 8 * 1024 + +// warningsTruncatedMarker introduces a warnings payload which was itself too +// large to return, so that the notices which remain are not mistaken for all +// of them. +const warningsTruncatedMarker = "[...older notices omitted...]" + +var logsTool = &mcp.Tool{ + Name: "logs", + Title: "Get Function Logs", + Description: "Retrieve a finite snapshot of recent logs from a deployed Function (prints and returns, does not stream). Returns the most recent 1000 lines per pod by default; bound it explicitly with 'tail' and/or 'since' (time window, e.g. '5m'). Identify the Function by path (reads func.yaml) or by name.", + Annotations: &mcp.ToolAnnotations{ + Title: "Get Function Logs", + ReadOnlyHint: true, + // A logs snapshot is read-only but not idempotent: the same call + // made later returns a different (growing) set of log lines, so + // IdempotentHint is intentionally left unset (false). + }, +} + +func (s *Server) logsHandler(ctx context.Context, r *mcp.CallToolRequest, input LogsInput) (result *mcp.CallToolResult, output LogsOutput, err error) { + // Unlike the CLI, the MCP tool does not fall back to the server's working + // directory: an agent has no meaningful cwd, so require an explicit target. + // Exactly one of Path or Name must be provided (same shape as describe/delete). + // + // NOTE: no further validation is performed here. In particular + // 'namespace' is forwarded as given, including alongside 'path', where + // the CLI reads the namespace from the Function's own deploy identity and + // ignores the flag. Rejecting that combination would be a rule this tool + // enforces and 'func logs' does not, which is a divergence between the two + // surfaces rather than a fix. + if (input.Path != nil && input.Name != nil) || (input.Path == nil && input.Name == nil) { + err = fmt.Errorf("exactly one of 'path' or 'name' must be provided") + return + } + + // ExecuteSplit (rather than Execute/CombinedOutput) is required here so + // that the log content is clean stdout only. `func logs` uses stderr for + // everything which is not a log line, including notices which accompany a + // successful, zero-exit call: that the Function has scaled to zero and so + // has no logs, and that the logs of some (but not all) of its pods could + // be read. Those are surfaced separately as Warnings, since an empty or + // partial payload is otherwise indistinguishable from a complete one. + stdout, stderr, err := s.executor.ExecuteSplit(ctx, "logs", input.Args()...) + if err != nil { + err = fmt.Errorf("%w\nstdout: %s\nstderr: %s", err, string(stdout), string(stderr)) + return + } + + // Truncation of the logs is reported by Truncated alone. It is + // deliberately not also described in Warnings: two channels for one fact + // invite the caller to report it twice, and Warnings is what the CLI + // said, not what this tool did to the payload. + logs, truncated := truncateTail(string(stdout), maxLogBytes) + + warnings, warningsTruncated := truncateTail(strings.TrimSpace(string(stderr)), maxWarningBytes) + if warningsTruncated { + warnings = warningsTruncatedMarker + "\n" + warnings + } + + output = LogsOutput{ + Logs: logs, + Truncated: truncated, + Warnings: warnings, + } + return +} + +// truncateTail bounds s to its last 'limit' bytes, keeping the most recent lines +// and reporting whether anything was dropped. +// +// Whole lines are preserved in both directions: the first line returned is +// never a fragment of an older one, and no complete line is discarded in +// order to reach a line boundary the cut had already landed on. A single +// line longer than the limit is the one case which cannot be honored; its tail is +// returned, trimmed to a rune boundary so that the result is still valid +// UTF-8. +func truncateTail(s string, limit int) (string, bool) { + if len(s) <= limit { + return s, false + } + cut := len(s) - limit // >= 1, since len(s) > limit + + // A cut which lands immediately after a newline already begins a whole, + // intact line. Searching forward for a newline from here would find that + // line's own terminator and silently drop the entire line. + if s[cut-1] == '\n' { + return s[cut:], true + } + + // Otherwise the cut landed inside a line; drop that line's remainder. + if i := strings.IndexByte(s[cut:], '\n'); i >= 0 { + return s[cut+i+1:], true + } + + // No newline anywhere in the retained window: a single line longer than + // the limit, such as one JSON log record or an un-terminated stack trace. + return trimPartialRune(s[cut:]), true +} + +// trimPartialRune drops the leading bytes of a UTF-8 sequence which a +// byte-wise cut left incomplete, so that s begins on a rune boundary. +// Without it, cutting multi-byte content mid-rune yields invalid UTF-8, which +// JSON encoding replaces with U+FFFD. +func trimPartialRune(s string) string { + for i := 0; i < len(s) && i < utf8.UTFMax; i++ { + if utf8.RuneStart(s[i]) { + return s[i:] + } + } + return s +} + +// LogsInput defines the input parameters for the logs tool. +// Exactly one of Path or Name must be provided. +type LogsInput struct { + Path *string `json:"path,omitempty" jsonschema:"Absolute path to the Function project directory (mutually exclusive with name)"` + Name *string `json:"name,omitempty" jsonschema:"Name of the deployed Function to fetch logs for (mutually exclusive with path)"` + Namespace *string `json:"namespace,omitempty" jsonschema:"Kubernetes namespace of the Function (default: current namespace). Applies when identifying the Function by 'name'; in 'path' mode the namespace is read from the Function's own deploy identity in func.yaml and this has no effect, as in the CLI"` + Since *string `json:"since,omitempty" jsonschema:"Return logs newer than a relative duration such as 30s, 5m, or 2h (default: all retained logs, subject to tail)"` + Tail *int `json:"tail,omitempty" jsonschema:"Number of most recent log lines to return per pod (default: 1000; a negative value means unlimited, as in the CLI). Lower it, or add 'since', to bound a large or unknown log volume further"` + Verbose *bool `json:"verbose,omitempty" jsonschema:"Enable verbose logging output. Note this is written to the same stream as the notices returned in 'warnings'"` +} + +func (i LogsInput) Args() []string { + args := []string{} + + if i.Path != nil { + args = append(args, "--path", *i.Path) + } else if i.Name != nil { + args = append(args, "--name", *i.Name) + } + + args = appendStringFlag(args, "--namespace", i.Namespace) + args = appendStringFlag(args, "--since", i.Since) + + // Always bounded at the source: the caller's value when given, otherwise + // the default. See defaultTailLines. + tail := defaultTailLines + if i.Tail != nil { + tail = *i.Tail + } + args = append(args, "--tail", strconv.Itoa(tail)) + + args = appendBoolFlag(args, "--verbose", i.Verbose) + return args +} + +// LogsOutput defines the structured output returned by the logs tool. +type LogsOutput struct { + Logs string `json:"logs" jsonschema:"Log output from the deployed Function. When more than one pod is serving the Function, each line is prefixed with the pod it came from"` + Truncated bool `json:"truncated,omitempty" jsonschema:"True if older lines were dropped because the output exceeded the tool's size limit; only the most recent lines are included. Re-run with a smaller 'tail' or a shorter 'since' window rather than assuming the output is complete"` + Warnings string `json:"warnings,omitempty" jsonschema:"Non-fatal notices the CLI emitted while gathering the logs. Empty or partial output is explained here: that the Function has scaled to zero and so has no logs to print, or that the logs of some of its pods could not be read. Always check this before concluding that a Function produced no output"` +} diff --git a/pkg/mcp/tools_logs_test.go b/pkg/mcp/tools_logs_test.go new file mode 100644 index 0000000000..fedd4c1de5 --- /dev/null +++ b/pkg/mcp/tools_logs_test.go @@ -0,0 +1,520 @@ +package mcp + +import ( + "context" + "fmt" + "strconv" + "strings" + "testing" + "unicode/utf8" + + "github.com/modelcontextprotocol/go-sdk/mcp" + "knative.dev/func/pkg/mcp/mock" +) + +// TestTool_Logs_Args_ByPath ensures the logs tool passes all arguments correctly +// when identifying the Function by path, and returns the executor's stdout as +// the log content. +func TestTool_Logs_Args_ByPath(t *testing.T) { + stringFlags := map[string]struct { + jsonKey string + flag string + value string + }{ + "path": {"path", "--path", "/home/user/myfunc"}, + "since": {"since", "--since", "10m"}, + } + + boolFlags := map[string]string{ + "verbose": "--verbose", + } + + const wantLogs = "2024/01/01 12:00:00 INFO handler invoked\n" + + executor := mock.NewExecutor() + executor.ExecuteSplitFn = func(ctx context.Context, subcommand string, args ...string) ([]byte, []byte, error) { + if subcommand != "logs" { + t.Fatalf("expected subcommand 'logs', got %q", subcommand) + } + // '--tail 20' is a flag/value pair like the string flags, but is + // passed as an integer in the tool's input schema, so it is checked + // separately from the table-driven string flags. + validateArgLength(t, args, len(stringFlags)+1, len(boolFlags)) + validateStringFlags(t, args, stringFlags) + validateBoolFlags(t, args, boolFlags) + if got := argsToMap(args)["--tail"]; got != "20" { + t.Fatalf("expected --tail 20, got %q", got) + } + return []byte(wantLogs), nil, nil + } + + client, _, err := newTestPair(t, WithExecutor(executor)) + if err != nil { + t.Fatal(err) + } + + args := buildInputArgs(stringFlags, boolFlags) + args["tail"] = 20 + + result, err := client.CallTool(t.Context(), &mcp.CallToolParams{ + Name: "logs", + Arguments: args, + }) + if err != nil { + t.Fatal(err) + } + if result.IsError { + t.Fatalf("unexpected error result: %v", result) + } + if !executor.ExecuteSplitInvoked { + t.Fatal("executor was not invoked") + } + + var output LogsOutput + if err := unmarshalStructuredContent(result, &output); err != nil { + t.Fatal(err) + } + if output.Logs != wantLogs { + t.Errorf("expected logs %q, got %q", wantLogs, output.Logs) + } + if output.Truncated { + t.Error("expected output to not be marked truncated") + } +} + +// TestTool_Logs_Args_ByName ensures the logs tool passes --name when the +// Function is identified by name instead of path, that 'namespace' is +// forwarded in this mode, and that the log content is returned. +func TestTool_Logs_Args_ByName(t *testing.T) { + stringFlags := map[string]struct { + jsonKey string + flag string + value string + }{ + "name": {"name", "--name", "my-function"}, + "namespace": {"namespace", "--namespace", "prod"}, + } + + boolFlags := map[string]string{} + + const wantLogs = "log line 1\nlog line 2\n" + + executor := mock.NewExecutor() + executor.ExecuteSplitFn = func(ctx context.Context, subcommand string, args ...string) ([]byte, []byte, error) { + if subcommand != "logs" { + t.Fatalf("expected subcommand 'logs', got %q", subcommand) + } + // No 'tail' was provided, so the default bound is forwarded in + // addition to the flags under test. + validateArgLength(t, args, len(stringFlags)+1, len(boolFlags)) + validateStringFlags(t, args, stringFlags) + if got := argsToMap(args)["--tail"]; got != strconv.Itoa(defaultTailLines) { + t.Fatalf("expected --tail %d, got %q", defaultTailLines, got) + } + return []byte(wantLogs), nil, nil + } + + client, _, err := newTestPair(t, WithExecutor(executor)) + if err != nil { + t.Fatal(err) + } + + result, err := client.CallTool(t.Context(), &mcp.CallToolParams{ + Name: "logs", + Arguments: buildInputArgs(stringFlags, boolFlags), + }) + if err != nil { + t.Fatal(err) + } + if result.IsError { + t.Fatalf("unexpected error result: %v", result) + } + if !executor.ExecuteSplitInvoked { + t.Fatal("executor was not invoked") + } + + var output LogsOutput + if err := unmarshalStructuredContent(result, &output); err != nil { + t.Fatal(err) + } + if output.Logs != wantLogs { + t.Errorf("expected logs %q, got %q", wantLogs, output.Logs) + } +} + +// TestTool_Logs_MutuallyExclusive ensures that providing both 'path' and 'name' +// returns an error rather than executing the command. +func TestTool_Logs_MutuallyExclusive(t *testing.T) { + executor := mock.NewExecutor() + executor.ExecuteSplitFn = func(ctx context.Context, subcommand string, args ...string) ([]byte, []byte, error) { + t.Fatal("executor should not be called when both path and name are provided") + return nil, nil, nil + } + + client, _, err := newTestPair(t, WithExecutor(executor)) + if err != nil { + t.Fatal(err) + } + + result, err := client.CallTool(t.Context(), &mcp.CallToolParams{ + Name: "logs", + Arguments: map[string]any{ + "path": "/home/user/myfunc", + "name": "my-function", + }, + }) + if err != nil { + t.Fatal(err) + } + if !result.IsError { + t.Fatal("expected an error result when both path and name are provided") + } +} + +// TestTool_Logs_RequiresPathOrName ensures the logs tool errors when neither +// 'path' nor 'name' is provided. Unlike the CLI, the MCP tool does not fall +// back to the server's working directory, since an agent has no meaningful cwd. +func TestTool_Logs_RequiresPathOrName(t *testing.T) { + executor := mock.NewExecutor() + executor.ExecuteSplitFn = func(ctx context.Context, subcommand string, args ...string) ([]byte, []byte, error) { + t.Fatal("executor should not be called when neither path nor name is provided") + return nil, nil, nil + } + + client, _, err := newTestPair(t, WithExecutor(executor)) + if err != nil { + t.Fatal(err) + } + + result, err := client.CallTool(t.Context(), &mcp.CallToolParams{ + Name: "logs", + Arguments: map[string]any{}, + }) + if err != nil { + t.Fatal(err) + } + if !result.IsError { + t.Fatal("expected an error result when neither path nor name is provided") + } +} + +// TestTool_Logs_NamespaceWithPath ensures 'namespace' is forwarded even when +// combined with 'path', rather than being rejected. The CLI accepts the +// combination (it reads the namespace from the Function's own deploy identity +// and ignores the flag), so rejecting it here would make the tool enforce a +// rule 'func logs' does not. +func TestTool_Logs_NamespaceWithPath(t *testing.T) { + executor := mock.NewExecutor() + executor.ExecuteSplitFn = func(ctx context.Context, subcommand string, args ...string) ([]byte, []byte, error) { + argsMap := argsToMap(args) + if got := argsMap["--path"]; got != "/home/user/myfunc" { + t.Fatalf("expected --path /home/user/myfunc, got %q", got) + } + if got := argsMap["--namespace"]; got != "prod" { + t.Fatalf("expected --namespace prod, got %q", got) + } + return []byte("log line\n"), nil, nil + } + + client, _, err := newTestPair(t, WithExecutor(executor)) + if err != nil { + t.Fatal(err) + } + + result, err := client.CallTool(t.Context(), &mcp.CallToolParams{ + Name: "logs", + Arguments: map[string]any{ + "path": "/home/user/myfunc", + "namespace": "prod", + }, + }) + if err != nil { + t.Fatal(err) + } + if result.IsError { + t.Fatalf("unexpected error result: %v", result) + } + if !executor.ExecuteSplitInvoked { + t.Fatal("executor was not invoked") + } +} + +// TestTool_Logs_TailUnlimited ensures an explicit negative 'tail' is passed +// through rather than being replaced by the default bound. Negative means +// unlimited in the CLI, and is the caller's only way to ask for every +// retained line. +func TestTool_Logs_TailUnlimited(t *testing.T) { + executor := mock.NewExecutor() + executor.ExecuteSplitFn = func(ctx context.Context, subcommand string, args ...string) ([]byte, []byte, error) { + if got := argsToMap(args)["--tail"]; got != "-1" { + t.Fatalf("expected --tail -1, got %q", got) + } + return []byte("log line\n"), nil, nil + } + + client, _, err := newTestPair(t, WithExecutor(executor)) + if err != nil { + t.Fatal(err) + } + + result, err := client.CallTool(t.Context(), &mcp.CallToolParams{ + Name: "logs", + Arguments: map[string]any{"name": "myfunc", "tail": -1}, + }) + if err != nil { + t.Fatal(err) + } + if result.IsError { + t.Fatalf("unexpected error result: %v", result) + } +} + +// TestTool_Logs_Warnings ensures the notices 'func logs' writes to stderr on an +// otherwise-successful call are surfaced rather than discarded, and that they +// do not leak into the log content itself. A Function scaled to zero exits +// zero with empty stdout, which is otherwise indistinguishable from a Function +// which ran and printed nothing. +func TestTool_Logs_Warnings(t *testing.T) { + const notice = "No running or recently terminated pods found for function 'myfunc' in namespace 'prod'. It may have scaled to zero, in which case there are no logs to print." + + executor := mock.NewExecutor() + executor.ExecuteSplitFn = func(ctx context.Context, subcommand string, args ...string) ([]byte, []byte, error) { + return nil, []byte(notice + "\n"), nil + } + + client, _, err := newTestPair(t, WithExecutor(executor)) + if err != nil { + t.Fatal(err) + } + + result, err := client.CallTool(t.Context(), &mcp.CallToolParams{ + Name: "logs", + Arguments: map[string]any{"name": "myfunc", "namespace": "prod"}, + }) + if err != nil { + t.Fatal(err) + } + if result.IsError { + t.Fatalf("unexpected error result: %v", result) + } + + var output LogsOutput + if err := unmarshalStructuredContent(result, &output); err != nil { + t.Fatal(err) + } + if output.Logs != "" { + t.Errorf("expected empty logs, got %q", output.Logs) + } + if output.Warnings != notice { + t.Errorf("expected warnings %q, got %q", notice, output.Warnings) + } +} + +// TestTool_Logs_Truncated ensures a log payload larger than the limit is +// bounded to its most recent lines and reported as truncated, rather than +// being returned whole or silently shortened. +func TestTool_Logs_Truncated(t *testing.T) { + const lastLine = "the most recent line\n" + oversized := strings.Repeat("a log line of some length\n", (maxLogBytes/26)+100) + lastLine + + executor := mock.NewExecutor() + executor.ExecuteSplitFn = func(ctx context.Context, subcommand string, args ...string) ([]byte, []byte, error) { + return []byte(oversized), nil, nil + } + + client, _, err := newTestPair(t, WithExecutor(executor)) + if err != nil { + t.Fatal(err) + } + + result, err := client.CallTool(t.Context(), &mcp.CallToolParams{ + Name: "logs", + Arguments: map[string]any{"name": "myfunc"}, + }) + if err != nil { + t.Fatal(err) + } + if result.IsError { + t.Fatalf("unexpected error result: %v", result) + } + + var output LogsOutput + if err := unmarshalStructuredContent(result, &output); err != nil { + t.Fatal(err) + } + if !output.Truncated { + t.Error("expected output to be marked truncated") + } + if len(output.Logs) > maxLogBytes { + t.Errorf("expected at most %d bytes of logs, got %d", maxLogBytes, len(output.Logs)) + } + if !strings.HasSuffix(output.Logs, lastLine) { + t.Error("expected the most recent lines to be retained") + } + if first, _, _ := strings.Cut(output.Logs, "\n"); first != "a log line of some length" { + t.Errorf("expected truncation to a line boundary, got partial first line %q", first) + } + // Truncation is reported by 'truncated' alone. Repeating it in + // 'warnings' would give the caller two channels for one fact, and + // 'warnings' is reserved for what the CLI reported. + if output.Warnings != "" { + t.Errorf("expected truncation to be reported by 'truncated' only, got warnings %q", output.Warnings) + } +} + +// TestTool_Logs_WarningsBounded ensures the notices returned alongside the +// logs are themselves bounded. stderr is not guaranteed to be a line or two: +// 'verbose' writes there, as does one warning per unreadable pod, so an +// unbounded 'warnings' would defeat the cap on the payload as a whole. +func TestTool_Logs_WarningsBounded(t *testing.T) { + const lastNotice = "Warning: the newest notice" + oversized := strings.Repeat("Warning: a pod's logs could not be read\n", (maxWarningBytes/40)+100) + lastNotice + + executor := mock.NewExecutor() + executor.ExecuteSplitFn = func(ctx context.Context, subcommand string, args ...string) ([]byte, []byte, error) { + return []byte("log line\n"), []byte(oversized), nil + } + + client, _, err := newTestPair(t, WithExecutor(executor)) + if err != nil { + t.Fatal(err) + } + + result, err := client.CallTool(t.Context(), &mcp.CallToolParams{ + Name: "logs", + Arguments: map[string]any{"name": "myfunc"}, + }) + if err != nil { + t.Fatal(err) + } + if result.IsError { + t.Fatalf("unexpected error result: %v", result) + } + + var output LogsOutput + if err := unmarshalStructuredContent(result, &output); err != nil { + t.Fatal(err) + } + if len(output.Warnings) > maxWarningBytes+len(warningsTruncatedMarker)+1 { + t.Errorf("expected warnings bounded to ~%d bytes, got %d", maxWarningBytes, len(output.Warnings)) + } + if !strings.HasPrefix(output.Warnings, warningsTruncatedMarker) { + t.Error("expected dropped notices to be marked, so the remainder is not mistaken for all of them") + } + if !strings.HasSuffix(output.Warnings, lastNotice) { + t.Error("expected the most recent notices to be retained") + } + // Bounding the notices must not be confused with bounding the logs. + if output.Truncated { + t.Error("expected 'truncated' to describe the logs only, not the warnings") + } +} + +// TestTruncateTail exercises the bounding directly, with a small limit so the +// boundary cases are exact. Two of them are regressions: a cut which lands on +// a line boundary must not consume the intact line which begins there, and a +// line longer than the limit must not be returned as a broken rune. +func TestTruncateTail(t *testing.T) { + tests := []struct { + name string + logs string + limit int + want string + wantTruncated bool + }{ + { + name: "under the limit is returned whole", + logs: "aaaa\nbbbb\n", limit: 100, + want: "aaaa\nbbbb\n", wantTruncated: false, + }, + { + name: "exactly at the limit is returned whole", + logs: "aaaa\nbbbb\n", limit: 10, + want: "aaaa\nbbbb\n", wantTruncated: false, + }, + { + // The cut falls immediately after "aaaa\n", so "bbbb" is a + // whole, intact line. Searching forward for a newline from + // there would find that line's own terminator and drop it. + name: "cut on a line boundary keeps the line beginning there", + logs: "aaaa\nbbbb\ncccc\n", limit: 10, + want: "bbbb\ncccc\n", wantTruncated: true, + }, + { + // The cut falls inside "aaaa", whose remainder must go. + name: "cut inside a line drops that line's remainder", + logs: "aaaa\nbbbb\ncccc\n", limit: 12, + want: "bbbb\ncccc\n", wantTruncated: true, + }, + { + name: "single line longer than the limit keeps its tail", + logs: "abcdef", limit: 3, + want: "def", wantTruncated: true, + }, + { + // Three-byte runes and no newline to re-align on: a byte-wise + // cut lands mid-rune and would otherwise emit invalid UTF-8, + // which JSON encoding silently replaces with U+FFFD. + name: "single long line is trimmed to a rune boundary", + logs: "世世世世", limit: 5, + want: "世", wantTruncated: true, + }, + { + // Content which was not valid UTF-8 to begin with (binary + // written to stdout) cannot be made valid by trimming. The + // contract is only that this introduces no corruption of its + // own, so such input is passed through as it was found. + name: "input which is already invalid UTF-8 is passed through", + logs: "\xb8\xb8\xb8\xb8", limit: 3, + want: "\xb8\xb8\xb8", wantTruncated: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, truncated := truncateTail(tt.logs, tt.limit) + if got != tt.want { + t.Errorf("expected %q, got %q", tt.want, got) + } + if truncated != tt.wantTruncated { + t.Errorf("expected truncated %v, got %v", tt.wantTruncated, truncated) + } + if len(got) > tt.limit && tt.limit > 0 { + t.Errorf("expected at most %d bytes, got %d", tt.limit, len(got)) + } + // Bounding must not introduce invalid UTF-8 into content which + // was valid: JSON encoding replaces it with U+FFFD. + if utf8.ValidString(tt.logs) && !utf8.ValidString(got) { + t.Errorf("expected valid UTF-8, got % x", got) + } + }) + } +} + +// TestTool_Logs_ExecutorError ensures a failed command surfaces as a tool error +// which includes both streams, since the CLI reports why it failed on stderr. +func TestTool_Logs_ExecutorError(t *testing.T) { + executor := mock.NewExecutor() + executor.ExecuteSplitFn = func(ctx context.Context, subcommand string, args ...string) ([]byte, []byte, error) { + return nil, []byte("function not deployed or not found"), fmt.Errorf("exit status 1") + } + + client, _, err := newTestPair(t, WithExecutor(executor)) + if err != nil { + t.Fatal(err) + } + + result, err := client.CallTool(t.Context(), &mcp.CallToolParams{ + Name: "logs", + Arguments: map[string]any{"name": "myfunc"}, + }) + if err != nil { + t.Fatal(err) + } + if !result.IsError { + t.Fatal("expected an error result when the command fails") + } + if msg := resultToString(result); !strings.Contains(msg, "function not deployed or not found") { + t.Errorf("expected the command's stderr in the error, got %q", msg) + } +} diff --git a/templates/quarkus/cloudevents/pom.xml b/templates/quarkus/cloudevents/pom.xml index 1225886717..5d5b1f5fc9 100644 --- a/templates/quarkus/cloudevents/pom.xml +++ b/templates/quarkus/cloudevents/pom.xml @@ -12,7 +12,7 @@ UTF-8 quarkus-bom io.quarkus.platform - 3.37.1 + 3.39.1 true 3.0.0-M7 diff --git a/templates/quarkus/http/pom.xml b/templates/quarkus/http/pom.xml index 1225886717..5d5b1f5fc9 100644 --- a/templates/quarkus/http/pom.xml +++ b/templates/quarkus/http/pom.xml @@ -12,7 +12,7 @@ UTF-8 quarkus-bom io.quarkus.platform - 3.37.1 + 3.39.1 true 3.0.0-M7