From 7b3fe4ce267ee3440dbf03b75a12309736bb4af2 Mon Sep 17 00:00:00 2001 From: Nick Marais Date: Tue, 27 Jan 2026 13:11:15 +0000 Subject: [PATCH] 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) --- .../proto/go/usageevents/v1/usageevents.pb.go | 454 +++++++++--------- .../teleport/usageevents/v1/usageevents.proto | 1 + gen/proto/go/prehog/v1alpha/teleport.pb.go | 8 +- gen/proto/ts/prehog/v1alpha/teleport_pb.ts | 6 +- lib/web/mwi_wizards_test.go | 20 +- .../machine-id-gha-k8s-wizard.tf.tmpl | 10 +- ...github+kubernetes-complex-terraform.golden | 8 + .../github+kubernetes-empty-terraform.golden | 8 + .../github+kubernetes-step1-terraform.golden | 8 + .../github+kubernetes-step2-terraform.golden | 8 + proto/prehog/v1alpha/teleport.proto | 1 + .../components/TextEditor/TextEditor.jsx | 5 + .../Bots/Add/GitHubActionsK8s/CodePanel.tsx | 46 +- .../ConfigureAccess.story.tsx | 32 +- .../GitHubActionsK8s/ConfigureAccess.test.tsx | 91 +++- .../Add/GitHubActionsK8s/ConfigureAccess.tsx | 39 +- .../GitHubActionsK8s/ConnectGitHub.story.tsx | 14 +- .../GitHubActionsK8s/ConnectGitHub.test.tsx | 18 +- .../Add/GitHubActionsK8s/ConnectGitHub.tsx | 25 +- .../Bots/Add/GitHubActionsK8s/Finish.test.tsx | 214 +++++++++ .../src/Bots/Add/GitHubActionsK8s/Finish.tsx | 273 ++++++++--- .../GitHubActionsK8s.story.tsx | 102 ++++ .../GitHubActionsK8s.test.tsx | 20 +- .../Add/GitHubActionsK8s/GitHubActionsK8s.tsx | 2 +- .../KubernetesLabelsSelect.test.tsx | 56 ++- .../KubernetesLabelsSelect.tsx | 155 ++++-- .../src/Bots/Add/GitHubActionsK8s/Welcome.tsx | 98 ++-- .../Bots/Add/GitHubActionsK8s/templates.ts | 15 +- .../useGitHubK8sFlow.test.tsx | 64 ++- .../Add/GitHubActionsK8s/useGitHubK8sFlow.tsx | 30 +- .../teleport/src/services/userEvent/types.ts | 1 + .../teleport/src/test/helpers/bots.ts | 28 +- .../teleport/src/test/helpers/resources.ts | 26 +- 33 files changed, 1419 insertions(+), 467 deletions(-) create mode 100644 web/packages/teleport/src/Bots/Add/GitHubActionsK8s/Finish.test.tsx create mode 100644 web/packages/teleport/src/Bots/Add/GitHubActionsK8s/GitHubActionsK8s.story.tsx diff --git a/api/gen/proto/go/usageevents/v1/usageevents.pb.go b/api/gen/proto/go/usageevents/v1/usageevents.pb.go index 48d71b07f38..baa599d6cb1 100644 --- a/api/gen/proto/go/usageevents/v1/usageevents.pb.go +++ b/api/gen/proto/go/usageevents/v1/usageevents.pb.go @@ -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) { diff --git a/api/proto/teleport/usageevents/v1/usageevents.proto b/api/proto/teleport/usageevents/v1/usageevents.proto index 592b37578e1..3e5f8b3b578 100644 --- a/api/proto/teleport/usageevents/v1/usageevents.proto +++ b/api/proto/teleport/usageevents/v1/usageevents.proto @@ -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 diff --git a/gen/proto/go/prehog/v1alpha/teleport.pb.go b/gen/proto/go/prehog/v1alpha/teleport.pb.go index 0d3067a4d89..cd63a58a209 100644 --- a/gen/proto/go/prehog/v1alpha/teleport.pb.go +++ b/gen/proto/go/prehog/v1alpha/teleport.pb.go @@ -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 = "" + "\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" + diff --git a/gen/proto/ts/prehog/v1alpha/teleport_pb.ts b/gen/proto/ts/prehog/v1alpha/teleport_pb.ts index 9568a6e952a..5d320aa4d07 100644 --- a/gen/proto/ts/prehog/v1alpha/teleport_pb.ts +++ b/gen/proto/ts/prehog/v1alpha/teleport_pb.ts @@ -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 diff --git a/lib/web/mwi_wizards_test.go b/lib/web/mwi_wizards_test.go index f9a6b739e9f..504a32cdb39 100644 --- a/lib/web/mwi_wizards_test.go +++ b/lib/web/mwi_wizards_test.go @@ -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, ), ) diff --git a/lib/web/templates/machine-id-gha-k8s-wizard.tf.tmpl b/lib/web/templates/machine-id-gha-k8s-wizard.tf.tmpl index cb620828016..a9a869e9dbe 100644 --- a/lib/web/templates/machine-id-gha-k8s-wizard.tf.tmpl +++ b/lib/web/templates/machine-id-gha-k8s-wizard.tf.tmpl @@ -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" . }} diff --git a/lib/web/testdata/TestMachineIDWizard/github+kubernetes-complex-terraform.golden b/lib/web/testdata/TestMachineIDWizard/github+kubernetes-complex-terraform.golden index aeb46fc6748..c520ecfed94 100644 --- a/lib/web/testdata/TestMachineIDWizard/github+kubernetes-complex-terraform.golden +++ b/lib/web/testdata/TestMachineIDWizard/github+kubernetes-complex-terraform.golden @@ -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/ diff --git a/lib/web/testdata/TestMachineIDWizard/github+kubernetes-empty-terraform.golden b/lib/web/testdata/TestMachineIDWizard/github+kubernetes-empty-terraform.golden index b8a925fb682..47e4ef4c5bd 100644 --- a/lib/web/testdata/TestMachineIDWizard/github+kubernetes-empty-terraform.golden +++ b/lib/web/testdata/TestMachineIDWizard/github+kubernetes-empty-terraform.golden @@ -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/ diff --git a/lib/web/testdata/TestMachineIDWizard/github+kubernetes-step1-terraform.golden b/lib/web/testdata/TestMachineIDWizard/github+kubernetes-step1-terraform.golden index fe7e82e8928..09f77a41b34 100644 --- a/lib/web/testdata/TestMachineIDWizard/github+kubernetes-step1-terraform.golden +++ b/lib/web/testdata/TestMachineIDWizard/github+kubernetes-step1-terraform.golden @@ -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/ diff --git a/lib/web/testdata/TestMachineIDWizard/github+kubernetes-step2-terraform.golden b/lib/web/testdata/TestMachineIDWizard/github+kubernetes-step2-terraform.golden index b81f5b45270..79072e7a6fd 100644 --- a/lib/web/testdata/TestMachineIDWizard/github+kubernetes-step2-terraform.golden +++ b/lib/web/testdata/TestMachineIDWizard/github+kubernetes-step2-terraform.golden @@ -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/ diff --git a/proto/prehog/v1alpha/teleport.proto b/proto/prehog/v1alpha/teleport.proto index 3155da7c58b..b822552a3de 100644 --- a/proto/prehog/v1alpha/teleport.proto +++ b/proto/prehog/v1alpha/teleport.proto @@ -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 diff --git a/web/packages/shared/components/TextEditor/TextEditor.jsx b/web/packages/shared/components/TextEditor/TextEditor.jsx index 3c08dc430a8..c272022817b 100644 --- a/web/packages/shared/components/TextEditor/TextEditor.jsx +++ b/web/packages/shared/components/TextEditor/TextEditor.jsx @@ -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); diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/CodePanel.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/CodePanel.tsx index 8eb030f59b4..addd113e725 100644 --- a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/CodePanel.tsx +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/CodePanel.tsx @@ -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]); }} /> + setShowCopyDialog(false)} + dialogCss={() => ({ + maxWidth: '480px', + width: '100%', + })} + > + + Incomplete templates + + + + The code templates may not be complete yet. Continue to the end of + the guide to ensure you've added all the required details. + + + + setShowCopyDialog(false)}> + Ok + + + ); } diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConfigureAccess.story.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConfigureAccess.story.tsx index b3cb2866ca7..b3715ae58fb 100644 --- a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConfigureAccess.story.tsx +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConfigureAccess.story.tsx @@ -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: { diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConfigureAccess.test.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConfigureAccess.test.tsx index 6f11dc2ff45..c5691e062cb 100644 --- a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConfigureAccess.test.tsx +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConfigureAccess.test.tsx @@ -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; +}) { + server.use( + fetchUnifiedResourcesSuccess({ + response: opts?.response + ? { + ...opts.response, + items: opts.response.agents, + } + : undefined, + }) + ); +} diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConfigureAccess.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConfigureAccess.tsx index caa16802f7f..fa5d01d01f2 100644 --- a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConfigureAccess.tsx +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConfigureAccess.tsx @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +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 ( @@ -72,9 +86,9 @@ export function ConfigureAccess(props: FlowStepProps) { - 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 which clusters can be accessed using labels + and what level of access using groups and users. @@ -111,7 +125,7 @@ export function ConfigureAccess(props: FlowStepProps) { > Teleport Kubernetes Access Controls {' '} - docs for information about using Kubernetes labels. + docs for information about using labels. ({ 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}"`} /> 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}"`} /> See the{' '} @@ -216,6 +246,7 @@ export function ConfigureAccess(props: FlowStepProps) { diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConnectGitHub.story.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConnectGitHub.story.tsx index ad1f828a17c..8e5ee05941a 100644 --- a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConnectGitHub.story.tsx +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConnectGitHub.story.tsx @@ -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(), + ], }, }, }; diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConnectGitHub.test.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConnectGitHub.test.tsx index 00778e8db87..021f9777020 100644 --- a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConnectGitHub.test.tsx +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConnectGitHub.test.tsx @@ -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(); diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConnectGitHub.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConnectGitHub.tsx index 7c73b6624c7..299b61ade7e 100644 --- a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConnectGitHub.tsx +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/ConnectGitHub.tsx @@ -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" /> - - Specifying a branch is recommended to prevent workflows running - from any branch to access your Teleport resources. - + {state.allowAnyBranch && ( + + Recommended restriction + + )} {/* TODO(nicholasmarais1158): Make SectionBox a component instead of reusing it from Roles */} + + Use the options below to further restrict which workflows can + access your Teleport resources. + { dispatch({ @@ -333,6 +345,7 @@ export function ConnectGitHub(props: FlowStepProps) { diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/Finish.test.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/Finish.test.tsx new file mode 100644 index 00000000000..ee01eb47c4c --- /dev/null +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/Finish.test.tsx @@ -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 . + */ + +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 ; + }, + }; +}); + +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['state']; + disableTracking?: boolean; +}) { + const user = userEvent.setup({ + advanceTimers: t => jest.advanceTimersByTime(t), + }); + const onNextStep = jest.fn(); + const onPrevStep = jest.fn(); + return { + ...render(, { + wrapper: makeWrapper(opts), + }), + user, + onNextStep, + onPrevStep, + }; +} + +function makeWrapper(opts?: { + initialState?: ReturnType['state']; + disableTracking?: boolean; +}) { + const { initialState, disableTracking = false } = opts ?? {}; + const ctx = createTeleportContext(); + + return ({ children }: PropsWithChildren) => { + return ( + + + + + + {children} + + + + + + ); + }; +} diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/Finish.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/Finish.tsx index d62f0016998..5de277283d2 100644 --- a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/Finish.tsx +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/Finish.tsx @@ -16,10 +16,12 @@ * along with this program. If not, see . */ -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([]); 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 ( - -

- Setup Workflow -

- - - To complete the setup; - -
    -
  • Use the Infrastructure as Code templates to create resources
  • -
  • Copy the workflow template and add it to your repository
  • -
- - - See the{' '} - { - tracking.link( - IntegrationEnrollStep.MWIGHAK8SSetupWorkflow, - IAC_LINK - ); - }} - > - Infrastructure as Code - {' '} - docs for information about setting up and using IaC with Teleport. - - - - See the{' '} - { - tracking.link( - IntegrationEnrollStep.MWIGHAK8SSetupWorkflow, - TBOT_GHA_LINK - ); - }} - > - Deploying tbot on GitHub Actions - {' '} - docs for information about running tbot in a GitHub Actions workflow. - - - - setShowDoneCheck(true)}> - Done - - Back - -
- - setShowDoneCheck(false)}> + + {({ validator }) => ( + +

+ Set Up Workflow +

+ + { + 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 && ( + + 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 + + )} + + + To complete the setup; + +
    +
  1. + Use the Infrastructure as Code templates to create resources +
  2. +
  3. Copy the workflow template and add it to your repository
  4. +
  5. Run the workflow
  6. +
+ + + See the{' '} + { + tracking.link( + IntegrationEnrollStep.MWIGHAK8SSetupWorkflow, + IAC_LINK + ); + }} + > + Infrastructure as Code + {' '} + docs for information about setting up and using IaC with Teleport. + + + + See the{' '} + { + tracking.link( + IntegrationEnrollStep.MWIGHAK8SSetupWorkflow, + TBOT_GHA_LINK + ); + }} + > + Deploying tbot on GitHub Actions + {' '} + docs for information about running tbot in a GitHub Actions + workflow. + + + + handleClose(validator)}> + Close + + Back + +
+ )} +
+ + setShowCloseCheck(false)}> Are you sure you would like to complete the guide? @@ -137,14 +279,14 @@ export function Finish(props: FlowStepProps) { - + Confirm setShowDoneCheck(false)} + onClick={() => setShowCloseCheck(false)} > Cancel @@ -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', + }; + }; diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/GitHubActionsK8s.story.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/GitHubActionsK8s.story.tsx new file mode 100644 index 00000000000..8b260e7dff3 --- /dev/null +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/GitHubActionsK8s.story.tsx @@ -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 . + */ + +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; + +type Story = StoryObj; + +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 ( + + + + + + + + + + + + + + ); +} + +const Container = styled(Flex)` + flex-direction: column; + height: 820px; + overflow: auto; +`; diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/GitHubActionsK8s.test.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/GitHubActionsK8s.test.tsx index 6f37c811709..9bfecfa06a6 100644 --- a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/GitHubActionsK8s.test.tsx +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/GitHubActionsK8s.test.tsx @@ -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' })); diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/GitHubActionsK8s.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/GitHubActionsK8s.tsx index e699f2e0904..59170df4803 100644 --- a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/GitHubActionsK8s.tsx +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/GitHubActionsK8s.tsx @@ -42,7 +42,7 @@ const views: View[] = [ component: ConfigureAccess, }, { - name: 'Setup workflow', + name: 'Set Up Workflow', component: Finish, }, ]; diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/KubernetesLabelsSelect.test.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/KubernetesLabelsSelect.test.tsx index a061777fae2..44c1b1fd8df 100644 --- a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/KubernetesLabelsSelect.test.tsx +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/KubernetesLabelsSelect.test.tsx @@ -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; }) { - server.use(fetchUnifiedResourcesSuccess(opts)); + server.use( + fetchUnifiedResourcesSuccess({ + response: opts?.response + ? { + ...opts.response, + items: opts.response.agents, + } + : undefined, + }) + ); } diff --git a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/KubernetesLabelsSelect.tsx b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/KubernetesLabelsSelect.tsx index c4e6863bd87..a7c68478b70 100644 --- a/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/KubernetesLabelsSelect.tsx +++ b/web/packages/teleport/src/Bots/Add/GitHubActionsK8s/KubernetesLabelsSelect.tsx @@ -94,10 +94,10 @@ export function KubernetesLabelsSelect( return ( - - Labels + + Teleport Labels - + {selected.map(l => (