feat: GHA + K8s guide improvements (#62778)

* Add IaC info alert to welcome step

* Add conditional info alert to "allow any branch" input

* Rename "done" button (to "finish")

* Add repo restriction help text

* Ensure "refs/heads/" prefix for branch input

* Add code panel to Welcome step

* Add `inProgress` mode to CodePanel

* Shift CodePanel left on finish step

* De-emphasise final button and make prior steps clearer

* Explain how to apply terraform template

* Improve manual label entry

* Update labels field label

* Enforce wildcard label

* Update label values format (OR)

* Explain workflows on "main branch"

* Clarify prerequisites

* Improve groups and users entry

* Copy tweak on Configure Access step

* Add groups and users validation

* Add kubernetes cluster selector

* Fix Terraform template tests

* Move cluster select to Setup Workflow step

* Tweak copy warning wording

* With emphasis not clarity

* Replace "setup" with "set up"

* Make cluster selector async searchable

* Fix lint issue

* Fix lint issue (again)
This commit is contained in:
Nick Marais
2026-01-27 13:11:15 +00:00
committed by GitHub
parent 9771063ffb
commit 7b3fe4ce26
33 changed files with 1419 additions and 467 deletions
+229 -225
View File
@@ -563,6 +563,7 @@ const (
IntegrationEnrollField_INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_GROUPS IntegrationEnrollField = 9
IntegrationEnrollField_INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_USERS IntegrationEnrollField = 10
IntegrationEnrollField_INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_RESOURCE_RULES IntegrationEnrollField = 11
IntegrationEnrollField_INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_CLUSTER_NAME IntegrationEnrollField = 12
)
var IntegrationEnrollField_name = map[int32]string{
@@ -578,6 +579,7 @@ var IntegrationEnrollField_name = map[int32]string{
9: "INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_GROUPS",
10: "INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_USERS",
11: "INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_RESOURCE_RULES",
12: "INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_CLUSTER_NAME",
}
var IntegrationEnrollField_value = map[string]int32{
@@ -593,6 +595,7 @@ var IntegrationEnrollField_value = map[string]int32{
"INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_GROUPS": 9,
"INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_USERS": 10,
"INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_RESOURCE_RULES": 11,
"INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_CLUSTER_NAME": 12,
}
func (x IntegrationEnrollField) String() string {
@@ -6320,14 +6323,14 @@ func init() {
}
var fileDescriptor_94cf2ca1c69fd564 = []byte{
// 6496 bytes of a gzipped FileDescriptorProto
// 6507 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x7d, 0x5b, 0x6c, 0x1c, 0xc9,
0x75, 0xe8, 0xf4, 0x90, 0x94, 0xc8, 0x92, 0x44, 0xb5, 0x5a, 0x12, 0x35, 0x22, 0x45, 0x3d, 0x46,
0x6f, 0xee, 0x2e, 0xb5, 0xd2, 0x6a, 0xbd, 0xda, 0x87, 0xbd, 0xdb, 0xec, 0x69, 0xce, 0xb4, 0x38,
0x33, 0x3d, 0x5b, 0xdd, 0x23, 0x9a, 0x6b, 0x18, 0x75, 0x9b, 0xd3, 0x45, 0xb2, 0x2f, 0x87, 0xd3,
0xe3, 0xee, 0x1e, 0x72, 0x89, 0x0b, 0xc3, 0xbe, 0xbe, 0xf7, 0x1a, 0x17, 0xd7, 0xf6, 0x75, 0x02,
0xe3, 0xee, 0x1e, 0x72, 0x89, 0x0b, 0xc3, 0xbe, 0xbe, 0xf7, 0x1a, 0x17, 0xd7, 0xf6, 0xb5, 0x03,
0x24, 0x46, 0x80, 0x0d, 0xf2, 0x40, 0x1e, 0x08, 0xe0, 0x00, 0xf9, 0x32, 0xfc, 0x95, 0x8f, 0xfc,
0x05, 0x48, 0x3e, 0x0c, 0x04, 0xc8, 0x47, 0xf2, 0x13, 0xf8, 0x2b, 0x7f, 0xfe, 0x4a, 0x80, 0xc4,
0x05, 0x48, 0x3e, 0x02, 0x04, 0xc8, 0x47, 0xf2, 0x13, 0xf8, 0x2b, 0x7f, 0xfe, 0x4a, 0x80, 0xc4,
0x79, 0xa1, 0x1e, 0x3d, 0xd3, 0x33, 0xd3, 0x3d, 0xdd, 0xda, 0x68, 0x63, 0x43, 0xfb, 0xc7, 0xa9,
0x3a, 0xaf, 0x3a, 0x75, 0xea, 0x9c, 0x53, 0x55, 0xa7, 0x9a, 0xe0, 0x7e, 0x80, 0xdb, 0xb8, 0xeb,
0x7a, 0xc1, 0x83, 0x9e, 0x6f, 0xed, 0x62, 0x7c, 0x88, 0x3b, 0x81, 0xff, 0xe0, 0xf0, 0x61, 0xf4,
@@ -6353,14 +6356,14 @@ var fileDescriptor_94cf2ca1c69fd564 = []byte{
0x59, 0x8f, 0xb7, 0x51, 0x8c, 0xf9, 0x47, 0xf7, 0x57, 0x13, 0x16, 0xc6, 0xea, 0x28, 0x11, 0xd8,
0x47, 0x2d, 0xee, 0x03, 0x29, 0xec, 0x35, 0x02, 0xdc, 0x35, 0x02, 0x2b, 0xe8, 0xf9, 0xd2, 0xfb,
0xe0, 0x84, 0x4f, 0xff, 0xe2, 0xa4, 0xef, 0xa6, 0x92, 0x66, 0x88, 0x90, 0xa3, 0x91, 0xb5, 0x84,
0x3d, 0xcf, 0xf5, 0xf8, 0x84, 0xb2, 0x1f, 0xc5, 0xdf, 0x17, 0xc0, 0x42, 0x53, 0x8b, 0xa0, 0x78,
0x3d, 0xcf, 0xf5, 0xf8, 0x84, 0xb2, 0x1f, 0xc5, 0xdf, 0x13, 0xc0, 0x42, 0x53, 0x8b, 0xa0, 0x78,
0x01, 0xb6, 0x99, 0xaa, 0x54, 0x30, 0x7b, 0xc0, 0x87, 0x46, 0x79, 0x9e, 0xca, 0x30, 0x9c, 0x50,
0x17, 0xb0, 0x8f, 0x2a, 0x29, 0x7d, 0xc1, 0xf3, 0x94, 0xc8, 0x2b, 0x19, 0x04, 0x0f, 0x47, 0x1d,
0x0a, 0x5f, 0xfc, 0x57, 0x01, 0x5c, 0x1f, 0x88, 0x19, 0x2a, 0xcd, 0xc0, 0x6d, 0xdc, 0x22, 0x2b,
0xe4, 0x85, 0x0a, 0x5c, 0x8b, 0x4c, 0x23, 0x13, 0xf9, 0x61, 0xe6, 0x69, 0x1c, 0x90, 0x0b, 0x49,
0x44, 0xc6, 0x3f, 0xf5, 0xe9, 0xc7, 0xff, 0x7f, 0xf2, 0xc4, 0x09, 0x85, 0x00, 0x5a, 0x27, 0xc0,
0xbb, 0x9e, 0x45, 0x46, 0x2e, 0x6f, 0x1a, 0xba, 0x56, 0x52, 0x14, 0xb7, 0xd3, 0xc1, 0xad, 0xe0,
0xa5, 0xd7, 0xc3, 0x8f, 0xf3, 0x51, 0x3b, 0x28, 0x59, 0x81, 0xb5, 0x6d, 0xf9, 0x18, 0x96, 0x0c,
0xa5, 0xd7, 0xc3, 0x4f, 0xf2, 0x51, 0x3b, 0x28, 0x59, 0x81, 0xb5, 0x6d, 0xf9, 0x18, 0x96, 0x0c,
0xb5, 0xe3, 0xb9, 0xed, 0xf6, 0xcb, 0x3e, 0x7e, 0xe9, 0x09, 0x28, 0xf8, 0xd4, 0xe8, 0xb1, 0x8d,
0x42, 0xca, 0x3e, 0x6a, 0xb9, 0xbd, 0x4e, 0x50, 0x98, 0xbe, 0x2e, 0xdc, 0x9b, 0x82, 0x0b, 0x61,
0x7f, 0x28, 0x8a, 0xaf, 0x90, 0xde, 0xe2, 0x3f, 0x09, 0xe0, 0xca, 0x40, 0x73, 0x1b, 0xbd, 0x6d,
@@ -6379,7 +6382,7 @@ var fileDescriptor_94cf2ca1c69fd564 = []byte{
0x5a, 0x04, 0x0b, 0xd1, 0x7e, 0xb9, 0x26, 0x7f, 0xa4, 0xd7, 0x91, 0xaa, 0x18, 0x62, 0xbe, 0xf8,
0xc9, 0x34, 0xb8, 0x35, 0x18, 0xbf, 0xe2, 0x61, 0x2b, 0xc0, 0xe1, 0xaf, 0x63, 0xc5, 0xed, 0xec,
0x38, 0xbb, 0x2f, 0xbd, 0x5d, 0xba, 0xe0, 0x4c, 0x8b, 0x8e, 0x74, 0xd8, 0x2e, 0x9f, 0x66, 0x30,
0x98, 0x64, 0x85, 0xad, 0xb2, 0xbf, 0x43, 0xfb, 0x6c, 0x45, 0x7e, 0x15, 0xff, 0x50, 0x00, 0xa7,
0x98, 0x64, 0x85, 0xad, 0xb2, 0xbf, 0x43, 0xfb, 0x6c, 0x45, 0x7e, 0x15, 0xff, 0x40, 0x00, 0xa7,
0xa3, 0xdd, 0xc4, 0x7a, 0x14, 0xbd, 0xbe, 0xae, 0x95, 0xe3, 0xad, 0x67, 0xac, 0x5b, 0xde, 0x34,
0x90, 0xaa, 0x3c, 0x42, 0x86, 0x51, 0x13, 0x05, 0x32, 0xff, 0xf1, 0xdd, 0xaa, 0xa6, 0xa8, 0x62,
0x3e, 0x1e, 0x1d, 0x96, 0x0c, 0x6a, 0x02, 0x53, 0xd2, 0x65, 0x70, 0x31, 0x06, 0x7d, 0xc3, 0x10,
@@ -6387,7 +6390,7 @@ var fileDescriptor_94cf2ca1c69fd564 = []byte{
0xf3, 0xd1, 0xc5, 0x11, 0x0e, 0x9f, 0xcd, 0x5c, 0xcf, 0xc3, 0x35, 0xb3, 0x6a, 0xbc, 0xf4, 0x3a,
0xf8, 0xff, 0x79, 0xf0, 0x30, 0xea, 0x20, 0xfd, 0xfd, 0xc0, 0xed, 0x92, 0x30, 0x78, 0x88, 0x4b,
0x8e, 0x87, 0x5b, 0x81, 0xeb, 0x1d, 0x9b, 0xae, 0xdb, 0xf6, 0xb5, 0x8e, 0x1f, 0x58, 0x9f, 0x83,
0x6c, 0xe0, 0x3b, 0x79, 0xb0, 0x9a, 0xa6, 0x90, 0xbe, 0x89, 0xbc, 0xf4, 0xda, 0xf8, 0xe3, 0x3c,
0x6c, 0xe0, 0x3b, 0x79, 0xb0, 0x9a, 0xa6, 0x90, 0xbe, 0x89, 0xbc, 0xf4, 0xda, 0xf8, 0xa3, 0x3c,
0xb8, 0x33, 0xd0, 0x86, 0xdc, 0x0b, 0xdc, 0xf0, 0xef, 0x48, 0x0a, 0xf9, 0xd2, 0x47, 0x90, 0xbb,
0xe0, 0x6c, 0x7c, 0x3a, 0x3d, 0xef, 0x0d, 0xa7, 0xd1, 0xdf, 0xcc, 0x83, 0x9b, 0x03, 0x75, 0xa9,
0xca, 0x23, 0xba, 0x6a, 0x3a, 0x9f, 0xa7, 0xbd, 0xe8, 0x3f, 0x0a, 0xe0, 0xf2, 0x68, 0xc6, 0x45,
@@ -6397,7 +6400,7 @@ var fileDescriptor_94cf2ca1c69fd564 = []byte{
0x14, 0x07, 0x8a, 0x68, 0x78, 0x4e, 0xa7, 0xe5, 0x74, 0xad, 0xb6, 0xff, 0xf9, 0x09, 0x96, 0xff,
0x2c, 0x80, 0xe5, 0x81, 0x06, 0x4c, 0xec, 0x07, 0xfc, 0xe0, 0xed, 0xf3, 0xe0, 0xf7, 0xff, 0x41,
0x00, 0x85, 0x88, 0x17, 0xe0, 0x17, 0x2f, 0xf6, 0x4b, 0x3f, 0xee, 0x25, 0xe2, 0xf5, 0xb9, 0xb7,
0xc7, 0x47, 0xd0, 0x6d, 0x47, 0x2f, 0x87, 0xfe, 0x92, 0x5a, 0xc4, 0x50, 0xaf, 0x61, 0x1d, 0x46,
0xc7, 0x47, 0xd0, 0x6d, 0x47, 0x2f, 0x87, 0xfe, 0x82, 0x5a, 0xc4, 0x50, 0xaf, 0x61, 0x1d, 0x46,
0x2f, 0x3b, 0x6e, 0x82, 0x33, 0x24, 0x43, 0xb0, 0x2d, 0xcf, 0x46, 0x3d, 0x1f, 0xb3, 0xcb, 0x84,
0x59, 0x78, 0x3a, 0x6c, 0x6c, 0xfa, 0xd8, 0x96, 0x96, 0xc0, 0xdc, 0xb1, 0x75, 0xd0, 0x66, 0x00,
0x79, 0x0a, 0x30, 0x4b, 0x1a, 0x68, 0xe7, 0x1d, 0x70, 0xf6, 0xc0, 0xb5, 0x31, 0x3a, 0xda, 0xc3,
@@ -6406,7 +6409,7 @@ var fileDescriptor_94cf2ca1c69fd564 = []byte{
0x43, 0xec, 0xf9, 0x8e, 0xdb, 0x41, 0xf4, 0xac, 0xdf, 0x2f, 0x4c, 0x5f, 0x9f, 0xba, 0x37, 0x07,
0x17, 0x19, 0xd0, 0xa6, 0x13, 0xec, 0x29, 0x7d, 0x10, 0x95, 0x42, 0x14, 0x6f, 0x90, 0xed, 0xe2,
0xf0, 0x58, 0x49, 0x72, 0xd3, 0x8e, 0x8c, 0xf8, 0x55, 0xb0, 0x32, 0x02, 0xf2, 0xcc, 0xc1, 0x47,
0x25, 0xb7, 0xd5, 0x3b, 0xc0, 0x9d, 0xc0, 0x1a, 0x3e, 0x5e, 0x2b, 0xfe, 0x48, 0x00, 0x17, 0x65,
0x25, 0xb7, 0xd5, 0x3b, 0xc0, 0x9d, 0xc0, 0x1a, 0x3e, 0x5e, 0x2b, 0xfe, 0x58, 0x00, 0x17, 0x65,
0xdf, 0x77, 0xc8, 0x4a, 0xa1, 0x06, 0xd3, 0x5f, 0x29, 0x77, 0xc1, 0x59, 0x2e, 0x21, 0xc5, 0x41,
0xfd, 0x6b, 0x96, 0xf9, 0x68, 0xb3, 0x66, 0x4b, 0x37, 0xc0, 0xe9, 0xc0, 0x0d, 0xac, 0x36, 0x0a,
0xdc, 0x7d, 0xdc, 0x61, 0xd7, 0x08, 0x53, 0xf0, 0x14, 0x6d, 0x33, 0x69, 0x13, 0xd1, 0x71, 0xd7,
@@ -6415,7 +6418,7 @@ var fileDescriptor_94cf2ca1c69fd564 = []byte{
0x93, 0x5b, 0xfd, 0x18, 0xb7, 0x7a, 0x9f, 0x42, 0xec, 0x65, 0x00, 0x3a, 0x64, 0xd6, 0x58, 0x36,
0xc9, 0x84, 0x9e, 0x23, 0x2d, 0x34, 0x91, 0x1c, 0x1b, 0xd5, 0x54, 0x86, 0x51, 0x4d, 0x67, 0x1d,
0xd5, 0x4c, 0xc2, 0xa8, 0x9e, 0x80, 0x45, 0x36, 0xa8, 0x3a, 0x3e, 0x52, 0x22, 0xe2, 0xf6, 0xef,
0xc6, 0x5a, 0x56, 0x80, 0x77, 0x5d, 0xef, 0x38, 0xbc, 0x1b, 0x0b, 0x7f, 0x17, 0xff, 0x44, 0x00,
0xc6, 0x5a, 0x56, 0x80, 0x77, 0x5d, 0xef, 0x38, 0xbc, 0x1b, 0x0b, 0x7f, 0x17, 0xff, 0x58, 0x00,
0xe7, 0x19, 0xaa, 0x4c, 0xaf, 0x6e, 0x21, 0xfe, 0x5a, 0x0f, 0xfb, 0xd4, 0xba, 0xc3, 0xd5, 0xc6,
0xce, 0xea, 0x18, 0xe2, 0xe9, 0xb0, 0x91, 0x1e, 0x7e, 0xfd, 0x42, 0x66, 0xf0, 0x13, 0x01, 0x9c,
0x0e, 0x25, 0x26, 0xcd, 0xd2, 0x02, 0x38, 0x61, 0xd1, 0xbf, 0xb8, 0x8c, 0xfc, 0xd7, 0x2f, 0x46,
@@ -6425,16 +6428,16 @@ var fileDescriptor_94cf2ca1c69fd564 = []byte{
0x5f, 0xe4, 0xea, 0xfd, 0xf0, 0xe1, 0x6a, 0xad, 0x0f, 0xbe, 0xe1, 0x74, 0x6c, 0x38, 0x7f, 0x30,
0xf4, 0xbb, 0xf8, 0x15, 0x20, 0x0e, 0x04, 0x60, 0x8b, 0x5e, 0x2a, 0x8f, 0xb9, 0xfa, 0x64, 0xb7,
0x3a, 0x3e, 0xc6, 0x81, 0xb3, 0x1f, 0x26, 0xde, 0xec, 0xda, 0x9f, 0x1d, 0xf1, 0x12, 0x26, 0xa1,
0xea, 0xc5, 0x11, 0xff, 0x53, 0x01, 0x2c, 0x8c, 0x4e, 0xcc, 0x0b, 0xd6, 0x8e, 0xf4, 0x51, 0x38,
0xea, 0xc5, 0x11, 0xff, 0x13, 0x01, 0x2c, 0x8c, 0x4e, 0xcc, 0x0b, 0xd6, 0x8e, 0xf4, 0x51, 0x38,
0x97, 0xa8, 0x4f, 0x2f, 0x2d, 0x22, 0x26, 0xd9, 0x4a, 0x38, 0xad, 0xb5, 0x49, 0xf2, 0xbf, 0xe0,
0x09, 0xf8, 0x2f, 0x97, 0xff, 0x05, 0xcf, 0xf1, 0x67, 0x2a, 0xff, 0x6f, 0xe7, 0xa3, 0xf2, 0x97,
0x09, 0xf8, 0x2f, 0x97, 0xff, 0x05, 0xcf, 0xf1, 0x67, 0x2a, 0xff, 0x6f, 0xe5, 0xa3, 0xf2, 0x97,
0x3d, 0xab, 0x13, 0xf8, 0xa6, 0xdb, 0xf4, 0xb1, 0x27, 0xad, 0x82, 0xf3, 0x34, 0x62, 0x20, 0xcf,
0x6d, 0x63, 0x1f, 0xed, 0x92, 0x3e, 0x9e, 0x34, 0xcc, 0xc0, 0x73, 0xb4, 0x8b, 0xc4, 0x5c, 0xbf,
0xcc, 0x3a, 0x48, 0xd0, 0x67, 0xf0, 0x4e, 0x67, 0x0f, 0x7b, 0x0e, 0xbd, 0x18, 0x1c, 0xc2, 0x9c,
0xa2, 0x98, 0x8b, 0x14, 0x48, 0x0b, 0x61, 0x86, 0x48, 0xbc, 0x0e, 0x2e, 0x30, 0x12, 0x81, 0x67,
0x39, 0xc1, 0x00, 0x33, 0x4f, 0x31, 0x25, 0xda, 0x67, 0xd2, 0xae, 0x10, 0x43, 0x01, 0x57, 0x47,
0x99, 0x8e, 0xe0, 0x4e, 0x53, 0xdc, 0xa5, 0x61, 0xae, 0xc3, 0x44, 0x96, 0xc0, 0x5c, 0xcf, 0xc7,
0x1e, 0xa2, 0x5e, 0x6c, 0x66, 0x50, 0x8b, 0x51, 0xb7, 0x0e, 0x70, 0xf1, 0x07, 0x53, 0x51, 0x0d,
0x1e, 0xa2, 0x5e, 0x6c, 0x66, 0x50, 0x8b, 0x51, 0xb7, 0x0e, 0x70, 0xf1, 0x87, 0x53, 0x51, 0x0d,
0x41, 0x7c, 0xe8, 0xe0, 0xa3, 0x17, 0xbd, 0xc2, 0x9e, 0x80, 0xcb, 0xb6, 0x75, 0xec, 0xa3, 0xae,
0xe5, 0x07, 0xa8, 0x83, 0x3f, 0x0e, 0x90, 0xd5, 0xb3, 0x9d, 0x00, 0x91, 0x75, 0xc0, 0x07, 0x7f,
0x91, 0x00, 0x34, 0x2c, 0x12, 0x30, 0x3f, 0x0e, 0x64, 0xd2, 0x5b, 0x22, 0x22, 0xac, 0x83, 0x6b,
@@ -6445,7 +6448,7 @@ var fileDescriptor_94cf2ca1c69fd564 = []byte{
0xed, 0x90, 0x9c, 0x8f, 0x23, 0xcf, 0x50, 0xe4, 0x4b, 0x0c, 0xa4, 0x64, 0x1d, 0xeb, 0x3b, 0x35,
0xd2, 0x1f, 0x62, 0xbf, 0x0d, 0x2e, 0x77, 0x7a, 0xd4, 0xb6, 0xdd, 0x1d, 0xe4, 0xe1, 0x03, 0xf7,
0x10, 0xdb, 0x88, 0x8b, 0x5a, 0x38, 0x41, 0x47, 0xbe, 0xc0, 0x00, 0xf4, 0x1d, 0xc8, 0xba, 0x79,
0xa0, 0x28, 0xfe, 0x9a, 0x30, 0x3e, 0x31, 0x2f, 0x7a, 0xe9, 0x3d, 0x04, 0x17, 0x59, 0x94, 0x42,
0xa0, 0x28, 0xfe, 0xaa, 0x30, 0x3e, 0x31, 0x2f, 0x7a, 0xe9, 0x3d, 0x04, 0x17, 0x59, 0x94, 0x42,
0x24, 0x4c, 0x21, 0x3e, 0x50, 0xc7, 0xe6, 0xa5, 0x2b, 0x92, 0x35, 0xc2, 0x5f, 0xb3, 0x8b, 0xdf,
0x15, 0xc0, 0xe5, 0x48, 0x59, 0x04, 0xbb, 0xd8, 0x4e, 0x8a, 0xab, 0xd2, 0x1a, 0x98, 0x8e, 0x04,
0xc7, 0xd5, 0x44, 0x29, 0xc7, 0x28, 0xd2, 0x20, 0x49, 0x71, 0x87, 0xcd, 0x77, 0x6a, 0xc4, 0x7c,
@@ -6460,7 +6463,7 @@ var fileDescriptor_94cf2ca1c69fd564 = []byte{
0x8a, 0x58, 0x27, 0x3b, 0xdd, 0xcf, 0x74, 0x11, 0xbc, 0x10, 0x55, 0xa8, 0x60, 0x86, 0xee, 0xc9,
0xb9, 0x22, 0x1e, 0x64, 0x27, 0x42, 0x07, 0x08, 0x19, 0x76, 0xf1, 0xdf, 0x84, 0x04, 0x27, 0x40,
0xf6, 0x9c, 0xdd, 0xe3, 0x5f, 0xde, 0xf1, 0xaf, 0x83, 0x69, 0xba, 0x77, 0x64, 0xc3, 0x7f, 0x0e,
0x79, 0xc8, 0xd0, 0x68, 0x65, 0x07, 0xc5, 0x2f, 0xfe, 0x85, 0x00, 0x6e, 0xc4, 0x28, 0xa0, 0xea,
0x79, 0xc8, 0xd0, 0x68, 0x65, 0x07, 0xc5, 0x2f, 0xfe, 0xb9, 0x00, 0x6e, 0xc4, 0x28, 0xa0, 0xea,
0x74, 0xf6, 0x23, 0x07, 0x32, 0xbf, 0x8c, 0x1a, 0x90, 0xc0, 0x74, 0xdb, 0xe9, 0xec, 0xf3, 0x38,
0x42, 0xff, 0x2e, 0xfe, 0xad, 0x00, 0xce, 0xf7, 0xcb, 0x91, 0x69, 0xea, 0xd3, 0x3f, 0x50, 0x4a,
0xdf, 0x72, 0x47, 0x2e, 0xb4, 0x90, 0xeb, 0x39, 0xbb, 0x4e, 0x87, 0x3b, 0xcf, 0xfe, 0x85, 0x96,
@@ -6489,7 +6492,7 @@ var fileDescriptor_94cf2ca1c69fd564 = []byte{
0x03, 0xbf, 0x30, 0x4b, 0x81, 0x99, 0x70, 0xc6, 0x1b, 0x6b, 0xac, 0x95, 0xec, 0xcb, 0x38, 0xa4,
0x75, 0xd0, 0xee, 0xaf, 0x4f, 0xbf, 0x30, 0x47, 0xa1, 0x99, 0x8c, 0x86, 0x75, 0xd0, 0x0e, 0x17,
0x69, 0x04, 0xc3, 0x75, 0xec, 0x56, 0x04, 0x03, 0x44, 0x30, 0x74, 0xc7, 0x6e, 0x0d, 0x30, 0xfa,
0x2a, 0xb1, 0x5a, 0x74, 0xaf, 0xe6, 0x17, 0x4e, 0x45, 0x54, 0x22, 0xf3, 0xc6, 0xe2, 0x0f, 0x04,
0x2a, 0xb1, 0x5a, 0x74, 0xaf, 0xe6, 0x17, 0x4e, 0x45, 0x54, 0x22, 0xf3, 0xc6, 0xe2, 0x0f, 0x05,
0x70, 0xab, 0xa9, 0x45, 0x26, 0x5b, 0xf1, 0xdc, 0xa3, 0xce, 0x53, 0x7c, 0x84, 0xdb, 0x25, 0x67,
0x67, 0xe7, 0x99, 0x83, 0x8f, 0xd8, 0xbc, 0x3f, 0x01, 0x05, 0x6b, 0x67, 0x67, 0xb8, 0x40, 0x15,
0x45, 0x6a, 0xe2, 0xe7, 0xe0, 0x42, 0xd8, 0xdf, 0xaf, 0xe2, 0x66, 0x67, 0xf5, 0x8f, 0xc1, 0xc2,
@@ -6501,7 +6504,7 @@ var fileDescriptor_94cf2ca1c69fd564 = []byte{
0x7d, 0x8f, 0x14, 0x58, 0xfe, 0x7e, 0xd4, 0xd9, 0xcd, 0x92, 0x86, 0xf0, 0xb5, 0x85, 0xe3, 0xfb,
0xbd, 0x21, 0xaa, 0x73, 0xb4, 0x85, 0x76, 0x5f, 0x00, 0x33, 0xc4, 0xbb, 0x84, 0xfb, 0x0f, 0xf6,
0x83, 0xf8, 0xfe, 0xbe, 0x1d, 0x46, 0x8a, 0x19, 0x66, 0xe0, 0x7c, 0xbf, 0x99, 0x15, 0x33, 0xfc,
0xe6, 0x07, 0xe0, 0x6c, 0x93, 0x78, 0x21, 0x2a, 0x89, 0xde, 0xc1, 0xfa, 0x8e, 0xd4, 0x04, 0x67,
0xc6, 0x07, 0xe0, 0x6c, 0x93, 0x78, 0x21, 0x2a, 0x89, 0xde, 0xc1, 0xfa, 0x8e, 0xd4, 0x04, 0x67,
0x7b, 0x0e, 0xda, 0xa6, 0x2f, 0x71, 0x50, 0x8b, 0x44, 0xcd, 0xd4, 0xbd, 0xdc, 0xf8, 0xc3, 0x9d,
0x4a, 0x0e, 0x9e, 0xe9, 0x39, 0x91, 0x56, 0xe9, 0x13, 0x01, 0xdc, 0xef, 0x39, 0xc8, 0x65, 0x0f,
0x53, 0x10, 0x3f, 0x0f, 0xc5, 0x68, 0xd7, 0x45, 0x81, 0x8b, 0xec, 0xf0, 0xe5, 0x0e, 0xe7, 0xc8,
@@ -6511,202 +6514,202 @@ var fileDescriptor_94cf2ca1c69fd564 = []byte{
0x8a, 0x05, 0x1b, 0xd5, 0x55, 0x8c, 0x34, 0x6d, 0x2b, 0xe8, 0xcf, 0xce, 0x4c, 0x56, 0x5d, 0xa5,
0xbc, 0x5d, 0x1a, 0xd2, 0x55, 0x32, 0xac, 0xf4, 0xbf, 0x05, 0x70, 0x3d, 0x22, 0x9d, 0x8f, 0x03,
0xd4, 0xea, 0x3f, 0x73, 0x42, 0x3e, 0x7d, 0x61, 0x44, 0x9d, 0xe5, 0xa9, 0x47, 0xef, 0xa4, 0x0b,
0x95, 0xf4, 0x48, 0xaa, 0x92, 0x83, 0x57, 0xfa, 0xd2, 0xc4, 0x00, 0x49, 0xbf, 0x22, 0x80, 0x5b,
0x11, 0x31, 0x3c, 0x5e, 0xd2, 0x88, 0x5a, 0xe1, 0x5b, 0xa7, 0x50, 0x94, 0x93, 0x54, 0x94, 0x2f,
0xa5, 0x8b, 0x32, 0xe9, 0xb5, 0x54, 0x25, 0x07, 0xaf, 0xf7, 0xc5, 0x49, 0x00, 0x0c, 0x35, 0xe3,
0xf1, 0xf7, 0x47, 0x88, 0xec, 0x5d, 0xc9, 0x02, 0x64, 0xef, 0x9f, 0xf8, 0x74, 0xcd, 0xa6, 0x6a,
0x26, 0xe5, 0xf5, 0x14, 0xd3, 0x4c, 0x32, 0x90, 0xf4, 0x31, 0xb8, 0x12, 0x27, 0x45, 0xf7, 0x98,
0x4b, 0x30, 0x47, 0x25, 0xf8, 0x42, 0x76, 0x09, 0xa2, 0xcf, 0xaf, 0x2a, 0x39, 0x58, 0x18, 0xe3,
0xce, 0x01, 0xa4, 0xff, 0x01, 0x96, 0xc7, 0x39, 0x77, 0x3d, 0xa7, 0x13, 0x70, 0xd6, 0x80, 0xb2,
0x7e, 0x2b, 0x2b, 0xeb, 0x91, 0xc7, 0x5b, 0x95, 0x1c, 0xbc, 0x3c, 0xc2, 0x7b, 0x00, 0x21, 0xb5,
0xc1, 0xe5, 0x9e, 0x83, 0x6c, 0xee, 0xc4, 0x49, 0xd6, 0xe5, 0x91, 0x50, 0x42, 0x89, 0xd3, 0xa0,
0x76, 0x6a, 0xc2, 0xc6, 0x2a, 0xfe, 0x09, 0x54, 0x25, 0x07, 0x17, 0x7a, 0x4e, 0xec, 0xe3, 0xa8,
0xef, 0x30, 0xf3, 0xeb, 0xb3, 0x1b, 0xc4, 0xba, 0xb0, 0x0e, 0x8c, 0x73, 0x3e, 0x4d, 0x39, 0xbf,
0x9d, 0x81, 0x73, 0xfc, 0xab, 0x26, 0x66, 0x79, 0x29, 0x2f, 0x9f, 0xbe, 0x41, 0x0d, 0xaf, 0x2f,
0x0c, 0xaf, 0x9d, 0xf7, 0x59, 0x19, 0x3c, 0x17, 0xe4, 0x0c, 0x15, 0xe4, 0xcd, 0x4f, 0x55, 0x44,
0xcf, 0x6c, 0x6e, 0xc2, 0xa3, 0x87, 0xff, 0xcb, 0x1c, 0xe8, 0x40, 0x02, 0x9e, 0xd0, 0x0f, 0xd6,
0x25, 0x13, 0x62, 0x9e, 0x0a, 0xf1, 0x24, 0x8b, 0x10, 0x71, 0xb5, 0xca, 0x95, 0x1c, 0xbc, 0x16,
0x91, 0x23, 0xb6, 0x9c, 0xf9, 0x37, 0x98, 0xf7, 0x1c, 0x17, 0xa5, 0x15, 0x96, 0xa9, 0xa0, 0x83,
0xa0, 0xed, 0x73, 0x81, 0xce, 0x52, 0x81, 0xbe, 0xf8, 0x1c, 0x02, 0x8d, 0x57, 0x0f, 0x57, 0x72,
0xf0, 0xd6, 0xb8, 0x54, 0x03, 0xb8, 0xa0, 0xcd, 0x0b, 0x28, 0xff, 0x4c, 0x00, 0x4f, 0x86, 0xe7,
0x89, 0xd6, 0x9e, 0x22, 0x8b, 0x16, 0x9f, 0x22, 0x3b, 0xac, 0x3e, 0x45, 0x81, 0xeb, 0xb6, 0x79,
0x32, 0xd9, 0x6e, 0x73, 0x49, 0x45, 0x2a, 0xe9, 0xd3, 0x4c, 0xf3, 0x97, 0xa9, 0xc6, 0xb7, 0x92,
0x83, 0x0f, 0xa3, 0x93, 0x9a, 0xad, 0x30, 0xf8, 0xc7, 0x02, 0x78, 0x9c, 0x69, 0x0c, 0x03, 0x75,
0x33, 0xf9, 0xcf, 0x51, 0xf9, 0xcb, 0x9f, 0x5a, 0xfe, 0xe1, 0x2a, 0xa3, 0x4a, 0x0e, 0xae, 0xa6,
0x09, 0x3f, 0x52, 0x97, 0xf4, 0x5b, 0x02, 0x78, 0x25, 0x2a, 0xb9, 0xd5, 0x23, 0x99, 0x47, 0x7f,
0x0f, 0x1a, 0x79, 0x4f, 0xc5, 0x04, 0x96, 0xa8, 0xc0, 0xef, 0x67, 0x10, 0x78, 0x52, 0xd5, 0x6c,
0x25, 0x07, 0xef, 0x0c, 0x04, 0x9d, 0x58, 0x5f, 0xfb, 0x47, 0x02, 0x78, 0x90, 0x62, 0xb9, 0x8e,
0x75, 0xc0, 0x36, 0x2f, 0xc7, 0x5c, 0xc8, 0xf3, 0x54, 0xc8, 0xb5, 0x4f, 0x63, 0xbf, 0xc3, 0x85,
0x6b, 0x95, 0x1c, 0xbc, 0x3f, 0xc1, 0x88, 0x35, 0xeb, 0x20, 0x5a, 0xe5, 0xf6, 0xab, 0x02, 0xb8,
0x13, 0x15, 0xb5, 0xdb, 0x2f, 0x06, 0x1b, 0x9b, 0xf7, 0x0b, 0x54, 0xc2, 0x77, 0x33, 0x48, 0x98,
0x54, 0x51, 0x56, 0xc9, 0xc1, 0xe2, 0x40, 0xb4, 0xc4, 0xba, 0xb3, 0x6f, 0x09, 0xe0, 0x46, 0x54,
0xa6, 0x00, 0xfb, 0x01, 0x91, 0xa6, 0x33, 0xe4, 0x8f, 0x2f, 0xa6, 0x46, 0xbf, 0x09, 0xe5, 0x5d,
0x95, 0x1c, 0x5c, 0x1e, 0x48, 0x12, 0x57, 0xff, 0xe5, 0x81, 0xa5, 0xa8, 0x0c, 0x61, 0x9e, 0x1b,
0xc6, 0xa1, 0x85, 0x94, 0x1b, 0xc4, 0xa4, 0xfa, 0x2a, 0x16, 0x76, 0x13, 0x6a, 0xaf, 0xda, 0xa0,
0xd0, 0x73, 0x48, 0x12, 0x66, 0x05, 0x18, 0x75, 0xf0, 0x11, 0xdd, 0xff, 0xf2, 0x88, 0x7b, 0x29,
0xe5, 0x80, 0x2b, 0xb1, 0xb2, 0xa9, 0x92, 0x83, 0x17, 0x7a, 0xce, 0x78, 0xa7, 0x74, 0x4c, 0x83,
0xfc, 0x28, 0x37, 0xdf, 0x3a, 0x0c, 0x59, 0x16, 0x52, 0x35, 0x3c, 0xa1, 0x5c, 0x8a, 0x0d, 0x34,
0x1e, 0x40, 0xfa, 0x06, 0xb8, 0x16, 0x37, 0x50, 0x5a, 0xa1, 0xc4, 0x99, 0x5f, 0x4e, 0x0d, 0x30,
0x13, 0xab, 0x9b, 0x2a, 0x39, 0xb8, 0x38, 0x3a, 0xea, 0x01, 0x88, 0xf4, 0xbb, 0xcc, 0x85, 0x8c,
0x4a, 0xc0, 0xee, 0xe1, 0xa2, 0x15, 0x50, 0x5c, 0x9a, 0x45, 0x2a, 0x8d, 0x92, 0x55, 0x9a, 0x09,
0x85, 0x54, 0x95, 0x1c, 0xbc, 0x3d, 0x22, 0x58, 0x3c, 0xb4, 0xf4, 0x07, 0x02, 0x58, 0x8d, 0x9a,
0xa0, 0x33, 0x38, 0x76, 0x44, 0xd6, 0x91, 0xcf, 0x8e, 0x06, 0xf8, 0xb2, 0xe0, 0x56, 0xb9, 0x94,
0xba, 0x85, 0xc8, 0xf6, 0xf2, 0xb8, 0x92, 0x83, 0xf7, 0x06, 0x56, 0x1a, 0x85, 0x3d, 0xf2, 0x75,
0xc7, 0x6e, 0x0d, 0xbd, 0x52, 0xfe, 0xae, 0x00, 0x6e, 0xc7, 0xa7, 0x0c, 0xb6, 0x8f, 0x30, 0x3d,
0x20, 0xe5, 0xe2, 0x5d, 0xc9, 0x9c, 0x42, 0xc5, 0x3f, 0x08, 0x1e, 0x4e, 0xa1, 0xfa, 0x30, 0xb6,
0x1f, 0x7d, 0xfe, 0x1a, 0x30, 0xb3, 0x26, 0xf1, 0x36, 0x70, 0x11, 0x2b, 0x0d, 0x62, 0xb3, 0xc8,
0xa5, 0x58, 0x4e, 0x5d, 0xba, 0xf1, 0x2f, 0x4c, 0xb9, 0x45, 0xc7, 0xbf, 0x3e, 0xfd, 0x2a, 0x38,
0x67, 0xd1, 0x1a, 0x25, 0x34, 0xa8, 0x10, 0x2a, 0x5c, 0xa5, 0x9c, 0x92, 0x0f, 0x92, 0x63, 0xeb,
0xe9, 0x2a, 0x39, 0x28, 0x5a, 0x23, 0x1d, 0xa1, 0x4b, 0x8c, 0x9a, 0x00, 0xd7, 0x2c, 0x4d, 0x8f,
0xf9, 0xc8, 0xae, 0xa5, 0x2e, 0xd8, 0x09, 0x37, 0x99, 0xcc, 0x25, 0x4e, 0xba, 0xea, 0xe4, 0xa9,
0x72, 0x8c, 0x10, 0xfd, 0x53, 0x00, 0x26, 0xc7, 0xf5, 0xd4, 0x79, 0x9e, 0x7c, 0xc1, 0xc9, 0xe6,
0x39, 0xe5, 0x12, 0xf4, 0x7f, 0x09, 0xd4, 0x89, 0x84, 0xfb, 0xc6, 0xaf, 0x45, 0xbf, 0xb1, 0x11,
0x6e, 0x19, 0x6f, 0x64, 0xdd, 0xbd, 0x26, 0x7d, 0xa1, 0x63, 0x68, 0xf7, 0x1a, 0x03, 0x24, 0x7d,
0x04, 0xf8, 0x64, 0x21, 0x1c, 0x96, 0x17, 0x16, 0x8a, 0x94, 0xeb, 0x6b, 0x29, 0xd3, 0x3e, 0x5c,
0x8e, 0x58, 0xc9, 0xc1, 0xb3, 0xd6, 0x70, 0xbb, 0x74, 0x00, 0x2e, 0x71, 0xda, 0xc4, 0x41, 0x45,
0xab, 0x12, 0x0b, 0x37, 0x53, 0x4e, 0xee, 0x93, 0x8b, 0x03, 0x2b, 0x39, 0x78, 0xd1, 0x8a, 0xeb,
0x95, 0xb6, 0xc1, 0xc5, 0xc1, 0x29, 0x09, 0x73, 0x8c, 0x6c, 0x3a, 0x6f, 0x51, 0x66, 0xaf, 0x26,
0x32, 0x8b, 0xb9, 0xdb, 0xa8, 0xe4, 0xe0, 0x79, 0x2f, 0xe6, 0xca, 0xe3, 0x08, 0x5c, 0x49, 0x38,
0x5c, 0x67, 0xac, 0x6e, 0xa7, 0x8c, 0x2b, 0xf9, 0x42, 0x80, 0x38, 0xfc, 0x9d, 0xe4, 0xeb, 0x82,
0x6d, 0xc0, 0x47, 0x8d, 0x78, 0x41, 0x82, 0xc7, 0xea, 0x1e, 0x0b, 0x77, 0x52, 0x06, 0x17, 0x53,
0x2b, 0x49, 0x06, 0x67, 0xc5, 0x94, 0x50, 0x56, 0xc1, 0x99, 0x3e, 0x0f, 0x3a, 0x4b, 0x77, 0x29,
0xed, 0xdb, 0xa9, 0xb4, 0x09, 0x70, 0x25, 0x07, 0x4f, 0x5b, 0xd1, 0x2a, 0xc7, 0x2d, 0x20, 0x45,
0x6b, 0x27, 0xd8, 0x8c, 0x14, 0xee, 0xa5, 0x94, 0x64, 0x8f, 0x16, 0xf9, 0x51, 0x6f, 0x32, 0x5a,
0xf8, 0x37, 0x42, 0xba, 0x47, 0x0b, 0xc6, 0x0a, 0xf7, 0x33, 0x93, 0x66, 0x15, 0x66, 0xc3, 0xa4,
0x79, 0xd5, 0xd9, 0x08, 0x69, 0x9b, 0x16, 0x94, 0x14, 0x56, 0x32, 0x93, 0x66, 0x15, 0x28, 0xc3,
0xa4, 0x79, 0x55, 0x4a, 0x1b, 0x5c, 0x8e, 0x92, 0xe6, 0x35, 0x5d, 0x5c, 0x2f, 0xaf, 0xa4, 0x9c,
0x0b, 0xc4, 0x17, 0xf9, 0x55, 0x72, 0x70, 0xc1, 0x8a, 0x2f, 0xff, 0x8b, 0xe7, 0xc6, 0x55, 0xf5,
0xea, 0x73, 0x72, 0xeb, 0x2b, 0x6c, 0x8c, 0x1b, 0x57, 0x5b, 0x3c, 0x37, 0xae, 0xbd, 0xd7, 0x9e,
0x93, 0x5b, 0x5f, 0x87, 0x63, 0xdc, 0xb8, 0x26, 0x0f, 0xc0, 0x62, 0x94, 0x1b, 0x2d, 0xf5, 0xf2,
0x49, 0xb4, 0xec, 0xf9, 0xd8, 0x2b, 0xac, 0x66, 0x66, 0x17, 0xad, 0x77, 0x1b, 0x66, 0x37, 0x54,
0x09, 0xf7, 0xff, 0x04, 0x50, 0x8c, 0x26, 0x08, 0xd1, 0x9b, 0xa4, 0xc1, 0x31, 0x4b, 0xe1, 0x41,
0xea, 0x99, 0x6c, 0xea, 0x6b, 0x3d, 0x76, 0x26, 0xdb, 0x07, 0x6b, 0x8d, 0x83, 0x49, 0xfb, 0xe0,
0x52, 0xcc, 0x09, 0x0b, 0x76, 0x5a, 0xb8, 0xf0, 0x7a, 0x6a, 0x8a, 0x9d, 0xf0, 0x56, 0x8e, 0xa5,
0xd8, 0x23, 0x9d, 0x4e, 0x0b, 0x8f, 0x32, 0x0b, 0xd3, 0x4d, 0xd7, 0xc6, 0x85, 0x87, 0x99, 0x99,
0x8d, 0xbc, 0x4f, 0x1b, 0x66, 0x36, 0xe8, 0x94, 0xbe, 0x02, 0xce, 0x05, 0xd6, 0x2e, 0x8f, 0x43,
0x98, 0x04, 0x44, 0xef, 0xb8, 0xf0, 0x28, 0x25, 0x16, 0xc5, 0x5d, 0x38, 0x92, 0x58, 0x14, 0x58,
0xbb, 0xd1, 0x76, 0x29, 0x00, 0x8b, 0x3e, 0xbf, 0x9e, 0x41, 0x1e, 0xa5, 0x84, 0x76, 0x31, 0x3d,
0xc8, 0xee, 0xb5, 0x83, 0xc2, 0x1b, 0x29, 0x47, 0x52, 0x93, 0x6e, 0x76, 0x2a, 0x39, 0x78, 0xc9,
0x8f, 0xef, 0x1f, 0x5d, 0x16, 0xbc, 0x7e, 0x8c, 0x2f, 0xf9, 0xc7, 0x99, 0xed, 0x34, 0x5a, 0x75,
0x38, 0x6c, 0xa7, 0x43, 0xf5, 0x88, 0xf1, 0xdc, 0xf8, 0x22, 0x7c, 0xf3, 0x39, 0xb9, 0xc5, 0x2d,
0xc2, 0xa1, 0x22, 0xbb, 0x6d, 0x70, 0x31, 0x34, 0x8c, 0x63, 0xb4, 0x83, 0x83, 0xd6, 0x1e, 0x8f,
0x81, 0x5f, 0x48, 0x89, 0x48, 0x31, 0xd7, 0x5b, 0x24, 0x22, 0xd9, 0x31, 0xb7, 0x5e, 0xdf, 0x1b,
0x39, 0xcd, 0xe3, 0x06, 0x38, 0xe0, 0xcb, 0xf6, 0xf8, 0x85, 0xb7, 0x32, 0x1f, 0x9e, 0x25, 0x7f,
0x66, 0x61, 0xf8, 0x48, 0x2f, 0x16, 0x4e, 0xfa, 0xe6, 0xc8, 0xce, 0x7e, 0xbf, 0xb7, 0x8d, 0xe9,
0xe5, 0xec, 0xd0, 0x36, 0xe1, 0x49, 0xe6, 0x03, 0xce, 0xf1, 0xaf, 0xdf, 0x0c, 0x1f, 0x70, 0xd2,
0xfe, 0xfd, 0xa1, 0xed, 0xc1, 0xb7, 0xe3, 0x55, 0x62, 0x75, 0xbb, 0xf4, 0x98, 0xb5, 0x7f, 0xc0,
0xf9, 0x76, 0xea, 0x09, 0xf7, 0xa4, 0x67, 0x94, 0xc3, 0x8e, 0x28, 0xf6, 0xa1, 0x65, 0x17, 0x2c,
0x71, 0x6b, 0xdb, 0xf5, 0xac, 0xee, 0x1e, 0xdd, 0xd2, 0xf9, 0x2d, 0x2b, 0xcc, 0x84, 0xde, 0xa1,
0xfc, 0x5f, 0x4f, 0xb1, 0xb7, 0xb1, 0xfb, 0x7a, 0xb2, 0x9a, 0xac, 0x48, 0xd7, 0x91, 0x3f, 0xb8,
0xca, 0xff, 0x3e, 0xdb, 0xa8, 0x0d, 0x71, 0x6d, 0x79, 0xee, 0x51, 0x07, 0xfd, 0x77, 0x7c, 0x84,
0xdb, 0xc8, 0x76, 0x76, 0x76, 0xe8, 0x16, 0xb8, 0xf0, 0x6e, 0xaa, 0x3d, 0xa4, 0xdf, 0x20, 0x33,
0x7b, 0x98, 0x08, 0x27, 0xfd, 0x37, 0x70, 0x91, 0x16, 0x71, 0xd0, 0x7b, 0x53, 0x7a, 0xe7, 0xc9,
0x47, 0xff, 0x5e, 0xda, 0x4d, 0xe5, 0xd8, 0xf5, 0x6b, 0x25, 0x07, 0xa5, 0xde, 0xf8, 0xa5, 0x2c,
0x3b, 0x50, 0x8f, 0xdd, 0x37, 0xe1, 0x2e, 0x67, 0xf6, 0xc5, 0x54, 0x7b, 0x4b, 0xae, 0x4d, 0x64,
0xf6, 0x36, 0xa1, 0x76, 0xf1, 0xd7, 0x05, 0x70, 0x2f, 0x41, 0x02, 0x7e, 0x9c, 0xe5, 0x76, 0x71,
0x38, 0xe9, 0x5f, 0x4a, 0x0d, 0x81, 0xa9, 0x25, 0x82, 0xec, 0xf6, 0x2f, 0xbd, 0x92, 0xf0, 0x13,
0x01, 0xac, 0xc4, 0xcb, 0x45, 0x6b, 0xd0, 0x46, 0xb7, 0x74, 0xef, 0xa7, 0x5e, 0xbe, 0x65, 0xa8,
0xd9, 0x63, 0x67, 0x1f, 0x59, 0x8a, 0xfb, 0xf8, 0x99, 0x42, 0xec, 0x56, 0x93, 0x3e, 0xc5, 0xea,
0x86, 0xe7, 0xa6, 0x1f, 0x7c, 0x9a, 0xbd, 0x66, 0xa4, 0x8e, 0x2e, 0x71, 0xaf, 0x19, 0xad, 0xb5,
0xfb, 0x3e, 0x3b, 0x25, 0x8d, 0x11, 0xa7, 0xed, 0x74, 0xf6, 0x87, 0x4e, 0x17, 0xe4, 0xd4, 0x2d,
0x67, 0x4a, 0x59, 0x5b, 0x25, 0x07, 0x6f, 0xc4, 0x08, 0x34, 0x0c, 0xb4, 0x76, 0x12, 0xcc, 0x50,
0x9a, 0x4f, 0xa7, 0x67, 0xf3, 0xe2, 0x14, 0xb1, 0xc1, 0xfe, 0x4e, 0x98, 0xc4, 0xe5, 0xf0, 0xd2,
0x8c, 0x4a, 0xb6, 0xf2, 0x43, 0x71, 0xf0, 0x41, 0xc4, 0x70, 0xb3, 0x26, 0xdd, 0x00, 0xcb, 0x25,
0xcd, 0x50, 0xf4, 0x67, 0x2a, 0x44, 0x50, 0x35, 0xf4, 0x26, 0x54, 0x46, 0x3f, 0x67, 0x74, 0x05,
0x14, 0xc6, 0x41, 0x0c, 0x15, 0x3e, 0x53, 0xa1, 0x28, 0x48, 0xd7, 0xc1, 0x95, 0xf1, 0xde, 0x8d,
0xe6, 0x9a, 0x0a, 0xeb, 0xaa, 0xa9, 0x1a, 0x62, 0x5e, 0x7a, 0x03, 0x3c, 0x18, 0x87, 0x28, 0xc9,
0xa6, 0xbc, 0x26, 0x1b, 0x2a, 0x6a, 0xe8, 0x86, 0x59, 0x86, 0xaa, 0x81, 0x0c, 0xb5, 0xba, 0x8e,
0x2a, 0xba, 0x61, 0xaa, 0x25, 0x71, 0x4a, 0x7a, 0x1d, 0xbc, 0x3a, 0x01, 0xa9, 0xb6, 0x65, 0x7c,
0x58, 0x1d, 0xc2, 0x98, 0x96, 0x1e, 0x81, 0xd5, 0x49, 0x18, 0x7a, 0xbd, 0xac, 0x97, 0xd6, 0x86,
0x70, 0x66, 0xa4, 0x57, 0xc0, 0xdd, 0x2c, 0xa2, 0xc1, 0x92, 0x21, 0x9e, 0x90, 0xee, 0x81, 0x5b,
0xa9, 0x22, 0x11, 0xc8, 0x93, 0xd2, 0x1d, 0x50, 0x1c, 0x87, 0x94, 0x1b, 0x8d, 0xaa, 0xa6, 0xc8,
0xa6, 0xa6, 0xd7, 0x51, 0xc5, 0x34, 0x1b, 0xe2, 0xac, 0x74, 0x1b, 0xdc, 0x98, 0x0c, 0x67, 0x2a,
0x0d, 0x71, 0x2e, 0x1e, 0x6c, 0x53, 0xab, 0x97, 0xf4, 0x4d, 0x03, 0x95, 0x54, 0x63, 0xc3, 0xd4,
0x1b, 0x22, 0x90, 0x5e, 0x05, 0xf7, 0x26, 0xc8, 0x67, 0x7c, 0x58, 0x65, 0x73, 0x46, 0x65, 0x3c,
0x95, 0xa2, 0xe0, 0xc1, 0xd0, 0xd5, 0x92, 0x51, 0xd1, 0xd6, 0x4d, 0xf1, 0xb4, 0xf4, 0x18, 0xbc,
0x9e, 0x89, 0x7e, 0x54, 0xc5, 0x67, 0x52, 0xf8, 0x40, 0xb5, 0xa4, 0x0d, 0x4f, 0xfd, 0x7c, 0xd6,
0x49, 0x29, 0x2b, 0x0d, 0xf1, 0x6c, 0xa6, 0x49, 0x21, 0x90, 0x62, 0x66, 0xf5, 0x10, 0xe8, 0x73,
0xd2, 0xbb, 0xe0, 0xad, 0xe7, 0x51, 0x0f, 0x5f, 0x0f, 0x55, 0xd5, 0x30, 0x44, 0x49, 0x7a, 0x0d,
0xdc, 0xcf, 0x82, 0x2c, 0x7f, 0xd4, 0x84, 0xaa, 0x78, 0x5e, 0xba, 0x0b, 0x6e, 0x4e, 0x00, 0x2f,
0x6d, 0xd5, 0xe5, 0x9a, 0x5e, 0x5a, 0x13, 0x2f, 0xa4, 0x98, 0xb8, 0x22, 0x1b, 0x86, 0x5c, 0x2f,
0x41, 0x19, 0x6d, 0xa8, 0x5b, 0x46, 0x43, 0x56, 0x54, 0x43, 0xbc, 0x98, 0x32, 0x6b, 0x03, 0x9c,
0xe8, 0x1c, 0x2c, 0x48, 0x4f, 0xc0, 0xe3, 0x09, 0x58, 0x6a, 0x55, 0x36, 0x4c, 0x4d, 0x31, 0x54,
0x19, 0x2a, 0x95, 0x21, 0xcc, 0x4b, 0x99, 0xe6, 0x9b, 0xe3, 0xcb, 0x4a, 0x45, 0x15, 0x0b, 0x29,
0xda, 0x62, 0x18, 0x35, 0xb5, 0xa6, 0xc3, 0xad, 0xd2, 0x9a, 0x78, 0x39, 0x13, 0x03, 0xaa, 0x59,
0xc4, 0x18, 0x2c, 0xa6, 0x0c, 0x86, 0x61, 0x28, 0xd5, 0xa6, 0x61, 0x8e, 0x18, 0xef, 0x92, 0xb4,
0x02, 0xee, 0xa4, 0x5a, 0x17, 0x9b, 0xc5, 0x2b, 0xd2, 0x2a, 0x58, 0xc9, 0x64, 0x5f, 0x0c, 0x7e,
0x39, 0x65, 0x32, 0x07, 0xf0, 0x35, 0x4d, 0x81, 0xba, 0xa1, 0xaf, 0x9b, 0xe2, 0x55, 0xe9, 0x0b,
0xe0, 0xd1, 0xa4, 0xc9, 0xd4, 0x95, 0x0d, 0xa8, 0xcb, 0x4a, 0x65, 0xc4, 0xcf, 0x5d, 0x4b, 0xb1,
0xfd, 0xd0, 0x37, 0xca, 0x66, 0x55, 0x36, 0xc4, 0xeb, 0x29, 0x6b, 0xca, 0xa8, 0xeb, 0x9b, 0xeb,
0x55, 0x79, 0x43, 0x15, 0x6f, 0x24, 0xd0, 0xd5, 0x95, 0x88, 0x76, 0x4b, 0x06, 0x6a, 0x40, 0xfd,
0xcb, 0x5b, 0x62, 0x31, 0xc1, 0x14, 0xa3, 0xd0, 0x15, 0xad, 0x5c, 0x41, 0xf2, 0x33, 0x59, 0xab,
0xca, 0x6b, 0x5a, 0x55, 0x33, 0xb7, 0xc4, 0x9b, 0xd2, 0x5b, 0xe0, 0x8d, 0x14, 0x2c, 0xba, 0x42,
0x34, 0x05, 0x41, 0xb5, 0xac, 0x19, 0x26, 0xa4, 0xae, 0x53, 0xbc, 0x15, 0xef, 0x85, 0x0d, 0xb9,
0x56, 0x8d, 0xba, 0x58, 0xf1, 0xb6, 0x54, 0x04, 0x57, 0xc7, 0xe1, 0x54, 0xe5, 0x11, 0xfb, 0x70,
0x5f, 0x5d, 0x51, 0xc5, 0x3b, 0x09, 0x46, 0xa7, 0x2b, 0xa3, 0x6e, 0x18, 0xd5, 0xf5, 0x3a, 0x92,
0x4b, 0xe2, 0x5d, 0xe9, 0x16, 0xb8, 0x3e, 0x29, 0x2e, 0xd2, 0x0f, 0xba, 0xdd, 0x8b, 0xb7, 0xfd,
0x68, 0x04, 0x90, 0x37, 0x0d, 0xa4, 0xe8, 0x75, 0x43, 0xaf, 0xaa, 0xe2, 0x7d, 0xe9, 0x1a, 0x58,
0x1a, 0x07, 0xaf, 0x29, 0x0d, 0x64, 0x98, 0x25, 0x4d, 0x17, 0x57, 0xe8, 0x67, 0x0f, 0xe3, 0x01,
0x0c, 0x55, 0x7c, 0x45, 0xba, 0x0f, 0x6e, 0x27, 0xe1, 0x43, 0x55, 0xae, 0xc9, 0x6b, 0x55, 0x95,
0xc5, 0xa6, 0x57, 0x57, 0x7e, 0x4f, 0x00, 0xf3, 0xc3, 0x9f, 0x1e, 0x1e, 0xe2, 0x6e, 0x98, 0xb2,
0xd9, 0x34, 0x46, 0x32, 0x85, 0x25, 0x70, 0x69, 0x14, 0xc0, 0x68, 0x2a, 0x0a, 0x71, 0x8a, 0x42,
0x6c, 0xe7, 0x86, 0xd6, 0x68, 0xa8, 0x25, 0x31, 0x2f, 0x5d, 0x06, 0x17, 0x47, 0x3b, 0x55, 0x08,
0x75, 0x28, 0x4e, 0xc5, 0xe1, 0xc9, 0x6b, 0x3a, 0xa4, 0x41, 0x7f, 0xe5, 0x67, 0x79, 0x30, 0xa5,
0x98, 0xb2, 0x74, 0x1e, 0x9c, 0x55, 0x4c, 0x79, 0xfc, 0x23, 0x8f, 0xa4, 0x51, 0x6e, 0x9a, 0x15,
0xa2, 0xc3, 0xba, 0xaa, 0x98, 0x3a, 0x49, 0x59, 0x2e, 0x81, 0xf3, 0xb4, 0x5d, 0x31, 0xb5, 0x67,
0x24, 0x93, 0x31, 0x0c, 0x4d, 0xaf, 0x93, 0x4c, 0xa5, 0xdf, 0x41, 0x44, 0x46, 0x50, 0xfd, 0xb0,
0xa9, 0x1a, 0xa6, 0x21, 0x4e, 0x85, 0x1d, 0x0d, 0xa8, 0xd6, 0xb4, 0x66, 0x0d, 0x19, 0xcd, 0x46,
0x43, 0x87, 0xa6, 0x38, 0x1d, 0x76, 0x98, 0x90, 0x78, 0x8f, 0x12, 0x2a, 0xa9, 0xcf, 0x34, 0xe2,
0x76, 0x67, 0x42, 0xde, 0xcd, 0x46, 0x19, 0xca, 0x25, 0x15, 0xad, 0xc9, 0xf5, 0xba, 0x0a, 0xc5,
0x13, 0x21, 0xc2, 0x9a, 0x56, 0xad, 0x6a, 0xf5, 0x32, 0x32, 0x9a, 0xb5, 0x9a, 0x0c, 0xb7, 0xc4,
0x93, 0xe1, 0x08, 0x38, 0xef, 0xaa, 0x66, 0x98, 0xe2, 0x2c, 0xfd, 0x14, 0xe0, 0xa0, 0xb1, 0xa6,
0xd7, 0x35, 0x53, 0x87, 0x5a, 0xbd, 0x2c, 0xce, 0xd1, 0x8f, 0x0c, 0x9a, 0x32, 0x52, 0xbf, 0x6c,
0xaa, 0xb0, 0x2e, 0x57, 0x91, 0xdc, 0x2c, 0x69, 0x26, 0x32, 0x4c, 0x1d, 0xca, 0x65, 0x55, 0x04,
0xa1, 0x00, 0xfa, 0x06, 0x91, 0xc2, 0x20, 0xba, 0xdb, 0xaa, 0x2b, 0xe2, 0x29, 0x49, 0x04, 0xa7,
0x29, 0x5e, 0xdd, 0x84, 0x32, 0xd2, 0x4a, 0xe2, 0x69, 0xe9, 0x1c, 0x38, 0xd3, 0x87, 0x34, 0x14,
0xad, 0x26, 0x9e, 0x09, 0xf9, 0x6a, 0x25, 0xb5, 0x6e, 0x6a, 0xe6, 0x16, 0x32, 0x54, 0xa5, 0x09,
0xc9, 0x72, 0x9c, 0x5f, 0xf9, 0x11, 0x00, 0x17, 0x63, 0xdf, 0xd1, 0x91, 0x30, 0xa6, 0xd5, 0x4d,
0xb5, 0xcc, 0x16, 0x20, 0x52, 0xeb, 0x50, 0xaf, 0x56, 0xd1, 0x86, 0x56, 0x1f, 0xfd, 0x7c, 0xe2,
0x0d, 0xb0, 0x9c, 0x04, 0x68, 0x54, 0x65, 0x65, 0x43, 0x14, 0xc8, 0xea, 0x49, 0x02, 0x21, 0x2b,
0x42, 0xd7, 0x4a, 0x8a, 0x98, 0x27, 0x89, 0x51, 0x12, 0x54, 0x43, 0x2e, 0xab, 0xb0, 0xd4, 0x34,
0xb7, 0xc4, 0xa9, 0x49, 0xfc, 0xd4, 0x9a, 0xac, 0x55, 0xc5, 0x69, 0x92, 0xc5, 0x26, 0x81, 0x3c,
0xd5, 0xa0, 0x2c, 0xce, 0x48, 0x37, 0xc1, 0xb5, 0x24, 0x08, 0x6a, 0x9e, 0xb0, 0x24, 0x9e, 0x20,
0x2e, 0x27, 0x09, 0xa8, 0x26, 0x9b, 0xa6, 0x0a, 0x6b, 0xba, 0x61, 0x8a, 0x27, 0x27, 0x0d, 0xaf,
0x66, 0x20, 0x53, 0x95, 0x6b, 0x86, 0x38, 0x3b, 0x09, 0x4a, 0x6f, 0x18, 0x65, 0xb5, 0xae, 0xa9,
0xe2, 0xdc, 0x24, 0xd1, 0xc9, 0x94, 0x8a, 0x60, 0xe2, 0xe0, 0xe4, 0xda, 0xba, 0x78, 0x6a, 0xb2,
0xdc, 0x4a, 0x45, 0xab, 0xab, 0xcc, 0x54, 0xde, 0x04, 0x0f, 0xd3, 0xe1, 0x50, 0x59, 0x33, 0x2b,
0xcd, 0x35, 0xba, 0xbe, 0xc8, 0xba, 0x3a, 0x23, 0x3d, 0x00, 0xaf, 0x64, 0x40, 0x53, 0x34, 0xa8,
0x54, 0x55, 0x45, 0x13, 0xe7, 0x89, 0x5b, 0xcc, 0xc6, 0xa7, 0x2a, 0xaf, 0x89, 0x67, 0x49, 0xe8,
0xcd, 0x00, 0xfe, 0x54, 0xad, 0x6f, 0x68, 0x75, 0x43, 0x14, 0x33, 0xc2, 0xcb, 0x75, 0x43, 0x5b,
0xab, 0xaa, 0xe2, 0xb9, 0x49, 0xea, 0x21, 0x41, 0x5a, 0x53, 0xd4, 0xba, 0xbe, 0x29, 0x4a, 0x93,
0x26, 0xac, 0xbf, 0xde, 0xce, 0x93, 0x80, 0x96, 0x68, 0x49, 0xb2, 0x29, 0x97, 0xf4, 0x32, 0xd2,
0xea, 0x0a, 0x5d, 0x7b, 0xa8, 0x26, 0xd7, 0xe5, 0xb2, 0x5a, 0x53, 0xeb, 0xa6, 0x78, 0x81, 0x64,
0x23, 0x59, 0xc4, 0xde, 0x24, 0x69, 0x5f, 0x36, 0x58, 0x92, 0xeb, 0x2e, 0x90, 0x28, 0x9e, 0x85,
0x2e, 0xcd, 0x5b, 0x68, 0x82, 0x97, 0x01, 0x9a, 0xe6, 0x9f, 0x55, 0xb2, 0x71, 0x28, 0x48, 0x0f,
0xc1, 0x6b, 0x19, 0x30, 0x22, 0x7b, 0xc6, 0xcb, 0x93, 0x2c, 0x86, 0xac, 0xff, 0xbe, 0x63, 0x52,
0xd4, 0xba, 0xa9, 0x42, 0x71, 0x71, 0xd2, 0x94, 0x72, 0x73, 0x84, 0x6a, 0x43, 0xe7, 0x9e, 0x54,
0x5c, 0xca, 0x68, 0x61, 0x32, 0x2c, 0xeb, 0x4a, 0x49, 0xbc, 0x22, 0x7d, 0x00, 0xde, 0x7b, 0x6e,
0xc3, 0x8f, 0x8e, 0x68, 0x79, 0xe5, 0xaf, 0x67, 0x62, 0xfc, 0xa6, 0x11, 0xe0, 0x6e, 0x82, 0xdf,
0x34, 0x4c, 0xb5, 0x31, 0xe2, 0x37, 0xe3, 0xc7, 0x48, 0x01, 0xe5, 0x4d, 0x43, 0x53, 0xc2, 0x20,
0xc7, 0xdc, 0xa3, 0x20, 0x7d, 0x09, 0xbc, 0x33, 0x19, 0xde, 0x50, 0x4d, 0xae, 0x11, 0x12, 0x6f,
0x50, 0x49, 0x5d, 0x97, 0x9b, 0x55, 0x13, 0xe9, 0x9b, 0x24, 0x56, 0xe5, 0xa5, 0x2f, 0x82, 0xb7,
0x27, 0xe3, 0x37, 0x1b, 0x55, 0x5d, 0x66, 0x33, 0x42, 0xf3, 0x2a, 0xa3, 0x81, 0x6a, 0xaa, 0x29,
0x13, 0x2b, 0x16, 0xa7, 0x48, 0xb2, 0x3a, 0x19, 0xdd, 0x54, 0x0d, 0x93, 0x46, 0x9c, 0x50, 0x70,
0x92, 0x8f, 0x4d, 0x27, 0xac, 0x0f, 0x8a, 0xc7, 0x14, 0x0c, 0x65, 0xa4, 0x40, 0x55, 0x36, 0x55,
0x14, 0x81, 0x13, 0x67, 0x26, 0x31, 0x1c, 0x45, 0x2c, 0x6b, 0xe1, 0x96, 0x4d, 0x3c, 0x91, 0x8d,
0x21, 0xfd, 0x22, 0x2f, 0xd9, 0x55, 0x18, 0x46, 0x05, 0x29, 0x2a, 0x24, 0x6e, 0x3c, 0x7e, 0x29,
0xc4, 0x32, 0x84, 0x24, 0x81, 0x9b, 0x25, 0x29, 0x70, 0x12, 0x46, 0x6d, 0x53, 0x2b, 0x57, 0xe4,
0x8d, 0x27, 0x46, 0x7f, 0x1a, 0x19, 0x0d, 0x71, 0x6e, 0xd2, 0xc0, 0x86, 0xb0, 0xb8, 0x84, 0xdc,
0xc8, 0x41, 0x36, 0x6e, 0x86, 0x6a, 0x36, 0x1b, 0x68, 0x53, 0x87, 0x1b, 0xeb, 0x55, 0x7d, 0x53,
0x3c, 0x95, 0xb0, 0x34, 0x46, 0xb0, 0x36, 0xd5, 0xaa, 0xa2, 0xd7, 0x54, 0xf1, 0xf4, 0xca, 0xcf,
0x85, 0xd8, 0x77, 0xe3, 0xe1, 0x9b, 0xef, 0x44, 0xab, 0xa5, 0x99, 0x9c, 0xa2, 0x97, 0x46, 0x8f,
0x9b, 0xe2, 0x3d, 0x57, 0x14, 0x7e, 0x90, 0x53, 0x66, 0x80, 0xed, 0xa7, 0x98, 0xf7, 0xc0, 0xad,
0x14, 0xd8, 0x30, 0xe3, 0x4c, 0xa7, 0x3a, 0x48, 0x40, 0xff, 0x26, 0x0f, 0x0a, 0x49, 0xc7, 0xac,
0x49, 0x84, 0x98, 0xa5, 0x8f, 0x0c, 0x3b, 0xde, 0xc3, 0x84, 0xb0, 0x03, 0xc5, 0x87, 0x3e, 0xa6,
0xf4, 0x8c, 0x6c, 0x4d, 0x48, 0x26, 0xc0, 0xa2, 0xac, 0x90, 0x99, 0x42, 0x64, 0x13, 0x52, 0x95,
0xd7, 0xd4, 0x2a, 0x6a, 0x68, 0xca, 0x06, 0x5d, 0xf0, 0x25, 0xf0, 0xc1, 0xf3, 0x52, 0x18, 0x93,
0x63, 0x4a, 0xaa, 0x80, 0xd2, 0xf3, 0x52, 0xe9, 0xef, 0x45, 0x60, 0xb3, 0xaa, 0x86, 0xf2, 0x4c,
0xaf, 0xfc, 0x64, 0x06, 0x2c, 0xc4, 0x9f, 0x13, 0x27, 0xcc, 0xe6, 0xba, 0xa6, 0x56, 0x47, 0xb3,
0x95, 0xf4, 0x48, 0xaa, 0x92, 0x83, 0x57, 0xfa, 0xd2, 0xc4, 0x00, 0x49, 0x3f, 0x10, 0xc0, 0xad,
0x88, 0x18, 0x1e, 0x2f, 0x69, 0x44, 0xad, 0xf0, 0xad, 0x53, 0x28, 0xca, 0x49, 0x2a, 0xca, 0x97,
0xd2, 0x45, 0x99, 0xf4, 0x5a, 0xaa, 0x92, 0x83, 0xd7, 0xfb, 0xe2, 0x24, 0x00, 0x86, 0x9a, 0xf1,
0xf8, 0xfb, 0x23, 0x44, 0xf6, 0xae, 0x64, 0x01, 0xb2, 0xf7, 0x4f, 0x7c, 0xba, 0x66, 0x53, 0x35,
0x93, 0xf2, 0x7a, 0x8a, 0x69, 0x26, 0x19, 0x48, 0xfa, 0x18, 0x5c, 0x89, 0x93, 0xa2, 0x7b, 0xcc,
0x25, 0x98, 0xa3, 0x12, 0x7c, 0x21, 0xbb, 0x04, 0xd1, 0xe7, 0x57, 0x95, 0x1c, 0x2c, 0x8c, 0x71,
0xe7, 0x00, 0xd2, 0xff, 0x00, 0xcb, 0xe3, 0x9c, 0xbb, 0x9e, 0xd3, 0x09, 0x38, 0x6b, 0x40, 0x59,
0xbf, 0x95, 0x95, 0xf5, 0xc8, 0xe3, 0xad, 0x4a, 0x0e, 0x5e, 0x1e, 0xe1, 0x3d, 0x80, 0x90, 0xda,
0xe0, 0x72, 0xcf, 0x41, 0x36, 0x77, 0xe2, 0x24, 0xeb, 0xf2, 0x48, 0x28, 0xa1, 0xc4, 0x69, 0x50,
0x3b, 0x35, 0x61, 0x63, 0x15, 0xff, 0x04, 0xaa, 0x92, 0x83, 0x0b, 0x3d, 0x27, 0xf6, 0x71, 0xd4,
0x77, 0x98, 0xf9, 0xf5, 0xd9, 0x0d, 0x62, 0x5d, 0x58, 0x07, 0xc6, 0x39, 0x9f, 0xa6, 0x9c, 0xdf,
0xce, 0xc0, 0x39, 0xfe, 0x55, 0x13, 0xb3, 0xbc, 0x94, 0x97, 0x4f, 0xdf, 0xa0, 0x86, 0xd7, 0x17,
0x86, 0xd7, 0xce, 0xfb, 0xac, 0x0c, 0x9e, 0x0b, 0x72, 0x86, 0x0a, 0xf2, 0xe6, 0xa7, 0x2a, 0xa2,
0x67, 0x36, 0x37, 0xe1, 0xd1, 0xc3, 0xff, 0x65, 0x0e, 0x74, 0x20, 0x01, 0x4f, 0xe8, 0x07, 0xeb,
0x92, 0x09, 0x31, 0x4f, 0x85, 0x78, 0x92, 0x45, 0x88, 0xb8, 0x5a, 0xe5, 0x4a, 0x0e, 0x5e, 0x8b,
0xc8, 0x11, 0x5b, 0xce, 0xfc, 0xeb, 0xcc, 0x7b, 0x8e, 0x8b, 0xd2, 0x0a, 0xcb, 0x54, 0xd0, 0x41,
0xd0, 0xf6, 0xb9, 0x40, 0x67, 0xa9, 0x40, 0x5f, 0x7c, 0x0e, 0x81, 0xc6, 0xab, 0x87, 0x2b, 0x39,
0x78, 0x6b, 0x5c, 0xaa, 0x01, 0x5c, 0xd0, 0xe6, 0x05, 0x94, 0x7f, 0x2a, 0x80, 0x27, 0xc3, 0xf3,
0x44, 0x6b, 0x4f, 0x91, 0x45, 0x8b, 0x4f, 0x91, 0x1d, 0x56, 0x9f, 0xa2, 0xc0, 0x75, 0xdb, 0x3c,
0x99, 0x6c, 0xb7, 0xb9, 0xa4, 0x22, 0x95, 0xf4, 0x69, 0xa6, 0xf9, 0xcb, 0x54, 0xe3, 0x5b, 0xc9,
0xc1, 0x87, 0xd1, 0x49, 0xcd, 0x56, 0x18, 0xfc, 0x13, 0x01, 0x3c, 0xce, 0x34, 0x86, 0x81, 0xba,
0x99, 0xfc, 0xe7, 0xa8, 0xfc, 0xe5, 0x4f, 0x2d, 0xff, 0x70, 0x95, 0x51, 0x25, 0x07, 0x57, 0xd3,
0x84, 0x1f, 0xa9, 0x4b, 0xfa, 0x4d, 0x01, 0xbc, 0x12, 0x95, 0xdc, 0xea, 0x91, 0xcc, 0xa3, 0xbf,
0x07, 0x8d, 0xbc, 0xa7, 0x62, 0x02, 0x4b, 0x54, 0xe0, 0xf7, 0x33, 0x08, 0x3c, 0xa9, 0x6a, 0xb6,
0x92, 0x83, 0x77, 0x06, 0x82, 0x4e, 0xac, 0xaf, 0xfd, 0x43, 0x01, 0x3c, 0x48, 0xb1, 0x5c, 0xc7,
0x3a, 0x60, 0x9b, 0x97, 0x63, 0x2e, 0xe4, 0x79, 0x2a, 0xe4, 0xda, 0xa7, 0xb1, 0xdf, 0xe1, 0xc2,
0xb5, 0x4a, 0x0e, 0xde, 0x9f, 0x60, 0xc4, 0x9a, 0x75, 0x10, 0xad, 0x72, 0xfb, 0x15, 0x01, 0xdc,
0x89, 0x8a, 0xda, 0xed, 0x17, 0x83, 0x8d, 0xcd, 0xfb, 0x05, 0x2a, 0xe1, 0xbb, 0x19, 0x24, 0x4c,
0xaa, 0x28, 0xab, 0xe4, 0x60, 0x71, 0x20, 0x5a, 0x62, 0xdd, 0xd9, 0xb7, 0x04, 0x70, 0x23, 0x2a,
0x53, 0x80, 0xfd, 0x80, 0x48, 0xd3, 0x19, 0xf2, 0xc7, 0x17, 0x53, 0xa3, 0xdf, 0x84, 0xf2, 0xae,
0x4a, 0x0e, 0x2e, 0x0f, 0x24, 0x89, 0xab, 0xff, 0xf2, 0xc0, 0x52, 0x54, 0x86, 0x30, 0xcf, 0x0d,
0xe3, 0xd0, 0x42, 0xca, 0x0d, 0x62, 0x52, 0x7d, 0x15, 0x0b, 0xbb, 0x09, 0xb5, 0x57, 0x6d, 0x50,
0xe8, 0x39, 0x24, 0x09, 0xb3, 0x02, 0x8c, 0x3a, 0xf8, 0x88, 0xee, 0x7f, 0x79, 0xc4, 0xbd, 0x94,
0x72, 0xc0, 0x95, 0x58, 0xd9, 0x54, 0xc9, 0xc1, 0x0b, 0x3d, 0x67, 0xbc, 0x53, 0x3a, 0xa6, 0x41,
0x7e, 0x94, 0x9b, 0x6f, 0x1d, 0x86, 0x2c, 0x0b, 0xa9, 0x1a, 0x9e, 0x50, 0x2e, 0xc5, 0x06, 0x1a,
0x0f, 0x20, 0x7d, 0x03, 0x5c, 0x8b, 0x1b, 0x28, 0xad, 0x50, 0xe2, 0xcc, 0x2f, 0xa7, 0x06, 0x98,
0x89, 0xd5, 0x4d, 0x95, 0x1c, 0x5c, 0x1c, 0x1d, 0xf5, 0x00, 0x44, 0xfa, 0x1d, 0xe6, 0x42, 0x46,
0x25, 0x60, 0xf7, 0x70, 0xd1, 0x0a, 0x28, 0x2e, 0xcd, 0x22, 0x95, 0x46, 0xc9, 0x2a, 0xcd, 0x84,
0x42, 0xaa, 0x4a, 0x0e, 0xde, 0x1e, 0x11, 0x2c, 0x1e, 0x5a, 0xfa, 0x7d, 0x01, 0xac, 0x46, 0x4d,
0xd0, 0x19, 0x1c, 0x3b, 0x22, 0xeb, 0xc8, 0x67, 0x47, 0x03, 0x7c, 0x59, 0x70, 0xab, 0x5c, 0x4a,
0xdd, 0x42, 0x64, 0x7b, 0x79, 0x5c, 0xc9, 0xc1, 0x7b, 0x03, 0x2b, 0x8d, 0xc2, 0x1e, 0xf9, 0xba,
0x63, 0xb7, 0x86, 0x5e, 0x29, 0x7f, 0x57, 0x00, 0xb7, 0xe3, 0x53, 0x06, 0xdb, 0x47, 0x98, 0x1e,
0x90, 0x72, 0xf1, 0xae, 0x64, 0x4e, 0xa1, 0xe2, 0x1f, 0x04, 0x0f, 0xa7, 0x50, 0x7d, 0x18, 0xdb,
0x8f, 0x3e, 0x7f, 0x0d, 0x98, 0x59, 0x93, 0x78, 0x1b, 0xb8, 0x88, 0x95, 0x06, 0xb1, 0x59, 0xe4,
0x52, 0x2c, 0xa7, 0x2e, 0xdd, 0xf8, 0x17, 0xa6, 0xdc, 0xa2, 0xe3, 0x5f, 0x9f, 0x7e, 0x15, 0x9c,
0xb3, 0x68, 0x8d, 0x12, 0x1a, 0x54, 0x08, 0x15, 0xae, 0x52, 0x4e, 0xc9, 0x07, 0xc9, 0xb1, 0xf5,
0x74, 0x95, 0x1c, 0x14, 0xad, 0x91, 0x8e, 0xd0, 0x25, 0x46, 0x4d, 0x80, 0x6b, 0x96, 0xa6, 0xc7,
0x7c, 0x64, 0xd7, 0x52, 0x17, 0xec, 0x84, 0x9b, 0x4c, 0xe6, 0x12, 0x27, 0x5d, 0x75, 0xf2, 0x54,
0x39, 0x46, 0x88, 0xfe, 0x29, 0x00, 0x93, 0xe3, 0x7a, 0xea, 0x3c, 0x4f, 0xbe, 0xe0, 0x64, 0xf3,
0x9c, 0x72, 0x09, 0xfa, 0xbf, 0x04, 0xea, 0x44, 0xc2, 0x7d, 0xe3, 0xd7, 0xa2, 0xdf, 0xd8, 0x08,
0xb7, 0x8c, 0x37, 0xb2, 0xee, 0x5e, 0x93, 0xbe, 0xd0, 0x31, 0xb4, 0x7b, 0x8d, 0x01, 0x92, 0x3e,
0x02, 0x7c, 0xb2, 0x10, 0x0e, 0xcb, 0x0b, 0x0b, 0x45, 0xca, 0xf5, 0xb5, 0x94, 0x69, 0x1f, 0x2e,
0x47, 0xac, 0xe4, 0xe0, 0x59, 0x6b, 0xb8, 0x5d, 0x3a, 0x00, 0x97, 0x38, 0x6d, 0xe2, 0xa0, 0xa2,
0x55, 0x89, 0x85, 0x9b, 0x29, 0x27, 0xf7, 0xc9, 0xc5, 0x81, 0x95, 0x1c, 0xbc, 0x68, 0xc5, 0xf5,
0x4a, 0xdb, 0xe0, 0xe2, 0xe0, 0x94, 0x84, 0x39, 0x46, 0x36, 0x9d, 0xb7, 0x28, 0xb3, 0x57, 0x13,
0x99, 0xc5, 0xdc, 0x6d, 0x54, 0x72, 0xf0, 0xbc, 0x17, 0x73, 0xe5, 0x71, 0x04, 0xae, 0x24, 0x1c,
0xae, 0x33, 0x56, 0xb7, 0x53, 0xc6, 0x95, 0x7c, 0x21, 0x40, 0x1c, 0xfe, 0x4e, 0xf2, 0x75, 0xc1,
0x36, 0xe0, 0xa3, 0x46, 0xbc, 0x20, 0xc1, 0x63, 0x75, 0x8f, 0x85, 0x3b, 0x29, 0x83, 0x8b, 0xa9,
0x95, 0x24, 0x83, 0xb3, 0x62, 0x4a, 0x28, 0xab, 0xe0, 0x4c, 0x9f, 0x07, 0x9d, 0xa5, 0xbb, 0x94,
0xf6, 0xed, 0x54, 0xda, 0x04, 0xb8, 0x92, 0x83, 0xa7, 0xad, 0x68, 0x95, 0xe3, 0x16, 0x90, 0xa2,
0xb5, 0x13, 0x6c, 0x46, 0x0a, 0xf7, 0x52, 0x4a, 0xb2, 0x47, 0x8b, 0xfc, 0xa8, 0x37, 0x19, 0x2d,
0xfc, 0x1b, 0x21, 0xdd, 0xa3, 0x05, 0x63, 0x85, 0xfb, 0x99, 0x49, 0xb3, 0x0a, 0xb3, 0x61, 0xd2,
0xbc, 0xea, 0x6c, 0x84, 0xb4, 0x4d, 0x0b, 0x4a, 0x0a, 0x2b, 0x99, 0x49, 0xb3, 0x0a, 0x94, 0x61,
0xd2, 0xbc, 0x2a, 0xa5, 0x0d, 0x2e, 0x47, 0x49, 0xf3, 0x9a, 0x2e, 0xae, 0x97, 0x57, 0x52, 0xce,
0x05, 0xe2, 0x8b, 0xfc, 0x2a, 0x39, 0xb8, 0x60, 0xc5, 0x97, 0xff, 0xc5, 0x73, 0xe3, 0xaa, 0x7a,
0xf5, 0x39, 0xb9, 0xf5, 0x15, 0x36, 0xc6, 0x8d, 0xab, 0x2d, 0x9e, 0x1b, 0xd7, 0xde, 0x6b, 0xcf,
0xc9, 0xad, 0xaf, 0xc3, 0x31, 0x6e, 0x5c, 0x93, 0x07, 0x60, 0x31, 0xca, 0x8d, 0x96, 0x7a, 0xf9,
0x24, 0x5a, 0xf6, 0x7c, 0xec, 0x15, 0x56, 0x33, 0xb3, 0x8b, 0xd6, 0xbb, 0x0d, 0xb3, 0x1b, 0xaa,
0x84, 0xfb, 0x7f, 0x02, 0x28, 0x46, 0x13, 0x84, 0xe8, 0x4d, 0xd2, 0xe0, 0x98, 0xa5, 0xf0, 0x20,
0xf5, 0x4c, 0x36, 0xf5, 0xb5, 0x1e, 0x3b, 0x93, 0xed, 0x83, 0xb5, 0xc6, 0xc1, 0xa4, 0x7d, 0x70,
0x29, 0xe6, 0x84, 0x05, 0x3b, 0x2d, 0x5c, 0x78, 0x3d, 0x35, 0xc5, 0x4e, 0x78, 0x2b, 0xc7, 0x52,
0xec, 0x91, 0x4e, 0xa7, 0x85, 0x47, 0x99, 0x85, 0xe9, 0xa6, 0x6b, 0xe3, 0xc2, 0xc3, 0xcc, 0xcc,
0x46, 0xde, 0xa7, 0x0d, 0x33, 0x1b, 0x74, 0x4a, 0x5f, 0x01, 0xe7, 0x02, 0x6b, 0x97, 0xc7, 0x21,
0x4c, 0x02, 0xa2, 0x77, 0x5c, 0x78, 0x94, 0x12, 0x8b, 0xe2, 0x2e, 0x1c, 0x49, 0x2c, 0x0a, 0xac,
0xdd, 0x68, 0xbb, 0x14, 0x80, 0x45, 0x9f, 0x5f, 0xcf, 0x20, 0x8f, 0x52, 0x42, 0xbb, 0x98, 0x1e,
0x64, 0xf7, 0xda, 0x41, 0xe1, 0x8d, 0x94, 0x23, 0xa9, 0x49, 0x37, 0x3b, 0x95, 0x1c, 0xbc, 0xe4,
0xc7, 0xf7, 0x8f, 0x2e, 0x0b, 0x5e, 0x3f, 0xc6, 0x97, 0xfc, 0xe3, 0xcc, 0x76, 0x1a, 0xad, 0x3a,
0x1c, 0xb6, 0xd3, 0xa1, 0x7a, 0xc4, 0x78, 0x6e, 0x7c, 0x11, 0xbe, 0xf9, 0x9c, 0xdc, 0xe2, 0x16,
0xe1, 0x50, 0x91, 0xdd, 0x36, 0xb8, 0x18, 0x1a, 0xc6, 0x31, 0xda, 0xc1, 0x41, 0x6b, 0x8f, 0xc7,
0xc0, 0x2f, 0xa4, 0x44, 0xa4, 0x98, 0xeb, 0x2d, 0x12, 0x91, 0xec, 0x98, 0x5b, 0xaf, 0xef, 0x8d,
0x9c, 0xe6, 0x71, 0x03, 0x1c, 0xf0, 0x65, 0x7b, 0xfc, 0xc2, 0x5b, 0x99, 0x0f, 0xcf, 0x92, 0x3f,
0xb3, 0x30, 0x7c, 0xa4, 0x17, 0x0b, 0x27, 0x7d, 0x73, 0x64, 0x67, 0xbf, 0xdf, 0xdb, 0xc6, 0xf4,
0x72, 0x76, 0x68, 0x9b, 0xf0, 0x24, 0xf3, 0x01, 0xe7, 0xf8, 0xd7, 0x6f, 0x86, 0x0f, 0x38, 0x69,
0xff, 0xfe, 0xd0, 0xf6, 0xe0, 0xdb, 0xf1, 0x2a, 0xb1, 0xba, 0x5d, 0x7a, 0xcc, 0xda, 0x3f, 0xe0,
0x7c, 0x3b, 0xf5, 0x84, 0x7b, 0xd2, 0x33, 0xca, 0x61, 0x47, 0x14, 0xfb, 0xd0, 0xb2, 0x0b, 0x96,
0xb8, 0xb5, 0xed, 0x7a, 0x56, 0x77, 0x8f, 0x6e, 0xe9, 0xfc, 0x96, 0x15, 0x66, 0x42, 0xef, 0x50,
0xfe, 0xaf, 0xa7, 0xd8, 0xdb, 0xd8, 0x7d, 0x3d, 0x59, 0x4d, 0x56, 0xa4, 0xeb, 0xc8, 0x1f, 0x5c,
0xe5, 0x7f, 0x9f, 0x6d, 0xd4, 0x86, 0xb8, 0xb6, 0x3c, 0xf7, 0xa8, 0x83, 0xfe, 0x3b, 0x3e, 0xc2,
0x6d, 0x64, 0x3b, 0x3b, 0x3b, 0x74, 0x0b, 0x5c, 0x78, 0x37, 0xd5, 0x1e, 0xd2, 0x6f, 0x90, 0x99,
0x3d, 0x4c, 0x84, 0x93, 0xfe, 0x1b, 0xb8, 0x48, 0x8b, 0x38, 0xe8, 0xbd, 0x29, 0xbd, 0xf3, 0xe4,
0xa3, 0x7f, 0x2f, 0xed, 0xa6, 0x72, 0xec, 0xfa, 0xb5, 0x92, 0x83, 0x52, 0x6f, 0xfc, 0x52, 0x96,
0x1d, 0xa8, 0xc7, 0xee, 0x9b, 0x70, 0x97, 0x33, 0xfb, 0x62, 0xaa, 0xbd, 0x25, 0xd7, 0x26, 0x32,
0x7b, 0x9b, 0x50, 0xbb, 0xf8, 0x6b, 0x02, 0xb8, 0x97, 0x20, 0x01, 0x3f, 0xce, 0x72, 0xbb, 0x38,
0x9c, 0xf4, 0x2f, 0xa5, 0x86, 0xc0, 0xd4, 0x12, 0x41, 0x76, 0xfb, 0x97, 0x5e, 0x49, 0xf8, 0x89,
0x00, 0x56, 0xe2, 0xe5, 0xa2, 0x35, 0x68, 0xa3, 0x5b, 0xba, 0xf7, 0x53, 0x2f, 0xdf, 0x32, 0xd4,
0xec, 0xb1, 0xb3, 0x8f, 0x2c, 0xc5, 0x7d, 0xfc, 0x4c, 0x21, 0x76, 0xab, 0x49, 0x9f, 0x62, 0x75,
0xc3, 0x73, 0xd3, 0x0f, 0x3e, 0xcd, 0x5e, 0x33, 0x52, 0x47, 0x97, 0xb8, 0xd7, 0x8c, 0xd6, 0xda,
0x7d, 0x9f, 0x9d, 0x92, 0xc6, 0x88, 0xd3, 0x76, 0x3a, 0xfb, 0x43, 0xa7, 0x0b, 0x72, 0xea, 0x96,
0x33, 0xa5, 0xac, 0xad, 0x92, 0x83, 0x37, 0x62, 0x04, 0x1a, 0x06, 0x5a, 0x3b, 0x09, 0x66, 0x28,
0xcd, 0xa7, 0xd3, 0xb3, 0x79, 0x71, 0x8a, 0xd8, 0x60, 0x7f, 0x27, 0x4c, 0xe2, 0x72, 0x78, 0x69,
0x46, 0x25, 0x5b, 0xf9, 0x91, 0x38, 0xf8, 0x20, 0x62, 0xb8, 0x59, 0x93, 0x6e, 0x80, 0xe5, 0x92,
0x66, 0x28, 0xfa, 0x33, 0x15, 0x22, 0xa8, 0x1a, 0x7a, 0x13, 0x2a, 0xa3, 0x9f, 0x33, 0xba, 0x02,
0x0a, 0xe3, 0x20, 0x86, 0x0a, 0x9f, 0xa9, 0x50, 0x14, 0xa4, 0xeb, 0xe0, 0xca, 0x78, 0xef, 0x46,
0x73, 0x4d, 0x85, 0x75, 0xd5, 0x54, 0x0d, 0x31, 0x2f, 0xbd, 0x01, 0x1e, 0x8c, 0x43, 0x94, 0x64,
0x53, 0x5e, 0x93, 0x0d, 0x15, 0x35, 0x74, 0xc3, 0x2c, 0x43, 0xd5, 0x40, 0x86, 0x5a, 0x5d, 0x47,
0x15, 0xdd, 0x30, 0xd5, 0x92, 0x38, 0x25, 0xbd, 0x0e, 0x5e, 0x9d, 0x80, 0x54, 0xdb, 0x32, 0x3e,
0xac, 0x0e, 0x61, 0x4c, 0x4b, 0x8f, 0xc0, 0xea, 0x24, 0x0c, 0xbd, 0x5e, 0xd6, 0x4b, 0x6b, 0x43,
0x38, 0x33, 0xd2, 0x2b, 0xe0, 0x6e, 0x16, 0xd1, 0x60, 0xc9, 0x10, 0x4f, 0x48, 0xf7, 0xc0, 0xad,
0x54, 0x91, 0x08, 0xe4, 0x49, 0xe9, 0x0e, 0x28, 0x8e, 0x43, 0xca, 0x8d, 0x46, 0x55, 0x53, 0x64,
0x53, 0xd3, 0xeb, 0xa8, 0x62, 0x9a, 0x0d, 0x71, 0x56, 0xba, 0x0d, 0x6e, 0x4c, 0x86, 0x33, 0x95,
0x86, 0x38, 0x17, 0x0f, 0xb6, 0xa9, 0xd5, 0x4b, 0xfa, 0xa6, 0x81, 0x4a, 0xaa, 0xb1, 0x61, 0xea,
0x0d, 0x11, 0x48, 0xaf, 0x82, 0x7b, 0x13, 0xe4, 0x33, 0x3e, 0xac, 0xb2, 0x39, 0xa3, 0x32, 0x9e,
0x4a, 0x51, 0xf0, 0x60, 0xe8, 0x6a, 0xc9, 0xa8, 0x68, 0xeb, 0xa6, 0x78, 0x5a, 0x7a, 0x0c, 0x5e,
0xcf, 0x44, 0x3f, 0xaa, 0xe2, 0x33, 0x29, 0x7c, 0xa0, 0x5a, 0xd2, 0x86, 0xa7, 0x7e, 0x3e, 0xeb,
0xa4, 0x94, 0x95, 0x86, 0x78, 0x36, 0xd3, 0xa4, 0x10, 0x48, 0x31, 0xb3, 0x7a, 0x08, 0xf4, 0x39,
0xe9, 0x5d, 0xf0, 0xd6, 0xf3, 0xa8, 0x87, 0xaf, 0x87, 0xaa, 0x6a, 0x18, 0xa2, 0x24, 0xbd, 0x06,
0xee, 0x67, 0x41, 0x96, 0x3f, 0x6a, 0x42, 0x55, 0x3c, 0x2f, 0xdd, 0x05, 0x37, 0x27, 0x80, 0x97,
0xb6, 0xea, 0x72, 0x4d, 0x2f, 0xad, 0x89, 0x17, 0x52, 0x4c, 0x5c, 0x91, 0x0d, 0x43, 0xae, 0x97,
0xa0, 0x8c, 0x36, 0xd4, 0x2d, 0xa3, 0x21, 0x2b, 0xaa, 0x21, 0x5e, 0x4c, 0x99, 0xb5, 0x01, 0x4e,
0x74, 0x0e, 0x16, 0xa4, 0x27, 0xe0, 0xf1, 0x04, 0x2c, 0xb5, 0x2a, 0x1b, 0xa6, 0xa6, 0x18, 0xaa,
0x0c, 0x95, 0xca, 0x10, 0xe6, 0xa5, 0x4c, 0xf3, 0xcd, 0xf1, 0x65, 0xa5, 0xa2, 0x8a, 0x85, 0x14,
0x6d, 0x31, 0x8c, 0x9a, 0x5a, 0xd3, 0xe1, 0x56, 0x69, 0x4d, 0xbc, 0x9c, 0x89, 0x01, 0xd5, 0x2c,
0x62, 0x0c, 0x16, 0x53, 0x06, 0xc3, 0x30, 0x94, 0x6a, 0xd3, 0x30, 0x47, 0x8c, 0x77, 0x49, 0x5a,
0x01, 0x77, 0x52, 0xad, 0x8b, 0xcd, 0xe2, 0x15, 0x69, 0x15, 0xac, 0x64, 0xb2, 0x2f, 0x06, 0xbf,
0x9c, 0x32, 0x99, 0x03, 0xf8, 0x9a, 0xa6, 0x40, 0xdd, 0xd0, 0xd7, 0x4d, 0xf1, 0xaa, 0xf4, 0x05,
0xf0, 0x68, 0xd2, 0x64, 0xea, 0xca, 0x06, 0xd4, 0x65, 0xa5, 0x32, 0xe2, 0xe7, 0xae, 0xa5, 0xd8,
0x7e, 0xe8, 0x1b, 0x65, 0xb3, 0x2a, 0x1b, 0xe2, 0xf5, 0x94, 0x35, 0x65, 0xd4, 0xf5, 0xcd, 0xf5,
0xaa, 0xbc, 0xa1, 0x8a, 0x37, 0x12, 0xe8, 0xea, 0x4a, 0x44, 0xbb, 0x25, 0x03, 0x35, 0xa0, 0xfe,
0xe5, 0x2d, 0xb1, 0x98, 0x60, 0x8a, 0x51, 0xe8, 0x8a, 0x56, 0xae, 0x20, 0xf9, 0x99, 0xac, 0x55,
0xe5, 0x35, 0xad, 0xaa, 0x99, 0x5b, 0xe2, 0x4d, 0xe9, 0x2d, 0xf0, 0x46, 0x0a, 0x16, 0x5d, 0x21,
0x9a, 0x82, 0xa0, 0x5a, 0xd6, 0x0c, 0x13, 0x52, 0xd7, 0x29, 0xde, 0x8a, 0xf7, 0xc2, 0x86, 0x5c,
0xab, 0x46, 0x5d, 0xac, 0x78, 0x5b, 0x2a, 0x82, 0xab, 0xe3, 0x70, 0xaa, 0xf2, 0x88, 0x7d, 0xb8,
0xaf, 0xae, 0xa8, 0xe2, 0x9d, 0x04, 0xa3, 0xd3, 0x95, 0x51, 0x37, 0x8c, 0xea, 0x7a, 0x1d, 0xc9,
0x25, 0xf1, 0xae, 0x74, 0x0b, 0x5c, 0x9f, 0x14, 0x17, 0xe9, 0x07, 0xdd, 0xee, 0xc5, 0xdb, 0x7e,
0x34, 0x02, 0xc8, 0x9b, 0x06, 0x52, 0xf4, 0xba, 0xa1, 0x57, 0x55, 0xf1, 0xbe, 0x74, 0x0d, 0x2c,
0x8d, 0x83, 0xd7, 0x94, 0x06, 0x32, 0xcc, 0x92, 0xa6, 0x8b, 0x2b, 0xf4, 0xb3, 0x87, 0xf1, 0x00,
0x86, 0x2a, 0xbe, 0x22, 0xdd, 0x07, 0xb7, 0x93, 0xf0, 0xa1, 0x2a, 0xd7, 0xe4, 0xb5, 0xaa, 0xca,
0x62, 0xd3, 0xab, 0x2b, 0xbf, 0x2b, 0x80, 0xf9, 0xe1, 0x4f, 0x0f, 0x0f, 0x71, 0x37, 0x4c, 0xd9,
0x6c, 0x1a, 0x23, 0x99, 0xc2, 0x12, 0xb8, 0x34, 0x0a, 0x60, 0x34, 0x15, 0x85, 0x38, 0x45, 0x21,
0xb6, 0x73, 0x43, 0x6b, 0x34, 0xd4, 0x92, 0x98, 0x97, 0x2e, 0x83, 0x8b, 0xa3, 0x9d, 0x2a, 0x84,
0x3a, 0x14, 0xa7, 0xe2, 0xf0, 0xe4, 0x35, 0x1d, 0xd2, 0xa0, 0xbf, 0xf2, 0xb3, 0x3c, 0x98, 0x52,
0x4c, 0x59, 0x3a, 0x0f, 0xce, 0x2a, 0xa6, 0x3c, 0xfe, 0x91, 0x47, 0xd2, 0x28, 0x37, 0xcd, 0x0a,
0xd1, 0x61, 0x5d, 0x55, 0x4c, 0x9d, 0xa4, 0x2c, 0x97, 0xc0, 0x79, 0xda, 0xae, 0x98, 0xda, 0x33,
0x92, 0xc9, 0x18, 0x86, 0xa6, 0xd7, 0x49, 0xa6, 0xd2, 0xef, 0x20, 0x22, 0x23, 0xa8, 0x7e, 0xd8,
0x54, 0x0d, 0xd3, 0x10, 0xa7, 0xc2, 0x8e, 0x06, 0x54, 0x6b, 0x5a, 0xb3, 0x86, 0x8c, 0x66, 0xa3,
0xa1, 0x43, 0x53, 0x9c, 0x0e, 0x3b, 0x4c, 0x48, 0xbc, 0x47, 0x09, 0x95, 0xd4, 0x67, 0x1a, 0x71,
0xbb, 0x33, 0x21, 0xef, 0x66, 0xa3, 0x0c, 0xe5, 0x92, 0x8a, 0xd6, 0xe4, 0x7a, 0x5d, 0x85, 0xe2,
0x89, 0x10, 0x61, 0x4d, 0xab, 0x56, 0xb5, 0x7a, 0x19, 0x19, 0xcd, 0x5a, 0x4d, 0x86, 0x5b, 0xe2,
0xc9, 0x70, 0x04, 0x9c, 0x77, 0x55, 0x33, 0x4c, 0x71, 0x96, 0x7e, 0x0a, 0x70, 0xd0, 0x58, 0xd3,
0xeb, 0x9a, 0xa9, 0x43, 0xad, 0x5e, 0x16, 0xe7, 0xe8, 0x47, 0x06, 0x4d, 0x19, 0xa9, 0x5f, 0x36,
0x55, 0x58, 0x97, 0xab, 0x48, 0x6e, 0x96, 0x34, 0x13, 0x19, 0xa6, 0x0e, 0xe5, 0xb2, 0x2a, 0x82,
0x50, 0x00, 0x7d, 0x83, 0x48, 0x61, 0x10, 0xdd, 0x6d, 0xd5, 0x15, 0xf1, 0x94, 0x24, 0x82, 0xd3,
0x14, 0xaf, 0x6e, 0x42, 0x19, 0x69, 0x25, 0xf1, 0xb4, 0x74, 0x0e, 0x9c, 0xe9, 0x43, 0x1a, 0x8a,
0x56, 0x13, 0xcf, 0x84, 0x7c, 0xb5, 0x92, 0x5a, 0x37, 0x35, 0x73, 0x0b, 0x19, 0xaa, 0xd2, 0x84,
0x64, 0x39, 0xce, 0xaf, 0xfc, 0x18, 0x80, 0x8b, 0xb1, 0xef, 0xe8, 0x48, 0x18, 0xd3, 0xea, 0xa6,
0x5a, 0x66, 0x0b, 0x10, 0xa9, 0x75, 0xa8, 0x57, 0xab, 0x68, 0x43, 0xab, 0x8f, 0x7e, 0x3e, 0xf1,
0x06, 0x58, 0x4e, 0x02, 0x34, 0xaa, 0xb2, 0xb2, 0x21, 0x0a, 0x64, 0xf5, 0x24, 0x81, 0x90, 0x15,
0xa1, 0x6b, 0x25, 0x45, 0xcc, 0x93, 0xc4, 0x28, 0x09, 0xaa, 0x21, 0x97, 0x55, 0x58, 0x6a, 0x9a,
0x5b, 0xe2, 0xd4, 0x24, 0x7e, 0x6a, 0x4d, 0xd6, 0xaa, 0xe2, 0x34, 0xc9, 0x62, 0x93, 0x40, 0x9e,
0x6a, 0x50, 0x16, 0x67, 0xa4, 0x9b, 0xe0, 0x5a, 0x12, 0x04, 0x35, 0x4f, 0x58, 0x12, 0x4f, 0x10,
0x97, 0x93, 0x04, 0x54, 0x93, 0x4d, 0x53, 0x85, 0x35, 0xdd, 0x30, 0xc5, 0x93, 0x93, 0x86, 0x57,
0x33, 0x90, 0xa9, 0xca, 0x35, 0x43, 0x9c, 0x9d, 0x04, 0xa5, 0x37, 0x8c, 0xb2, 0x5a, 0xd7, 0x54,
0x71, 0x6e, 0x92, 0xe8, 0x64, 0x4a, 0x45, 0x30, 0x71, 0x70, 0x72, 0x6d, 0x5d, 0x3c, 0x35, 0x59,
0x6e, 0xa5, 0xa2, 0xd5, 0x55, 0x66, 0x2a, 0x6f, 0x82, 0x87, 0xe9, 0x70, 0xa8, 0xac, 0x99, 0x95,
0xe6, 0x1a, 0x5d, 0x5f, 0x64, 0x5d, 0x9d, 0x91, 0x1e, 0x80, 0x57, 0x32, 0xa0, 0x29, 0x1a, 0x54,
0xaa, 0xaa, 0xa2, 0x89, 0xf3, 0xc4, 0x2d, 0x66, 0xe3, 0x53, 0x95, 0xd7, 0xc4, 0xb3, 0x24, 0xf4,
0x66, 0x00, 0x7f, 0xaa, 0xd6, 0x37, 0xb4, 0xba, 0x21, 0x8a, 0x19, 0xe1, 0xe5, 0xba, 0xa1, 0xad,
0x55, 0x55, 0xf1, 0xdc, 0x24, 0xf5, 0x90, 0x20, 0xad, 0x29, 0x6a, 0x5d, 0xdf, 0x14, 0xa5, 0x49,
0x13, 0xd6, 0x5f, 0x6f, 0xe7, 0x49, 0x40, 0x4b, 0xb4, 0x24, 0xd9, 0x94, 0x4b, 0x7a, 0x19, 0x69,
0x75, 0x85, 0xae, 0x3d, 0x54, 0x93, 0xeb, 0x72, 0x59, 0xad, 0xa9, 0x75, 0x53, 0xbc, 0x40, 0xb2,
0x91, 0x2c, 0x62, 0x6f, 0x92, 0xb4, 0x2f, 0x1b, 0x2c, 0xc9, 0x75, 0x17, 0x48, 0x14, 0xcf, 0x42,
0x97, 0xe6, 0x2d, 0x34, 0xc1, 0xcb, 0x00, 0x4d, 0xf3, 0xcf, 0x2a, 0xd9, 0x38, 0x14, 0xa4, 0x87,
0xe0, 0xb5, 0x0c, 0x18, 0x91, 0x3d, 0xe3, 0xe5, 0x49, 0x16, 0x43, 0xd6, 0x7f, 0xdf, 0x31, 0x29,
0x6a, 0xdd, 0x54, 0xa1, 0xb8, 0x38, 0x69, 0x4a, 0xb9, 0x39, 0x42, 0xb5, 0xa1, 0x73, 0x4f, 0x2a,
0x2e, 0x65, 0xb4, 0x30, 0x19, 0x96, 0x75, 0xa5, 0x24, 0x5e, 0x91, 0x3e, 0x00, 0xef, 0x3d, 0xb7,
0xe1, 0x47, 0x47, 0xb4, 0xbc, 0xf2, 0xd7, 0x33, 0x31, 0x7e, 0xd3, 0x08, 0x70, 0x37, 0xc1, 0x6f,
0x1a, 0xa6, 0xda, 0x18, 0xf1, 0x9b, 0xf1, 0x63, 0xa4, 0x80, 0xf2, 0xa6, 0xa1, 0x29, 0x61, 0x90,
0x63, 0xee, 0x51, 0x90, 0xbe, 0x04, 0xde, 0x99, 0x0c, 0x6f, 0xa8, 0x26, 0xd7, 0x08, 0x89, 0x37,
0xa8, 0xa4, 0xae, 0xcb, 0xcd, 0xaa, 0x89, 0xf4, 0x4d, 0x12, 0xab, 0xf2, 0xd2, 0x17, 0xc1, 0xdb,
0x93, 0xf1, 0x9b, 0x8d, 0xaa, 0x2e, 0xb3, 0x19, 0xa1, 0x79, 0x95, 0xd1, 0x40, 0x35, 0xd5, 0x94,
0x89, 0x15, 0x8b, 0x53, 0x24, 0x59, 0x9d, 0x8c, 0x6e, 0xaa, 0x86, 0x49, 0x23, 0x4e, 0x28, 0x38,
0xc9, 0xc7, 0xa6, 0x13, 0xd6, 0x07, 0xc5, 0x63, 0x0a, 0x86, 0x32, 0x52, 0xa0, 0x2a, 0x9b, 0x2a,
0x8a, 0xc0, 0x89, 0x33, 0x93, 0x18, 0x8e, 0x22, 0x96, 0xb5, 0x70, 0xcb, 0x26, 0x9e, 0xc8, 0xc6,
0x90, 0x7e, 0x91, 0x97, 0xec, 0x2a, 0x0c, 0xa3, 0x82, 0x14, 0x15, 0x12, 0x37, 0x1e, 0xbf, 0x14,
0x62, 0x19, 0x42, 0x92, 0xc0, 0xcd, 0x92, 0x14, 0x38, 0x09, 0xa3, 0xb6, 0xa9, 0x95, 0x2b, 0xf2,
0xc6, 0x13, 0xa3, 0x3f, 0x8d, 0x8c, 0x86, 0x38, 0x37, 0x69, 0x60, 0x43, 0x58, 0x5c, 0x42, 0x6e,
0xe4, 0x20, 0x1b, 0x37, 0x43, 0x35, 0x9b, 0x0d, 0xb4, 0xa9, 0xc3, 0x8d, 0xf5, 0xaa, 0xbe, 0x29,
0x9e, 0x4a, 0x58, 0x1a, 0x23, 0x58, 0x9b, 0x6a, 0x55, 0xd1, 0x6b, 0xaa, 0x78, 0x7a, 0xe5, 0xe7,
0x42, 0xec, 0xbb, 0xf1, 0xf0, 0xcd, 0x77, 0xa2, 0xd5, 0xd2, 0x4c, 0x4e, 0xd1, 0x4b, 0xa3, 0xc7,
0x4d, 0xf1, 0x9e, 0x2b, 0x0a, 0x3f, 0xc8, 0x29, 0x33, 0xc0, 0xf6, 0x53, 0xcc, 0x7b, 0xe0, 0x56,
0x0a, 0x6c, 0x98, 0x71, 0xa6, 0x53, 0x1d, 0x24, 0xa0, 0x7f, 0x93, 0x07, 0x85, 0xa4, 0x63, 0xd6,
0x24, 0x42, 0xcc, 0xd2, 0x47, 0x86, 0x1d, 0xef, 0x61, 0x42, 0xd8, 0x81, 0xe2, 0x43, 0x1f, 0x53,
0x7a, 0x46, 0xb6, 0x26, 0x24, 0x13, 0x60, 0x51, 0x56, 0xc8, 0x4c, 0x21, 0xb2, 0x09, 0xa9, 0xca,
0x6b, 0x6a, 0x15, 0x35, 0x34, 0x65, 0x83, 0x2e, 0xf8, 0x12, 0xf8, 0xe0, 0x79, 0x29, 0x8c, 0xc9,
0x31, 0x25, 0x55, 0x40, 0xe9, 0x79, 0xa9, 0xf4, 0xf7, 0x22, 0xb0, 0x59, 0x55, 0x43, 0x79, 0xa6,
0x57, 0x7e, 0x70, 0x02, 0x2c, 0xc4, 0x9f, 0x13, 0x27, 0xcc, 0xe6, 0xba, 0xa6, 0x56, 0x47, 0xb3,
0xcd, 0xf7, 0xc0, 0x93, 0x44, 0xc8, 0x31, 0xb5, 0x92, 0x20, 0x61, 0x90, 0x24, 0x7b, 0x0b, 0x35,
0x61, 0x55, 0x14, 0x12, 0x96, 0x50, 0x02, 0xf6, 0x1a, 0x94, 0xeb, 0x4a, 0x45, 0xcc, 0x27, 0x2c,
0xd8, 0x04, 0xac, 0xfe, 0xd2, 0x9b, 0x92, 0xde, 0x06, 0x6f, 0x66, 0xc7, 0x53, 0xeb, 0xcf, 0x34,
@@ -6715,18 +6718,19 @@ var fileDescriptor_94cf2ca1c69fd564 = []byte{
0x64, 0x40, 0x37, 0x65, 0x53, 0x53, 0xd0, 0xd3, 0xcd, 0x0d, 0x43, 0x3c, 0x29, 0x3d, 0x01, 0x8f,
0x33, 0x50, 0x19, 0xb5, 0x59, 0x92, 0x1e, 0x3f, 0x37, 0x66, 0x19, 0xea, 0xcd, 0x86, 0x21, 0xce,
0x25, 0x38, 0xfc, 0x09, 0x98, 0x64, 0x03, 0x45, 0x1c, 0x6a, 0xfc, 0x12, 0x9b, 0x80, 0x38, 0x64,
0xd8, 0x86, 0x78, 0x6a, 0xe5, 0x87, 0x71, 0x1f, 0x36, 0x09, 0xdf, 0xbd, 0x27, 0x64, 0x62, 0xd4,
0xe5, 0xc4, 0xfc, 0x9f, 0x81, 0x78, 0xf7, 0x32, 0x80, 0x36, 0x55, 0x08, 0xe5, 0x75, 0x1d, 0xd6,
0x12, 0xed, 0x78, 0x00, 0x3b, 0x92, 0xba, 0x6c, 0xc9, 0xb5, 0xaa, 0x98, 0x5f, 0x79, 0x1f, 0x9c,
0xe4, 0xb5, 0x56, 0x64, 0xe3, 0xba, 0xae, 0xca, 0x26, 0x89, 0x2f, 0x63, 0x9b, 0xfe, 0xb0, 0x63,
0x74, 0x1b, 0x2c, 0xac, 0xfc, 0x8e, 0x00, 0x96, 0x26, 0xbc, 0x84, 0x26, 0xb1, 0x26, 0x44, 0x86,
0xaa, 0xa2, 0xd7, 0x6a, 0x6a, 0xbd, 0xc4, 0x24, 0x8c, 0x3d, 0x60, 0x58, 0x01, 0x77, 0x26, 0x83,
0xd7, 0x75, 0x93, 0xc1, 0x0a, 0x24, 0x69, 0x9f, 0x0c, 0x5b, 0xd2, 0xeb, 0xaa, 0x98, 0x5f, 0xfb,
0xea, 0x9f, 0xff, 0xf4, 0xaa, 0xf0, 0x93, 0x9f, 0x5e, 0x15, 0xfe, 0xee, 0xa7, 0x57, 0x85, 0x8f,
0xf4, 0x5d, 0x27, 0xd8, 0xeb, 0x6d, 0xaf, 0xb6, 0xdc, 0x83, 0x07, 0xbb, 0x9e, 0x75, 0xe8, 0xb0,
0x5a, 0x5c, 0xab, 0xfd, 0x60, 0xf0, 0xaf, 0xcf, 0xba, 0xce, 0x83, 0x5d, 0xdc, 0x79, 0x40, 0x5f,
0xad, 0x3f, 0xd8, 0x75, 0x47, 0xfe, 0x9f, 0xda, 0xbb, 0x91, 0x9f, 0x87, 0x0f, 0xb7, 0x4f, 0x50,
0xb0, 0x37, 0xfe, 0x23, 0x00, 0x00, 0xff, 0xff, 0xc7, 0xc7, 0x14, 0xee, 0x7f, 0x6d, 0x00, 0x00,
0xd8, 0x86, 0x78, 0x2a, 0x21, 0x26, 0x4f, 0xa0, 0x10, 0x1e, 0x4c, 0xd5, 0x65, 0xea, 0x6d, 0x7f,
0x14, 0xf7, 0x61, 0x94, 0xf0, 0xdd, 0x7c, 0x42, 0x26, 0x47, 0x5d, 0x56, 0xcc, 0xff, 0x29, 0x88,
0x77, 0x4f, 0x03, 0x68, 0x53, 0x85, 0x50, 0x5e, 0xd7, 0x61, 0x2d, 0x71, 0x1d, 0x0c, 0x60, 0x47,
0x52, 0x9f, 0x2d, 0xb9, 0x56, 0x15, 0xf3, 0x2b, 0xef, 0x83, 0x93, 0xbc, 0x56, 0x8b, 0x6c, 0x7c,
0xd7, 0x55, 0xd9, 0x24, 0xf1, 0x69, 0xec, 0xd0, 0x20, 0xec, 0x18, 0xdd, 0x46, 0x0b, 0x2b, 0xbf,
0x2d, 0x80, 0xa5, 0x09, 0x2f, 0xa9, 0x49, 0xac, 0x0a, 0x91, 0xa1, 0xaa, 0xe8, 0xb5, 0x9a, 0x5a,
0x2f, 0x31, 0x09, 0x63, 0x0f, 0x28, 0x56, 0xc0, 0x9d, 0xc9, 0xe0, 0x75, 0xdd, 0x64, 0xb0, 0x02,
0x49, 0xfa, 0x27, 0xc3, 0x96, 0xf4, 0xba, 0x2a, 0xe6, 0xd7, 0xbe, 0xfa, 0x67, 0x3f, 0xbd, 0x2a,
0xfc, 0xe5, 0x4f, 0xaf, 0x0a, 0x7f, 0xf7, 0xd3, 0xab, 0xc2, 0x47, 0xfa, 0xae, 0x13, 0xec, 0xf5,
0xb6, 0x57, 0x5b, 0xee, 0xc1, 0x83, 0x5d, 0xcf, 0x3a, 0x74, 0x58, 0x2d, 0xaf, 0xd5, 0x7e, 0x30,
0xf8, 0xd7, 0x69, 0x5d, 0xe7, 0xc1, 0x2e, 0xee, 0x3c, 0xa0, 0xaf, 0xde, 0x1f, 0xec, 0xba, 0x23,
0xff, 0x8f, 0xed, 0xdd, 0xc8, 0xcf, 0xc3, 0x87, 0xdb, 0x27, 0x28, 0xd8, 0x1b, 0xff, 0x11, 0x00,
0x00, 0xff, 0xff, 0x34, 0x10, 0x18, 0x83, 0xbf, 0x6d, 0x00, 0x00,
}
func (m *UIBannerClickEvent) Marshal() (dAtA []byte, err error) {
@@ -758,6 +758,7 @@ enum IntegrationEnrollField {
INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_GROUPS = 9;
INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_USERS = 10;
INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_RESOURCE_RULES = 11;
INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_CLUSTER_NAME = 12;
}
// UIIntegrationEnrollCodeCopyEvent is emitted when the user copies generated
+6 -2
View File
@@ -935,6 +935,7 @@ const (
IntegrationEnrollField_INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_GROUPS IntegrationEnrollField = 9
IntegrationEnrollField_INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_USERS IntegrationEnrollField = 10
IntegrationEnrollField_INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_RESOURCE_RULES IntegrationEnrollField = 11
IntegrationEnrollField_INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_CLUSTER_NAME IntegrationEnrollField = 12
)
// Enum value maps for IntegrationEnrollField.
@@ -952,6 +953,7 @@ var (
9: "INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_GROUPS",
10: "INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_USERS",
11: "INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_RESOURCE_RULES",
12: "INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_CLUSTER_NAME",
}
IntegrationEnrollField_value = map[string]int32{
"INTEGRATION_ENROLL_FIELD_UNSPECIFIED": 0,
@@ -966,6 +968,7 @@ var (
"INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_GROUPS": 9,
"INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_USERS": 10,
"INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_RESOURCE_RULES": 11,
"INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_CLUSTER_NAME": 12,
}
)
@@ -11384,7 +11387,7 @@ const file_prehog_v1alpha_teleport_proto_rawDesc = "" +
"<INTEGRATION_ENROLL_SECTION_MWIGHAK8S_GITHUB_ADVANCED_OPTIONS\x10\x01\x12@\n" +
"<INTEGRATION_ENROLL_SECTION_MWIGHAK8S_KUBERNETES_LABEL_PICKER\x10\x02\x12D\n" +
"@INTEGRATION_ENROLL_SECTION_MWIGHAK8S_KUBERNETES_ADVANCED_OPTIONS\x10\x03\x12H\n" +
"DINTEGRATION_ENROLL_SECTION_MWIGHAK8S_KUBERNETES_RESOURCE_RULE_PICKER\x10\x04*\xd0\x05\n" +
"DINTEGRATION_ENROLL_SECTION_MWIGHAK8S_KUBERNETES_RESOURCE_RULE_PICKER\x10\x04*\x90\x06\n" +
"\x16IntegrationEnrollField\x12(\n" +
"$INTEGRATION_ENROLL_FIELD_UNSPECIFIED\x10\x00\x12<\n" +
"8INTEGRATION_ENROLL_FIELD_MWIGHAK8S_GITHUB_REPOSITORY_URL\x10\x01\x124\n" +
@@ -11398,7 +11401,8 @@ const file_prehog_v1alpha_teleport_proto_rawDesc = "" +
"4INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_GROUPS\x10\t\x127\n" +
"3INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_USERS\x10\n" +
"\x12@\n" +
"<INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_RESOURCE_RULES\x10\v*\xab\x01\n" +
"<INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_RESOURCE_RULES\x10\v\x12>\n" +
":INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_CLUSTER_NAME\x10\f*\xab\x01\n" +
"\x19IntegrationEnrollCodeType\x12,\n" +
"(INTEGRATION_ENROLL_CODE_TYPE_UNSPECIFIED\x10\x00\x12*\n" +
"&INTEGRATION_ENROLL_CODE_TYPE_TERRAFORM\x10\x01\x124\n" +
+5 -1
View File
@@ -4474,7 +4474,11 @@ export enum IntegrationEnrollField {
/**
* @generated from protobuf enum value: INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_RESOURCE_RULES = 11;
*/
MWIGHAK8S_KUBERNETES_RESOURCE_RULES = 11
MWIGHAK8S_KUBERNETES_RESOURCE_RULES = 11,
/**
* @generated from protobuf enum value: INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_CLUSTER_NAME = 12;
*/
MWIGHAK8S_KUBERNETES_CLUSTER_NAME = 12
}
/**
* IntegrationEnrollCodeType identifies the type of code that was copied in an
+15 -5
View File
@@ -129,18 +129,28 @@ func TestMachineIDWizard(t *testing.T) {
// Cut the provider configuration out of the golden file, as this
// contains values that will change between test runs (e.g. major
// version number and proxy address).
providerConfigIdx := strings.Index(body.Terraform, "terraform {")
beforeProviderConfig := body.Terraform[:providerConfigIdx]
providerConfig := body.Terraform[providerConfigIdx:]
const providerConfigStartLine = 17
const providerConfigEndLine = 29
terraformLines := strings.Split(body.Terraform, "\n")
providerConfig := strings.Join(
terraformLines[providerConfigStartLine-1:providerConfigEndLine],
"\n",
)
remainingLines := append(
terraformLines[:providerConfigStartLine-1],
terraformLines[providerConfigEndLine:]...,
)
remainingConfig := strings.Join(remainingLines, "\n")
if golden.ShouldSet() {
golden.Set(t, []byte(beforeProviderConfig))
golden.Set(t, []byte(remainingConfig))
}
require.Empty(t,
cmp.Diff(
string(golden.Get(t)),
beforeProviderConfig,
remainingConfig,
),
)
@@ -2,10 +2,19 @@
# Actions workflow to enroll with Teleport and connect to allowed Kubernetes
# clusters protected by Teleport.
# To apply this template
# - Log in to your cluster using `tsh login --proxy=teleport.example.com --user=email@example.com`
# - Generate a temporary bot using `eval "$(tctl terraform env)"`
# - Run `terraform init` followed by `terraform plan`
# - To apply the changes, run `terraform apply`
# Note: Consider contributing the resources below to your Infrastructure as
# Code (IaC) repo (if you have one) instead of executing Terraform locally.
# See the Teleport Terraform Provider docs for more information about using
# Terraform to manage Teleport resources:
# https://goteleport.com/docs/zero-trust-access/infrastructure-as-code/terraform-provider/
{{ template "terraform-provider.tf.tmpl" . }}
# A role defines which Kubernetes clusters can be accessed, and the level of
# access granted. Fine tune the access using the `kubernetes_*` attributes.
# All available options can be found in the Teleport Terraform Provider
@@ -18,4 +27,3 @@
# A provision token allows a GitHub Actions workflow to enroll with Teleport and
# assume the identity of the bot defined above and its access.
{{ .TokenConfig }}
{{ template "terraform-provider.tf.tmpl" . }}
@@ -2,6 +2,14 @@
# Actions workflow to enroll with Teleport and connect to allowed Kubernetes
# clusters protected by Teleport.
# To apply this template
# - Log in to your cluster using `tsh login --proxy=teleport.example.com --user=email@example.com`
# - Generate a temporary bot using `eval "$(tctl terraform env)"`
# - Run `terraform init` followed by `terraform plan`
# - To apply the changes, run `terraform apply`
# Note: Consider contributing the resources below to your Infrastructure as
# Code (IaC) repo (if you have one) instead of executing Terraform locally.
# See the Teleport Terraform Provider docs for more information about using
# Terraform to manage Teleport resources:
# https://goteleport.com/docs/zero-trust-access/infrastructure-as-code/terraform-provider/
@@ -2,6 +2,14 @@
# Actions workflow to enroll with Teleport and connect to allowed Kubernetes
# clusters protected by Teleport.
# To apply this template
# - Log in to your cluster using `tsh login --proxy=teleport.example.com --user=email@example.com`
# - Generate a temporary bot using `eval "$(tctl terraform env)"`
# - Run `terraform init` followed by `terraform plan`
# - To apply the changes, run `terraform apply`
# Note: Consider contributing the resources below to your Infrastructure as
# Code (IaC) repo (if you have one) instead of executing Terraform locally.
# See the Teleport Terraform Provider docs for more information about using
# Terraform to manage Teleport resources:
# https://goteleport.com/docs/zero-trust-access/infrastructure-as-code/terraform-provider/
@@ -2,6 +2,14 @@
# Actions workflow to enroll with Teleport and connect to allowed Kubernetes
# clusters protected by Teleport.
# To apply this template
# - Log in to your cluster using `tsh login --proxy=teleport.example.com --user=email@example.com`
# - Generate a temporary bot using `eval "$(tctl terraform env)"`
# - Run `terraform init` followed by `terraform plan`
# - To apply the changes, run `terraform apply`
# Note: Consider contributing the resources below to your Infrastructure as
# Code (IaC) repo (if you have one) instead of executing Terraform locally.
# See the Teleport Terraform Provider docs for more information about using
# Terraform to manage Teleport resources:
# https://goteleport.com/docs/zero-trust-access/infrastructure-as-code/terraform-provider/
@@ -2,6 +2,14 @@
# Actions workflow to enroll with Teleport and connect to allowed Kubernetes
# clusters protected by Teleport.
# To apply this template
# - Log in to your cluster using `tsh login --proxy=teleport.example.com --user=email@example.com`
# - Generate a temporary bot using `eval "$(tctl terraform env)"`
# - Run `terraform init` followed by `terraform plan`
# - To apply the changes, run `terraform apply`
# Note: Consider contributing the resources below to your Infrastructure as
# Code (IaC) repo (if you have one) instead of executing Terraform locally.
# See the Teleport Terraform Provider docs for more information about using
# Terraform to manage Teleport resources:
# https://goteleport.com/docs/zero-trust-access/infrastructure-as-code/terraform-provider/
+1
View File
@@ -1294,6 +1294,7 @@ enum IntegrationEnrollField {
INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_GROUPS = 9;
INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_USERS = 10;
INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_RESOURCE_RULES = 11;
INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_CLUSTER_NAME = 12;
}
// UIIntegrationEnrollCodeCopyEvent is emitted when the user copies generated
@@ -36,6 +36,10 @@ import StyledTextEditor from './StyledTextEditor';
const { UndoManager } = ace.require('ace/undomanager');
class TextEditor extends Component {
handleEditorCopy = () => {
this.props.onCopy?.();
};
onChange = () => {
const isClean = this.editor.session.getUndoManager().isClean();
if (this.props.onDirty) {
@@ -115,6 +119,7 @@ class TextEditor extends Component {
this.editor.renderer.setShowPrintMargin(false);
this.editor.renderer.setShowGutter(true);
this.editor.on('input', this.onChange);
this.editor.on('copy', this.handleEditorCopy);
this.editor.setReadOnly(readOnly);
this.editor.setTheme({ cssClass: 'ace-teleport' });
this.initSessions(data);
@@ -18,6 +18,14 @@
import { useState } from 'react';
import { ButtonSecondary } from 'design/Button';
import Dialog, {
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from 'design/Dialog';
import Text from 'design/Text';
import { Tabs } from 'shared/components/Editor/Tabs';
import TextEditor from 'shared/components/TextEditor/TextEditor';
@@ -29,9 +37,13 @@ import {
import { useTracking } from '../Shared/useTracking';
import { useGitHubK8sFlow } from './useGitHubK8sFlow';
export function CodePanel(props: { trackingStep: IntegrationEnrollStep }) {
const { trackingStep } = props;
export function CodePanel(props: {
trackingStep: IntegrationEnrollStep;
inProgress?: boolean;
}) {
const { trackingStep, inProgress = false } = props;
const [activeCodeTab, setActiveCodeTab] = useState(0);
const [showCopyDialog, setShowCopyDialog] = useState(false);
const { template } = useGitHubK8sFlow();
@@ -58,16 +70,42 @@ export function CodePanel(props: { trackingStep: IntegrationEnrollStep }) {
]}
activeIndex={activeCodeTab}
readOnly={true}
copyButton={true}
downloadButton={true}
copyButton={!inProgress}
downloadButton={!inProgress}
downloadFileName={files[activeCodeTab]}
onCopy={() => {
tracking.codeCopy(trackingStep, trackingTypes[activeCodeTab]);
if (inProgress) {
setShowCopyDialog(true);
}
}}
onDownload={() => {
tracking.codeCopy(trackingStep, trackingTypes[activeCodeTab]);
}}
/>
<Dialog
open={showCopyDialog}
onClose={() => setShowCopyDialog(false)}
dialogCss={() => ({
maxWidth: '480px',
width: '100%',
})}
>
<DialogHeader mb={4}>
<DialogTitle>Incomplete templates</DialogTitle>
</DialogHeader>
<DialogContent mb={4}>
<Text>
The code templates may not be complete yet. Continue to the end of
the guide to ensure you&apos;ve added all the required details.
</Text>
</DialogContent>
<DialogFooter>
<ButtonSecondary onClick={() => setShowCopyDialog(false)}>
Ok
</ButtonSecondary>
</DialogFooter>
</Dialog>
</>
);
}
@@ -29,7 +29,11 @@ import {
genWizardCiCdForever,
genWizardCiCdSuccess,
} from 'teleport/test/helpers/bots';
import { fetchUnifiedResourcesSuccess } from 'teleport/test/helpers/resources';
import {
fetchUnifiedResourcesError,
fetchUnifiedResourcesForever,
fetchUnifiedResourcesSuccess,
} from 'teleport/test/helpers/resources';
import { userEventCaptureSuccess } from 'teleport/test/helpers/userEvents';
import { TrackingProvider } from '../Shared/useTracking';
@@ -52,7 +56,7 @@ export const Happy: Story = {
parameters: {
msw: {
handlers: [
genWizardCiCdSuccess(),
genWizardCiCdSuccess({ prettyFormat: true }),
fetchUnifiedResourcesSuccess(),
userEventCaptureSuccess(),
],
@@ -84,6 +88,30 @@ export const TemplateFetching: Story = {
},
};
export const ClustersFetchFailed: Story = {
parameters: {
msw: {
handlers: [
genWizardCiCdSuccess({ prettyFormat: true }),
fetchUnifiedResourcesError(500, 'something went wrong'),
userEventCaptureSuccess(),
],
},
},
};
export const ClustersFetching: Story = {
parameters: {
msw: {
handlers: [
genWizardCiCdSuccess({ prettyFormat: true }),
fetchUnifiedResourcesForever(),
userEventCaptureSuccess(),
],
},
},
};
const queryClient = new QueryClient({
defaultOptions: {
queries: {
@@ -34,6 +34,10 @@ import {
import { ContextProvider } from 'teleport/index';
import { createTeleportContext } from 'teleport/mocks/contexts';
import {
ResourcesResponse,
UnifiedResource,
} from 'teleport/services/agents/types';
import { genWizardCiCdSuccess } from 'teleport/test/helpers/bots';
import { fetchUnifiedResourcesSuccess } from 'teleport/test/helpers/resources';
import { userEventCaptureSuccess } from 'teleport/test/helpers/userEvents';
@@ -65,6 +69,8 @@ afterAll(() => {
describe('ConfigureAccess', () => {
test('renders', async () => {
withListUnifiedResourcesSuccess();
renderComponent({
initialState: {
allowAnyBranch: false,
@@ -80,6 +86,7 @@ describe('ConfigureAccess', () => {
kubernetesGroups: ['viewers'],
kubernetesLabels: [{ name: '*', values: ['*'] }],
kubernetesUsers: ['user@example.com'],
kubernetesCluster: 'my-kubernetes-cluster',
},
});
@@ -87,13 +94,46 @@ describe('ConfigureAccess', () => {
screen.getByRole('heading', { name: 'Configure Access' })
).toBeInTheDocument();
expect(screen.getByLabelText('Kubernetes Groups')).toBeInTheDocument();
expect(screen.getByLabelText('Labels')).toBeInTheDocument();
expect(screen.getByLabelText('Teleport Labels')).toBeInTheDocument();
expect(screen.getByLabelText('Kubernetes Users')).toBeInTheDocument();
expect(screen.getByLabelText('Kubernetes Groups')).toBeInTheDocument();
});
test('navigation', async () => {
withListUnifiedResourcesSuccess();
const { onNextStep, onPrevStep, user } = renderComponent({
initialState: {
allowAnyBranch: false,
branch: 'main',
enterpriseJwks: '{"keys":[]}',
enterpriseSlug: 'octo-enterprise',
environment: 'production',
gitHubUrl: 'https://github.com/gravitational/teleport',
isBranchDisabled: false,
ref: 'main',
refType: 'branch',
workflow: 'my-workflow',
kubernetesGroups: ['viewers'],
kubernetesLabels: [{ name: '*', values: ['*'] }],
kubernetesUsers: [],
kubernetesCluster: 'my-kubernetes-cluster',
},
});
await user.click(screen.getByRole('button', { name: 'Next' }));
expect(onNextStep).toHaveBeenCalledTimes(1);
expect(onPrevStep).toHaveBeenCalledTimes(0);
await user.click(screen.getByRole('button', { name: 'Back' }));
expect(onNextStep).toHaveBeenCalledTimes(1);
expect(onPrevStep).toHaveBeenCalledTimes(1);
});
test('validates that groups or users are provided', async () => {
withListUnifiedResourcesSuccess();
const { onNextStep, user } = renderComponent({
initialState: {
allowAnyBranch: false,
branch: 'main',
@@ -108,25 +148,29 @@ describe('ConfigureAccess', () => {
kubernetesGroups: [],
kubernetesLabels: [{ name: '*', values: ['*'] }],
kubernetesUsers: [],
kubernetesCluster: 'my-kubernetes-cluster',
},
});
await user.click(screen.getByRole('button', { name: 'Next' }));
expect(onNextStep).toHaveBeenCalledTimes(1);
expect(onPrevStep).toHaveBeenCalledTimes(0);
await user.click(screen.getByRole('button', { name: 'Back' }));
expect(onNextStep).toHaveBeenCalledTimes(1);
expect(onPrevStep).toHaveBeenCalledTimes(1);
expect(onNextStep).not.toHaveBeenCalled();
expect(
screen.getAllByText('A Kubernetes group or user is required')
).toHaveLength(2);
});
test('input groups', async () => {
withListUnifiedResourcesSuccess();
const tracking = trackingTester();
const { user } = renderComponent();
const input = screen.getByLabelText('Kubernetes Groups');
await selectEvent.create(input, 'system:masters');
await selectEvent.create(input, 'system:masters', {
createOptionText: 'Add group "system:masters"',
});
await user.type(input, 'viewers{enter}');
expect(screen.getByText('system:masters')).toBeInTheDocument();
@@ -145,16 +189,20 @@ describe('ConfigureAccess', () => {
});
test('input labels', async () => {
withListUnifiedResourcesSuccess();
const tracking = trackingTester();
const { user } = renderComponent();
const input = screen.getByLabelText('Labels');
const input = screen.getByLabelText('Teleport Labels');
await user.click(within(input).getByRole('button'));
const modal = screen.getByTestId('Modal');
const manualInput = within(modal).getByPlaceholderText('name: value');
await user.type(manualInput, 'foo: bar{enter}');
const manualNameInput = within(modal).getByPlaceholderText('e.g. env');
await user.type(manualNameInput, 'foo');
const manualValueInput = within(modal).getByPlaceholderText('e.g. prod');
await user.type(manualValueInput, 'bar{enter}');
await user.click(within(modal).getByRole('button', { name: 'Done' }));
expect(modal).not.toBeInTheDocument();
@@ -175,12 +223,16 @@ describe('ConfigureAccess', () => {
});
test('input users', async () => {
withListUnifiedResourcesSuccess();
const tracking = trackingTester();
const { user } = renderComponent();
const input = screen.getByLabelText('Kubernetes Users');
await selectEvent.create(input, 'user1@example.com');
await selectEvent.create(input, 'user1@example.com', {
createOptionText: 'Add user "user1@example.com"',
});
await user.type(input, 'user2@example.com{enter}');
expect(screen.getByText('user1@example.com')).toBeInTheDocument();
@@ -241,3 +293,18 @@ function makeWrapper(opts?: {
);
};
}
function withListUnifiedResourcesSuccess(opts?: {
response?: ResourcesResponse<UnifiedResource>;
}) {
server.use(
fetchUnifiedResourcesSuccess({
response: opts?.response
? {
...opts.response,
items: opts.response.agents,
}
: undefined,
})
);
}
@@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { MultiValue } from 'react-select';
import styled from 'styled-components';
import Box from 'design/Box/Box';
@@ -24,6 +25,7 @@ import Flex from 'design/Flex/Flex';
import Link from 'design/Link/Link';
import Text, { H2 } from 'design/Text';
import { FieldSelectCreatable } from 'shared/components/FieldSelect/FieldSelectCreatable';
import { Rule } from 'shared/components/Validation/rules';
import Validator, { Validation } from 'shared/components/Validation/Validation';
import { SectionBox } from 'teleport/Roles/RoleEditor/StandardEditor/sections';
@@ -63,6 +65,18 @@ export function ConfigureAccess(props: FlowStepProps) {
nextStep?.();
};
const kubernetesAccessRule: Rule<
MultiValue<{ label: string; value: string }>
> = () => () => {
const valid =
state.kubernetesGroups.length > 0 || state.kubernetesUsers.length > 0;
return {
valid,
message: valid ? '' : 'A Kubernetes group or user is required',
};
};
return (
<Container>
<FormContainer>
@@ -72,9 +86,9 @@ export function ConfigureAccess(props: FlowStepProps) {
</H2>
<Text mb={3}>
Fine tune the access your workflow needs to perform its steps.
Restrict which clusters can be accessed using labels and what level
of access using the other options.
Choose a cluster to access. Then fine tune the access your workflow
needs. Restrict <em>which</em> clusters can be accessed using labels
and <em>what</em> level of access using groups and users.
</Text>
</Box>
@@ -111,7 +125,7 @@ export function ConfigureAccess(props: FlowStepProps) {
>
Teleport Kubernetes Access Controls
</Link>{' '}
docs for information about using Kubernetes labels.
docs for information about using labels.
</Text>
<FieldSelectCreatable
@@ -119,6 +133,7 @@ export function ConfigureAccess(props: FlowStepProps) {
mt={2}
placeholder="e.g. system:masters"
isMulti
rule={kubernetesAccessRule}
value={state.kubernetesGroups.map(g => ({
label: g,
value: g,
@@ -134,6 +149,13 @@ export function ConfigureAccess(props: FlowStepProps) {
);
}}
createOptionPosition="last"
components={{
// Hide the dropdown indicator and spacing
DropdownIndicator: () => null,
IndicatorSeparator: () => null,
}}
noOptionsMessage={() => 'Type to add a value'}
formatCreateLabel={input => `Add group "${input}"`}
/>
<Text mb={3}>
Add Kubernetes groups created using RoleBinding or
@@ -173,6 +195,7 @@ export function ConfigureAccess(props: FlowStepProps) {
mt={2}
isMulti
placeholder={'e.g. user@example.com'}
rule={kubernetesAccessRule}
value={state.kubernetesUsers.map(g => ({
label: g,
value: g,
@@ -188,6 +211,13 @@ export function ConfigureAccess(props: FlowStepProps) {
);
}}
createOptionPosition="last"
components={{
// Hide the dropdown indicator and spacing
DropdownIndicator: () => null,
IndicatorSeparator: () => null,
}}
noOptionsMessage={() => 'Type to add a value'}
formatCreateLabel={input => `Add user "${input}"`}
/>
<Text mb={3}>
See the{' '}
@@ -216,6 +246,7 @@ export function ConfigureAccess(props: FlowStepProps) {
<CodeContainer>
<CodePanel
trackingStep={IntegrationEnrollStep.MWIGHAK8SConfigureAccess}
inProgress
/>
</CodeContainer>
</Container>
@@ -29,6 +29,7 @@ import {
genWizardCiCdForever,
genWizardCiCdSuccess,
} from 'teleport/test/helpers/bots';
import { fetchUnifiedResourcesSuccess } from 'teleport/test/helpers/resources';
import { userEventCaptureSuccess } from 'teleport/test/helpers/userEvents';
import { TrackingProvider } from '../Shared/useTracking';
@@ -50,7 +51,11 @@ export default meta;
export const Happy: Story = {
parameters: {
msw: {
handlers: [genWizardCiCdSuccess(), userEventCaptureSuccess()],
handlers: [
genWizardCiCdSuccess({ prettyFormat: true }),
fetchUnifiedResourcesSuccess(),
userEventCaptureSuccess(),
],
},
},
};
@@ -60,6 +65,7 @@ export const TemplateFetchFailed: Story = {
msw: {
handlers: [
genWizardCiCdError(500, 'something went wrong'),
fetchUnifiedResourcesSuccess(),
userEventCaptureSuccess(),
],
},
@@ -69,7 +75,11 @@ export const TemplateFetchFailed: Story = {
export const TemplateFetching: Story = {
parameters: {
msw: {
handlers: [genWizardCiCdForever(), userEventCaptureSuccess()],
handlers: [
genWizardCiCdForever(),
fetchUnifiedResourcesSuccess(),
userEventCaptureSuccess(),
],
},
},
};
@@ -35,6 +35,7 @@ import cfg from 'teleport/config';
import { ContextProvider } from 'teleport/index';
import { createTeleportContext } from 'teleport/mocks/contexts';
import { genWizardCiCdSuccess } from 'teleport/test/helpers/bots';
import { fetchUnifiedResourcesSuccess } from 'teleport/test/helpers/resources';
import { userEventCaptureSuccess } from 'teleport/test/helpers/userEvents';
import { trackingTester } from '../Shared/trackingTester';
@@ -49,6 +50,7 @@ beforeAll(() => {
// Basic mock for all tests
server.use(genWizardCiCdSuccess());
server.use(fetchUnifiedResourcesSuccess());
server.use(userEventCaptureSuccess());
jest.useFakeTimers();
@@ -72,12 +74,13 @@ describe('ConnectGitHub', () => {
environment: 'production',
gitHubUrl: 'https://github.com/gravitational/teleport',
isBranchDisabled: false,
ref: 'main',
ref: 'refs/heads/main',
refType: 'branch',
workflow: 'my-workflow',
kubernetesGroups: [],
kubernetesLabels: [{ name: '*', values: ['*'] }],
kubernetesUsers: [],
kubernetesCluster: '',
},
});
@@ -89,9 +92,7 @@ describe('ConnectGitHub', () => {
screen.getByPlaceholderText('https://github.com/gravitational/teleport')
).toHaveValue('https://github.com/gravitational/teleport');
expect(screen.getAllByPlaceholderText('refs/heads/main')[0]).toHaveValue(
'main'
);
expect(screen.getByPlaceholderText('main')).toHaveValue('main');
expect(screen.getByPlaceholderText('my-workflow')).toHaveValue(
'my-workflow'
@@ -99,8 +100,8 @@ describe('ConnectGitHub', () => {
expect(screen.getByPlaceholderText('production')).toHaveValue('production');
expect(screen.getAllByPlaceholderText('refs/heads/main')[1]).toHaveValue(
'main'
expect(screen.getByPlaceholderText('refs/heads/main')).toHaveValue(
'refs/heads/main'
);
expect(screen.getByPlaceholderText('octo-enterprise')).toHaveValue(
@@ -122,12 +123,13 @@ describe('ConnectGitHub', () => {
environment: 'production',
gitHubUrl: 'https://github.com/gravitational/teleport',
isBranchDisabled: false,
ref: 'main',
ref: 'refs/heads/main',
refType: 'branch',
workflow: 'my-workflow',
kubernetesGroups: [],
kubernetesLabels: [{ name: '*', values: ['*'] }],
kubernetesUsers: [],
kubernetesCluster: '',
},
});
@@ -171,7 +173,7 @@ describe('ConnectGitHub', () => {
await user.type(input, 'main');
expect(input).toHaveValue('main');
expect(screen.getByLabelText('Git Ref')).toHaveValue('main');
expect(screen.getByLabelText('Git Ref')).toHaveValue('refs/heads/main');
// Skip start event
tracking.skip();
@@ -75,6 +75,8 @@ export function ConnectGitHub(props: FlowStepProps) {
const refTypeValue: RefTypeOption =
refTypeOptions.find(o => o.value === state.refType) ?? refTypeOptions[0];
const refPlaceholder =
refTypeValue.value === 'tag' ? 'refs/tags/release-v1' : 'refs/heads/main';
const hasGHEHost = !!state.info?.host && state.info.host !== GITHUB_HOST;
@@ -120,7 +122,7 @@ export function ConnectGitHub(props: FlowStepProps) {
}
disabled={state.isBranchDisabled}
label="Branch"
placeholder="refs/heads/main"
placeholder="main"
value={state.branch}
onChange={e => {
dispatch({
@@ -146,10 +148,16 @@ export function ConnectGitHub(props: FlowStepProps) {
}
size="small"
/>
<Text mb={3}>
Specifying a branch is recommended to prevent workflows running
from any branch to access your Teleport resources.
</Text>
{state.allowAnyBranch && (
<Info
mt={3}
mb={3}
alignItems="flex-start"
details="Specifying a branch is recommended to prevent workflows running from unintended branches (such as PR branches) from accessing your Teleport resources."
>
Recommended restriction
</Info>
)}
{/* TODO(nicholasmarais1158): Make SectionBox a component instead of reusing it from Roles */}
<SectionBox
@@ -172,6 +180,10 @@ export function ConnectGitHub(props: FlowStepProps) {
);
}}
>
<Text mb={3}>
Use the options below to further restrict which workflows can
access your Teleport resources.
</Text>
<FieldInput
label="Workflow"
placeholder="my-workflow"
@@ -210,7 +222,7 @@ export function ConnectGitHub(props: FlowStepProps) {
<FieldInput
flex={1}
label={'Git Ref'}
placeholder="refs/heads/main"
placeholder={refPlaceholder}
value={state.ref}
onChange={e => {
dispatch({
@@ -333,6 +345,7 @@ export function ConnectGitHub(props: FlowStepProps) {
<CodeContainer>
<CodePanel
trackingStep={IntegrationEnrollStep.MWIGHAK8SConnectGitHub}
inProgress
/>
</CodeContainer>
</Container>
@@ -0,0 +1,214 @@
/**
* Teleport
* Copyright (C) 2026 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { QueryClientProvider } from '@tanstack/react-query';
import { setupServer } from 'msw/node';
import { PropsWithChildren } from 'react';
import selectEvent from 'react-select-event';
import darkTheme from 'design/theme/themes/darkTheme';
import { ConfiguredThemeProvider } from 'design/ThemeProvider';
import {
act,
render,
screen,
testQueryClient,
userEvent,
} from 'design/utils/testing';
import { ContextProvider } from 'teleport/index';
import { createTeleportContext } from 'teleport/mocks/contexts';
import { genWizardCiCdSuccess } from 'teleport/test/helpers/bots';
import { fetchUnifiedResourcesSuccess } from 'teleport/test/helpers/resources';
import { userEventCaptureSuccess } from 'teleport/test/helpers/userEvents';
import { trackingTester } from '../Shared/trackingTester';
import { TrackingProvider } from '../Shared/useTracking';
import { Finish } from './Finish';
import { GitHubK8sFlowProvider, useGitHubK8sFlow } from './useGitHubK8sFlow';
// Switching the async component for its sync equivalent - the latter is a pain
// to test without getting act() warnings and errors.
jest.mock('shared/components/FieldSelect/FieldSelectCreatable', () => {
const actual = jest.requireActual(
'shared/components/FieldSelect/FieldSelectCreatable'
);
return {
...actual,
FieldSelectCreatableAsync: (props: {
loadOptions?: unknown;
defaultOptions?: unknown;
}) => {
const {
// eslint-disable-next-line unused-imports/no-unused-vars
loadOptions,
// eslint-disable-next-line unused-imports/no-unused-vars
defaultOptions,
...rest
} = props;
return <actual.FieldSelectCreatable {...rest} />;
},
};
});
const server = setupServer();
beforeAll(() => {
server.listen();
// Basic mock for all tests
server.use(genWizardCiCdSuccess());
server.use(fetchUnifiedResourcesSuccess());
server.use(userEventCaptureSuccess());
jest.useFakeTimers();
});
afterAll(() => {
server.close();
jest.useRealTimers();
jest.resetAllMocks();
});
describe('Finish', () => {
test('renders', async () => {
renderComponent({
initialState: {
allowAnyBranch: false,
branch: 'main',
enterpriseJwks: '{"keys":[]}',
enterpriseSlug: 'octo-enterprise',
environment: 'production',
gitHubUrl: 'https://github.com/gravitational/teleport',
isBranchDisabled: false,
ref: 'main',
refType: 'branch',
workflow: 'my-workflow',
kubernetesGroups: ['viewers'],
kubernetesLabels: [{ name: '*', values: ['*'] }],
kubernetesUsers: ['user@example.com'],
kubernetesCluster: 'my-kubernetes-cluster',
},
});
expect(
screen.getByRole('heading', { name: 'Set Up Workflow' })
).toBeInTheDocument();
expect(
screen.getByLabelText('Select a cluster to access')
).toBeInTheDocument();
expect(screen.getByText('To complete the setup')).toBeInTheDocument();
});
test('cluster', async () => {
const tracking = trackingTester();
renderComponent();
const input = screen.getByLabelText('Select a cluster to access');
await selectEvent.create(input, 'test-cluster', {
createOptionText: 'Use cluster "test-cluster"',
});
expect(screen.getByText('test-cluster')).toBeInTheDocument();
// Skip start event
tracking.skip();
// Field tracking is debounced, so move time along to send the event
await act(() => jest.advanceTimersByTimeAsync(1000));
tracking.assertField(
expect.any(String),
'INTEGRATION_ENROLL_STEP_MWIGHAK8S_SETUP_WORKFLOW',
'INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_CLUSTER_NAME'
);
});
test('validates that a cluster is provided', async () => {
const { onNextStep, user } = renderComponent({
initialState: {
allowAnyBranch: false,
branch: 'main',
enterpriseJwks: '{"keys":[]}',
enterpriseSlug: 'octo-enterprise',
environment: 'production',
gitHubUrl: 'https://github.com/gravitational/teleport',
isBranchDisabled: false,
ref: 'main',
refType: 'branch',
workflow: 'my-workflow',
kubernetesGroups: ['viewers'],
kubernetesLabels: [{ name: '*', values: ['*'] }],
kubernetesUsers: [],
kubernetesCluster: '',
},
});
await user.click(screen.getByRole('button', { name: 'Close' }));
expect(onNextStep).not.toHaveBeenCalled();
expect(
screen.getAllByText('A Kubernetes cluster is required')
).toHaveLength(1);
});
});
function renderComponent(opts?: {
initialState?: ReturnType<typeof useGitHubK8sFlow>['state'];
disableTracking?: boolean;
}) {
const user = userEvent.setup({
advanceTimers: t => jest.advanceTimersByTime(t),
});
const onNextStep = jest.fn();
const onPrevStep = jest.fn();
return {
...render(<Finish nextStep={onNextStep} prevStep={onPrevStep} />, {
wrapper: makeWrapper(opts),
}),
user,
onNextStep,
onPrevStep,
};
}
function makeWrapper(opts?: {
initialState?: ReturnType<typeof useGitHubK8sFlow>['state'];
disableTracking?: boolean;
}) {
const { initialState, disableTracking = false } = opts ?? {};
const ctx = createTeleportContext();
return ({ children }: PropsWithChildren) => {
return (
<QueryClientProvider client={testQueryClient}>
<ContextProvider ctx={ctx}>
<ConfiguredThemeProvider theme={darkTheme}>
<TrackingProvider disabled={disableTracking}>
<GitHubK8sFlowProvider intitialState={initialState}>
{children}
</GitHubK8sFlowProvider>
</TrackingProvider>
</ConfiguredThemeProvider>
</ContextProvider>
</QueryClientProvider>
);
};
}
@@ -16,10 +16,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useState } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import { useMemo, useState } from 'react';
import { useHistory } from 'react-router';
import { styled } from 'styled-components';
import { Warning } from 'design/Alert/Alert';
import Box from 'design/Box/Box';
import {
ButtonPrimary,
@@ -33,26 +35,97 @@ import DialogTitle from 'design/Dialog/DialogTitle';
import Flex from 'design/Flex/Flex';
import Link from 'design/Link/Link';
import { H2, P2 } from 'design/Text/Text';
import { FieldSelectCreatableAsync } from 'shared/components/FieldSelect/FieldSelectCreatable';
import { Rule } from 'shared/components/Validation/rules';
import Validator, { Validation } from 'shared/components/Validation/Validation';
import cfg from 'teleport/config';
import { Kube } from 'teleport/services/kube/types';
import {
IntegrationEnrollField,
IntegrationEnrollStatusCode,
IntegrationEnrollStep,
} from 'teleport/services/userEvent';
import useTeleport from 'teleport/useTeleport';
import { FlowStepProps } from '../Shared/GuidedFlow';
import { makeKubernetesAccessChecker } from '../Shared/kubernetes';
import { useTracking } from '../Shared/useTracking';
import { CodePanel } from './CodePanel';
import { useGitHubK8sFlow } from './useGitHubK8sFlow';
export function Finish(props: FlowStepProps) {
const { prevStep } = props;
const [showDoneCheck, setShowDoneCheck] = useState(false);
const { dispatch, state } = useGitHubK8sFlow();
const [showCloseCheck, setShowCloseCheck] = useState(false);
const [kubernetesClusters, setKubernetesClusters] = useState<Kube[]>([]);
const history = useHistory();
const tracking = useTracking();
const handleDone = () => {
const ctx = useTeleport();
const flags = ctx.getFeatureFlags();
const hasKubernetesListPermission = flags.kubernetes;
const queryClient = useQueryClient();
const fetchClusters = async (search: string) => {
if (!hasKubernetesListPermission) {
return [];
}
// Use an imperative query instead of `useQuery` (reactive). Results need to
// be returned from this function.
const result = await queryClient.fetchQuery({
queryKey: [
'list',
'unified_resources',
cfg.proxyCluster,
['kube_cluster'],
search,
],
queryFn: ({ signal }) =>
ctx.resourceService.fetchUnifiedResources(
cfg.proxyCluster,
{
kinds: ['kube_cluster'],
limit: 32,
search,
},
signal
),
staleTime: 30_000,
});
const clusters = result.agents.filter(
(resource): resource is Kube => resource.kind === 'kube_cluster'
);
setKubernetesClusters(clusters);
const options = clusters.map(cluster => ({
label: cluster.name,
value: cluster.name,
}));
return options ?? [];
};
const handleClose = (validator: Validator) => {
if (!validator.validate()) {
tracking.error(
IntegrationEnrollStep.MWIGHAK8SSetupWorkflow,
'validation error'
);
return;
}
setShowCloseCheck(true);
};
const handleConfirmClose = () => {
tracking.step(
IntegrationEnrollStep.MWIGHAK8SSetupWorkflow,
IntegrationEnrollStatusCode.Success
@@ -61,70 +134,139 @@ export function Finish(props: FlowStepProps) {
history.replace(cfg.getBotsRoute());
};
const accessChecker = useMemo(
() => makeKubernetesAccessChecker(state.kubernetesLabels),
[state.kubernetesLabels]
);
const selectedCluster = kubernetesClusters.find(
c => c.name === state.kubernetesCluster
);
const showSelectedClusterAccessWarning = selectedCluster
? !accessChecker.check(selectedCluster.labels)
: false;
return (
<Container>
<Box flex={4}>
<H2 mb={3} mt={3}>
Setup Workflow
</H2>
<P2>
<strong>To complete the setup</strong>;
</P2>
<ul>
<li>Use the Infrastructure as Code templates to create resources</li>
<li>Copy the workflow template and add it to your repository</li>
</ul>
<P2>
See the{' '}
<Link
target="_blank"
href={IAC_LINK}
onClick={() => {
tracking.link(
IntegrationEnrollStep.MWIGHAK8SSetupWorkflow,
IAC_LINK
);
}}
>
Infrastructure as Code
</Link>{' '}
docs for information about setting up and using IaC with Teleport.
</P2>
<P2>
See the{' '}
<Link
target="_blank"
href={TBOT_GHA_LINK}
onClick={() => {
tracking.link(
IntegrationEnrollStep.MWIGHAK8SSetupWorkflow,
TBOT_GHA_LINK
);
}}
>
Deploying tbot on GitHub Actions
</Link>{' '}
docs for information about running tbot in a GitHub Actions workflow.
</P2>
<Flex gap={2} pt={5}>
<ButtonPrimary onClick={() => setShowDoneCheck(true)}>
Done
</ButtonPrimary>
<ButtonSecondary onClick={prevStep}>Back</ButtonSecondary>
</Flex>
</Box>
<CodeContainer>
<CodePanel
trackingStep={IntegrationEnrollStep.MWIGHAK8SSetupWorkflow}
inProgress={!state.kubernetesCluster}
/>
</CodeContainer>
<Dialog open={showDoneCheck} onClose={() => setShowDoneCheck(false)}>
<Validation>
{({ validator }) => (
<Box flex={4}>
<H2 mb={3} mt={3}>
Set Up Workflow
</H2>
<FieldSelectCreatableAsync
label="Select a cluster to access"
mt={2}
isClearable
isSearchable
defaultOptions
loadOptions={fetchClusters}
rule={kubernetesClusterRule}
value={
state.kubernetesCluster
? {
label: state.kubernetesCluster,
value: state.kubernetesCluster,
}
: null
}
onChange={option => {
dispatch({
type: 'kubernetes-cluster-changed',
value: option?.value ?? '',
});
tracking.field(
IntegrationEnrollStep.MWIGHAK8SSetupWorkflow,
IntegrationEnrollField.MWIGHAK8SKubernetesClusterName
);
}}
noOptionsMessage={() => {
return 'Enter a cluster name manually';
}}
formatCreateLabel={input => `Use cluster "${input}"`}
/>
{showSelectedClusterAccessWarning && (
<Warning
alignItems="flex-start"
mt={4}
details={
<>
Based on the labels configured, the workflow will not have
access to the selected cluster. Amend the labels, or select
another cluster.
</>
}
>
Cluster is inaccessible
</Warning>
)}
<P2>
<strong>To complete the setup</strong>;
</P2>
<ol>
<li>
Use the Infrastructure as Code templates to create resources
</li>
<li>Copy the workflow template and add it to your repository</li>
<li>Run the workflow</li>
</ol>
<P2>
See the{' '}
<Link
target="_blank"
href={IAC_LINK}
onClick={() => {
tracking.link(
IntegrationEnrollStep.MWIGHAK8SSetupWorkflow,
IAC_LINK
);
}}
>
Infrastructure as Code
</Link>{' '}
docs for information about setting up and using IaC with Teleport.
</P2>
<P2>
See the{' '}
<Link
target="_blank"
href={TBOT_GHA_LINK}
onClick={() => {
tracking.link(
IntegrationEnrollStep.MWIGHAK8SSetupWorkflow,
TBOT_GHA_LINK
);
}}
>
Deploying tbot on GitHub Actions
</Link>{' '}
docs for information about running tbot in a GitHub Actions
workflow.
</P2>
<Flex gap={2} pt={5}>
<ButtonPrimary onClick={() => handleClose(validator)}>
Close
</ButtonPrimary>
<ButtonSecondary onClick={prevStep}>Back</ButtonSecondary>
</Flex>
</Box>
)}
</Validation>
<Dialog open={showCloseCheck} onClose={() => setShowCloseCheck(false)}>
<DialogHeader mb={4}>
<DialogTitle>
Are you sure you would like to complete the guide?
@@ -137,14 +279,14 @@ export function Finish(props: FlowStepProps) {
</P2>
</DialogContent>
<Flex gap={3}>
<ButtonWarning block size="large" onClick={handleDone}>
<ButtonWarning block size="large" onClick={handleConfirmClose}>
Confirm
</ButtonWarning>
<ButtonSecondary
block
size="large"
autoFocus
onClick={() => setShowDoneCheck(false)}
onClick={() => setShowCloseCheck(false)}
>
Cancel
</ButtonSecondary>
@@ -170,3 +312,12 @@ const IAC_LINK =
'https://goteleport.com/docs/zero-trust-access/infrastructure-as-code/';
const TBOT_GHA_LINK =
'https://goteleport.com/docs/machine-workload-identity/deployment/github-actions/';
const kubernetesClusterRule: Rule<{ label: string; value: string } | null> =
value => () => {
const valid = Boolean(value?.value);
return {
valid,
message: valid ? '' : 'A Kubernetes cluster is required',
};
};
@@ -0,0 +1,102 @@
/**
* Teleport
* Copyright (C) 2026 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Meta, StoryObj } from '@storybook/react-vite';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MemoryRouter } from 'react-router';
import styled from 'styled-components';
import Flex from 'design/Flex/Flex';
import { InfoGuidePanelProvider } from 'shared/components/SlidingSidePanel/InfoGuide';
import { BotFlowType } from 'teleport/Bots/types';
import cfg from 'teleport/config';
import { ContextProvider } from 'teleport/index';
import { ContentMinWidth } from 'teleport/Main/Main';
import { createTeleportContext } from 'teleport/mocks/contexts';
import { genWizardCiCdSuccess } from 'teleport/test/helpers/bots';
import { fetchUnifiedResourcesSuccess } from 'teleport/test/helpers/resources';
import { userEventCaptureSuccess } from 'teleport/test/helpers/userEvents';
import { GitHubActionsK8s } from './GitHubActionsK8s';
const meta = {
title: 'Teleport/Bots/Add/GitHubActions+K8s/GitHubActionsK8s',
component: Wrapper,
beforeEach: () => {
queryClient.clear(); // Prevent cached data sharing between stories
},
} satisfies Meta<typeof Wrapper>;
type Story = StoryObj<typeof meta>;
export default meta;
export const Happy: Story = {
parameters: {
msw: {
handlers: [
genWizardCiCdSuccess({ prettyFormat: true }),
fetchUnifiedResourcesSuccess({
delayMs: 1000,
mockSearch: true,
}),
userEventCaptureSuccess(),
],
},
},
};
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: false,
},
},
});
function Wrapper() {
const ctx = createTeleportContext();
return (
<QueryClientProvider client={queryClient}>
<ContextProvider ctx={ctx}>
<InfoGuidePanelProvider>
<ContentMinWidth>
<MemoryRouter
initialEntries={[
cfg.getBotsNewRoute(BotFlowType.GitHubActionsK8s),
]}
>
<Container>
<GitHubActionsK8s />
</Container>
</MemoryRouter>
</ContentMinWidth>
</InfoGuidePanelProvider>
</ContextProvider>
</QueryClientProvider>
);
}
const Container = styled(Flex)`
flex-direction: column;
height: 820px;
overflow: auto;
`;
@@ -21,6 +21,7 @@ import { createMemoryHistory } from 'history';
import { setupServer } from 'msw/node';
import { PropsWithChildren } from 'react';
import { Router } from 'react-router';
import selectEvent from 'react-select-event';
import darkTheme from 'design/theme/themes/darkTheme';
import { ConfiguredThemeProvider } from 'design/ThemeProvider';
@@ -39,6 +40,7 @@ import { ContextProvider } from 'teleport/index';
import { ContentMinWidth } from 'teleport/Main/Main';
import { createTeleportContext } from 'teleport/mocks/contexts';
import { genWizardCiCdSuccess } from 'teleport/test/helpers/bots';
import { fetchUnifiedResourcesSuccess } from 'teleport/test/helpers/resources';
import { userEventCaptureSuccess } from 'teleport/test/helpers/userEvents';
import { trackingTester } from '../Shared/trackingTester';
@@ -52,6 +54,7 @@ beforeAll(() => {
server.use(genWizardCiCdSuccess());
server.use(userEventCaptureSuccess());
server.use(fetchUnifiedResourcesSuccess());
jest.useFakeTimers();
});
@@ -135,6 +138,9 @@ describe('GitHubActionsK8s', () => {
screen.getByRole('heading', { name: 'Configure Access' })
).toBeInTheDocument();
const input = screen.getByLabelText('Kubernetes Groups');
await user.type(input, 'viewers{enter}');
await user.click(screen.getByRole('button', { name: 'Next' }));
tracking.assertStep(
@@ -144,10 +150,20 @@ describe('GitHubActionsK8s', () => {
);
expect(
screen.getByRole('heading', { name: 'Setup Workflow' })
screen.getByRole('heading', { name: 'Set Up Workflow' })
).toBeInTheDocument();
await user.click(screen.getByRole('button', { name: 'Done' }));
const select = screen.getByLabelText('Select a cluster to access');
await selectEvent.select(select, 'kube-lon-staging-01.example.com');
await act(() => jest.advanceTimersByTimeAsync(1000));
tracking.assertField(
'test-tracking-event-id',
'INTEGRATION_ENROLL_STEP_MWIGHAK8S_SETUP_WORKFLOW',
'INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_CLUSTER_NAME'
);
await user.click(screen.getByRole('button', { name: 'Close' }));
await user.click(screen.getByRole('button', { name: 'Confirm' }));
@@ -42,7 +42,7 @@ const views: View[] = [
component: ConfigureAccess,
},
{
name: 'Setup workflow',
name: 'Set Up Workflow',
component: Finish,
},
];
@@ -271,11 +271,12 @@ describe('KubernetesLabelsSelect', () => {
).toBeInTheDocument();
expect(
within(modal).getByRole('heading', { name: 'Selected Labels (0)' })
within(modal).getByRole('heading', { name: 'Selected Labels (1)' })
).toBeInTheDocument();
expect(
within(selectedSection!).queryByText('env: dev')
).not.toBeInTheDocument();
expect(within(selectedSection!).getByText('*: *')).toBeInTheDocument();
});
test('manual entry', async () => {
@@ -292,16 +293,30 @@ describe('KubernetesLabelsSelect', () => {
.getByRole('heading', { name: 'Selected Labels (0)' })
.closest('div');
const input = within(selectedSection!).getByPlaceholderText('name: value');
await user.type(input, 'foo: bar{enter}');
await user.type(input, 'foo2:bar: baz{enter}'); // colon in name
await user.type(input, 'foo3: bar:baz{enter}'); // colon in value
await user.type(input, 'foo4: bar: baz{enter}'); // colon in value
await user.type(input, 'env: *{enter}');
const nameInput = within(selectedSection!).getByLabelText('Name');
const valueInput = within(selectedSection!).getByLabelText('Value');
await user.type(nameInput, 'foo');
await user.type(valueInput, 'bar{enter}');
await user.type(nameInput, 'foo2:bar'); // colon in name
await user.type(valueInput, 'baz{enter}');
await user.type(nameInput, 'foo3');
await user.type(valueInput, 'bar:baz'); // colon in value
await user.type(nameInput, '{enter}');
await user.type(nameInput, 'foo4');
await user.type(valueInput, 'bar: baz{enter}'); // colon and space in value
await user.type(nameInput, 'env');
await user.type(valueInput, '*{enter}');
await user.type(nameInput, 'region');
// Extra square brackets are required to escape the initial pair
await user.type(input, 'region: ^eu-(west|east)-[[0-9]]+$');
await user.type(valueInput, '^eu-(west|east)-[[0-9]]+$');
await user.click(
within(selectedSection!).getByRole('button', { name: 'Add' })
within(selectedSection!).getByRole('button', { name: 'add label' })
);
expect(
@@ -329,8 +344,10 @@ describe('KubernetesLabelsSelect', () => {
const modal = screen.getByTestId('Modal');
const input = within(modal).getByPlaceholderText('name: value');
await user.type(input, 'foo: bar{enter}');
const nameInput = within(modal).getByLabelText('Name');
const valueInput = within(modal).getByLabelText('Value');
await user.type(nameInput, 'foo');
await user.type(valueInput, 'bar{enter}');
await user.click(within(modal).getByRole('button', { name: 'Done' }));
@@ -358,8 +375,10 @@ describe('KubernetesLabelsSelect', () => {
const modal = screen.getByTestId('Modal');
const input = within(modal).getByPlaceholderText('name: value');
await user.type(input, 'foo:bar{enter}');
const nameInput = within(modal).getByLabelText('Name');
const valueInput = within(modal).getByLabelText('Value');
await user.type(nameInput, 'foo');
await user.type(valueInput, 'bar{enter}');
await user.click(within(modal).getByRole('button', { name: 'Cancel' }));
@@ -430,5 +449,14 @@ function makeWrapper(opts?: {
function withListUnifiedResourcesSuccess(opts?: {
response?: ResourcesResponse<UnifiedResource>;
}) {
server.use(fetchUnifiedResourcesSuccess(opts));
server.use(
fetchUnifiedResourcesSuccess({
response: opts?.response
? {
...opts.response,
items: opts.response.agents,
}
: undefined,
})
);
}
@@ -94,10 +94,10 @@ export function KubernetesLabelsSelect(
return (
<Box {...styles}>
<LabelInput mb={0} aria-label="Labels">
<LabelContent>Labels</LabelContent>
<LabelInput mb={0} aria-label="Teleport Labels">
<LabelContent>Teleport Labels</LabelContent>
<LabelsContainer aria-describedby={'labels-select-helper-text'}>
<LabelsContainer aria-describedby={'labels-select-helper-text'} mt={1}>
{selected.map(l => (
<Label key={l.name} label={formatLabel(l)} style="secondary" />
))}
@@ -153,7 +153,8 @@ function Picker(props: {
const { selected: initial, onChange, onCancel } = props;
const [selected, setSelected] = useState(initial);
const [manualInput, setManualInput] = useState('');
const [manualName, setManualName] = useState('');
const [manualValue, setManualValue] = useState('');
const ctx = useTeleport();
const flags = ctx.getFeatureFlags();
@@ -178,7 +179,13 @@ function Picker(props: {
fetchNextPage,
} = useInfiniteQuery({
enabled: hasListPermission,
queryKey: ['list', 'unified_resources', cfg.proxyCluster, ['kube_cluster']],
queryKey: [
'list',
'unified_resources',
'paged',
cfg.proxyCluster,
['kube_cluster'],
],
queryFn: ({ pageParam, signal }) =>
ctx.resourceService.fetchUnifiedResources(
cfg.proxyCluster,
@@ -197,20 +204,29 @@ function Picker(props: {
const handleAdd = (name: string, value: string) => {
setSelected(prev => {
const index = prev.findIndex(l => l.name === name);
if (index == -1) {
return [...prev, { name, values: [value] }];
// Clear other labels when the wildcard is added
if (isWildcardPair(name, value)) {
return [WILDCARD_LABEL];
}
return prev.map(l => {
if (l.name === name) {
const vs = new Set([...l.values, value]);
return {
...l,
values: [...vs],
};
}
return l;
});
const index = prev.findIndex(l => l.name === name);
let next: KubernetesLabel[];
if (index == -1) {
next = [...prev, { name, values: [value] }];
} else {
next = prev.map(l => {
if (l.name === name) {
const vs = new Set([...l.values, value]);
return {
...l,
values: [...vs],
};
}
return l;
});
}
return stripWildcard(next);
});
};
@@ -219,32 +235,36 @@ function Picker(props: {
return;
}
const [n, ...rest] = manualInput.split(': ');
const name = n.trim();
const value = rest.join(': ').trim();
const name = manualName.trim();
const value = manualValue.trim();
handleAdd(name, value);
validator.reset();
setManualInput('');
setManualName('');
setManualValue('');
};
const handleRemove = (name: string, value?: string) => {
setSelected(prev => {
let next: KubernetesLabel[];
if (value === undefined) {
return prev.filter(l => l.name !== name);
next = prev.filter(l => l.name !== name);
} else {
next = prev.flatMap(l => {
if (l.name === name) {
l.values = l.values.filter(v => v != value);
if (l.values.length < 1) {
return [];
}
}
return [l];
});
}
return prev.flatMap(l => {
if (l.name === name) {
l.values = l.values.filter(v => v != value);
if (l.values.length < 1) {
return [];
}
}
return [l];
});
// Add wildcard if all labels are removed
return next.length === 0 ? [WILDCARD_LABEL] : next;
});
};
@@ -261,7 +281,7 @@ function Picker(props: {
<Dialog open onClose={onCancel}>
<DialogHeader>
<Box width={'100%'}>
<DialogTitle>Kubernetes Labels</DialogTitle>
<DialogTitle>Teleport Labels</DialogTitle>
<Text>Select one or more labels to configure access.</Text>
{!hasListPermission ? (
@@ -362,14 +382,26 @@ function Picker(props: {
flex={1}
m={0}
size="small"
rule={requireValidManualLabel}
label={'Add custom label'}
placeholder="name: value"
value={manualInput}
onChange={e => setManualInput(e.target.value)}
toolTipContent="e.g. env: prod, region: us-*, name: ^kube-(a|b).+$"
rule={requireValidLabelName}
label="Name"
placeholder="e.g. env"
value={manualName}
onChange={e => setManualName(e.target.value)}
/>
<ButtonSecondary type="submit">Add</ButtonSecondary>
<FieldInput
flex={1}
m={0}
size="small"
rule={requireValidLabelValue}
label="Value"
placeholder="e.g. prod"
value={manualValue}
onChange={e => setManualValue(e.target.value)}
toolTipContent="e.g. prod, us-*, ^kube-(a|b).+$"
/>
<ButtonIcon type="submit" aria-label="add label">
<Plus size="small" />
</ButtonIcon>
</ManualInputContainer>
</div>
</form>
@@ -396,6 +428,7 @@ const PickerContainer = styled(Flex)`
`;
const ColumnContainer = styled(Flex)`
min-width: 360px;
flex: 1;
flex-direction: column;
overflow: auto;
@@ -424,16 +457,42 @@ const EmptyText = styled(Text)`
color: ${p => p.theme.colors.text.muted};
`;
function formatLabel(label: KubernetesLabel) {
return `${label.name}: ${label.values.join(' or ')}`;
function formatLabel({ name, values }: KubernetesLabel) {
if (values.length === 1) {
return `${name}: ${values[0]}`;
}
return `${name}: ( ${values.join(' | ')} )`;
}
const manualLabelRegex = /^ *[a-z0-9:*]+ *: .+$/;
export const requireValidManualLabel: Rule = value => () => {
const match = manualLabelRegex.test(value);
const WILDCARD_LABEL_NAME = '*';
const WILDCARD_LABEL_VALUE = '*';
const WILDCARD_LABEL: KubernetesLabel = {
name: WILDCARD_LABEL_NAME,
values: [WILDCARD_LABEL_VALUE],
};
function isWildcardPair(name: string, value: string) {
return name === WILDCARD_LABEL_NAME && value === WILDCARD_LABEL_VALUE;
}
function stripWildcard(labels: KubernetesLabel[]) {
return labels.filter(l => l.name !== WILDCARD_LABEL_NAME);
}
const labelNameRegex = /^([a-z0-9:]+|\*)$/; // 1-n alphanumerics or colon, or a single asterisk
export const requireValidLabelName: Rule = value => () => {
const match = labelNameRegex.test(value.trim());
return {
valid: match,
message: match ? undefined : 'Must be in the format "name: value"',
message: match ? undefined : 'Alphanumeric or * is required',
};
};
export const requireValidLabelValue: Rule = value => () => {
const trimmed = value.trim();
return {
valid: trimmed.length > 0,
message: trimmed.length > 0 ? undefined : 'Value is required',
};
};
@@ -18,6 +18,7 @@
import styled, { useTheme } from 'styled-components';
import { Info } from 'design/Alert/Alert';
import Box from 'design/Box/Box';
import { ButtonPrimary } from 'design/Button/Button';
import Flex from 'design/Flex/Flex';
@@ -33,6 +34,7 @@ import {
import { FlowStepProps } from '../Shared/GuidedFlow';
import { useTracking } from '../Shared/useTracking';
import { CodePanel } from './CodePanel';
import welcomeDark from './gha-k8s-welcome-dark.svg';
import welcomeLight from './gha-k8s-welcome-light.svg';
@@ -58,53 +60,87 @@ export function Welcome(props: FlowStepProps) {
return (
<Container>
<Box>
<H2 mb={3} mt={3}>
GitHub Actions + Kubernetes
</H2>
<FormContainer>
<Box>
<H2 mb={3} mt={3}>
GitHub Actions + Kubernetes
</H2>
<P2>
Use <code>kubectl</code> and other tools from your GitHub Actions
workflows.
</P2>
<P2>
Use <code>kubectl</code> and other tools from your GitHub Actions
workflows.
</P2>
<P2>Youll need:</P2>
<ul>
<li>A GitHub repository</li>
<li>Access rules for your workflow</li>
</ul>
<Image py={4} width={420} src={welcomeImage[theme.type]} />
<P2>
See the{' '}
<Link
target="_blank"
href={IAC_LINK}
onClick={() => {
tracking.link(IntegrationEnrollStep.MWIGHAK8SWelcome, IAC_LINK);
}}
<P2>Youll need:</P2>
<ul>
<li>A GitHub repository</li>
<li>An enrolled Kubernetes cluster</li>
</ul>
<Info
alignItems="flex-start"
mt={4}
details={
<>
This guide uses Infrastructure as Code (IaC) to create the
resources required to complete the setup. See the{' '}
<Link
target="_blank"
href={IAC_LINK}
onClick={() => {
tracking.link(
IntegrationEnrollStep.MWIGHAK8SWelcome,
IAC_LINK
);
}}
>
Infrastructure as Code
</Link>{' '}
docs for information about setting up and using IaC with
Teleport.
</>
}
>
Infrastructure as Code
</Link>{' '}
docs for information about setting up and using IaC with Teleport.
</P2>
Infrastructure as Code required
</Info>
<Flex gap={2} pt={5}>
<ButtonPrimary onClick={handleNext}>Start</ButtonPrimary>
</Flex>
</Box>
<Flex gap={2} pt={1}>
<ButtonPrimary onClick={handleNext}>Start</ButtonPrimary>
</Flex>
</Box>
</FormContainer>
<Image p={5} width={420} src={welcomeImage[theme.type]} />
<CodeContainer>
<CodePanel
trackingStep={IntegrationEnrollStep.MWIGHAK8SWelcome}
inProgress
/>
</CodeContainer>
</Container>
);
}
const Container = styled(Flex)`
align-items: flex-start;
flex: 1;
overflow: auto;
gap: ${({ theme }) => theme.space[1]}px;
`;
const FormContainer = styled(Flex)`
flex: 4;
flex-direction: column;
overflow: auto;
padding-right: ${({ theme }) => theme.space[5]}px;
`;
const CodeContainer = styled(Flex)`
flex: 6;
flex-direction: column;
overflow: auto;
`;
type ImageSpec = {
[K in Theme['type']]: string;
};
@@ -19,11 +19,17 @@
export function makeGhaWorkflow(params: {
tokenName: string;
clusterPublicUrl: string;
clusterName: string;
}) {
return GHA_WORKFLOW.replaceAll(
':token_name',
JSON.stringify(params.tokenName)
).replaceAll(':cluster_public_url', JSON.stringify(params.clusterPublicUrl));
)
.replaceAll(':cluster_public_url', JSON.stringify(params.clusterPublicUrl))
.replaceAll(
':kubernetes_cluster',
JSON.stringify(params.clusterName || 'my-kubernetes-cluster')
);
}
export const GHA_WORKFLOW = `# This file contains a GitHub Actions workflow which enrolls with Teleport in
@@ -34,6 +40,9 @@ export const GHA_WORKFLOW = `# This file contains a GitHub Actions workflow whic
# the events that trigger your workflow, such as when pushing to a named branch
# or triggering it manually.
# Note: the workflow file may need to exist on the "main branch" in your repo
# before it will appear in GitHub Actions.
# For more information about using GitHub Actions read the getting started
# guide; https://docs.github.com/en/actions/get-started/quickstart
@@ -42,9 +51,7 @@ on: workflow_dispatch
env:
TELEPORT_PROXY_ADDR: :cluster_public_url
TELEPORT_JOIN_TOKEN_NAME: :token_name
# TODO: Provide the name of the Kubernetes cluster in Teleport
TELEPORT_K8S_CLUSTER_NAME: my-kubernetes-cluster
TELEPORT_K8S_CLUSTER_NAME: :kubernetes_cluster
jobs:
demo:
@@ -66,7 +66,7 @@ describe('useGitHubK8sFlow', () => {
wrapper: Wrapper,
});
await waitForAPI(result.current);
await waitForGenerateCall(result);
expect(result.current.state).toStrictEqual(withDefaultState({}));
expect(result.current.template.terraform.data).toBe(
@@ -108,7 +108,7 @@ describe('useGitHubK8sFlow', () => {
})
);
await waitForAPI(result.current);
await waitForGenerateCall(result);
expect(result.current.template.terraform.data).toContain(
'"enterprise_server_host":"github.example.com"'
@@ -153,14 +153,14 @@ describe('useGitHubK8sFlow', () => {
expect(result.current.state).toStrictEqual(
withDefaultState({
branch: 'release-*',
ref: 'release-*',
ref: 'refs/heads/release-*',
})
);
await waitForAPI(result.current);
await waitForGenerateCall(result);
expect(result.current.template.terraform.data).toContain(
'"ref":"release-*"'
'"ref":"refs/heads/release-*"'
);
expect(result.current.template.terraform.data).toContain(
'"ref_type":"branch"'
@@ -206,7 +206,7 @@ describe('useGitHubK8sFlow', () => {
})
);
await waitForAPI(result.current);
await waitForGenerateCall(result);
expect(result.current.template.terraform.data).toContain(
'"workflow":"my-workflow"'
@@ -233,7 +233,7 @@ describe('useGitHubK8sFlow', () => {
})
);
await waitForAPI(result.current);
await waitForGenerateCall(result);
expect(result.current.template.terraform.data).toContain(
'"environment":"production"'
@@ -261,7 +261,7 @@ describe('useGitHubK8sFlow', () => {
})
);
await waitForAPI(result.current);
await waitForGenerateCall(result);
expect(result.current.template.terraform.data).toContain(
'"ref":"release-*"'
@@ -296,7 +296,7 @@ describe('useGitHubK8sFlow', () => {
})
);
await waitForAPI(result.current);
await waitForGenerateCall(result);
expect(result.current.template.terraform.data).toContain(
'"ref_type":"tag"'
@@ -323,7 +323,7 @@ describe('useGitHubK8sFlow', () => {
})
);
await waitForAPI(result.current);
await waitForGenerateCall(result);
expect(result.current.template.terraform.data).toContain(
'"enterprise_slug":"octo-enterprise"'
@@ -350,13 +350,40 @@ describe('useGitHubK8sFlow', () => {
})
);
await waitForAPI(result.current);
await waitForGenerateCall(result);
expect(result.current.template.terraform.data).toContain(
'"static_jwks":"{\\"keys\\": []}"'
);
});
test('kubernetes cluster', async () => {
withGenWizardCiCdSuccess();
const { result } = renderHook(() => useGitHubK8sFlow(), {
wrapper: Wrapper,
});
act(() => {
result.current.dispatch({
type: 'kubernetes-cluster-changed',
value: 'my-kubernetes-cluster',
});
});
expect(result.current.state).toStrictEqual(
withDefaultState({
kubernetesCluster: 'my-kubernetes-cluster',
})
);
await waitForGenerateCall(result);
expect(result.current.template.ghaWorkflow).toContain(
'TELEPORT_K8S_CLUSTER_NAME: "my-kubernetes-cluster"'
);
});
test('kubernetes groups', async () => {
withGenWizardCiCdSuccess();
@@ -377,7 +404,7 @@ describe('useGitHubK8sFlow', () => {
})
);
await waitForAPI(result.current);
await waitForGenerateCall(result);
expect(result.current.template.terraform.data).toContain(
'"groups":["system:masters"]'
@@ -409,7 +436,7 @@ describe('useGitHubK8sFlow', () => {
})
);
await waitForAPI(result.current);
await waitForGenerateCall(result);
expect(result.current.template.terraform.data).toContain(
'"labels":{"foo":["bar"]}'
@@ -436,7 +463,7 @@ describe('useGitHubK8sFlow', () => {
})
);
await waitForAPI(result.current);
await waitForGenerateCall(result);
expect(result.current.template.terraform.data).toContain(
'"users":["user1@example.com"]'
@@ -444,9 +471,13 @@ describe('useGitHubK8sFlow', () => {
});
});
async function waitForAPI(context: ReturnType<typeof useGitHubK8sFlow>) {
async function waitForGenerateCall(result: {
current: ReturnType<typeof useGitHubK8sFlow>;
}) {
await act(jest.advanceTimersToNextTimerAsync);
return waitFor(() => expect(context.template.terraform.loading).toBeFalsy());
return waitFor(() => {
expect(result.current.template.terraform.loading).toBeFalsy();
});
}
function withDefaultState(
@@ -471,6 +502,7 @@ function withDefaultState(
},
],
kubernetesUsers: [],
kubernetesCluster: '',
...overrides,
};
}
@@ -152,6 +152,7 @@ export function GitHubK8sFlowProvider(
ghaWorkflow: makeGhaWorkflow({
tokenName: `gha-${state.info?.owner ?? 'gravitational'}-${state.info?.repository ?? 'teleport'}`,
clusterPublicUrl: cluster.publicURL,
clusterName: state.kubernetesCluster,
}),
},
};
@@ -175,11 +176,17 @@ function reducer(prev: State, action: Action): State {
info,
};
case 'branch-changed':
const branch = action.value;
const ref = branch
? branch.startsWith('refs/heads/')
? branch
: `refs/heads/${branch}`
: '';
return {
...prev,
branch: action.value,
branch,
allowAnyBranch: false,
ref: action.value,
ref,
refType: 'branch',
};
case 'allow-any-branch-toggled':
@@ -189,14 +196,19 @@ function reducer(prev: State, action: Action): State {
branch: '',
ref: '',
};
case 'ref-changed':
case 'ref-changed': {
const branch =
prev.refType === 'branch'
? action.value.replace(/^refs\/heads\//, '')
: '';
return {
...prev,
ref: action.value,
// Keep ref in sync with branch while ref type is 'branch'
branch: prev.refType === 'branch' ? action.value : '',
branch: branch,
isBranchDisabled: prev.refType !== 'branch',
};
}
case 'ref-type-changed':
return {
...prev,
@@ -240,6 +252,11 @@ function reducer(prev: State, action: Action): State {
...prev,
kubernetesUsers: action.value,
};
case 'kubernetes-cluster-changed':
return {
...prev,
kubernetesCluster: action.value,
};
default:
const exhaustiveCheck: never = action;
throw new Error(`Unhandled action type: ${exhaustiveCheck}`);
@@ -255,7 +272,8 @@ type Action =
| 'workflow-changed'
| 'environment-changed'
| 'slug-changed'
| 'jwks-changed';
| 'jwks-changed'
| 'kubernetes-cluster-changed';
value: string;
}
| {
@@ -294,6 +312,7 @@ type State = {
kubernetesGroups: string[];
kubernetesLabels: KubernetesLabel[];
kubernetesUsers: string[];
kubernetesCluster: string;
};
const defaultState: State = {
@@ -310,6 +329,7 @@ const defaultState: State = {
kubernetesGroups: [],
kubernetesLabels: [{ name: '*', values: ['*'] }],
kubernetesUsers: [],
kubernetesCluster: '',
};
type Context = {
@@ -164,6 +164,7 @@ export enum IntegrationEnrollField {
MWIGHAK8SKubernetesGroups = 'INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_GROUPS',
MWIGHAK8SKubernetesUsers = 'INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_USERS',
MWIGHAK8SKubernetesResourceRules = 'INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_RESOURCE_RULES',
MWIGHAK8SKubernetesClusterName = 'INTEGRATION_ENROLL_FIELD_MWIGHAK8S_KUBERNETES_CLUSTER_NAME',
}
/**
+18 -10
View File
@@ -179,17 +179,25 @@ export const editBotForever = () =>
export const genWizardCiCdSuccess = (options?: {
response?: Awaited<ReturnType<typeof generateGhaK8sTemplates>>;
prettyFormat?: boolean;
}) => {
return http.post(cfg.api.bot.genWizardCiCd, async ({ request }) => {
const {
response = {
terraform: TERRAFORM_MOCK.replaceAll(
':body',
await request.clone().text()
),
},
} = options ?? {};
return HttpResponse.json(response);
const { response, prettyFormat } = options ?? {};
const body = prettyFormat
? JSON.stringify(await request.clone().json(), undefined, 2)
.split('\n')
.map(l => `# ${l}`)
.join('\n')
: `# ${await request.clone().text()}`;
const terraform = TERRAFORM_MOCK.replaceAll(':body', body);
return HttpResponse.json(
response ?? {
terraform,
}
);
});
};
@@ -215,7 +223,7 @@ export const genWizardCiCdForever = () =>
);
const TERRAFORM_MOCK = `# POST ${cfg.api.bot.genWizardCiCd}
# :body
:body
# This is a mocked Terraform template
resource "teleport_bot" "bot_name" {
@@ -28,7 +28,9 @@ import { JsonObject } from 'teleport/types';
const path = '/v1/webapi/sites/:clusterId/resources';
export const fetchUnifiedResourcesSuccess = (opts?: {
response?: ResourcesResponse<UnifiedResource>;
response?: ResourcesResponse<UnifiedResource> & { items: UnifiedResource[] };
delayMs?: number;
mockSearch?: boolean;
}) => {
const {
response = {
@@ -91,10 +93,28 @@ export const fetchUnifiedResourcesSuccess = (opts?: {
},
],
},
delayMs = 0,
mockSearch = false,
} = opts ?? {};
return http.get(path, () => {
return HttpResponse.json(response);
return http.get(path, async ({ request }) => {
const url = new URL(request.url);
const search = url.searchParams.get('search');
if (delayMs) {
await new Promise(res => setTimeout(res, delayMs));
}
return HttpResponse.json({
...response,
...(mockSearch && search
? {
items: response.items.filter(
a => a.kind != 'kube_cluster' || a.name.includes(search)
),
}
: {}),
});
});
};