From 2f2958a0f42068552de67370caccb84522267c65 Mon Sep 17 00:00:00 2001 From: Soumya Ghosh Dastidar <44349253+gdsoumya@users.noreply.github.com> Date: Mon, 27 Nov 2023 09:56:35 +0530 Subject: [PATCH 01/66] fix: fixed cli admin dashboard cmd (#16430) * fix: fixed cli admin dashboard cmd Signed-off-by: Soumya Ghosh Dastidar * feat: update docs Signed-off-by: Soumya Ghosh Dastidar --------- Signed-off-by: Soumya Ghosh Dastidar --- cmd/argocd/commands/admin/admin.go | 2 +- cmd/argocd/commands/admin/dashboard.go | 10 +++++++--- cmd/argocd/commands/headless/headless.go | 11 +++++++---- docs/user-guide/commands/argocd_admin_dashboard.md | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cmd/argocd/commands/admin/admin.go b/cmd/argocd/commands/admin/admin.go index 0615166175259..49c81e4da4bfe 100644 --- a/cmd/argocd/commands/admin/admin.go +++ b/cmd/argocd/commands/admin/admin.go @@ -138,7 +138,7 @@ $ argocd admin initial-password reset command.AddCommand(NewRepoCommand()) command.AddCommand(NewImportCommand()) command.AddCommand(NewExportCommand()) - command.AddCommand(NewDashboardCommand()) + command.AddCommand(NewDashboardCommand(clientOpts)) command.AddCommand(NewNotificationsCommand()) command.AddCommand(NewInitialPasswordCommand()) diff --git a/cmd/argocd/commands/admin/dashboard.go b/cmd/argocd/commands/admin/dashboard.go index 33d2866e1f1c0..21b621d264022 100644 --- a/cmd/argocd/commands/admin/dashboard.go +++ b/cmd/argocd/commands/admin/dashboard.go @@ -3,7 +3,9 @@ package admin import ( "fmt" + "github.com/argoproj/argo-cd/v2/util/cli" "github.com/spf13/cobra" + "k8s.io/client-go/tools/clientcmd" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize" @@ -14,11 +16,12 @@ import ( "github.com/argoproj/argo-cd/v2/util/errors" ) -func NewDashboardCommand() *cobra.Command { +func NewDashboardCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( port int address string compressionStr string + clientConfig clientcmd.ClientConfig ) cmd := &cobra.Command{ Use: "dashboard", @@ -28,7 +31,8 @@ func NewDashboardCommand() *cobra.Command { compression, err := cache.CompressionTypeFromString(compressionStr) errors.CheckError(err) - errors.CheckError(headless.MaybeStartLocalServer(ctx, &argocdclient.ClientOptions{Core: true}, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address, compression)) + clientOpts.Core = true + errors.CheckError(headless.MaybeStartLocalServer(ctx, clientOpts, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address, compression, clientConfig)) println(fmt.Sprintf("Argo CD UI is available at http://%s:%d", address, port)) <-ctx.Done() }, @@ -42,7 +46,7 @@ $ argocd admin dashboard --port 8080 --address 127.0.0.1 $ argocd admin dashboard --redis-compress gzip `, } - initialize.InitCommand(cmd) + clientConfig = cli.AddKubectlFlagsToSet(cmd.Flags()) cmd.Flags().IntVar(&port, "port", common.DefaultPortAPIServer, "Listen on given port") cmd.Flags().StringVar(&address, "address", common.DefaultAddressAdminDashboard, "Listen on given address") cmd.Flags().StringVar(&compressionStr, "redis-compress", env.StringFromEnv("REDIS_COMPRESSION", string(cache.RedisCompressionGZip)), "Enable this if the application controller is configured with redis compression enabled. (possible values: gzip, none)") diff --git a/cmd/argocd/commands/headless/headless.go b/cmd/argocd/commands/headless/headless.go index 070d9c9c83bcb..5c9828fc9f131 100644 --- a/cmd/argocd/commands/headless/headless.go +++ b/cmd/argocd/commands/headless/headless.go @@ -153,9 +153,11 @@ func testAPI(ctx context.Context, clientOpts *apiclient.ClientOptions) error { // // If the clientOpts enables core mode, but the local config does not have core mode enabled, this function will // not start the local server. -func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string, compression cache.RedisCompressionType) error { - flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError) - clientConfig := cli.AddKubectlFlagsToSet(flags) +func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string, compression cache.RedisCompressionType, clientConfig clientcmd.ClientConfig) error { + if clientConfig == nil { + flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError) + clientConfig = cli.AddKubectlFlagsToSet(flags) + } startInProcessAPI := clientOpts.Core if !startInProcessAPI { // Core mode is enabled on client options. Check the local config to see if we should start the API server. @@ -244,6 +246,7 @@ func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOpti if !cache2.WaitForCacheSync(ctx.Done(), srv.Initialized) { log.Fatal("Timed out waiting for project cache to sync") } + tries := 5 for i := 0; i < tries; i++ { err = testAPI(ctx, clientOpts) @@ -265,7 +268,7 @@ func NewClientOrDie(opts *apiclient.ClientOptions, c *cobra.Command) apiclient.C ctxStr := initialize.RetrieveContextIfChanged(c.Flag("context")) // If we're in core mode, start the API server on the fly and configure the client `opts` to use it. // If we're not in core mode, this function call will do nothing. - err := MaybeStartLocalServer(ctx, opts, ctxStr, nil, nil, cache.RedisCompressionNone) + err := MaybeStartLocalServer(ctx, opts, ctxStr, nil, nil, cache.RedisCompressionNone, nil) if err != nil { log.Fatal(err) } diff --git a/docs/user-guide/commands/argocd_admin_dashboard.md b/docs/user-guide/commands/argocd_admin_dashboard.md index be4d5c18468ab..71e11a173906a 100644 --- a/docs/user-guide/commands/argocd_admin_dashboard.md +++ b/docs/user-guide/commands/argocd_admin_dashboard.md @@ -44,6 +44,7 @@ $ argocd admin dashboard --redis-compress gzip --proxy-url string If provided, this URL will be used to connect via proxy --redis-compress string Enable this if the application controller is configured with redis compression enabled. (possible values: gzip, none) (default "gzip") --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --server string The address and port of the Kubernetes API server --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use @@ -73,7 +74,6 @@ $ argocd admin dashboard --redis-compress gzip --redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy") --redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis") --repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server") - --server string Argo CD server address --server-crt string Server certificate file --server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server") ``` From 11df9900ffbf8ec19dc844f62cc4cc84900d1cd8 Mon Sep 17 00:00:00 2001 From: Prashant Shahi Date: Mon, 27 Nov 2023 10:45:00 +0530 Subject: [PATCH 02/66] feat(opentelemetry): :sparkles: support for secured OTLP endpoint and headers (#15573) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(opentelemetry): :sparkles: support for secured OTLP endpoint and headers Signed-off-by: Prashant Shahi * docs(opentelemetry): 📝 include new otlp headers in docs Signed-off-by: Prashant Shahi * docs(opentelemetry): 📝 update readme docs as per integration tests Signed-off-by: Prashant Shahi * docs(opentelemetry): 📝 update readme docs as per integration tests Signed-off-by: Prashant Shahi * chore: resolve indentation issues Signed-off-by: Prashant Shahi * chore: fix indentation issues Signed-off-by: Prashant Shahi * chore: include OTLP options in deployment manifests Signed-off-by: Prashant Shahi * fix: update manifests to resolve failing CI Signed-off-by: Prashant Shahi --------- Signed-off-by: Prashant Shahi --- .../commands/argocd_application_controller.go | 6 ++- .../commands/argocd_cmp_server.go | 6 ++- .../commands/argocd_repo_server.go | 6 ++- cmd/argocd-server/commands/argocd_server.go | 6 ++- .../operator-manual/argocd-cmd-params-cm.yaml | 6 ++- .../argocd-application-controller.md | 2 + .../server-commands/argocd-repo-server.md | 2 + .../server-commands/argocd-server.md | 2 + ...ocd-application-controller-deployment.yaml | 12 ++++++ ...cd-application-controller-statefulset.yaml | 12 ++++++ .../argocd-repo-server-deployment.yaml | 12 ++++++ .../base/server/argocd-server-deployment.yaml | 12 ++++++ manifests/core-install.yaml | 24 ++++++++++++ manifests/ha/install.yaml | 36 ++++++++++++++++++ manifests/ha/namespace-install.yaml | 36 ++++++++++++++++++ manifests/install.yaml | 36 ++++++++++++++++++ manifests/namespace-install.yaml | 36 ++++++++++++++++++ util/env/env.go | 27 +++++++++++++ util/env/env_test.go | 38 +++++++++++++++++++ util/trace/trace.go | 16 ++++++-- 20 files changed, 325 insertions(+), 8 deletions(-) diff --git a/cmd/argocd-application-controller/commands/argocd_application_controller.go b/cmd/argocd-application-controller/commands/argocd_application_controller.go index 4f7e587e36564..74d0af45c9d7a 100644 --- a/cmd/argocd-application-controller/commands/argocd_application_controller.go +++ b/cmd/argocd-application-controller/commands/argocd_application_controller.go @@ -66,6 +66,8 @@ func NewCommand() *cobra.Command { repoServerPlaintext bool repoServerStrictTLS bool otlpAddress string + otlpInsecure bool + otlpHeaders map[string]string otlpAttrs []string applicationNamespaces []string persistResourceHealth bool @@ -173,7 +175,7 @@ func NewCommand() *cobra.Command { stats.RegisterHeapDumper("memprofile") if otlpAddress != "" { - closeTracer, err := trace.InitTracer(ctx, "argocd-controller", otlpAddress, otlpAttrs) + closeTracer, err := trace.InitTracer(ctx, "argocd-controller", otlpAddress, otlpInsecure, otlpHeaders, otlpAttrs) if err != nil { log.Fatalf("failed to initialize tracing: %v", err) } @@ -206,6 +208,8 @@ func NewCommand() *cobra.Command { command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS", false), "Whether to use strict validation of the TLS cert presented by the repo server") command.Flags().StringSliceVar(&metricsAplicationLabels, "metrics-application-labels", []string{}, "List of Application labels that will be added to the argocd_application_labels metric") command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to") + command.Flags().BoolVar(&otlpInsecure, "otlp-insecure", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE", true), "OpenTelemetry collector insecure mode") + command.Flags().StringToStringVar(&otlpHeaders, "otlp-headers", env.ParseStringToStringFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS", map[string]string{}, ","), "List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2)") command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_APPLICATION_CONTROLLER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)") command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces that applications are allowed to be reconciled from") command.Flags().BoolVar(&persistResourceHealth, "persist-resource-health", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_PERSIST_RESOURCE_HEALTH", true), "Enables storing the managed resources health in the Application CRD") diff --git a/cmd/argocd-cmp-server/commands/argocd_cmp_server.go b/cmd/argocd-cmp-server/commands/argocd_cmp_server.go index 62f45b24aedb5..526a199cb5490 100644 --- a/cmd/argocd-cmp-server/commands/argocd_cmp_server.go +++ b/cmd/argocd-cmp-server/commands/argocd_cmp_server.go @@ -26,6 +26,8 @@ func NewCommand() *cobra.Command { var ( configFilePath string otlpAddress string + otlpInsecure bool + otlpHeaders map[string]string otlpAttrs []string ) var command = cobra.Command{ @@ -56,7 +58,7 @@ func NewCommand() *cobra.Command { if otlpAddress != "" { var closer func() var err error - closer, err = traceutil.InitTracer(ctx, "argocd-cmp-server", otlpAddress, otlpAttrs) + closer, err = traceutil.InitTracer(ctx, "argocd-cmp-server", otlpAddress, otlpInsecure, otlpHeaders, otlpAttrs) if err != nil { log.Fatalf("failed to initialize tracing: %v", err) } @@ -83,6 +85,8 @@ func NewCommand() *cobra.Command { command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error") command.Flags().StringVar(&configFilePath, "config-dir-path", common.DefaultPluginConfigFilePath, "Config management plugin configuration file location, Default is '/home/argocd/cmp-server/config/'") command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_CMP_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to") + command.Flags().BoolVar(&otlpInsecure, "otlp-insecure", env.ParseBoolFromEnv("ARGOCD_CMP_SERVER_OTLP_INSECURE", true), "OpenTelemetry collector insecure mode") + command.Flags().StringToStringVar(&otlpHeaders, "otlp-headers", env.ParseStringToStringFromEnv("ARGOCD_CMP_SERVER_OTLP_HEADERS", map[string]string{}, ","), "List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2)") command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_CMP_SERVER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)") return &command } diff --git a/cmd/argocd-repo-server/commands/argocd_repo_server.go b/cmd/argocd-repo-server/commands/argocd_repo_server.go index 69358d2a91efd..2a16d192e01bd 100644 --- a/cmd/argocd-repo-server/commands/argocd_repo_server.go +++ b/cmd/argocd-repo-server/commands/argocd_repo_server.go @@ -54,6 +54,8 @@ func NewCommand() *cobra.Command { metricsPort int metricsHost string otlpAddress string + otlpInsecure bool + otlpHeaders map[string]string otlpAttrs []string cacheSrc func() (*reposervercache.Cache, error) tlsConfigCustomizer tls.ConfigCustomizer @@ -129,7 +131,7 @@ func NewCommand() *cobra.Command { if otlpAddress != "" { var closer func() var err error - closer, err = traceutil.InitTracer(ctx, "argocd-repo-server", otlpAddress, otlpAttrs) + closer, err = traceutil.InitTracer(ctx, "argocd-repo-server", otlpAddress, otlpInsecure, otlpHeaders, otlpAttrs) if err != nil { log.Fatalf("failed to initialize tracing: %v", err) } @@ -196,6 +198,8 @@ func NewCommand() *cobra.Command { command.Flags().StringVar(&metricsHost, "metrics-address", env.StringFromEnv("ARGOCD_REPO_SERVER_METRICS_LISTEN_ADDRESS", common.DefaultAddressRepoServerMetrics), "Listen on given address for metrics") command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortRepoServerMetrics, "Start metrics server on given port") command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_REPO_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to") + command.Flags().BoolVar(&otlpInsecure, "otlp-insecure", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_OTLP_INSECURE", true), "OpenTelemetry collector insecure mode") + command.Flags().StringToStringVar(&otlpHeaders, "otlp-headers", env.ParseStringToStringFromEnv("ARGOCD_REPO_OTLP_HEADERS", map[string]string{}, ","), "List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2)") command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_REPO_SERVER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)") command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_TLS", false), "Disable TLS on the gRPC endpoint") command.Flags().StringVar(&maxCombinedDirectoryManifestsSize, "max-combined-directory-manifests-size", env.StringFromEnv("ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE", "10M"), "Max combined size of manifest files in a directory-type Application") diff --git a/cmd/argocd-server/commands/argocd_server.go b/cmd/argocd-server/commands/argocd_server.go index 8709c7de09523..6eeb5b299ce0f 100644 --- a/cmd/argocd-server/commands/argocd_server.go +++ b/cmd/argocd-server/commands/argocd_server.go @@ -50,6 +50,8 @@ func NewCommand() *cobra.Command { metricsHost string metricsPort int otlpAddress string + otlpInsecure bool + otlpHeaders map[string]string otlpAttrs []string glogLevel int clientConfig clientcmd.ClientConfig @@ -200,7 +202,7 @@ func NewCommand() *cobra.Command { var closer func() ctx, cancel := context.WithCancel(ctx) if otlpAddress != "" { - closer, err = traceutil.InitTracer(ctx, "argocd-server", otlpAddress, otlpAttrs) + closer, err = traceutil.InitTracer(ctx, "argocd-server", otlpAddress, otlpInsecure, otlpHeaders, otlpAttrs) if err != nil { log.Fatalf("failed to initialize tracing: %v", err) } @@ -239,6 +241,8 @@ func NewCommand() *cobra.Command { command.Flags().StringVar(&metricsHost, env.StringFromEnv("ARGOCD_SERVER_METRICS_LISTEN_ADDRESS", "metrics-address"), common.DefaultAddressAPIServerMetrics, "Listen for metrics on given address") command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDAPIServerMetrics, "Start metrics on given port") command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to") + command.Flags().BoolVar(&otlpInsecure, "otlp-insecure", env.ParseBoolFromEnv("ARGOCD_SERVER_OTLP_INSECURE", true), "OpenTelemetry collector insecure mode") + command.Flags().StringToStringVar(&otlpHeaders, "otlp-headers", env.ParseStringToStringFromEnv("ARGOCD_SERVER_OTLP_HEADERS", map[string]string{}, ","), "List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2)") command.Flags().StringSliceVar(&otlpAttrs, "otlp-attrs", env.StringsFromEnv("ARGOCD_SERVER_OTLP_ATTRS", []string{}, ","), "List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)") command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_SERVER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.") command.Flags().StringVar(&frameOptions, "x-frame-options", env.StringFromEnv("ARGOCD_SERVER_X_FRAME_OPTIONS", "sameorigin"), "Set X-Frame-Options header in HTTP responses to `value`. To disable, set to \"\".") diff --git a/docs/operator-manual/argocd-cmd-params-cm.yaml b/docs/operator-manual/argocd-cmd-params-cm.yaml index bb55c6fb213f3..695119bf0a27f 100644 --- a/docs/operator-manual/argocd-cmd-params-cm.yaml +++ b/docs/operator-manual/argocd-cmd-params-cm.yaml @@ -17,7 +17,11 @@ data: redis.db: # Open-Telemetry collector address: (e.g. "otel-collector:4317") - otlp.address: + otlp.address: "" + # Open-Telemetry collector insecure: (e.g. "true") + otlp.insecure: "true" + # Open-Telemetry collector headers: (e.g. "key1=value1,key2=value2") + otlp.headers: "" # List of additional namespaces where applications may be created in and # reconciled from. The namespace where Argo CD is installed to will always diff --git a/docs/operator-manual/server-commands/argocd-application-controller.md b/docs/operator-manual/server-commands/argocd-application-controller.md index e03cf7fc51536..434c30621b8bd 100644 --- a/docs/operator-manual/server-commands/argocd-application-controller.md +++ b/docs/operator-manual/server-commands/argocd-application-controller.md @@ -44,6 +44,8 @@ argocd-application-controller [flags] --operation-processors int Number of application operation processors (default 10) --otlp-address string OpenTelemetry collector address to send traces to --otlp-attrs strings List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value) + --otlp-headers stringToString List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2) (default []) + --otlp-insecure OpenTelemetry collector insecure mode (default true) --password string Password for basic authentication to the API server --persist-resource-health Enables storing the managed resources health in the Application CRD (default true) --proxy-url string If provided, this URL will be used to connect via proxy diff --git a/docs/operator-manual/server-commands/argocd-repo-server.md b/docs/operator-manual/server-commands/argocd-repo-server.md index 33ecaf7c76dd4..7be45fe18d26f 100644 --- a/docs/operator-manual/server-commands/argocd-repo-server.md +++ b/docs/operator-manual/server-commands/argocd-repo-server.md @@ -29,6 +29,8 @@ argocd-repo-server [flags] --metrics-port int Start metrics server on given port (default 8084) --otlp-address string OpenTelemetry collector address to send traces to --otlp-attrs strings List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value) + --otlp-headers stringToString List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2) (default []) + --otlp-insecure OpenTelemetry collector insecure mode (default true) --parallelismlimit int Limit on number of concurrent manifests generate requests. Any value less the 1 means no limit. --plugin-tar-exclude stringArray Globs to filter when sending tarballs to plugins. --port int Listen on given port for incoming connections (default 8081) diff --git a/docs/operator-manual/server-commands/argocd-server.md b/docs/operator-manual/server-commands/argocd-server.md index c33cf0bedcbcf..e3dcc937243df 100644 --- a/docs/operator-manual/server-commands/argocd-server.md +++ b/docs/operator-manual/server-commands/argocd-server.md @@ -61,6 +61,8 @@ argocd-server [flags] --oidc-cache-expiration duration Cache expiration for OIDC state (default 3m0s) --otlp-address string OpenTelemetry collector address to send traces to --otlp-attrs strings List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value) + --otlp-headers stringToString List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2) (default []) + --otlp-insecure OpenTelemetry collector insecure mode (default true) --password string Password for basic authentication to the API server --port int Listen on given port (default 8080) --proxy-url string If provided, this URL will be used to connect via proxy diff --git a/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml b/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml index 9b618d96367dc..4862721961f21 100644 --- a/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml +++ b/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml @@ -136,6 +136,18 @@ spec: name: argocd-cmd-params-cm key: otlp.address optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.insecure + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.headers + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: diff --git a/manifests/base/application-controller/argocd-application-controller-statefulset.yaml b/manifests/base/application-controller/argocd-application-controller-statefulset.yaml index 3c576a421a25c..f5e5c759e0750 100644 --- a/manifests/base/application-controller/argocd-application-controller-statefulset.yaml +++ b/manifests/base/application-controller/argocd-application-controller-statefulset.yaml @@ -143,6 +143,18 @@ spec: name: argocd-cmd-params-cm key: otlp.address optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.insecure + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.headers + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: diff --git a/manifests/base/repo-server/argocd-repo-server-deployment.yaml b/manifests/base/repo-server/argocd-repo-server-deployment.yaml index 728c80c14bc2a..907bc80a34e56 100644 --- a/manifests/base/repo-server/argocd-repo-server-deployment.yaml +++ b/manifests/base/repo-server/argocd-repo-server-deployment.yaml @@ -120,6 +120,18 @@ spec: name: argocd-cmd-params-cm key: otlp.address optional: true + - name: ARGOCD_REPO_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.insecure + optional: true + - name: ARGOCD_REPO_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.headers + optional: true - name: ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE valueFrom: configMapKeyRef: diff --git a/manifests/base/server/argocd-server-deployment.yaml b/manifests/base/server/argocd-server-deployment.yaml index b09891d26e529..6df5f9701713f 100644 --- a/manifests/base/server/argocd-server-deployment.yaml +++ b/manifests/base/server/argocd-server-deployment.yaml @@ -215,6 +215,18 @@ spec: name: argocd-cmd-params-cm key: otlp.address optional: true + - name: ARGOCD_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.insecure + optional: true + - name: ARGOCD_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.headers + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: diff --git a/manifests/core-install.yaml b/manifests/core-install.yaml index 2469f171351fe..beda1e8a5103f 100644 --- a/manifests/core-install.yaml +++ b/manifests/core-install.yaml @@ -21262,6 +21262,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE valueFrom: configMapKeyRef: @@ -21587,6 +21599,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: diff --git a/manifests/ha/install.yaml b/manifests/ha/install.yaml index 0f637cd786dc2..50254b138d6ab 100644 --- a/manifests/ha/install.yaml +++ b/manifests/ha/install.yaml @@ -22749,6 +22749,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE valueFrom: configMapKeyRef: @@ -23144,6 +23156,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: @@ -23402,6 +23426,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: diff --git a/manifests/ha/namespace-install.yaml b/manifests/ha/namespace-install.yaml index 33ada40e37004..59aad0e49bda3 100644 --- a/manifests/ha/namespace-install.yaml +++ b/manifests/ha/namespace-install.yaml @@ -2136,6 +2136,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE valueFrom: configMapKeyRef: @@ -2531,6 +2543,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: @@ -2789,6 +2813,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: diff --git a/manifests/install.yaml b/manifests/install.yaml index cb4cda559caf0..4fd267106eaf6 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -21795,6 +21795,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE valueFrom: configMapKeyRef: @@ -22188,6 +22200,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: @@ -22446,6 +22470,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 9e6f98030d89b..fdd4eacd14efb 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -1182,6 +1182,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_REPO_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_REPO_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE valueFrom: configMapKeyRef: @@ -1575,6 +1587,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_SERVER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_SERVER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: @@ -1833,6 +1857,18 @@ spec: key: otlp.address name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE + valueFrom: + configMapKeyRef: + key: otlp.insecure + name: argocd-cmd-params-cm + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS + valueFrom: + configMapKeyRef: + key: otlp.headers + name: argocd-cmd-params-cm + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: configMapKeyRef: diff --git a/util/env/env.go b/util/env/env.go index 221db7b0efaab..985484c1ae80b 100644 --- a/util/env/env.go +++ b/util/env/env.go @@ -186,3 +186,30 @@ func ParseBoolFromEnv(envVar string, defaultValue bool) bool { } return defaultValue } + +// ParseStringToStringVar parses given value from the environment as a map of string. +// Returns default value if envVar is not set. +func ParseStringToStringFromEnv(envVar string, defaultValue map[string]string, seperator string) map[string]string { + str := os.Getenv(envVar) + str = strings.TrimSpace(str) + if str == "" { + return defaultValue + } + + parsed := make(map[string]string) + for _, pair := range strings.Split(str, seperator) { + keyvalue := strings.Split(pair, "=") + if len(keyvalue) != 2 { + log.Warnf("Invalid key-value pair when parsing environment '%s' as a string map", str) + return defaultValue + } + key := strings.TrimSpace(keyvalue[0]) + value := strings.TrimSpace(keyvalue[1]) + if _, ok := parsed[key]; ok { + log.Warnf("Duplicate key '%s' when parsing environment '%s' as a string map", key, str) + return defaultValue + } + parsed[key] = value + } + return parsed +} diff --git a/util/env/env_test.go b/util/env/env_test.go index 691d235805b23..9178592ed3552 100644 --- a/util/env/env_test.go +++ b/util/env/env_test.go @@ -210,3 +210,41 @@ func TestStringsFromEnv(t *testing.T) { }) } } + +func TestParseStringToStringFromEnv(t *testing.T) { + envKey := "SOMEKEY" + def := map[string]string{} + + testCases := []struct { + name string + env string + expected map[string]string + def map[string]string + sep string + }{ + {"success, no key-value", "", map[string]string{}, def, ","}, + {"success, one key, no value", "key1=", map[string]string{"key1": ""}, def, ","}, + {"success, one key, no value, with spaces", "key1 = ", map[string]string{"key1": ""}, def, ","}, + {"success, one pair", "key1=value1", map[string]string{"key1": "value1"}, def, ","}, + {"success, one pair with spaces", "key1 = value1", map[string]string{"key1": "value1"}, def, ","}, + {"success, one pair with spaces and no value", "key1 = ", map[string]string{"key1": ""}, def, ","}, + {"success, two keys, no value", "key1=,key2=", map[string]string{"key1": "", "key2": ""}, def, ","}, + {"success, two keys, no value, with spaces", "key1 = , key2 = ", map[string]string{"key1": "", "key2": ""}, def, ","}, + {"success, two pairs", "key1=value1,key2=value2", map[string]string{"key1": "value1", "key2": "value2"}, def, ","}, + {"success, two pairs with semicolon as seperator", "key1=value1;key2=value2", map[string]string{"key1": "value1", "key2": "value2"}, def, ";"}, + {"success, two pairs with spaces", "key1 = value1, key2 = value2", map[string]string{"key1": "value1", "key2": "value2"}, def, ","}, + {"failure, one key", "key1", map[string]string{}, def, ","}, + {"failure, duplicate keys", "key1=value1,key1=value2", map[string]string{}, def, ","}, + {"failure, one key ending with two successive equals to", "key1==", map[string]string{}, def, ","}, + {"failure, one valid pair and invalid one key", "key1=value1,key2", map[string]string{}, def, ","}, + {"failure, two valid pairs and invalid two keys", "key1=value1,key2=value2,key3,key4", map[string]string{}, def, ","}, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + t.Setenv(envKey, tt.env) + got := ParseStringToStringFromEnv(envKey, tt.def, tt.sep) + assert.Equal(t, tt.expected, got) + }) + } +} diff --git a/util/trace/trace.go b/util/trace/trace.go index 64a0361dfa3d5..9d281bf0d4c76 100644 --- a/util/trace/trace.go +++ b/util/trace/trace.go @@ -13,10 +13,11 @@ import ( "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.6.1" + "google.golang.org/grpc/credentials" ) // InitTracer initializes the trace provider and the otel grpc exporter. -func InitTracer(ctx context.Context, serviceName, otlpAddress string, otlpAttrs []string) (func(), error) { +func InitTracer(ctx context.Context, serviceName, otlpAddress string, otlpInsecure bool, otlpHeaders map[string]string, otlpAttrs []string) (func(), error) { attrs := make([]attribute.KeyValue, 0, len(otlpAttrs)) for i := range otlpAttrs { attr := otlpAttrs[i] @@ -38,10 +39,19 @@ func InitTracer(ctx context.Context, serviceName, otlpAddress string, otlpAttrs return nil, fmt.Errorf("failed to create resource: %w", err) } - // Set up a trace exporter + // set up grpc options based on secure/insecure connection + var secureOption otlptracegrpc.Option + if otlpInsecure { + secureOption = otlptracegrpc.WithInsecure() + } else { + secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, "")) + } + + // set up a trace exporter exporter, err := otlptracegrpc.New(ctx, - otlptracegrpc.WithInsecure(), + secureOption, otlptracegrpc.WithEndpoint(otlpAddress), + otlptracegrpc.WithHeaders(otlpHeaders), ) if err != nil { return nil, fmt.Errorf("failed to create trace exporter: %w", err) From 0cb99808242a9b180015f3593551b6eb75e19ba8 Mon Sep 17 00:00:00 2001 From: Andrew Block Date: Mon, 27 Nov 2023 02:22:30 -0600 Subject: [PATCH 03/66] Renamed/corrected OCI proposal filename (#16452) Signed-off-by: Andrew Block --- docs/proposals/{native-ocp-support.md => native-oci-support.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/proposals/{native-ocp-support.md => native-oci-support.md} (100%) diff --git a/docs/proposals/native-ocp-support.md b/docs/proposals/native-oci-support.md similarity index 100% rename from docs/proposals/native-ocp-support.md rename to docs/proposals/native-oci-support.md From b34e587163164dbc589dc438585dad981839b262 Mon Sep 17 00:00:00 2001 From: shijiadong2022 Date: Tue, 28 Nov 2023 16:30:52 +0800 Subject: [PATCH 04/66] fix(ui): User Info blob is too far to the right relative to Applications/Settings/Documentation (#12016) Signed-off-by: Shi, Stone --- ui/src/app/sidebar/sidebar.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/app/sidebar/sidebar.scss b/ui/src/app/sidebar/sidebar.scss index a3ff7a0355c28..20ab71b969294 100644 --- a/ui/src/app/sidebar/sidebar.scss +++ b/ui/src/app/sidebar/sidebar.scss @@ -81,6 +81,7 @@ $deselected-text: #818d94; margin-left: 2px; margin-right: -2px; margin-top: 12px; + width: 32px; } &--active { From 820f4d861a7789f299143ed89816001091abf923 Mon Sep 17 00:00:00 2001 From: nandinisingh759 <40529546+nandinisingh759@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:36:52 -0800 Subject: [PATCH 05/66] feat(server): log app Spec along with event (#16416) * adding in spec to logappevent params ARGO-1017: Signed-off-by: Nandini * updating logappevent to include spec Signed-off-by: Nandini * updated logevent to take marshal logfields into json Signed-off-by: Nandini * removing spec from parameters just grabbing from app.spec Signed-off-by: Nandini * removing app.spec from test Signed-off-by: Nandini --------- Signed-off-by: Nandini Co-authored-by: Nandini Singh --- util/argo/audit_logger.go | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/util/argo/audit_logger.go b/util/argo/audit_logger.go index 1645e8d7d65d8..104b0dcd6143e 100644 --- a/util/argo/audit_logger.go +++ b/util/argo/audit_logger.go @@ -2,6 +2,7 @@ package argo import ( "context" + "encoding/json" "fmt" "time" @@ -45,7 +46,7 @@ const ( EventReasonOperationCompleted = "OperationCompleted" ) -func (l *AuditLogger) logEvent(objMeta ObjectRef, gvk schema.GroupVersionKind, info EventInfo, message string, logFields map[string]string) { +func (l *AuditLogger) logEvent(objMeta ObjectRef, gvk schema.GroupVersionKind, info EventInfo, message string, logFields map[string]interface{}) { logCtx := log.WithFields(log.Fields{ "type": info.Type, "reason": info.Reason, @@ -53,6 +54,19 @@ func (l *AuditLogger) logEvent(objMeta ObjectRef, gvk schema.GroupVersionKind, i for field, val := range logFields { logCtx = logCtx.WithField(field, val) } + logFieldStrings := make(map[string]string) + for field, val := range logFields { + if valStr, ok := val.(string); ok { + logFieldStrings[field] = valStr + continue + } + vJsonStr, err := json.Marshal(val) + if err != nil { + logCtx.Errorf("Unable to marshal audit event field %v: %v", field, err) + continue + } + logFieldStrings[field] = string(vJsonStr) + } switch gvk.Kind { case application.ApplicationKind: @@ -66,7 +80,7 @@ func (l *AuditLogger) logEvent(objMeta ObjectRef, gvk schema.GroupVersionKind, i event := v1.Event{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%v.%x", objMeta.Name, t.UnixNano()), - Annotations: logFields, + Annotations: logFieldStrings, }, Source: v1.EventSource{ Component: l.component, @@ -101,13 +115,14 @@ func (l *AuditLogger) LogAppEvent(app *v1alpha1.Application, info EventInfo, mes ResourceVersion: app.ObjectMeta.ResourceVersion, UID: app.ObjectMeta.UID, } - fields := map[string]string{ + fields := map[string]interface{}{ "dest-server": app.Spec.Destination.Server, "dest-namespace": app.Spec.Destination.Namespace, } if user != "" { fields["user"] = user } + fields["spec"] = app.Spec l.logEvent(objectMeta, v1alpha1.ApplicationSchemaGroupVersionKind, info, message, fields) } @@ -118,7 +133,7 @@ func (l *AuditLogger) LogAppSetEvent(app *v1alpha1.ApplicationSet, info EventInf ResourceVersion: app.ObjectMeta.ResourceVersion, UID: app.ObjectMeta.UID, } - fields := map[string]string{} + fields := make(map[string]interface{}) if user != "" { fields["user"] = user } @@ -132,7 +147,7 @@ func (l *AuditLogger) LogResourceEvent(res *v1alpha1.ResourceNode, info EventInf ResourceVersion: res.ResourceRef.Version, UID: types.UID(res.ResourceRef.UID), } - fields := map[string]string{} + fields := make(map[string]interface{}) if user != "" { fields["user"] = user } From 0c21ef95983adce4908b958861fc08e088e05740 Mon Sep 17 00:00:00 2001 From: Gilad Salmon Date: Wed, 29 Nov 2023 00:17:49 -0800 Subject: [PATCH 06/66] fix: upgrade notifications-engine (#16354) * fix: upgrade notifications-engine Signed-off-by: Gilad Salmon * update notification-engine version Signed-off-by: pashakostohrys * go mod tidy Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * use correct go version in codeql Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * silly Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * make notifications-docs Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --------- Signed-off-by: Gilad Salmon Signed-off-by: pashakostohrys Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Co-authored-by: pasha-codefresh Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --- .github/workflows/codeql.yml | 7 ++++- .../notifications/services/awssqs.md | 6 ++-- .../notifications/services/github.md | 9 ++++-- .../notifications/services/googlechat.md | 21 ++++++++------ .../notifications/services/slack.md | 14 ++++++---- .../notifications/services/teams.md | 2 +- .../notifications/services/webhook.md | 15 ++++++++-- go.mod | 11 ++++++-- go.sum | 28 +++++++++++++++++-- 9 files changed, 85 insertions(+), 28 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 58426890abcbf..2311d43925bb7 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,10 +27,15 @@ jobs: # CodeQL runs on ubuntu-latest and windows-latest runs-on: ubuntu-22.04 - steps: - name: Checkout repository uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + + # Use correct go version. https://github.com/github/codeql-action/issues/1842#issuecomment-1704398087 + - name: Setup Golang + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0 + with: + go-version-file: go.mod # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/docs/operator-manual/notifications/services/awssqs.md b/docs/operator-manual/notifications/services/awssqs.md index 6bbc47cbbc0b5..6b744f4744b93 100755 --- a/docs/operator-manual/notifications/services/awssqs.md +++ b/docs/operator-manual/notifications/services/awssqs.md @@ -4,10 +4,10 @@ This notification service is capable of sending simple messages to AWS SQS queue. -* `queue` - name of the queue you are intending to send messages to. Can be overwriten with target destination annotation. +* `queue` - name of the queue you are intending to send messages to. Can be overridden with target destination annotation. * `region` - region of the sqs queue can be provided via env variable AWS_DEFAULT_REGION * `key` - optional, aws access key must be either referenced from a secret via variable or via env variable AWS_ACCESS_KEY_ID -* `secret` - optional, aws access secret must be either referenced from a secret via variableor via env variable AWS_SECRET_ACCESS_KEY +* `secret` - optional, aws access secret must be either referenced from a secret via variable or via env variable AWS_SECRET_ACCESS_KEY * `account` optional, external accountId of the queue * `endpointUrl` optional, useful for development with localstack @@ -63,7 +63,7 @@ stringData: ### Minimal configuration using AWS Env variables -Ensure following list of enviromental variable is injected via OIDC, or other method. And assuming SQS is local to the account. +Ensure following list of environment variables are injected via OIDC, or other method. And assuming SQS is local to the account. You may skip usage of secret for sensitive data and omit other parameters. (Setting parameters via ConfigMap takes precedent.) Variables: diff --git a/docs/operator-manual/notifications/services/github.md b/docs/operator-manual/notifications/services/github.md index a3f89f8c87ef0..be76ab150d1a1 100755 --- a/docs/operator-manual/notifications/services/github.md +++ b/docs/operator-manual/notifications/services/github.md @@ -12,7 +12,7 @@ The GitHub notification service changes commit status using [GitHub Apps](https: ## Configuration 1. Create a GitHub Apps using https://github.com/settings/apps/new -2. Change repository permissions to enable write commit statuses and/or deployments +2. Change repository permissions to enable write commit statuses and/or deployments and/or pull requests comments ![2](https://user-images.githubusercontent.com/18019529/108397381-3ca57980-725b-11eb-8d17-5b8992dc009e.png) 3. Generate a private key, and download it automatically ![3](https://user-images.githubusercontent.com/18019529/108397926-d4a36300-725b-11eb-83fe-74795c8c3e03.png) @@ -76,6 +76,10 @@ template.app-deployed: | logURL: "{{.context.argocdUrl}}/applications/{{.app.metadata.name}}?operation=true" requiredContexts: [] autoMerge: true + pullRequestComment: + content: | + Application {{.app.metadata.name}} is now running new version of deployments manifests. + See more here: {{.context.argocdUrl}}/applications/{{.app.metadata.name}}?operation=true ``` **Notes**: @@ -83,4 +87,5 @@ template.app-deployed: | - If `github.repoURLPath` and `github.revisionPath` are same as above, they can be omitted. - Automerge is optional and `true` by default for github deployments to ensure the requested ref is up to date with the default branch. Setting this option to `false` is required if you would like to deploy older refs in your default branch. - For more information see the [Github Deployment API Docs](https://docs.github.com/en/rest/deployments/deployments?apiVersion=2022-11-28#create-a-deployment). + For more information see the [GitHub Deployment API Docs](https://docs.github.com/en/rest/deployments/deployments?apiVersion=2022-11-28#create-a-deployment). +- If `github.pullRequestComment.content` is set to 65536 characters or more, it will be truncated. diff --git a/docs/operator-manual/notifications/services/googlechat.md b/docs/operator-manual/notifications/services/googlechat.md index 041ea6e022ef5..885ce685a4511 100755 --- a/docs/operator-manual/notifications/services/googlechat.md +++ b/docs/operator-manual/notifications/services/googlechat.md @@ -59,24 +59,27 @@ A card message can be defined as follows: ```yaml template.app-sync-succeeded: | googlechat: - cards: | + cardsV2: | - header: title: ArgoCD Bot Notification sections: - widgets: - - textParagraph: + - decoratedText: text: The app {{ .app.metadata.name }} has successfully synced! - widgets: - - keyValue: + - decoratedText: topLabel: Repository - content: {{ call .repo.RepoURLToHTTPS .app.spec.source.repoURL }} - - keyValue: + text: {{ call .repo.RepoURLToHTTPS .app.spec.source.repoURL }} + - decoratedText: topLabel: Revision - content: {{ .app.spec.source.targetRevision }} - - keyValue: + text: {{ .app.spec.source.targetRevision }} + - decoratedText: topLabel: Author - content: {{ (call .repo.GetCommitMetadata .app.status.sync.revision).Author }} + text: {{ (call .repo.GetCommitMetadata .app.status.sync.revision).Author }} ``` +All [Card fields](https://developers.google.com/chat/api/reference/rest/v1/cards#Card_1) are supported and can be used +in notifications. It is also possible to use the previous (now deprecated) `cards` key to use the legacy card fields, +but this is not recommended as Google has deprecated this field and recommends using the newer `cardsV2`. The card message can be written in JSON too. @@ -86,7 +89,7 @@ It is possible send both simple text and card messages in a chat thread by speci ```yaml template.app-sync-succeeded: | - message: The app {{ .app.metadata.name }} has succesfully synced! + message: The app {{ .app.metadata.name }} has successfully synced! googlechat: threadKey: {{ .app.metadata.name }} ``` diff --git a/docs/operator-manual/notifications/services/slack.md b/docs/operator-manual/notifications/services/slack.md index 15937597c19f2..0f3fdf1739210 100755 --- a/docs/operator-manual/notifications/services/slack.md +++ b/docs/operator-manual/notifications/services/slack.md @@ -6,11 +6,15 @@ If you want to send message using incoming webhook, you can use [webhook](./webh The Slack notification service configuration includes following settings: -* `token` - the app token -* `apiURL` - optional, the server url, e.g. https://example.com/api -* `username` - optional, the app username -* `icon` - optional, the app icon, e.g. :robot_face: or https://example.com/image.png -* `insecureSkipVerify` - optional bool, true or false +| **Option** | **Required** | **Type** | **Description** | **Example** | +| -------------------- | ------------ | -------------- | --------------- | ----------- | +| `apiURL` | False | `string` | The server URL. | `https://example.com/api` | +| `channels` | False | `list[string]` | | `["my-channel-1", "my-channel-2"]` | +| `icon` | False | `string` | The app icon. | `:robot_face:` or `https://example.com/image.png` | +| `insecureSkipVerify` | False | `bool` | | `true` | +| `signingSecret` | False | `string` | | `8f742231b10e8888abcd99yyyzzz85a5` | +| `token` | **True** | `string` | The app's OAuth access token. | `xoxb-1234567890-1234567890123-5n38u5ed63fgzqlvuyxvxcx6` | +| `username` | False | `string` | The app username. | `argocd` | ## Configuration diff --git a/docs/operator-manual/notifications/services/teams.md b/docs/operator-manual/notifications/services/teams.md index b5b9a228c43eb..8b8c6b819c795 100755 --- a/docs/operator-manual/notifications/services/teams.md +++ b/docs/operator-manual/notifications/services/teams.md @@ -113,7 +113,7 @@ template.app-sync-succeeded: | ### summary field -You can set a summary of the message that will be shown on Notifcation & Activity Feed +You can set a summary of the message that will be shown on Notification & Activity Feed ![](https://user-images.githubusercontent.com/6957724/116587921-84c4d480-a94d-11eb-9da4-f365151a12e7.jpg) diff --git a/docs/operator-manual/notifications/services/webhook.md b/docs/operator-manual/notifications/services/webhook.md index bd45b1f69e40b..965098402236f 100755 --- a/docs/operator-manual/notifications/services/webhook.md +++ b/docs/operator-manual/notifications/services/webhook.md @@ -1,7 +1,7 @@ # Webhook The webhook notification service allows sending a generic HTTP request using the templatized request body and URL. -Using Webhook you might trigger a Jenkins job, update Github commit status. +Using Webhook you might trigger a Jenkins job, update GitHub commit status. ## Parameters @@ -9,8 +9,17 @@ The Webhook notification service configuration includes following settings: - `url` - the url to send the webhook to - `headers` - optional, the headers to pass along with the webhook -- `basicAuth` - optional, the basic authentication to pass along with the webook +- `basicAuth` - optional, the basic authentication to pass along with the webhook - `insecureSkipVerify` - optional bool, true or false +- `retryWaitMin` - Optional, the minimum wait time between retries. Default value: 1s. +- `retryWaitMax` - Optional, the maximum wait time between retries. Default value: 5s. +- `retryMax` - Optional, the maximum number of retries. Default value: 3. + +## Retry Behavior + +The webhook service will automatically retry the request if it fails due to network errors or if the server returns a 5xx status code. The number of retries and the wait time between retries can be configured using the `retryMax`, `retryWaitMin`, and `retryWaitMax` parameters. + +The wait time between retries is between `retryWaitMin` and `retryWaitMax`. If all retries fail, the `Send` method will return an error. ## Configuration @@ -67,7 +76,7 @@ metadata: ## Examples -### Set Github commit status +### Set GitHub commit status ```yaml apiVersion: v1 diff --git a/go.mod b/go.mod index 3180996ab5f99..8b91fe20eef1b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/argoproj/argo-cd/v2 -go 1.19 +go 1.21 + +toolchain go1.21.0 require ( code.gitea.io/sdk/gitea v0.15.1 @@ -12,7 +14,7 @@ require ( github.com/alicebob/miniredis/v2 v2.30.4 github.com/antonmedv/expr v1.15.2 github.com/argoproj/gitops-engine v0.7.1-0.20231102154024-c0c2dd1f6f48 - github.com/argoproj/notifications-engine v0.4.1-0.20230905144632-9dcecdc3eebf + github.com/argoproj/notifications-engine v0.4.1-0.20231027194313-a8d185ecc0a9 github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1 github.com/aws/aws-sdk-go v1.44.317 github.com/bmatcuk/doublestar/v4 v4.6.0 @@ -126,11 +128,16 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.18.0 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/google/s2a-go v0.1.4 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/tidwall/gjson v1.14.4 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect + go.opencensus.io v0.24.0 // indirect + google.golang.org/api v0.132.0 // indirect google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect gopkg.in/retry.v1 v1.0.3 // indirect diff --git a/go.sum b/go.sum index bb7c2ca8627f6..dbe7107f59cba 100644 --- a/go.sum +++ b/go.sum @@ -686,6 +686,7 @@ github.com/alicebob/miniredis/v2 v2.30.4 h1:8S4/o1/KoUArAGbGwPxcwf0krlzceva2XVOS github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antonmedv/expr v1.15.2 h1:afFXpDWIC2n3bF+kTZE1JvFo+c34uaM3sTqh8z0xfdU= github.com/antonmedv/expr v1.15.2/go.mod h1:0E/6TxnOlRNp81GMzX9QfDPAmHo2Phg00y4JUv1ihsE= @@ -697,8 +698,8 @@ github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2 github.com/appscode/go v0.0.0-20191119085241-0887d8ec2ecc/go.mod h1:OawnOmAL4ZX3YaPdN+8HTNwBveT1jMsqP74moa9XUbE= github.com/argoproj/gitops-engine v0.7.1-0.20231102154024-c0c2dd1f6f48 h1:vnXMrNkBFC0H0KBkH1Jno31OVgJQR4KSd0ypEcfzQRA= github.com/argoproj/gitops-engine v0.7.1-0.20231102154024-c0c2dd1f6f48/go.mod h1:1TchqKw9XmYYZluyEHa1dTJQoZgbV6PhabB/e8Wf3KY= -github.com/argoproj/notifications-engine v0.4.1-0.20230905144632-9dcecdc3eebf h1:4wliaBwd6iKvT/5huDTJntaYtTSdwPLs00SOQwDSK6A= -github.com/argoproj/notifications-engine v0.4.1-0.20230905144632-9dcecdc3eebf/go.mod h1:TuK0BNKo34DIUOyCCGOB9ij+smGCxeCgt9ZB+0fMWno= +github.com/argoproj/notifications-engine v0.4.1-0.20231027194313-a8d185ecc0a9 h1:1lt0VXzmLK7Vv0kaeal3S6/JIfzPyBORkUWXhiqF3l0= +github.com/argoproj/notifications-engine v0.4.1-0.20231027194313-a8d185ecc0a9/go.mod h1:E/vv4+by868m0mmflaRfGBmKBtAupoF+mmyfekP8QCk= github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1 h1:qsHwwOJ21K2Ao0xPju1sNuqphyMnMYkyB3ZLoLtxWpo= github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1/go.mod h1:CZHlkyAD1/+FbEn6cB2DQTj48IoLGvEYsWEvtzP3238= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -761,7 +762,9 @@ github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl github.com/bradleyfalzon/ghinstallation/v2 v2.6.0 h1:IRY7Xy588KylkoycsUhFpW7cdGpy5Y5BPsz4IfuJtGk= github.com/bradleyfalzon/ghinstallation/v2 v2.6.0/go.mod h1:oQ3etOwN3TRH4EwgW5/7MxSVMGlMlzG/O8TU7eYdoSk= github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= +github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= @@ -805,6 +808,7 @@ github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= @@ -833,6 +837,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= +github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= @@ -846,6 +851,7 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= +github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= @@ -868,6 +874,7 @@ github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= @@ -892,6 +899,7 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -907,6 +915,7 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= @@ -919,6 +928,7 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmS github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -947,6 +957,7 @@ github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= @@ -1149,6 +1160,7 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM= github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -1163,6 +1175,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= +github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -1177,6 +1191,8 @@ github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38 github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw= github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 h1:4EZlYQIiyecYJlUbVkFXCXHz1QPhVXcHnQKAzBTPfQo= @@ -1330,6 +1346,7 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -1580,6 +1597,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= @@ -1727,6 +1745,7 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ= @@ -1756,6 +1775,7 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= @@ -1765,6 +1785,7 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -2324,6 +2345,8 @@ google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjY google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.132.0 h1:8t2/+qZ26kAOGSmOiHwVycqVaDg7q3JDILrNi/Z6rvc= +google.golang.org/api v0.132.0/go.mod h1:AeTBC6GpJnJSRJjktDcPX0QwtS8pGYZOV6MSuSCusw0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -2583,6 +2606,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 23f2767250d055e9eb8855ec5e60dd76c3734d30 Mon Sep 17 00:00:00 2001 From: Yudi A Phanama <11147376+phanama@users.noreply.github.com> Date: Wed, 29 Nov 2023 19:26:40 +0700 Subject: [PATCH 07/66] fix(cli): pass redis compression to cluster stats and shards commands (#16060) (#16421) --- cmd/argocd/commands/admin/cluster.go | 46 +++++++++++++++++++--------- util/cache/cache.go | 7 ++++- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/cmd/argocd/commands/admin/cluster.go b/cmd/argocd/commands/admin/cluster.go index a72aaebc201a0..6625787de7d71 100644 --- a/cmd/argocd/commands/admin/cluster.go +++ b/cmd/argocd/commands/admin/cluster.go @@ -69,7 +69,7 @@ type ClusterWithInfo struct { Namespaces []string } -func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClient *versioned.Clientset, replicas int, namespace string, portForwardRedis bool, cacheSrc func() (*appstatecache.Cache, error), shard int, redisName string, redisHaProxyName string) ([]ClusterWithInfo, error) { +func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClient *versioned.Clientset, replicas int, namespace string, portForwardRedis bool, cacheSrc func() (*appstatecache.Cache, error), shard int, redisName string, redisHaProxyName string, redisCompressionStr string) ([]ClusterWithInfo, error) { settingsMgr := settings.NewSettingsManager(ctx, kubeClient, namespace) argoDB := db.NewDB(namespace, settingsMgr, kubeClient) @@ -88,7 +88,11 @@ func loadClusters(ctx context.Context, kubeClient *kubernetes.Clientset, appClie return nil, err } client := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", port)}) - cache = appstatecache.NewCache(cacheutil.NewCache(cacheutil.NewRedisCache(client, time.Hour, cacheutil.RedisCompressionNone)), time.Hour) + compressionType, err := cacheutil.CompressionTypeFromString(redisCompressionStr) + if err != nil { + return nil, err + } + cache = appstatecache.NewCache(cacheutil.NewCache(cacheutil.NewRedisCache(client, time.Hour, compressionType)), time.Hour) } else { cache, err = cacheSrc() if err != nil { @@ -161,11 +165,12 @@ func getControllerReplicas(ctx context.Context, kubeClient *kubernetes.Clientset func NewClusterShardsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - shard int - replicas int - clientConfig clientcmd.ClientConfig - cacheSrc func() (*appstatecache.Cache, error) - portForwardRedis bool + shard int + replicas int + clientConfig clientcmd.ClientConfig + cacheSrc func() (*appstatecache.Cache, error) + portForwardRedis bool + redisCompressionStr string ) var command = cobra.Command{ Use: "shards", @@ -190,7 +195,7 @@ func NewClusterShardsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm return } - clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard, clientOpts.RedisName, clientOpts.RedisHaProxyName) + clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard, clientOpts.RedisName, clientOpts.RedisHaProxyName, redisCompressionStr) errors.CheckError(err) if len(clusters) == 0 { return @@ -204,6 +209,12 @@ func NewClusterShardsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm command.Flags().IntVar(&replicas, "replicas", 0, "Application controller replicas count. Inferred from number of running controller pods if not specified") command.Flags().BoolVar(&portForwardRedis, "port-forward-redis", true, "Automatically port-forward ha proxy redis from current namespace?") cacheSrc = appstatecache.AddCacheFlagsToCmd(&command) + + // parse all added flags so far to get the redis-compression flag that was added by AddCacheFlagsToCmd() above + // we can ignore unchecked error here as the command will be parsed again and checked when command.Execute() is run later + // nolint:errcheck + command.ParseFlags(os.Args[1:]) + redisCompressionStr, _ = command.Flags().GetString(cacheutil.CLIFlagRedisCompress) return &command } @@ -439,11 +450,12 @@ func NewClusterDisableNamespacedMode() *cobra.Command { func NewClusterStatsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - shard int - replicas int - clientConfig clientcmd.ClientConfig - cacheSrc func() (*appstatecache.Cache, error) - portForwardRedis bool + shard int + replicas int + clientConfig clientcmd.ClientConfig + cacheSrc func() (*appstatecache.Cache, error) + portForwardRedis bool + redisCompressionStr string ) var command = cobra.Command{ Use: "stats", @@ -464,7 +476,7 @@ func NewClusterStatsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma replicas, err = getControllerReplicas(ctx, kubeClient, namespace, clientOpts.AppControllerName) errors.CheckError(err) } - clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard, clientOpts.RedisName, clientOpts.RedisHaProxyName) + clusters, err := loadClusters(ctx, kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard, clientOpts.RedisName, clientOpts.RedisHaProxyName, redisCompressionStr) errors.CheckError(err) w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) @@ -480,6 +492,12 @@ func NewClusterStatsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma command.Flags().IntVar(&replicas, "replicas", 0, "Application controller replicas count. Inferred from number of running controller pods if not specified") command.Flags().BoolVar(&portForwardRedis, "port-forward-redis", true, "Automatically port-forward ha proxy redis from current namespace?") cacheSrc = appstatecache.AddCacheFlagsToCmd(&command) + + // parse all added flags so far to get the redis-compression flag that was added by AddCacheFlagsToCmd() above + // we can ignore unchecked error here as the command will be parsed again and checked when command.Execute() is run later + // nolint:errcheck + command.ParseFlags(os.Args[1:]) + redisCompressionStr, _ = command.Flags().GetString(cacheutil.CLIFlagRedisCompress) return &command } diff --git a/util/cache/cache.go b/util/cache/cache.go index fdea46cdea0d2..c9cb8c3b8607a 100644 --- a/util/cache/cache.go +++ b/util/cache/cache.go @@ -29,6 +29,11 @@ const ( defaultRedisRetryCount = 3 ) +const ( + // CLIFlagRedisCompress is a cli flag name to define the redis compression setting for data sent to redis + CLIFlagRedisCompress = "redis-compress" +) + func NewCache(client CacheClient) *Cache { return &Cache{client} } @@ -96,7 +101,7 @@ func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...func(client *redis.Client)) cmd.Flags().StringVar(&redisClientKey, "redis-client-key", "", "Path to Redis client key (e.g. /etc/certs/redis/client.crt).") cmd.Flags().BoolVar(&insecureRedis, "redis-insecure-skip-tls-verify", false, "Skip Redis server certificate validation.") cmd.Flags().StringVar(&redisCACertificate, "redis-ca-certificate", "", "Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation.") - cmd.Flags().StringVar(&compressionStr, "redis-compress", env.StringFromEnv("REDIS_COMPRESSION", string(RedisCompressionGZip)), "Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none)") + cmd.Flags().StringVar(&compressionStr, CLIFlagRedisCompress, env.StringFromEnv("REDIS_COMPRESSION", string(RedisCompressionGZip)), "Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none)") return func() (*Cache, error) { var tlsConfig *tls.Config = nil if redisUseTLS { From 4875b02b881cff3c2dc4b095792489aad1e38bbf Mon Sep 17 00:00:00 2001 From: Leonardo Luz Almeida Date: Wed, 29 Nov 2023 11:08:29 -0500 Subject: [PATCH 08/66] fix(controller): Address diff cache miss issues (#16458) * fix: Address diff cache miss issues Signed-off-by: Leonardo Luz Almeida * validate mergo.Merge errors Signed-off-by: Leonardo Luz Almeida * Address review comments Signed-off-by: Leonardo Luz Almeida * Allow setting log level at the controller Signed-off-by: Leonardo Luz Almeida * remove unnecessary log setup Signed-off-by: Leonardo Luz Almeida --------- Signed-off-by: Leonardo Luz Almeida --- controller/state.go | 57 +++++- controller/state_test.go | 252 +++++++++++++++++++++++++ pkg/apis/application/v1alpha1/types.go | 29 +++ 3 files changed, 329 insertions(+), 9 deletions(-) diff --git a/controller/state.go b/controller/state.go index 15da3d2e624ed..59e7fa31248ae 100644 --- a/controller/state.go +++ b/controller/state.go @@ -116,6 +116,10 @@ type appStateManager struct { repoErrorGracePeriod time.Duration } +// getRepoObjs will generate the manifests for the given application delegating the +// task to the repo-server. It returns the list of generated manifests as unstructured +// objects. It also returns the full response from all calls to the repo server as the +// second argument. func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, error) { ts := stats.NewTimingStats() helmRepos, err := m.db.ListHelmRepositories(context.Background()) @@ -580,21 +584,16 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 manifestRevisions = append(manifestRevisions, manifestInfo.Revision) } - // restore comparison using cached diff result if previous comparison was performed for the same revision - revisionChanged := len(manifestInfos) != len(sources) || !reflect.DeepEqual(app.Status.Sync.Revisions, manifestRevisions) - specChanged := !reflect.DeepEqual(app.Status.Sync.ComparedTo, v1alpha1.ComparedTo{Source: app.Spec.GetSource(), Destination: app.Spec.Destination, Sources: sources, IgnoreDifferences: app.Spec.IgnoreDifferences}) - - _, refreshRequested := app.IsRefreshRequested() - noCache = noCache || refreshRequested || app.Status.Expired(m.statusRefreshTimeout) || specChanged || revisionChanged + useDiffCache := useDiffCache(noCache, manifestInfos, sources, app, manifestRevisions, m.statusRefreshTimeout, logCtx) diffConfigBuilder := argodiff.NewDiffConfigBuilder(). WithDiffSettings(app.Spec.IgnoreDifferences, resourceOverrides, compareOptions.IgnoreAggregatedRoles). WithTracking(appLabelKey, string(trackingMethod)) - if noCache { - diffConfigBuilder.WithNoCache() + if useDiffCache { + diffConfigBuilder.WithCache(m.cache, app.InstanceName(m.namespace)) } else { - diffConfigBuilder.WithCache(m.cache, app.GetName()) + diffConfigBuilder.WithNoCache() } gvkParser, err := m.getGVKParser(app.Spec.Destination.Server) @@ -801,6 +800,46 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 return &compRes, nil } +// useDiffCache will determine if the diff should be calculated based +// on the existing live state cache or not. +func useDiffCache(noCache bool, manifestInfos []*apiclient.ManifestResponse, sources []v1alpha1.ApplicationSource, app *v1alpha1.Application, manifestRevisions []string, statusRefreshTimeout time.Duration, log *log.Entry) bool { + + if noCache { + log.WithField("useDiffCache", "false").Debug("noCache is true") + return false + } + _, refreshRequested := app.IsRefreshRequested() + if refreshRequested { + log.WithField("useDiffCache", "false").Debug("refreshRequested") + return false + } + if app.Status.Expired(statusRefreshTimeout) { + log.WithField("useDiffCache", "false").Debug("app.status.expired") + return false + } + + if len(manifestInfos) != len(sources) { + log.WithField("useDiffCache", "false").Debug("manifestInfos len != sources len") + return false + } + + revisionChanged := !reflect.DeepEqual(app.Status.GetRevisions(), manifestRevisions) + if revisionChanged { + log.WithField("useDiffCache", "false").Debug("revisionChanged") + return false + } + + currentSpec := app.BuildComparedToStatus() + specChanged := !reflect.DeepEqual(app.Status.Sync.ComparedTo, currentSpec) + if specChanged { + log.WithField("useDiffCache", "false").Debug("specChanged") + return false + } + + log.WithField("useDiffCache", "true").Debug("using diff cache") + return true +} + func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, revisions []string, sources []v1alpha1.ApplicationSource, hasMultipleSources bool, startedAt metav1.Time) error { var nextID int64 if len(app.Status.History) > 0 { diff --git a/controller/state_test.go b/controller/state_test.go index 6eb336cff2ffb..a240b30d688df 100644 --- a/controller/state_test.go +++ b/controller/state_test.go @@ -11,6 +11,9 @@ import ( synccommon "github.com/argoproj/gitops-engine/pkg/sync/common" "github.com/argoproj/gitops-engine/pkg/utils/kube" . "github.com/argoproj/gitops-engine/pkg/utils/testing" + "github.com/imdario/mergo" + "github.com/sirupsen/logrus" + logrustest "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/assert" v1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -1393,3 +1396,252 @@ func TestIsLiveResourceManaged(t *testing.T) { assert.True(t, manager.isSelfReferencedObj(managedWrongAPIGroup, config, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation)) }) } + +func TestUseDiffCache(t *testing.T) { + type fixture struct { + testName string + noCache bool + manifestInfos []*apiclient.ManifestResponse + sources []argoappv1.ApplicationSource + app *argoappv1.Application + manifestRevisions []string + statusRefreshTimeout time.Duration + expectedUseCache bool + } + + manifestInfos := func(revision string) []*apiclient.ManifestResponse { + return []*apiclient.ManifestResponse{ + { + Manifests: []string{ + "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"labels\":{\"app.kubernetes.io/instance\":\"httpbin\"},\"name\":\"httpbin-svc\",\"namespace\":\"httpbin\"},\"spec\":{\"ports\":[{\"name\":\"http-port\",\"port\":7777,\"targetPort\":80},{\"name\":\"test\",\"port\":333}],\"selector\":{\"app\":\"httpbin\"}}}", + "{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"labels\":{\"app.kubernetes.io/instance\":\"httpbin\"},\"name\":\"httpbin-deployment\",\"namespace\":\"httpbin\"},\"spec\":{\"replicas\":2,\"selector\":{\"matchLabels\":{\"app\":\"httpbin\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"httpbin\"}},\"spec\":{\"containers\":[{\"image\":\"kennethreitz/httpbin\",\"imagePullPolicy\":\"Always\",\"name\":\"httpbin\",\"ports\":[{\"containerPort\":80}]}]}}}}", + }, + Namespace: "", + Server: "", + Revision: revision, + SourceType: "Kustomize", + VerifyResult: "", + }, + } + } + sources := func() []argoappv1.ApplicationSource { + return []argoappv1.ApplicationSource{ + { + RepoURL: "https://some-repo.com", + Path: "argocd/httpbin", + TargetRevision: "HEAD", + }, + } + } + + app := func(namespace string, revision string, refresh bool, a *argoappv1.Application) *argoappv1.Application { + app := &argoappv1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "httpbin", + Namespace: namespace, + }, + Spec: argoappv1.ApplicationSpec{ + Source: &argoappv1.ApplicationSource{ + RepoURL: "https://some-repo.com", + Path: "argocd/httpbin", + TargetRevision: "HEAD", + }, + Destination: argoappv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "httpbin", + }, + Project: "default", + SyncPolicy: &argoappv1.SyncPolicy{ + SyncOptions: []string{ + "CreateNamespace=true", + "ServerSideApply=true", + }, + }, + }, + Status: argoappv1.ApplicationStatus{ + Resources: []argoappv1.ResourceStatus{}, + Sync: argoappv1.SyncStatus{ + Status: argoappv1.SyncStatusCodeSynced, + ComparedTo: argoappv1.ComparedTo{ + Source: argoappv1.ApplicationSource{ + RepoURL: "https://some-repo.com", + Path: "argocd/httpbin", + TargetRevision: "HEAD", + }, + Destination: argoappv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "httpbin", + }, + }, + Revision: revision, + Revisions: []string{}, + }, + ReconciledAt: &metav1.Time{ + Time: time.Now().Add(-time.Hour), + }, + }, + } + if refresh { + annotations := make(map[string]string) + annotations[argoappv1.AnnotationKeyRefresh] = string(argoappv1.RefreshTypeNormal) + app.SetAnnotations(annotations) + } + if a != nil { + err := mergo.Merge(app, a, mergo.WithOverride, mergo.WithOverwriteWithEmptyValue) + if err != nil { + t.Fatalf("error merging app: %s", err) + } + } + return app + } + + cases := []fixture{ + { + testName: "will use diff cache", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, nil), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: true, + }, + { + testName: "will use diff cache for multisource", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "", false, &argoappv1.Application{ + Spec: argoappv1.ApplicationSpec{ + Source: nil, + Sources: argoappv1.ApplicationSources{ + { + RepoURL: "multisource repo1", + }, + { + RepoURL: "multisource repo2", + }, + }, + }, + Status: argoappv1.ApplicationStatus{ + Resources: []argoappv1.ResourceStatus{}, + Sync: argoappv1.SyncStatus{ + Status: argoappv1.SyncStatusCodeSynced, + ComparedTo: argoappv1.ComparedTo{ + Source: argoappv1.ApplicationSource{}, + Sources: argoappv1.ApplicationSources{ + { + RepoURL: "multisource repo1", + }, + { + RepoURL: "multisource repo2", + }, + }, + }, + Revisions: []string{"rev1", "rev2"}, + }, + ReconciledAt: &metav1.Time{ + Time: time.Now().Add(-time.Hour), + }, + }, + }), + manifestRevisions: []string{"rev1", "rev2"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: true, + }, + { + testName: "will return false if nocache is true", + noCache: true, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, nil), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: false, + }, + { + testName: "will return false if requested refresh", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", true, nil), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: false, + }, + { + testName: "will return false if status expired", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, nil), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Minute, + expectedUseCache: false, + }, + { + testName: "will return false if there is a new revision", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, nil), + manifestRevisions: []string{"rev2"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: false, + }, + { + testName: "will return false if app spec repo changed", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, &argoappv1.Application{ + Spec: argoappv1.ApplicationSpec{ + Source: &argoappv1.ApplicationSource{ + RepoURL: "new-repo", + }, + }, + }), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: false, + }, + { + testName: "will return false if app spec IgnoreDifferences changed", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, &argoappv1.Application{ + Spec: argoappv1.ApplicationSpec{ + IgnoreDifferences: []argoappv1.ResourceIgnoreDifferences{ + { + Group: "app/v1", + Kind: "application", + Name: "httpbin", + Namespace: "httpbin", + JQPathExpressions: []string{"."}, + }, + }, + }, + }), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Hour * 24, + expectedUseCache: false, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + // Given + t.Parallel() + logger, _ := logrustest.NewNullLogger() + log := logrus.NewEntry(logger) + + // When + useDiffCache := useDiffCache(tc.noCache, tc.manifestInfos, tc.sources, tc.app, tc.manifestRevisions, tc.statusRefreshTimeout, log) + + // Then + assert.Equal(t, useDiffCache, tc.expectedUseCache) + }) + } +} diff --git a/pkg/apis/application/v1alpha1/types.go b/pkg/apis/application/v1alpha1/types.go index 1957ec59a341e..dc6a73f05dee5 100644 --- a/pkg/apis/application/v1alpha1/types.go +++ b/pkg/apis/application/v1alpha1/types.go @@ -955,6 +955,35 @@ type ApplicationStatus struct { ControllerNamespace string `json:"controllerNamespace,omitempty" protobuf:"bytes,13,opt,name=controllerNamespace"` } +// GetRevisions will return the current revision associated with the Application. +// If app has multisources, it will return all corresponding revisions preserving +// order from the app.spec.sources. If app has only one source, it will return a +// single revision in the list. +func (a *ApplicationStatus) GetRevisions() []string { + revisions := []string{} + if len(a.Sync.Revisions) > 0 { + revisions = a.Sync.Revisions + } else if a.Sync.Revision != "" { + revisions = append(revisions, a.Sync.Revision) + } + return revisions +} + +// BuildComparedToStatus will build a ComparedTo object based on the current +// Application state. +func (app *Application) BuildComparedToStatus() ComparedTo { + ct := ComparedTo{ + Destination: app.Spec.Destination, + IgnoreDifferences: app.Spec.IgnoreDifferences, + } + if app.Spec.HasMultipleSources() { + ct.Sources = app.Spec.Sources + } else { + ct.Source = app.Spec.GetSource() + } + return ct +} + // JWTTokens represents a list of JWT tokens type JWTTokens struct { Items []JWTToken `json:"items,omitempty" protobuf:"bytes,1,opt,name=items"` From c602302d62d208627004d6c6be4ab359c13be6b3 Mon Sep 17 00:00:00 2001 From: filiprafaj Date: Thu, 30 Nov 2023 04:32:42 +0100 Subject: [PATCH 09/66] fix PerconaXtraDBCluster health (#16434) Signed-off-by: Filip Rafaj Co-authored-by: Filip Rafaj --- .../pxc.percona.com/PerconaXtraDBCluster/health.lua | 2 +- .../PerconaXtraDBCluster/testdata/error.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/health.lua b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/health.lua index 9de2180197571..d614828d461f2 100644 --- a/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/health.lua +++ b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/health.lua @@ -27,7 +27,7 @@ if obj.status ~= nil then if obj.status.state == "error" then hs.status = "Degraded" - hs.message = "Cluster is on error: " .. table.concat(obj.status.messages, ", ") + hs.message = "Cluster is on error: " .. table.concat(obj.status.message, ", ") return hs end diff --git a/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/error.yaml b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/error.yaml index b6f1884be0819..4a373358dcd8c 100644 --- a/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/error.yaml +++ b/resource_customizations/pxc.percona.com/PerconaXtraDBCluster/testdata/error.yaml @@ -12,7 +12,7 @@ status: pmm: {} proxysql: {} pxc: - image: '' + image: "" ready: 1 size: 2 status: error @@ -20,5 +20,5 @@ status: ready: 1 size: 2 state: error - messages: - - we lost node + message: + - we lost node From 8070725eecac8a98a617049ec128f6149aeb7f03 Mon Sep 17 00:00:00 2001 From: Suraj yadav Date: Thu, 30 Nov 2023 09:22:07 +0530 Subject: [PATCH 10/66] feat(cli): Added example to admin-cluster.go and projectwindow.go files (#16128) * cluster.go, projectwindow.go Signed-off-by: Surajyadav * updated-examples Signed-off-by: Surajyadav * format-corrected Signed-off-by: Surajyadav * spell-mistake Signed-off-by: Surajyadav * format-correction Signed-off-by: Surajyadav * retrigger Signed-off-by: Surajyadav * retrigger-2 Signed-off-by: Surajyadav * retirgger-3 Signed-off-by: Surajyadav * update Signed-off-by: Surajyadav --------- Signed-off-by: Surajyadav Co-authored-by: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com> --- cmd/argocd/commands/admin/cluster.go | 30 ++++++++++++ cmd/argocd/commands/projectwindows.go | 49 ++++++++++++++++--- .../commands/argocd_admin_cluster.md | 14 ++++++ .../argocd_admin_cluster_kubeconfig.md | 17 +++++++ .../commands/argocd_admin_cluster_stats.md | 14 ++++++ .../commands/argocd_proj_windows.md | 17 +++++++ .../commands/argocd_proj_windows_add.md | 5 +- .../commands/argocd_proj_windows_delete.md | 11 +++++ ...argocd_proj_windows_disable-manual-sync.md | 11 +++++ .../argocd_proj_windows_enable-manual-sync.md | 14 ++++++ .../commands/argocd_proj_windows_list.md | 9 ++-- 11 files changed, 180 insertions(+), 11 deletions(-) diff --git a/cmd/argocd/commands/admin/cluster.go b/cmd/argocd/commands/admin/cluster.go index 6625787de7d71..5d14717a15e7d 100644 --- a/cmd/argocd/commands/admin/cluster.go +++ b/cmd/argocd/commands/admin/cluster.go @@ -44,6 +44,15 @@ func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientc var command = &cobra.Command{ Use: "cluster", Short: "Manage clusters configuration", + Example: ` +#Generate declarative config for a cluster +argocd admin cluster generate-spec my-cluster -o yaml + +#Generate a kubeconfig for a cluster named "my-cluster" and display it in the console +argocd admin cluster kubeconfig my-cluster + +#Print information namespaces which Argo CD manages in each cluster +argocd admin cluster namespaces my-cluster `, Run: func(c *cobra.Command, args []string) { c.HelpFunc()(c, args) }, @@ -460,6 +469,15 @@ func NewClusterStatsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma var command = cobra.Command{ Use: "stats", Short: "Prints information cluster statistics and inferred shard number", + Example: ` +#Display stats and shards for clusters +argocd admin cluster stats + +#Display Cluster Statistics for a Specific Shard +argocd admin cluster stats --shard=1 + +#In a multi-cluster environment to print stats for a specific cluster say(target-cluster) +argocd admin cluster stats target-cluster`, Run: func(cmd *cobra.Command, args []string) { ctx := cmd.Context() @@ -510,6 +528,18 @@ func NewClusterConfig() *cobra.Command { Use: "kubeconfig CLUSTER_URL OUTPUT_PATH", Short: "Generates kubeconfig for the specified cluster", DisableAutoGenTag: true, + Example: ` +#Generate a kubeconfig for a cluster named "my-cluster" on console +argocd admin cluster kubeconfig my-cluster + +#Listing available kubeconfigs for clusters managed by argocd +argocd admin cluster kubeconfig + +#Removing a specific kubeconfig file +argocd admin cluster kubeconfig my-cluster --delete + +#Generate a Kubeconfig for a Cluster with TLS Verification Disabled +argocd admin cluster kubeconfig https://cluster-api-url:6443 /path/to/output/kubeconfig.yaml --insecure-skip-tls-verify`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() diff --git a/cmd/argocd/commands/projectwindows.go b/cmd/argocd/commands/projectwindows.go index a46f9ece64c36..93843130ebb13 100644 --- a/cmd/argocd/commands/projectwindows.go +++ b/cmd/argocd/commands/projectwindows.go @@ -22,6 +22,18 @@ func NewProjectWindowsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com roleCommand := &cobra.Command{ Use: "windows", Short: "Manage a project's sync windows", + Example: ` +#Add a sync window to a project +argocd proj windows add my-project \ +--schedule "0 0 * * 1-5" \ +--duration 3600 \ +--prune + +#Delete a sync window from a project +argocd proj windows delete + +#List project sync windows +argocd proj windows list `, Run: func(c *cobra.Command, args []string) { c.HelpFunc()(c, args) os.Exit(1) @@ -42,6 +54,12 @@ func NewProjectWindowsDisableManualSyncCommand(clientOpts *argocdclient.ClientOp Use: "disable-manual-sync PROJECT ID", Short: "Disable manual sync for a sync window", Long: "Disable manual sync for a sync window. Requires ID which can be found by running \"argocd proj windows list PROJECT\"", + Example: ` +#Disable manual sync for a sync window for the Project +argocd proj windows disable-manual-sync PROJECT ID + +#Disbaling manual sync for a windows set on the default project with Id 0 +argocd proj windows disable-manual-sync default 0`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -79,6 +97,15 @@ func NewProjectWindowsEnableManualSyncCommand(clientOpts *argocdclient.ClientOpt Use: "enable-manual-sync PROJECT ID", Short: "Enable manual sync for a sync window", Long: "Enable manual sync for a sync window. Requires ID which can be found by running \"argocd proj windows list PROJECT\"", + Example: ` +#Enabling manual sync for a general case +argocd proj windows enable-manual-sync PROJECT ID + +#Enabling manual sync for a windows set on the default project with Id 2 +argocd proj windows enable-manual-sync default 2 + +#Enabling manual sync with a custom message +argocd proj windows enable-manual-sync my-app-project --message "Manual sync initiated by admin`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -125,14 +152,15 @@ func NewProjectWindowsAddWindowCommand(clientOpts *argocdclient.ClientOptions) * var command = &cobra.Command{ Use: "add PROJECT", Short: "Add a sync window to a project", - Example: `# Add a 1 hour allow sync window + Example: ` +#Add a 1 hour allow sync window argocd proj windows add PROJECT \ --kind allow \ --schedule "0 22 * * *" \ --duration 1h \ --applications "*" -# Add a deny sync window with the ability to manually sync. +#Add a deny sync window with the ability to manually sync. argocd proj windows add PROJECT \ --kind deny \ --schedule "30 10 * * *" \ @@ -180,6 +208,12 @@ func NewProjectWindowsDeleteCommand(clientOpts *argocdclient.ClientOptions) *cob var command = &cobra.Command{ Use: "delete PROJECT ID", Short: "Delete a sync window from a project. Requires ID which can be found by running \"argocd proj windows list PROJECT\"", + Example: ` +#Delete a sync window from a project (default) with ID 0 +argocd proj windows delete default 0 + +#Delete a sync window from a project (new-project) with ID 1 +argocd proj windows delete new-project 1`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() @@ -274,12 +308,15 @@ func NewProjectWindowsListCommand(clientOpts *argocdclient.ClientOptions) *cobra var command = &cobra.Command{ Use: "list PROJECT", Short: "List project sync windows", - Example: `# List project windows + Example: ` +#List project windows argocd proj windows list PROJECT - -# List project windows in yaml format + +#List project windows in yaml format argocd proj windows list PROJECT -o yaml -`, + +#List project windows info for a project name (test-project) +argocd proj windows list test-project`, Run: func(c *cobra.Command, args []string) { ctx := c.Context() diff --git a/docs/user-guide/commands/argocd_admin_cluster.md b/docs/user-guide/commands/argocd_admin_cluster.md index 1a469c3f818ca..bad60a0dd32bf 100644 --- a/docs/user-guide/commands/argocd_admin_cluster.md +++ b/docs/user-guide/commands/argocd_admin_cluster.md @@ -8,6 +8,20 @@ Manage clusters configuration argocd admin cluster [flags] ``` +### Examples + +``` + +#Generate declarative config for a cluster +argocd admin cluster generate-spec my-cluster -o yaml + +#Generate a kubeconfig for a cluster named "my-cluster" and display it in the console +argocd admin cluster kubeconfig my-cluster + +#Print information namespaces which Argo CD manages in each cluster +argocd admin cluster namespaces my-cluster +``` + ### Options ``` diff --git a/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md b/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md index 8105605e80cd0..38f61ce5cd8a2 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md +++ b/docs/user-guide/commands/argocd_admin_cluster_kubeconfig.md @@ -8,6 +8,23 @@ Generates kubeconfig for the specified cluster argocd admin cluster kubeconfig CLUSTER_URL OUTPUT_PATH [flags] ``` +### Examples + +``` + +#Generate a kubeconfig for a cluster named "my-cluster" on console +argocd admin cluster kubeconfig my-cluster + +#Listing available kubeconfigs for clusters managed by argocd +argocd admin cluster kubeconfig + +#Removing a specific kubeconfig file +argocd admin cluster kubeconfig my-cluster --delete + +#Generate a Kubeconfig for a Cluster with TLS Verification Disabled +argocd admin cluster kubeconfig https://cluster-api-url:6443 /path/to/output/kubeconfig.yaml --insecure-skip-tls-verify +``` + ### Options ``` diff --git a/docs/user-guide/commands/argocd_admin_cluster_stats.md b/docs/user-guide/commands/argocd_admin_cluster_stats.md index 9e916288adf7e..960fd12caaef1 100644 --- a/docs/user-guide/commands/argocd_admin_cluster_stats.md +++ b/docs/user-guide/commands/argocd_admin_cluster_stats.md @@ -8,6 +8,20 @@ Prints information cluster statistics and inferred shard number argocd admin cluster stats [flags] ``` +### Examples + +``` + +#Display stats and shards for clusters +argocd admin cluster stats + +#Display Cluster Statistics for a Specific Shard +argocd admin cluster stats --shard=1 + +#In a multi-cluster environment to print stats for a specific cluster say(target-cluster) +argocd admin cluster stats target-cluster +``` + ### Options ``` diff --git a/docs/user-guide/commands/argocd_proj_windows.md b/docs/user-guide/commands/argocd_proj_windows.md index dc1b68bf0191b..0b22c2098dc82 100644 --- a/docs/user-guide/commands/argocd_proj_windows.md +++ b/docs/user-guide/commands/argocd_proj_windows.md @@ -8,6 +8,23 @@ Manage a project's sync windows argocd proj windows [flags] ``` +### Examples + +``` + +#Add a sync window to a project +argocd proj windows add my-project \ +--schedule "0 0 * * 1-5" \ +--duration 3600 \ +--prune + +#Delete a sync window from a project +argocd proj windows delete + +#List project sync windows +argocd proj windows list +``` + ### Options ``` diff --git a/docs/user-guide/commands/argocd_proj_windows_add.md b/docs/user-guide/commands/argocd_proj_windows_add.md index 7f9eb50af8f5b..52fd3a8354ee3 100644 --- a/docs/user-guide/commands/argocd_proj_windows_add.md +++ b/docs/user-guide/commands/argocd_proj_windows_add.md @@ -11,14 +11,15 @@ argocd proj windows add PROJECT [flags] ### Examples ``` -# Add a 1 hour allow sync window + +#Add a 1 hour allow sync window argocd proj windows add PROJECT \ --kind allow \ --schedule "0 22 * * *" \ --duration 1h \ --applications "*" -# Add a deny sync window with the ability to manually sync. +#Add a deny sync window with the ability to manually sync. argocd proj windows add PROJECT \ --kind deny \ --schedule "30 10 * * *" \ diff --git a/docs/user-guide/commands/argocd_proj_windows_delete.md b/docs/user-guide/commands/argocd_proj_windows_delete.md index 316b25041fde2..6faf7dbeedc19 100644 --- a/docs/user-guide/commands/argocd_proj_windows_delete.md +++ b/docs/user-guide/commands/argocd_proj_windows_delete.md @@ -8,6 +8,17 @@ Delete a sync window from a project. Requires ID which can be found by running " argocd proj windows delete PROJECT ID [flags] ``` +### Examples + +``` + +#Delete a sync window from a project (default) with ID 0 +argocd proj windows delete default 0 + +#Delete a sync window from a project (new-project) with ID 1 +argocd proj windows delete new-project 1 +``` + ### Options ``` diff --git a/docs/user-guide/commands/argocd_proj_windows_disable-manual-sync.md b/docs/user-guide/commands/argocd_proj_windows_disable-manual-sync.md index 8951ad9371c90..e3b84ac38cc0e 100644 --- a/docs/user-guide/commands/argocd_proj_windows_disable-manual-sync.md +++ b/docs/user-guide/commands/argocd_proj_windows_disable-manual-sync.md @@ -12,6 +12,17 @@ Disable manual sync for a sync window. Requires ID which can be found by running argocd proj windows disable-manual-sync PROJECT ID [flags] ``` +### Examples + +``` + +#Disable manual sync for a sync window for the Project +argocd proj windows disable-manual-sync PROJECT ID + +#Disbaling manual sync for a windows set on the default project with Id 0 +argocd proj windows disable-manual-sync default 0 +``` + ### Options ``` diff --git a/docs/user-guide/commands/argocd_proj_windows_enable-manual-sync.md b/docs/user-guide/commands/argocd_proj_windows_enable-manual-sync.md index a1ca162840f7a..7ecbb50e6ac1b 100644 --- a/docs/user-guide/commands/argocd_proj_windows_enable-manual-sync.md +++ b/docs/user-guide/commands/argocd_proj_windows_enable-manual-sync.md @@ -12,6 +12,20 @@ Enable manual sync for a sync window. Requires ID which can be found by running argocd proj windows enable-manual-sync PROJECT ID [flags] ``` +### Examples + +``` + +#Enabling manual sync for a general case +argocd proj windows enable-manual-sync PROJECT ID + +#Enabling manual sync for a windows set on the default project with Id 2 +argocd proj windows enable-manual-sync default 2 + +#Enabling manual sync with a custom message +argocd proj windows enable-manual-sync my-app-project --message "Manual sync initiated by admin +``` + ### Options ``` diff --git a/docs/user-guide/commands/argocd_proj_windows_list.md b/docs/user-guide/commands/argocd_proj_windows_list.md index 94073db4775b8..3c361f90d2a68 100644 --- a/docs/user-guide/commands/argocd_proj_windows_list.md +++ b/docs/user-guide/commands/argocd_proj_windows_list.md @@ -11,12 +11,15 @@ argocd proj windows list PROJECT [flags] ### Examples ``` -# List project windows + +#List project windows argocd proj windows list PROJECT - -# List project windows in yaml format + +#List project windows in yaml format argocd proj windows list PROJECT -o yaml +#List project windows info for a project name (test-project) +argocd proj windows list test-project ``` ### Options From 9e92f555419f359656c5d9a67fcb3c06c40402e5 Mon Sep 17 00:00:00 2001 From: AS <11219262+ashutosh16@users.noreply.github.com> Date: Wed, 29 Nov 2023 19:53:46 -0800 Subject: [PATCH 11/66] fix(ui): add exec check to avoid API calls (#16168) * bug: add parent ref node info on resource list Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> * bug: add parent ref node info on resource list Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> * bug: add parent ref node info on resource list Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> * bug: add parent ref node info on resource list Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> * bug: add parent ref node info on resource list Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> * bug: add parent ref node info on resource list Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> --------- Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> --- .../application-resource-list.tsx | 56 +++++++++++-------- .../resource-details/resource-details.tsx | 2 +- ui/src/app/applications/components/utils.tsx | 4 +- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/ui/src/app/applications/components/application-details/application-resource-list.tsx b/ui/src/app/applications/components/application-details/application-resource-list.tsx index e5cad1bc0a93b..c5519fc4b6ff9 100644 --- a/ui/src/app/applications/components/application-details/application-resource-list.tsx +++ b/ui/src/app/applications/components/application-details/application-resource-list.tsx @@ -31,35 +31,45 @@ export const ApplicationResourceList = ({ return null; } const parentNode = ((resources || []).length > 0 && (getResNode(tree.nodes, nodeKey(resources[0])) as ResourceNode)?.parentRefs?.[0]) || ({} as ResourceRef); + const searchParams = new URLSearchParams(window.location.search); + const view = searchParams.get('view'); + const ParentRefDetails = () => { + return Object.keys(parentNode).length > 0 ? ( +
+
Parent Node Info
+
+
Name:
+
{parentNode?.name}
+
+
+
Kind:
+
{parentNode?.kind}
+
+
+ ) : ( +
+ ); + }; return (
-
- {Object.keys(parentNode).length > 0 && ( -
-
Parent Node Info
-
-
Name:
-
{parentNode?.name}
-
-
-
Kind:
-
{parentNode?.kind}
-
-
- )} -
+ {/* Display only when the view is set to or network */} + {(view === 'tree' || view === 'network') && ( +
+ +
+ )}
-
NAME
+
NAME
GROUP/KIND
SYNC ORDER
-
NAMESPACE
+
NAMESPACE
{(parentNode.kind === 'Rollout' || parentNode.kind === 'Deployment') &&
REVISION
} -
CREATED AT
-
STATUS
+
CREATED AT
+
STATUS
{resources @@ -79,7 +89,7 @@ export const ApplicationResourceList = ({
{ResourceLabel({kind: res.kind})}
-
+
{res.name} {res.kind === 'Application' && ( @@ -98,7 +108,7 @@ export const ApplicationResourceList = ({
{[res.group, res.kind].filter(item => !!item).join('/')}
{res.syncWave || '-'}
-
{res.namespace}
+
{res.namespace}
{res.kind === 'ReplicaSet' && ((getResNode(tree.nodes, nodeKey(res)) as ResourceNode).info || []) .filter(tag => !tag.name.includes('Node')) @@ -111,7 +121,7 @@ export const ApplicationResourceList = ({ ); })} -
+
{res.createdAt && ( @@ -121,7 +131,7 @@ export const ApplicationResourceList = ({ )}
-
+
{res.health && ( {res.health.status}   diff --git a/ui/src/app/applications/components/resource-details/resource-details.tsx b/ui/src/app/applications/components/resource-details/resource-details.tsx index 6477509370905..52d2fef184703 100644 --- a/ui/src/app/applications/components/resource-details/resource-details.tsx +++ b/ui/src/app/applications/components/resource-details/resource-details.tsx @@ -280,7 +280,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { const settings = await services.authService.settings(); const execEnabled = settings.execEnabled; const logsAllowed = await services.accounts.canI('logs', 'get', application.spec.project + '/' + application.metadata.name); - const execAllowed = await services.accounts.canI('exec', 'create', application.spec.project + '/' + application.metadata.name); + const execAllowed = execEnabled && (await services.accounts.canI('exec', 'create', application.spec.project + '/' + application.metadata.name)); const links = await services.applications.getResourceLinks(application.metadata.name, application.metadata.namespace, selectedNode).catch(() => null); return {controlledState, liveState, events, podState, execEnabled, execAllowed, logsAllowed, links}; }}> diff --git a/ui/src/app/applications/components/utils.tsx b/ui/src/app/applications/components/utils.tsx index 674ffc6728db4..f11ca2a916307 100644 --- a/ui/src/app/applications/components/utils.tsx +++ b/ui/src/app/applications/components/utils.tsx @@ -473,8 +473,8 @@ function getActionItems( const execAction = services.authService .settings() .then(async settings => { - const execAllowed = await services.accounts.canI('exec', 'create', application.spec.project + '/' + application.metadata.name); - if (resource.kind === 'Pod' && settings.execEnabled && execAllowed) { + const execAllowed = settings.execEnabled && (await services.accounts.canI('exec', 'create', application.spec.project + '/' + application.metadata.name)); + if (resource.kind === 'Pod' && execAllowed) { return [ { title: 'Exec', From 673d661821b6f698182c38890652118c8c1f2a1d Mon Sep 17 00:00:00 2001 From: Ashin Sabu <139749674+ashinsabu3@users.noreply.github.com> Date: Thu, 30 Nov 2023 09:55:15 +0530 Subject: [PATCH 12/66] Fix extra space in application tree pod group (#16358) Signed-off-by: Ashin Sabu --- .../application-resource-tree.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx b/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx index 6f28a40ea5046..3d5b1782a0e0c 100644 --- a/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx +++ b/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx @@ -564,11 +564,13 @@ function renderPodGroup(props: ApplicationResourceTreeProps, id: string, node: R
{[podGroupHealthy, podGroupDegraded, podGroupInProgress].map((pods, index) => { - return ( -
- {renderPodGroupByStatus(props, node, pods, showPodGroupByStatus)} -
- ); + if (pods.length > 0) { + return ( +
+ {renderPodGroupByStatus(props, node, pods, showPodGroupByStatus)} +
+ ); + } })}
From 5c187a19550c34e452c8c5220450e77515da7c0e Mon Sep 17 00:00:00 2001 From: Nathan Romriell Date: Thu, 30 Nov 2023 07:03:34 -0800 Subject: [PATCH 13/66] fix(repo-server): excess git requests, resolveReferencedSources and runManifestGenAsync not using cache (Issue #14725) (#16410) * fix(repo-server): excess git requests part 1, resolveReferencedSources and runManifestGenAsync Signed-off-by: nromriell * fix: remove unnecessary settings instantiation Signed-off-by: nromriell --------- Signed-off-by: nromriell --- reposerver/cache/mocks/reposervercache.go | 71 ++++ reposerver/repository/repository.go | 11 +- reposerver/repository/repository_test.go | 449 +++++++++++++++++----- util/cache/mocks/cacheclient.go | 65 ++++ 4 files changed, 488 insertions(+), 108 deletions(-) create mode 100644 reposerver/cache/mocks/reposervercache.go create mode 100644 util/cache/mocks/cacheclient.go diff --git a/reposerver/cache/mocks/reposervercache.go b/reposerver/cache/mocks/reposervercache.go new file mode 100644 index 0000000000000..0e49b5816178e --- /dev/null +++ b/reposerver/cache/mocks/reposervercache.go @@ -0,0 +1,71 @@ +package mocks + +import ( + "testing" + "time" + + "github.com/alicebob/miniredis/v2" + cacheutil "github.com/argoproj/argo-cd/v2/util/cache" + cacheutilmocks "github.com/argoproj/argo-cd/v2/util/cache/mocks" + "github.com/redis/go-redis/v9" + "github.com/stretchr/testify/mock" +) + +type MockCacheType int + +const ( + MockCacheTypeRedis MockCacheType = iota + MockCacheTypeInMem +) + +type MockRepoCache struct { + mock.Mock + RedisClient *cacheutilmocks.MockCacheClient + StopRedisCallback func() +} + +type MockCacheOptions struct { + RepoCacheExpiration time.Duration + RevisionCacheExpiration time.Duration + ReadDelay time.Duration + WriteDelay time.Duration +} + +type CacheCallCounts struct { + ExternalSets int + ExternalGets int + ExternalDeletes int +} + +// Checks that the cache was called the expected number of times +func (mockCache *MockRepoCache) AssertCacheCalledTimes(t *testing.T, calls *CacheCallCounts) { + mockCache.RedisClient.AssertNumberOfCalls(t, "Get", calls.ExternalGets) + mockCache.RedisClient.AssertNumberOfCalls(t, "Set", calls.ExternalSets) + mockCache.RedisClient.AssertNumberOfCalls(t, "Delete", calls.ExternalDeletes) +} + +func (mockCache *MockRepoCache) ConfigureDefaultCallbacks() { + mockCache.RedisClient.On("Get", mock.Anything, mock.Anything).Return(nil) + mockCache.RedisClient.On("Set", mock.Anything).Return(nil) + mockCache.RedisClient.On("Delete", mock.Anything).Return(nil) +} + +func NewInMemoryRedis() (*redis.Client, func()) { + cacheutil.NewInMemoryCache(5 * time.Second) + mr, err := miniredis.Run() + if err != nil { + panic(err) + } + return redis.NewClient(&redis.Options{Addr: mr.Addr()}), mr.Close +} + +func NewMockRepoCache(cacheOpts *MockCacheOptions) *MockRepoCache { + redisClient, stopRedis := NewInMemoryRedis() + redisCacheClient := &cacheutilmocks.MockCacheClient{ + ReadDelay: cacheOpts.ReadDelay, + WriteDelay: cacheOpts.WriteDelay, + BaseCache: cacheutil.NewRedisCache(redisClient, cacheOpts.RepoCacheExpiration, cacheutil.RedisCompressionNone)} + newMockCache := &MockRepoCache{RedisClient: redisCacheClient, StopRedisCallback: stopRedis} + newMockCache.ConfigureDefaultCallbacks() + return newMockCache +} diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go index f66bc71ac4621..f5a934ee12c5c 100644 --- a/reposerver/repository/repository.go +++ b/reposerver/repository/repository.go @@ -300,6 +300,7 @@ func (s *Service) runRepoOperation( var gitClient git.Client var helmClient helm.Client var err error + gitClientOpts := git.WithCache(s.cache, !settings.noRevisionCache && !settings.noCache) revision = textutils.FirstNonEmpty(revision, source.TargetRevision) unresolvedRevision := revision if source.IsHelm() { @@ -308,13 +309,13 @@ func (s *Service) runRepoOperation( return err } } else { - gitClient, revision, err = s.newClientResolveRevision(repo, revision, git.WithCache(s.cache, !settings.noRevisionCache && !settings.noCache)) + gitClient, revision, err = s.newClientResolveRevision(repo, revision, gitClientOpts) if err != nil { return err } } - repoRefs, err := resolveReferencedSources(hasMultipleSources, source.Helm, refSources, s.newClientResolveRevision) + repoRefs, err := resolveReferencedSources(hasMultipleSources, source.Helm, refSources, s.newClientResolveRevision, gitClientOpts) if err != nil { return err } @@ -463,7 +464,7 @@ type gitClientGetter func(repo *v1alpha1.Repository, revision string, opts ...gi // // Much of this logic is duplicated in runManifestGenAsync. If making changes here, check whether runManifestGenAsync // should be updated. -func resolveReferencedSources(hasMultipleSources bool, source *v1alpha1.ApplicationSourceHelm, refSources map[string]*v1alpha1.RefTarget, newClientResolveRevision gitClientGetter) (map[string]string, error) { +func resolveReferencedSources(hasMultipleSources bool, source *v1alpha1.ApplicationSourceHelm, refSources map[string]*v1alpha1.RefTarget, newClientResolveRevision gitClientGetter, gitClientOpts git.ClientOpts) (map[string]string, error) { repoRefs := make(map[string]string) if !hasMultipleSources || source == nil { return repoRefs, nil @@ -490,7 +491,7 @@ func resolveReferencedSources(hasMultipleSources bool, source *v1alpha1.Applicat normalizedRepoURL := git.NormalizeGitURL(refSourceMapping.Repo.Repo) _, ok = repoRefs[normalizedRepoURL] if !ok { - _, referencedCommitSHA, err := newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision) + _, referencedCommitSHA, err := newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision, gitClientOpts) if err != nil { log.Errorf("Failed to get git client for repo %s: %v", refSourceMapping.Repo.Repo, err) return nil, fmt.Errorf("failed to get git client for repo %s", refSourceMapping.Repo.Repo) @@ -728,7 +729,7 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA, return } } else { - gitClient, referencedCommitSHA, err := s.newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision) + gitClient, referencedCommitSHA, err := s.newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision, git.WithCache(s.cache, !q.NoRevisionCache && !q.NoCache)) if err != nil { log.Errorf("Failed to get git client for repo %s: %v", refSourceMapping.Repo.Repo, err) ch.errCh <- fmt.Errorf("failed to get git client for repo %s", refSourceMapping.Repo.Repo) diff --git a/reposerver/repository/repository_test.go b/reposerver/repository/repository_test.go index a24eb5008c47b..74d09dd6f86b8 100644 --- a/reposerver/repository/repository_test.go +++ b/reposerver/repository/repository_test.go @@ -1,6 +1,7 @@ package repository import ( + "bytes" "context" "encoding/json" "errors" @@ -17,6 +18,7 @@ import ( "testing" "time" + cacheutil "github.com/argoproj/argo-cd/v2/util/cache" log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/api/resource" @@ -28,13 +30,14 @@ import ( "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/yaml" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/reposerver/cache" + repositorymocks "github.com/argoproj/argo-cd/v2/reposerver/cache/mocks" "github.com/argoproj/argo-cd/v2/reposerver/metrics" fileutil "github.com/argoproj/argo-cd/v2/test/fixture/path" "github.com/argoproj/argo-cd/v2/util/argo" - cacheutil "github.com/argoproj/argo-cd/v2/util/cache" dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks" "github.com/argoproj/argo-cd/v2/util/git" gitmocks "github.com/argoproj/argo-cd/v2/util/git/mocks" @@ -51,12 +54,49 @@ gpg: Good signature from "GitHub (web-flow commit signing) " type clientFunc func(*gitmocks.Client, *helmmocks.Client, *iomocks.TempPaths) -func newServiceWithMocks(root string, signed bool) (*Service, *gitmocks.Client) { +type repoCacheMocks struct { + mock.Mock + cacheutilCache *cacheutil.Cache + cache *cache.Cache + mockCache *repositorymocks.MockRepoCache +} + +type newGitRepoHelmChartOptions struct { + chartName string + chartVersion string + // valuesFiles is a map of the values file name to the key/value pairs to be written to the file + valuesFiles map[string]map[string]string +} + +type newGitRepoOptions struct { + path string + createPath bool + remote string + addEmptyCommit bool + helmChartOptions newGitRepoHelmChartOptions +} + +func newCacheMocks() *repoCacheMocks { + mockRepoCache := repositorymocks.NewMockRepoCache(&repositorymocks.MockCacheOptions{ + RepoCacheExpiration: 1 * time.Minute, + RevisionCacheExpiration: 1 * time.Minute, + ReadDelay: 0, + WriteDelay: 0, + }) + cacheutilCache := cacheutil.NewCache(mockRepoCache.RedisClient) + return &repoCacheMocks{ + cacheutilCache: cacheutilCache, + cache: cache.NewCache(cacheutilCache, 1*time.Minute, 1*time.Minute), + mockCache: mockRepoCache, + } +} + +func newServiceWithMocks(t *testing.T, root string, signed bool) (*Service, *gitmocks.Client, *repoCacheMocks) { root, err := filepath.Abs(root) if err != nil { panic(err) } - return newServiceWithOpt(func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { + return newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { gitClient.On("Init").Return(nil) gitClient.On("Fetch", mock.Anything).Return(nil) gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) @@ -73,7 +113,7 @@ func newServiceWithMocks(root string, signed bool) (*Service, *gitmocks.Client) chart := "my-chart" oobChart := "out-of-bounds-chart" version := "1.1.0" - helmClient.On("GetIndex", true).Return(&helm.Index{Entries: map[string]helm.Entries{ + helmClient.On("GetIndex", mock.AnythingOfType("bool")).Return(&helm.Index{Entries: map[string]helm.Entries{ chart: {{Version: "1.0.0"}, {Version: version}}, oobChart: {{Version: "1.0.0"}, {Version: version}}, }}, nil) @@ -89,18 +129,16 @@ func newServiceWithMocks(root string, signed bool) (*Service, *gitmocks.Client) }, root) } -func newServiceWithOpt(cf clientFunc, root string) (*Service, *gitmocks.Client) { +func newServiceWithOpt(t *testing.T, cf clientFunc, root string) (*Service, *gitmocks.Client, *repoCacheMocks) { helmClient := &helmmocks.Client{} gitClient := &gitmocks.Client{} paths := &iomocks.TempPaths{} cf(gitClient, helmClient, paths) - service := NewService(metrics.NewMetricsServer(), cache.NewCache( - cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Minute)), - 1*time.Minute, - 1*time.Minute, - ), RepoServerInitConstants{ParallelismLimit: 1}, argo.NewResourceTracking(), &git.NoopCredsStore{}, root) + cacheMocks := newCacheMocks() + t.Cleanup(cacheMocks.mockCache.StopRedisCallback) + service := NewService(metrics.NewMetricsServer(), cacheMocks.cache, RepoServerInitConstants{ParallelismLimit: 1}, argo.NewResourceTracking(), &git.NoopCredsStore{}, root) - service.newGitClient = func(rawRepoURL string, root string, creds git.Creds, insecure bool, enableLfs bool, prosy string, opts ...git.ClientOpts) (client git.Client, e error) { + service.newGitClient = func(rawRepoURL string, root string, creds git.Creds, insecure bool, enableLfs bool, proxy string, opts ...git.ClientOpts) (client git.Client, e error) { return gitClient, nil } service.newHelmClient = func(repoURL string, creds helm.Creds, enableOci bool, proxy string, opts ...helm.ClientOpts) helm.Client { @@ -110,20 +148,20 @@ func newServiceWithOpt(cf clientFunc, root string) (*Service, *gitmocks.Client) return io.NopCloser } service.gitRepoPaths = paths - return service, gitClient + return service, gitClient, cacheMocks } -func newService(root string) *Service { - service, _ := newServiceWithMocks(root, false) +func newService(t *testing.T, root string) *Service { + service, _, _ := newServiceWithMocks(t, root, false) return service } -func newServiceWithSignature(root string) *Service { - service, _ := newServiceWithMocks(root, true) +func newServiceWithSignature(t *testing.T, root string) *Service { + service, _, _ := newServiceWithMocks(t, root, true) return service } -func newServiceWithCommitSHA(root, revision string) *Service { +func newServiceWithCommitSHA(t *testing.T, root, revision string) *Service { var revisionErr error commitSHARegex := regexp.MustCompile("^[0-9A-Fa-f]{40}$") @@ -131,7 +169,7 @@ func newServiceWithCommitSHA(root, revision string) *Service { revisionErr = errors.New("not a commit SHA") } - service, gitClient := newServiceWithOpt(func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { + service, gitClient, _ := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { gitClient.On("Init").Return(nil) gitClient.On("Fetch", mock.Anything).Return(nil) gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) @@ -150,7 +188,7 @@ func newServiceWithCommitSHA(root, revision string) *Service { } func TestGenerateYamlManifestInDir(t *testing.T) { - service := newService("../../manifests/base") + service := newService(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} q := apiclient.ManifestRequest{ @@ -247,7 +285,7 @@ func TestGenerateManifests_MissingSymlinkDestination(t *testing.T) { } func TestGenerateManifests_K8SAPIResetCache(t *testing.T) { - service := newService("../../manifests/base") + service := newService(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} q := apiclient.ManifestRequest{ @@ -275,7 +313,7 @@ func TestGenerateManifests_K8SAPIResetCache(t *testing.T) { } func TestGenerateManifests_EmptyCache(t *testing.T) { - service := newService("../../manifests/base") + service, gitMocks, mockCache := newServiceWithMocks(t, "../../manifests/base", false) src := argoappv1.ApplicationSource{Path: "."} q := apiclient.ManifestRequest{ @@ -291,11 +329,85 @@ func TestGenerateManifests_EmptyCache(t *testing.T) { res, err := service.GenerateManifest(context.Background(), &q) assert.NoError(t, err) assert.True(t, len(res.Manifests) > 0) + mockCache.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ + ExternalSets: 2, + ExternalGets: 2, + ExternalDeletes: 1}) + gitMocks.AssertCalled(t, "LsRemote", mock.Anything) + gitMocks.AssertCalled(t, "Fetch", mock.Anything) +} + +// Test that calling manifest generation on source helm reference helm files that when the revision is cached it does not call ls-remote +func TestGenerateManifestsHelmWithRefs_CachedNoLsRemote(t *testing.T) { + dir := t.TempDir() + repopath := fmt.Sprintf("%s/tmprepo", dir) + cacheMocks := newCacheMocks() + t.Cleanup(func() { + cacheMocks.mockCache.StopRedisCallback() + err := filepath.WalkDir(dir, + func(path string, di fs.DirEntry, err error) error { + if err == nil { + return os.Chmod(path, 0777) + } + return err + }) + if err != nil { + t.Fatal(err) + } + }) + service := NewService(metrics.NewMetricsServer(), cacheMocks.cache, RepoServerInitConstants{ParallelismLimit: 1}, argo.NewResourceTracking(), &git.NoopCredsStore{}, repopath) + var gitClient git.Client + var err error + service.newGitClient = func(rawRepoURL string, root string, creds git.Creds, insecure bool, enableLfs bool, proxy string, opts ...git.ClientOpts) (client git.Client, e error) { + opts = append(opts, git.WithEventHandlers(git.EventHandlers{ + // Primary check, we want to make sure ls-remote is not called when the item is in cache + OnLsRemote: func(repo string) func() { + return func() { + assert.Fail(t, "LsRemote should not be called when the item is in cache") + } + }, + })) + gitClient, err = git.NewClientExt(rawRepoURL, root, creds, insecure, enableLfs, proxy, opts...) + return gitClient, err + } + repoRemote := fmt.Sprintf("file://%s", repopath) + revision := initGitRepo(t, newGitRepoOptions{ + path: repopath, + createPath: true, + remote: repoRemote, + helmChartOptions: newGitRepoHelmChartOptions{ + chartName: "my-chart", + chartVersion: "v1.0.0", + valuesFiles: map[string]map[string]string{"test.yaml": {"testval": "test"}}}, + }) + src := argoappv1.ApplicationSource{RepoURL: repoRemote, Path: ".", TargetRevision: "HEAD", Helm: &argoappv1.ApplicationSourceHelm{ + ValueFiles: []string{"$ref/test.yaml"}, + }} + repo := &argoappv1.Repository{ + Repo: repoRemote, + } + q := apiclient.ManifestRequest{ + Repo: repo, + Revision: "HEAD", + HasMultipleSources: true, + ApplicationSource: &src, + ProjectName: "default", + ProjectSourceRepos: []string{"*"}, + RefSources: map[string]*argoappv1.RefTarget{"$ref": {TargetRevision: "HEAD", Repo: *repo}}, + } + err = cacheMocks.cacheutilCache.SetItem(fmt.Sprintf("git-refs|%s", repoRemote), [][2]string{{"HEAD", revision}}, 30*time.Second, false) + assert.NoError(t, err) + _, err = service.GenerateManifest(context.Background(), &q) + assert.NoError(t, err) + cacheMocks.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ + ExternalSets: 2, + ExternalGets: 5}) } // ensure we can use a semver constraint range (>= 1.0.0) and get back the correct chart (1.0.0) func TestHelmManifestFromChartRepo(t *testing.T) { - service := newService(".") + root := t.TempDir() + service, gitMocks, mockCache := newServiceWithMocks(t, root, false) source := &argoappv1.ApplicationSource{Chart: "my-chart", TargetRevision: ">= 1.0.0"} request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true, ProjectName: "something", ProjectSourceRepos: []string{"*"}} @@ -309,10 +421,14 @@ func TestHelmManifestFromChartRepo(t *testing.T) { Revision: "1.1.0", SourceType: "Helm", }, response) + mockCache.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ + ExternalSets: 1, + ExternalGets: 0}) + gitMocks.AssertNotCalled(t, "LsRemote", mock.Anything) } func TestHelmChartReferencingExternalValues(t *testing.T) { - service := newService(".") + service := newService(t, ".") spec := argoappv1.ApplicationSpec{ Sources: []argoappv1.ApplicationSource{ {RepoURL: "https://helm.example.com", Chart: "my-chart", TargetRevision: ">= 1.0.0", Helm: &argoappv1.ApplicationSourceHelm{ @@ -342,7 +458,7 @@ func TestHelmChartReferencingExternalValues(t *testing.T) { } func TestHelmChartReferencingExternalValues_OutOfBounds_Symlink(t *testing.T) { - service := newService(".") + service := newService(t, ".") err := os.Mkdir("testdata/oob-symlink", 0755) require.NoError(t, err) t.Cleanup(func() { @@ -376,7 +492,7 @@ func TestHelmChartReferencingExternalValues_OutOfBounds_Symlink(t *testing.T) { } func TestGenerateManifestsUseExactRevision(t *testing.T) { - service, gitClient := newServiceWithMocks(".", false) + service, gitClient, _ := newServiceWithMocks(t, ".", false) src := argoappv1.ApplicationSource{Path: "./testdata/recurse", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}} @@ -390,7 +506,7 @@ func TestGenerateManifestsUseExactRevision(t *testing.T) { } func TestRecurseManifestsInDir(t *testing.T) { - service := newService(".") + service := newService(t, ".") src := argoappv1.ApplicationSource{Path: "./testdata/recurse", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}} @@ -403,7 +519,7 @@ func TestRecurseManifestsInDir(t *testing.T) { } func TestInvalidManifestsInDir(t *testing.T) { - service := newService(".") + service := newService(t, ".") src := argoappv1.ApplicationSource{Path: "./testdata/invalid-manifests", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}} @@ -414,7 +530,7 @@ func TestInvalidManifestsInDir(t *testing.T) { } func TestInvalidMetadata(t *testing.T) { - service := newService(".") + service := newService(t, ".") src := argoappv1.ApplicationSource{Path: "./testdata/invalid-metadata", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}} q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, AppLabelKey: "test", AppName: "invalid-metadata", TrackingMethod: "annotation+label"} @@ -424,7 +540,7 @@ func TestInvalidMetadata(t *testing.T) { } func TestNilMetadataAccessors(t *testing.T) { - service := newService(".") + service := newService(t, ".") expected := "{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{\"argocd.argoproj.io/tracking-id\":\"nil-metadata-accessors:/ConfigMap:/my-map\"},\"labels\":{\"test\":\"nil-metadata-accessors\"},\"name\":\"my-map\"},\"stringData\":{\"foo\":\"bar\"}}" src := argoappv1.ApplicationSource{Path: "./testdata/nil-metadata-accessors", Directory: &argoappv1.ApplicationSourceDirectory{Recurse: true}} @@ -436,7 +552,7 @@ func TestNilMetadataAccessors(t *testing.T) { } func TestGenerateJsonnetManifestInDir(t *testing.T) { - service := newService(".") + service := newService(t, ".") q := apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -459,7 +575,7 @@ func TestGenerateJsonnetManifestInDir(t *testing.T) { } func TestGenerateJsonnetManifestInRootDir(t *testing.T) { - service := newService("testdata/jsonnet-1") + service := newService(t, "testdata/jsonnet-1") q := apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -482,7 +598,7 @@ func TestGenerateJsonnetManifestInRootDir(t *testing.T) { } func TestGenerateJsonnetLibOutside(t *testing.T) { - service := newService(".") + service := newService(t, ".") q := apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -553,7 +669,7 @@ func TestManifestGenErrorCacheByNumRequests(t *testing.T) { for _, tt := range tests { testName := fmt.Sprintf("gen-attempts-%d-pause-%d-total-%d", tt.PauseGenerationAfterFailedGenerationAttempts, tt.PauseGenerationOnFailureForRequests, tt.TotalCacheInvocations) t.Run(testName, func(t *testing.T) { - service := newService(".") + service := newService(t, ".") service.initConstants = RepoServerInitConstants{ ParallelismLimit: 1, @@ -631,7 +747,7 @@ func TestManifestGenErrorCacheFileContentsChange(t *testing.T) { tmpDir := t.TempDir() - service := newService(tmpDir) + service := newService(t, tmpDir) service.initConstants = RepoServerInitConstants{ ParallelismLimit: 1, @@ -701,7 +817,7 @@ func TestManifestGenErrorCacheByMinutesElapsed(t *testing.T) { for _, tt := range tests { testName := fmt.Sprintf("pause-time-%d", tt.PauseGenerationOnFailureForMinutes) t.Run(testName, func(t *testing.T) { - service := newService(".") + service := newService(t, ".") // Here we simulate the passage of time by overriding the now() function of Service currentTime := time.Now() @@ -771,7 +887,7 @@ func TestManifestGenErrorCacheByMinutesElapsed(t *testing.T) { func TestManifestGenErrorCacheRespectsNoCache(t *testing.T) { - service := newService(".") + service := newService(t, ".") service.initConstants = RepoServerInitConstants{ ParallelismLimit: 1, @@ -828,7 +944,7 @@ func TestManifestGenErrorCacheRespectsNoCache(t *testing.T) { } func TestGenerateHelmWithValues(t *testing.T) { - service := newService("../../util/helm/testdata/redis") + service := newService(t, "../../util/helm/testdata/redis") res, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -865,7 +981,7 @@ func TestGenerateHelmWithValues(t *testing.T) { } func TestHelmWithMissingValueFiles(t *testing.T) { - service := newService("../../util/helm/testdata/redis") + service := newService(t, "../../util/helm/testdata/redis") missingValuesFile := "values-prod-overrides.yaml" req := &apiclient.ManifestRequest{ @@ -893,7 +1009,7 @@ func TestHelmWithMissingValueFiles(t *testing.T) { } func TestGenerateHelmWithEnvVars(t *testing.T) { - service := newService("../../util/helm/testdata/redis") + service := newService(t, "../../util/helm/testdata/redis") res, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -930,7 +1046,7 @@ func TestGenerateHelmWithEnvVars(t *testing.T) { // The requested value file (`../minio/values.yaml`) is outside the app path (`./util/helm/testdata/redis`), however // since the requested value is still under the repo directory (`~/go/src/github.com/argoproj/argo-cd`), it is allowed func TestGenerateHelmWithValuesDirectoryTraversal(t *testing.T) { - service := newService("../../util/helm/testdata") + service := newService(t, "../../util/helm/testdata") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", @@ -947,7 +1063,7 @@ func TestGenerateHelmWithValuesDirectoryTraversal(t *testing.T) { assert.NoError(t, err) // Test the case where the path is "." - service = newService("./testdata") + service = newService(t, "./testdata") _, err = service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", @@ -961,7 +1077,7 @@ func TestGenerateHelmWithValuesDirectoryTraversal(t *testing.T) { } func TestChartRepoWithOutOfBoundsSymlink(t *testing.T) { - service := newService(".") + service := newService(t, ".") source := &argoappv1.ApplicationSource{Chart: "out-of-bounds-chart", TargetRevision: ">= 1.0.0"} request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true} _, err := service.GenerateManifest(context.Background(), request) @@ -971,7 +1087,7 @@ func TestChartRepoWithOutOfBoundsSymlink(t *testing.T) { // This is a Helm first-class app with a values file inside the repo directory // (`~/go/src/github.com/argoproj/argo-cd/reposerver/repository`), so it is allowed func TestHelmManifestFromChartRepoWithValueFile(t *testing.T) { - service := newService(".") + service := newService(t, ".") source := &argoappv1.ApplicationSource{ Chart: "my-chart", TargetRevision: ">= 1.0.0", @@ -1000,7 +1116,7 @@ func TestHelmManifestFromChartRepoWithValueFile(t *testing.T) { // This is a Helm first-class app with a values file outside the repo directory // (`~/go/src/github.com/argoproj/argo-cd/reposerver/repository`), so it is not allowed func TestHelmManifestFromChartRepoWithValueFileOutsideRepo(t *testing.T) { - service := newService(".") + service := newService(t, ".") source := &argoappv1.ApplicationSource{ Chart: "my-chart", TargetRevision: ">= 1.0.0", @@ -1015,7 +1131,7 @@ func TestHelmManifestFromChartRepoWithValueFileOutsideRepo(t *testing.T) { func TestHelmManifestFromChartRepoWithValueFileLinks(t *testing.T) { t.Run("Valid symlink", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") source := &argoappv1.ApplicationSource{ Chart: "my-chart", TargetRevision: ">= 1.0.0", @@ -1031,7 +1147,7 @@ func TestHelmManifestFromChartRepoWithValueFileLinks(t *testing.T) { } func TestGenerateHelmWithURL(t *testing.T) { - service := newService("../../util/helm/testdata/redis") + service := newService(t, "../../util/helm/testdata/redis") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -1054,7 +1170,7 @@ func TestGenerateHelmWithURL(t *testing.T) { // (`~/go/src/github.com/argoproj/argo-cd/util/helm/testdata/redis`), so it is blocked func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { t.Run("Values file with relative path pointing outside repo root", func(t *testing.T) { - service := newService("../../util/helm/testdata/redis") + service := newService(t, "../../util/helm/testdata/redis") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", @@ -1073,7 +1189,7 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { }) t.Run("Values file with relative path pointing inside repo root", func(t *testing.T) { - service := newService("./testdata") + service := newService(t, "./testdata") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", @@ -1091,7 +1207,7 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { }) t.Run("Values file with absolute path stays within repo root", func(t *testing.T) { - service := newService("./testdata") + service := newService(t, "./testdata") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", @@ -1109,7 +1225,7 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { }) t.Run("Values file with absolute path using back-references outside repo root", func(t *testing.T) { - service := newService("./testdata") + service := newService(t, "./testdata") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", @@ -1128,7 +1244,7 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { }) t.Run("Remote values file from forbidden protocol", func(t *testing.T) { - service := newService("./testdata") + service := newService(t, "./testdata") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", @@ -1147,7 +1263,7 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { }) t.Run("Remote values file from custom allowed protocol", func(t *testing.T) { - service := newService("./testdata") + service := newService(t, "./testdata") _, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, AppName: "test", @@ -1168,7 +1284,7 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) { // File parameter should not allow traversal outside of the repository root func TestGenerateHelmWithAbsoluteFileParameter(t *testing.T) { - service := newService("../..") + service := newService(t, "../..") file, err := os.CreateTemp("", "external-secret.txt") assert.NoError(t, err) @@ -1209,7 +1325,7 @@ func TestGenerateHelmWithAbsoluteFileParameter(t *testing.T) { // directory (`~/go/src/github.com/argoproj/argo-cd`), it is allowed. It is used as a means of // providing direct content to a helm chart via a specific key. func TestGenerateHelmWithFileParameter(t *testing.T) { - service := newService("../../util/helm/testdata") + service := newService(t, "../../util/helm/testdata") res, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -1234,7 +1350,7 @@ func TestGenerateHelmWithFileParameter(t *testing.T) { } func TestGenerateNullList(t *testing.T) { - service := newService(".") + service := newService(t, ".") t.Run("null list", func(t *testing.T) { res1, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ @@ -1302,7 +1418,7 @@ func TestGenerateFromUTF16(t *testing.T) { } func TestListApps(t *testing.T) { - service := newService("./testdata") + service := newService(t, "./testdata") res, err := service.ListApps(context.Background(), &apiclient.ListAppsRequest{Repo: &argoappv1.Repository{}}) assert.NoError(t, err) @@ -1329,7 +1445,7 @@ func TestListApps(t *testing.T) { } func TestGetAppDetailsHelm(t *testing.T) { - service := newService("../../util/helm/testdata/dependency") + service := newService(t, "../../util/helm/testdata/dependency") res, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1344,8 +1460,26 @@ func TestGetAppDetailsHelm(t *testing.T) { assert.Equal(t, "Helm", res.Type) assert.EqualValues(t, []string{"values-production.yaml", "values.yaml"}, res.Helm.ValueFiles) } + +func TestGetAppDetailsHelmUsesCache(t *testing.T) { + service := newService(t, "../../util/helm/testdata/dependency") + + res, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ + Repo: &argoappv1.Repository{}, + Source: &argoappv1.ApplicationSource{ + Path: ".", + }, + }) + + assert.NoError(t, err) + assert.NotNil(t, res.Helm) + + assert.Equal(t, "Helm", res.Type) + assert.EqualValues(t, []string{"values-production.yaml", "values.yaml"}, res.Helm.ValueFiles) +} + func TestGetAppDetailsHelm_WithNoValuesFile(t *testing.T) { - service := newService("../../util/helm/testdata/api-versions") + service := newService(t, "../../util/helm/testdata/api-versions") res, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1363,7 +1497,7 @@ func TestGetAppDetailsHelm_WithNoValuesFile(t *testing.T) { } func TestGetAppDetailsKustomize(t *testing.T) { - service := newService("../../util/kustomize/testdata/kustomization_yaml") + service := newService(t, "../../util/kustomize/testdata/kustomization_yaml") res, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1380,7 +1514,7 @@ func TestGetAppDetailsKustomize(t *testing.T) { } func TestGetHelmCharts(t *testing.T) { - service := newService("../..") + service := newService(t, "../..") res, err := service.GetHelmCharts(context.Background(), &apiclient.HelmChartsRequest{Repo: &argoappv1.Repository{}}) // fix flakiness @@ -1401,7 +1535,7 @@ func TestGetHelmCharts(t *testing.T) { } func TestGetRevisionMetadata(t *testing.T) { - service, gitClient := newServiceWithMocks("../..", false) + service, gitClient, _ := newServiceWithMocks(t, "../..", false) now := time.Now() gitClient.On("RevisionMetadata", mock.Anything).Return(&git.RevisionMetadata{ @@ -1469,7 +1603,7 @@ func TestGetRevisionMetadata(t *testing.T) { func TestGetSignatureVerificationResult(t *testing.T) { // Commit with signature and verification requested { - service := newServiceWithSignature("../../manifests/base") + service := newServiceWithSignature(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} q := apiclient.ManifestRequest{ @@ -1486,7 +1620,7 @@ func TestGetSignatureVerificationResult(t *testing.T) { } // Commit with signature and verification not requested { - service := newServiceWithSignature("../../manifests/base") + service := newServiceWithSignature(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, ProjectName: "something", @@ -1498,7 +1632,7 @@ func TestGetSignatureVerificationResult(t *testing.T) { } // Commit without signature and verification requested { - service := newService("../../manifests/base") + service := newService(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, VerifySignature: true, ProjectName: "something", @@ -1510,7 +1644,7 @@ func TestGetSignatureVerificationResult(t *testing.T) { } // Commit without signature and verification not requested { - service := newService("../../manifests/base") + service := newService(t, "../../manifests/base") src := argoappv1.ApplicationSource{Path: "."} q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, VerifySignature: true, ProjectName: "something", @@ -1543,7 +1677,7 @@ func Test_newEnv(t *testing.T) { } func TestService_newHelmClientResolveRevision(t *testing.T) { - service := newService(".") + service := newService(t, ".") t.Run("EmptyRevision", func(t *testing.T) { _, _, err := service.newHelmClientResolveRevision(&argoappv1.Repository{}, "", "", true) @@ -1557,7 +1691,7 @@ func TestService_newHelmClientResolveRevision(t *testing.T) { func TestGetAppDetailsWithAppParameterFile(t *testing.T) { t.Run("No app name set and app specific file exists", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "multi", func(t *testing.T, path string) { details, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1570,7 +1704,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) { }) }) t.Run("No app specific override", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "single-global", func(t *testing.T, path string) { details, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1584,7 +1718,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) { }) }) t.Run("Only app specific override", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "single-app-only", func(t *testing.T, path string) { details, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1598,7 +1732,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) { }) }) t.Run("App specific override", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "multi", func(t *testing.T, path string) { details, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1612,7 +1746,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) { }) }) t.Run("App specific overrides containing non-mergeable field", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "multi", func(t *testing.T, path string) { details, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1626,7 +1760,7 @@ func TestGetAppDetailsWithAppParameterFile(t *testing.T) { }) }) t.Run("Broken app-specific overrides", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "multi", func(t *testing.T, path string) { _, err := service.GetAppDetails(context.Background(), &apiclient.RepoServerAppDetailsQuery{ Repo: &argoappv1.Repository{}, @@ -1668,7 +1802,7 @@ func runWithTempTestdata(t *testing.T, path string, runner func(t *testing.T, pa func TestGenerateManifestsWithAppParameterFile(t *testing.T) { t.Run("Single global override", func(t *testing.T) { runWithTempTestdata(t, "single-global", func(t *testing.T, path string) { - service := newService(".") + service := newService(t, ".") manifests, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, ApplicationSource: &argoappv1.ApplicationSource{ @@ -1699,7 +1833,7 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { t.Run("Single global override Helm", func(t *testing.T) { runWithTempTestdata(t, "single-global-helm", func(t *testing.T, path string) { - service := newService(".") + service := newService(t, ".") manifests, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, ApplicationSource: &argoappv1.ApplicationSource{ @@ -1729,7 +1863,7 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { }) t.Run("Application specific override", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "single-app-only", func(t *testing.T, path string) { manifests, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -1760,8 +1894,29 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { }) }) + t.Run("Multi-source with source as ref only does not generate manifests", func(t *testing.T) { + service := newService(t, ".") + runWithTempTestdata(t, "single-app-only", func(t *testing.T, path string) { + manifests, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ + Repo: &argoappv1.Repository{}, + ApplicationSource: &argoappv1.ApplicationSource{ + Path: "", + Chart: "", + Ref: "test", + }, + AppName: "testapp-multi-ref-only", + ProjectName: "something", + ProjectSourceRepos: []string{"*"}, + HasMultipleSources: true, + }) + assert.NoError(t, err) + assert.Empty(t, manifests.Manifests) + assert.NotEmpty(t, manifests.Revision) + }) + }) + t.Run("Application specific override for other app", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "single-app-only", func(t *testing.T, path string) { manifests, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{ Repo: &argoappv1.Repository{}, @@ -1793,7 +1948,7 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) { }) t.Run("Override info does not appear in cache key", func(t *testing.T) { - service := newService(".") + service := newService(t, ".") runWithTempTestdata(t, "single-global", func(t *testing.T, path string) { source := &argoappv1.ApplicationSource{ Path: path, @@ -1843,7 +1998,7 @@ func TestGenerateManifestWithAnnotatedAndRegularGitTagHashes(t *testing.T) { ProjectSourceRepos: []string{"*"}, }, wantError: false, - service: newServiceWithCommitSHA(".", regularGitTagHash), + service: newServiceWithCommitSHA(t, ".", regularGitTagHash), }, { @@ -1859,7 +2014,7 @@ func TestGenerateManifestWithAnnotatedAndRegularGitTagHashes(t *testing.T) { ProjectSourceRepos: []string{"*"}, }, wantError: false, - service: newServiceWithCommitSHA(".", annotatedGitTaghash), + service: newServiceWithCommitSHA(t, ".", annotatedGitTaghash), }, { @@ -1875,7 +2030,7 @@ func TestGenerateManifestWithAnnotatedAndRegularGitTagHashes(t *testing.T) { ProjectSourceRepos: []string{"*"}, }, wantError: true, - service: newServiceWithCommitSHA(".", invalidGitTaghash), + service: newServiceWithCommitSHA(t, ".", invalidGitTaghash), }, } for _, tt := range tests { @@ -1900,7 +2055,7 @@ func TestGenerateManifestWithAnnotatedAndRegularGitTagHashes(t *testing.T) { func TestGenerateManifestWithAnnotatedTagsAndMultiSourceApp(t *testing.T) { annotatedGitTaghash := "95249be61b028d566c29d47b19e65c5603388a41" - service := newServiceWithCommitSHA(".", annotatedGitTaghash) + service := newServiceWithCommitSHA(t, ".", annotatedGitTaghash) refSources := map[string]*argoappv1.RefTarget{} @@ -2485,7 +2640,7 @@ func Test_findManifests(t *testing.T) { } func TestTestRepoOCI(t *testing.T) { - service := newService(".") + service := newService(t, ".") _, err := service.TestRepository(context.Background(), &apiclient.TestRepositoryRequest{ Repo: &argoappv1.Repository{ Repo: "https://demo.goharbor.io", @@ -2510,7 +2665,7 @@ func Test_getHelmDependencyRepos(t *testing.T) { func TestResolveRevision(t *testing.T) { - service := newService(".") + service := newService(t, ".") repo := &argoappv1.Repository{Repo: "https://github.com/argoproj/argo-cd"} app := &argoappv1.Application{Spec: argoappv1.ApplicationSpec{Source: &argoappv1.ApplicationSource{}}} resolveRevisionResponse, err := service.ResolveRevision(context.Background(), &apiclient.ResolveRevisionRequest{ @@ -2532,7 +2687,7 @@ func TestResolveRevision(t *testing.T) { func TestResolveRevisionNegativeScenarios(t *testing.T) { - service := newService(".") + service := newService(t, ".") repo := &argoappv1.Repository{Repo: "https://github.com/argoproj/argo-cd"} app := &argoappv1.Application{Spec: argoappv1.ApplicationSpec{Source: &argoappv1.ApplicationSource{}}} resolveRevisionResponse, err := service.ResolveRevision(context.Background(), &apiclient.ResolveRevisionRequest{ @@ -2579,19 +2734,57 @@ func TestDirectoryPermissionInitializer(t *testing.T) { require.Error(t, err) } -func initGitRepo(repoPath string, remote string) error { - if err := os.Mkdir(repoPath, 0755); err != nil { - return err +func addHelmToGitRepo(t *testing.T, options newGitRepoOptions) { + err := os.WriteFile(filepath.Join(options.path, "Chart.yaml"), []byte("name: test\nversion: v1.0.0"), 0777) + assert.NoError(t, err) + for valuesFileName, values := range options.helmChartOptions.valuesFiles { + valuesFileContents, err := yaml.Marshal(values) + assert.NoError(t, err) + err = os.WriteFile(filepath.Join(options.path, valuesFileName), valuesFileContents, 0777) + assert.NoError(t, err) + } + assert.NoError(t, err) + cmd := exec.Command("git", "add", "-A") + cmd.Dir = options.path + assert.NoError(t, cmd.Run()) + cmd = exec.Command("git", "commit", "-m", "Initial commit") + cmd.Dir = options.path + assert.NoError(t, cmd.Run()) +} + +func initGitRepo(t *testing.T, options newGitRepoOptions) (revision string) { + if options.createPath { + assert.NoError(t, os.Mkdir(options.path, 0755)) + } + + cmd := exec.Command("git", "init", options.path) + cmd.Dir = options.path + assert.NoError(t, cmd.Run()) + + if options.remote != "" { + cmd = exec.Command("git", "remote", "add", "origin", options.path) + cmd.Dir = options.path + assert.NoError(t, cmd.Run()) } - cmd := exec.Command("git", "init", repoPath) - cmd.Dir = repoPath - if err := cmd.Run(); err != nil { - return err + commitAdded := options.addEmptyCommit || options.helmChartOptions.chartName != "" + if options.addEmptyCommit { + cmd = exec.Command("git", "commit", "-m", "Initial commit", "--allow-empty") + cmd.Dir = options.path + assert.NoError(t, cmd.Run()) + } else if options.helmChartOptions.chartName != "" { + addHelmToGitRepo(t, options) } - cmd = exec.Command("git", "remote", "add", "origin", remote) - cmd.Dir = repoPath - return cmd.Run() + + if commitAdded { + var revB bytes.Buffer + cmd = exec.Command("git", "rev-parse", "HEAD", options.path) + cmd.Dir = options.path + cmd.Stdout = &revB + assert.NoError(t, cmd.Run()) + revision = strings.Split(revB.String(), "\n")[0] + } + return revision } func TestInit(t *testing.T) { @@ -2604,16 +2797,16 @@ func TestInit(t *testing.T) { }) repoPath := path.Join(dir, "repo1") - require.NoError(t, initGitRepo(repoPath, "https://github.com/argo-cd/test-repo1")) + initGitRepo(t, newGitRepoOptions{path: repoPath, remote: "https://github.com/argo-cd/test-repo1", createPath: true, addEmptyCommit: false}) - service := newService(".") + service := newService(t, ".") service.rootDir = dir require.NoError(t, service.Init()) _, err := os.ReadDir(dir) require.Error(t, err) - require.NoError(t, initGitRepo(path.Join(dir, "repo2"), "https://github.com/argo-cd/test-repo2")) + initGitRepo(t, newGitRepoOptions{path: path.Join(dir, "repo2"), remote: "https://github.com/argo-cd/test-repo2", createPath: true, addEmptyCommit: false}) } // TestCheckoutRevisionCanGetNonstandardRefs shows that we can fetch a revision that points to a non-standard ref. In @@ -2926,7 +3119,7 @@ func TestErrorGetGitDirectories(t *testing.T) { want *apiclient.GitDirectoriesResponse wantErr assert.ErrorAssertionFunc }{ - {name: "InvalidRepo", fields: fields{service: newService(".")}, args: args{ + {name: "InvalidRepo", fields: fields{service: newService(t, ".")}, args: args{ ctx: context.TODO(), request: &apiclient.GitDirectoriesRequest{ Repo: nil, @@ -2935,7 +3128,7 @@ func TestErrorGetGitDirectories(t *testing.T) { }, }, want: nil, wantErr: assert.Error}, {name: "InvalidResolveRevision", fields: fields{service: func() *Service { - s, _ := newServiceWithOpt(func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { + s, _, _ := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) gitClient.On("LsRemote", mock.Anything).Return("", fmt.Errorf("ah error")) paths.On("GetPath", mock.Anything).Return(".", nil) @@ -2966,7 +3159,7 @@ func TestErrorGetGitDirectories(t *testing.T) { func TestGetGitDirectories(t *testing.T) { // test not using the cache root := "./testdata/git-files-dirs" - s, _ := newServiceWithOpt(func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { + s, _, cacheMocks := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { gitClient.On("Init").Return(nil) gitClient.On("Fetch", mock.Anything).Return(nil) gitClient.On("Checkout", mock.Anything, mock.Anything).Once().Return(nil) @@ -2989,6 +3182,10 @@ func TestGetGitDirectories(t *testing.T) { directories, err = s.GetGitDirectories(context.TODO(), dirRequest) assert.Nil(t, err) assert.ElementsMatch(t, []string{"app", "app/bar", "app/foo/bar", "somedir", "app/foo"}, directories.GetPaths()) + cacheMocks.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ + ExternalSets: 1, + ExternalGets: 2, + }) } func TestErrorGetGitFiles(t *testing.T) { @@ -3006,7 +3203,7 @@ func TestErrorGetGitFiles(t *testing.T) { want *apiclient.GitFilesResponse wantErr assert.ErrorAssertionFunc }{ - {name: "InvalidRepo", fields: fields{service: newService(".")}, args: args{ + {name: "InvalidRepo", fields: fields{service: newService(t, ".")}, args: args{ ctx: context.TODO(), request: &apiclient.GitFilesRequest{ Repo: nil, @@ -3015,7 +3212,7 @@ func TestErrorGetGitFiles(t *testing.T) { }, }, want: nil, wantErr: assert.Error}, {name: "InvalidResolveRevision", fields: fields{service: func() *Service { - s, _ := newServiceWithOpt(func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { + s, _, _ := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { gitClient.On("Checkout", mock.Anything, mock.Anything).Return(nil) gitClient.On("LsRemote", mock.Anything).Return("", fmt.Errorf("ah error")) paths.On("GetPath", mock.Anything).Return(".", nil) @@ -3048,7 +3245,7 @@ func TestGetGitFiles(t *testing.T) { files := []string{"./testdata/git-files-dirs/somedir/config.yaml", "./testdata/git-files-dirs/config.yaml", "./testdata/git-files-dirs/config.yaml", "./testdata/git-files-dirs/app/foo/bar/config.yaml"} root := "" - s, _ := newServiceWithOpt(func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { + s, _, cacheMocks := newServiceWithOpt(t, func(gitClient *gitmocks.Client, helmClient *helmmocks.Client, paths *iomocks.TempPaths) { gitClient.On("Init").Return(nil) gitClient.On("Fetch", mock.Anything).Return(nil) gitClient.On("Checkout", mock.Anything, mock.Anything).Once().Return(nil) @@ -3081,6 +3278,10 @@ func TestGetGitFiles(t *testing.T) { fileResponse, err = s.GetGitFiles(context.TODO(), filesRequest) assert.Nil(t, err) assert.Equal(t, expected, fileResponse.GetMap()) + cacheMocks.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ + ExternalSets: 1, + ExternalGets: 2, + }) } func Test_getRepoSanitizerRegex(t *testing.T) { @@ -3090,3 +3291,45 @@ func Test_getRepoSanitizerRegex(t *testing.T) { msg = r.ReplaceAllString("error message containing /tmp/_argocd-repo/SENSITIVE/with/trailing/path and other stuff", "") assert.Equal(t, "error message containing /with/trailing/path and other stuff", msg) } + +func TestGetRevisionChartDetails(t *testing.T) { + t.Run("Test revision semvar", func(t *testing.T) { + root := t.TempDir() + service := newService(t, root) + _, err := service.GetRevisionChartDetails(context.Background(), &apiclient.RepoServerRevisionChartDetailsRequest{ + Repo: &v1alpha1.Repository{ + Repo: fmt.Sprintf("file://%s", root), + Name: "test-repo-name", + Type: "helm", + }, + Name: "test-name", + Revision: "test-revision", + }) + assert.ErrorContains(t, err, "invalid revision") + }) + + t.Run("Test GetRevisionChartDetails", func(t *testing.T) { + root := t.TempDir() + service := newService(t, root) + repoUrl := fmt.Sprintf("file://%s", root) + err := service.cache.SetRevisionChartDetails(repoUrl, "my-chart", "1.1.0", &argoappv1.ChartDetails{ + Description: "test-description", + Home: "test-home", + Maintainers: []string{"test-maintainer"}, + }) + assert.NoError(t, err) + chartDetails, err := service.GetRevisionChartDetails(context.Background(), &apiclient.RepoServerRevisionChartDetailsRequest{ + Repo: &v1alpha1.Repository{ + Repo: fmt.Sprintf("file://%s", root), + Name: "test-repo-name", + Type: "helm", + }, + Name: "my-chart", + Revision: "1.1.0", + }) + assert.NoError(t, err) + assert.Equal(t, "test-description", chartDetails.Description) + assert.Equal(t, "test-home", chartDetails.Home) + assert.Equal(t, []string{"test-maintainer"}, chartDetails.Maintainers) + }) +} diff --git a/util/cache/mocks/cacheclient.go b/util/cache/mocks/cacheclient.go new file mode 100644 index 0000000000000..e653847ec49a8 --- /dev/null +++ b/util/cache/mocks/cacheclient.go @@ -0,0 +1,65 @@ +package mocks + +import ( + "context" + "time" + + cache "github.com/argoproj/argo-cd/v2/util/cache" + "github.com/stretchr/testify/mock" +) + +type MockCacheClient struct { + mock.Mock + BaseCache cache.CacheClient + ReadDelay time.Duration + WriteDelay time.Duration +} + +func (c *MockCacheClient) Set(item *cache.Item) error { + args := c.Called(item) + if len(args) > 0 && args.Get(0) != nil { + return args.Get(0).(error) + } + if c.WriteDelay > 0 { + time.Sleep(c.WriteDelay) + } + return c.BaseCache.Set(item) +} + +func (c *MockCacheClient) Get(key string, obj interface{}) error { + args := c.Called(key, obj) + if len(args) > 0 && args.Get(0) != nil { + return args.Get(0).(error) + } + if c.ReadDelay > 0 { + time.Sleep(c.ReadDelay) + } + return c.BaseCache.Get(key, obj) +} + +func (c *MockCacheClient) Delete(key string) error { + args := c.Called(key) + if len(args) > 0 && args.Get(0) != nil { + return args.Get(0).(error) + } + if c.WriteDelay > 0 { + time.Sleep(c.WriteDelay) + } + return c.BaseCache.Delete(key) +} + +func (c *MockCacheClient) OnUpdated(ctx context.Context, key string, callback func() error) error { + args := c.Called(ctx, key, callback) + if len(args) > 0 && args.Get(0) != nil { + return args.Get(0).(error) + } + return c.BaseCache.OnUpdated(ctx, key, callback) +} + +func (c *MockCacheClient) NotifyUpdated(key string) error { + args := c.Called(key) + if len(args) > 0 && args.Get(0) != nil { + return args.Get(0).(error) + } + return c.BaseCache.NotifyUpdated(key) +} From 27e927be82e7eaddb7735db827149991fb612165 Mon Sep 17 00:00:00 2001 From: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Date: Thu, 30 Nov 2023 12:02:08 -0500 Subject: [PATCH 14/66] chore(deps): bump cosign-installer from 3.1.2 to 3.2.0 (#16495) Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --- .github/workflows/image-reuse.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/image-reuse.yaml b/.github/workflows/image-reuse.yaml index ac111ae6395a7..0838f38e4230d 100644 --- a/.github/workflows/image-reuse.yaml +++ b/.github/workflows/image-reuse.yaml @@ -74,9 +74,9 @@ jobs: go-version: ${{ inputs.go-version }} - name: Install cosign - uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2 + uses: sigstore/cosign-installer@1fc5bd396d372bee37d608f955b336615edf79c8 # v3.2.0 with: - cosign-release: 'v2.0.2' + cosign-release: 'v2.2.1' - uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2.2.0 - uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 From 19fa5b94180e96829d6d8d8cd769973ab0a88d0d Mon Sep 17 00:00:00 2001 From: May Zhang Date: Thu, 30 Nov 2023 13:40:33 -0800 Subject: [PATCH 15/66] feat: Argocd notification self service (#16488) * self service notification Signed-off-by: May Zhang * revert back the changes for redis-ha Signed-off-by: May Zhang * revert back the changes for redis-ha Signed-off-by: May Zhang * update notification engine Signed-off-by: May Zhang * re-trigger build Signed-off-by: May Zhang * self service notification Signed-off-by: May Zhang * revert back the changes for redis-ha Signed-off-by: May Zhang * revert back the changes for redis-ha Signed-off-by: May Zhang * update notification engine Signed-off-by: May Zhang * re-trigger build Signed-off-by: May Zhang * fix conflict Signed-off-by: May Zhang * fix conflict Signed-off-by: May Zhang * fix conflict Signed-off-by: May Zhang * fix conflict Signed-off-by: May Zhang * update notification enginer version Signed-off-by: May Zhang * update notification enginer version Signed-off-by: May Zhang * fixing go tidy Signed-off-by: May Zhang * fixing go tidy Signed-off-by: May Zhang * fixing go tidy Signed-off-by: May Zhang * fixing go tidy Signed-off-by: May Zhang * fixing go tidy Signed-off-by: May Zhang * add back checkAppNotInAdditionalNamespaces Signed-off-by: May Zhang * add cm and secret to clusterRole Signed-off-by: May Zhang * if applicationNamespaces is not used, then use namespaced appClient Signed-off-by: May Zhang * fix merge conflict Signed-off-by: May Zhang * fix doc and test based on review Signed-off-by: May Zhang * self service notification Signed-off-by: May Zhang * revert back the changes for redis-ha Signed-off-by: May Zhang * revert back the changes for redis-ha Signed-off-by: May Zhang * update notification engine Signed-off-by: May Zhang * re-trigger build Signed-off-by: May Zhang * fix conflict Signed-off-by: May Zhang * self service notification Signed-off-by: May Zhang * revert back the changes for redis-ha Signed-off-by: May Zhang * revert back the changes for redis-ha Signed-off-by: May Zhang * update notification engine Signed-off-by: May Zhang * re-trigger build Signed-off-by: May Zhang * fix conflict Signed-off-by: May Zhang * fix conflict Signed-off-by: May Zhang * fix conflict Signed-off-by: May Zhang * update notification enginer version Signed-off-by: May Zhang * update notification enginer version Signed-off-by: May Zhang * fixing go tidy Signed-off-by: May Zhang * fixing go tidy Signed-off-by: May Zhang * fixing go tidy Signed-off-by: May Zhang * fixing go tidy Signed-off-by: May Zhang * fixing go tidy Signed-off-by: May Zhang * add back checkAppNotInAdditionalNamespaces Signed-off-by: May Zhang * add cm and secret to clusterRole Signed-off-by: May Zhang * if applicationNamespaces is not used, then use namespaced appClient Signed-off-by: May Zhang * fix doc and test based on review Signed-off-by: May Zhang * disable defining and using secrets within notification templates for self-service Signed-off-by: May Zhang * tweaks Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * fix docs formatting Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * more docs and Procfile update for local run convenience Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --------- Signed-off-by: May Zhang Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --- Procfile | 3 +- .../commands/controller.go | 30 ++++---- cmd/argocd/commands/admin/notifications.go | 2 +- docs/operator-manual/app-any-namespace.md | 5 +- .../operator-manual/argocd-cmd-params-cm.yaml | 2 + docs/operator-manual/notifications/index.md | 68 +++++++++++++++++++ ...fications-controller-rbac-clusterrole.yaml | 11 ++- ...d-notifications-controller-deployment.yaml | 6 ++ manifests/ha/install.yaml | 6 ++ manifests/ha/namespace-install.yaml | 6 ++ manifests/install.yaml | 6 ++ manifests/namespace-install.yaml | 6 ++ .../controller/controller.go | 54 ++++++++++----- .../controller/controller_test.go | 45 ++++++------ server/notification/notification_test.go | 2 +- server/server.go | 2 +- util/notification/settings/settings.go | 29 +++++++- 17 files changed, 224 insertions(+), 59 deletions(-) diff --git a/Procfile b/Procfile index 2bb26a086fb1d..92f69ecf8ffbc 100644 --- a/Procfile +++ b/Procfile @@ -9,4 +9,5 @@ git-server: test/fixture/testrepos/start-git.sh helm-registry: test/fixture/testrepos/start-helm-registry.sh dev-mounter: [[ "$ARGOCD_E2E_TEST" != "true" ]] && go run hack/dev-mounter/main.go --configmap argocd-ssh-known-hosts-cm=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} --configmap argocd-tls-certs-cm=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} --configmap argocd-gpg-keys-cm=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} applicationset-controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}" -notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug" +notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --self-service-notification-enabled=${ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED:-'false'}" + diff --git a/cmd/argocd-notification/commands/controller.go b/cmd/argocd-notification/commands/controller.go index abd9a2e8475f0..cb30fd5277d4b 100644 --- a/cmd/argocd-notification/commands/controller.go +++ b/cmd/argocd-notification/commands/controller.go @@ -43,19 +43,20 @@ func addK8SFlagsToCmd(cmd *cobra.Command) clientcmd.ClientConfig { func NewCommand() *cobra.Command { var ( - clientConfig clientcmd.ClientConfig - processorsCount int - namespace string - appLabelSelector string - logLevel string - logFormat string - metricsPort int - argocdRepoServer string - argocdRepoServerPlaintext bool - argocdRepoServerStrictTLS bool - configMapName string - secretName string - applicationNamespaces []string + clientConfig clientcmd.ClientConfig + processorsCount int + namespace string + appLabelSelector string + logLevel string + logFormat string + metricsPort int + argocdRepoServer string + argocdRepoServerPlaintext bool + argocdRepoServerStrictTLS bool + configMapName string + secretName string + applicationNamespaces []string + selfServiceNotificationEnabled bool ) var command = cobra.Command{ Use: "controller", @@ -139,7 +140,7 @@ func NewCommand() *cobra.Command { log.Infof("serving metrics on port %d", metricsPort) log.Infof("loading configuration %d", metricsPort) - ctrl := notificationscontroller.NewController(k8sClient, dynamicClient, argocdService, namespace, applicationNamespaces, appLabelSelector, registry, secretName, configMapName) + ctrl := notificationscontroller.NewController(k8sClient, dynamicClient, argocdService, namespace, applicationNamespaces, appLabelSelector, registry, secretName, configMapName, selfServiceNotificationEnabled) err = ctrl.Init(ctx) if err != nil { return fmt.Errorf("failed to initialize controller: %w", err) @@ -163,5 +164,6 @@ func NewCommand() *cobra.Command { command.Flags().StringVar(&configMapName, "config-map-name", "argocd-notifications-cm", "Set notifications ConfigMap name") command.Flags().StringVar(&secretName, "secret-name", "argocd-notifications-secret", "Set notifications Secret name") command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces that this controller should send notifications for") + command.Flags().BoolVar(&selfServiceNotificationEnabled, "self-service-notification-enabled", env.ParseBoolFromEnv("ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED", false), "Allows the Argo CD notification controller to pull notification config from the namespace that the resource is in. This is useful for self-service notification.") return &command } diff --git a/cmd/argocd/commands/admin/notifications.go b/cmd/argocd/commands/admin/notifications.go index a1234cc53b7fe..3cbac0a53b5c2 100644 --- a/cmd/argocd/commands/admin/notifications.go +++ b/cmd/argocd/commands/admin/notifications.go @@ -36,7 +36,7 @@ func NewNotificationsCommand() *cobra.Command { "notifications", "argocd admin notifications", applications, - settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm"), func(clientConfig clientcmd.ClientConfig) { + settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm", false), func(clientConfig clientcmd.ClientConfig) { k8sCfg, err := clientConfig.ClientConfig() if err != nil { log.Fatalf("Failed to parse k8s config: %v", err) diff --git a/docs/operator-manual/app-any-namespace.md b/docs/operator-manual/app-any-namespace.md index 21743b7bc003d..21bfa5c4f5a0b 100644 --- a/docs/operator-manual/app-any-namespace.md +++ b/docs/operator-manual/app-any-namespace.md @@ -15,7 +15,10 @@ Some manual steps will need to be performed by the Argo CD administrator in orde !!! note This feature is considered beta as of now. Some of the implementation details may change over the course of time until it is promoted to a stable status. We will be happy if early adopters use this feature and provide us with bug reports and feedback. - + + +One additional advantage of adopting applications in any namespace is to allow end-users to configure notifications for their Argo CD application in the namespace where Argo CD application is running in. See notifications [namespace based configuration](notifications/index.md#namespace-based-configuration) page for more information. + ## Prerequisites ### Cluster-scoped Argo CD installation diff --git a/docs/operator-manual/argocd-cmd-params-cm.yaml b/docs/operator-manual/argocd-cmd-params-cm.yaml index 695119bf0a27f..5e8c04c7e50d6 100644 --- a/docs/operator-manual/argocd-cmd-params-cm.yaml +++ b/docs/operator-manual/argocd-cmd-params-cm.yaml @@ -210,3 +210,5 @@ data: notificationscontroller.log.level: "info" # Set the logging format. One of: text|json (default "text") notificationscontroller.log.format: "text" + # Enable self-service notifications config. Used in conjunction with apps-in-any-namespace. (default "false") + notificationscontroller.selfservice.enabled: "false" diff --git a/docs/operator-manual/notifications/index.md b/docs/operator-manual/notifications/index.md index c719d10e7611c..3609089e23d08 100644 --- a/docs/operator-manual/notifications/index.md +++ b/docs/operator-manual/notifications/index.md @@ -45,3 +45,71 @@ So you can just use them instead of reinventing new ones. ``` Try syncing an application to get notified when the sync is completed. + +## Namespace based configuration + +A common installation method for Argo CD Notifications is to install it in a dedicated namespace to manage a whole cluster. In this case, the administrator is the only +person who can configure notifications in that namespace generally. However, in some cases, it is required to allow end-users to configure notifications +for their Argo CD applications. For example, the end-user can configure notifications for their Argo CD application in the namespace where they have access to and their Argo CD application is running in. + +This feature is based on applications in any namespace. See [applications in any namespace](../app-any-namespace.md) page for more information. + +In order to enable this feature, the Argo CD administrator must reconfigure the argocd-notification-controller workloads to add `--application-namespaces` and `--self-service-notification-enabled` parameters to the container's startup command. +`--application-namespaces` controls the list of namespaces that Argo CD applications are in. `--self-service-notification-enabled` turns on this feature. + +The startup parameters for both can also be conveniently set up and kept in sync by specifying +the `application.namespaces` and `notificationscontroller.selfservice.enabled` in the argocd-cmd-params-cm ConfigMap instead of changing the manifests for the respective workloads. For example: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cmd-params-cm +data: + application.namespaces: app-team-one, app-team-two + notificationscontroller.selfservice.enabled: true +``` + +To use this feature, you can deploy configmap named `argocd-notifications-cm` and possibly a secret `argocd-notifications-secret` in the namespace where the Argo CD application lives. + +When it is configured this way the controller will send notifications using both the controller level configuration (the configmap located in the same namespaces as the controller) as well as +the configuration located in the same namespace where the Argo CD application is at. + +Example: Application team wants to receive notifications using PagerDutyV2, when the controller level configuration is only supporting Slack. + +The following two resources are deployed in the namespace where the Argo CD application lives. +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-notifications-cm +data: + service.pagerdutyv2: | + serviceKeys: + my-service: $pagerduty-key-my-service +... +``` +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: argo-cd-notification-secret +type: Opaque +data: + pagerduty-key-my-service: +``` + +When an Argo CD application has the following subscriptions, user receives application sync failure message from pager duty. +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + notifications.argoproj.io/subscribe.on-sync-failed.pagerdutyv2: "" +``` + +!!! note + When the same notification service and trigger are defined in controller level configuration and application level configuration, + both notifications will be sent according to its own configuration. + +[Defining and using secrets within notification templates](templates.md/#defining-and-using-secrets-within-notification-templates) function is not available when flag `--self-service-notification-enable` is on. diff --git a/examples/k8s-rbac/argocd-server-applications/argocd-notifications-controller-rbac-clusterrole.yaml b/examples/k8s-rbac/argocd-server-applications/argocd-notifications-controller-rbac-clusterrole.yaml index 05f92abb11717..ecbf6de3efb01 100644 --- a/examples/k8s-rbac/argocd-server-applications/argocd-notifications-controller-rbac-clusterrole.yaml +++ b/examples/k8s-rbac/argocd-server-applications/argocd-notifications-controller-rbac-clusterrole.yaml @@ -16,4 +16,13 @@ rules: - list - watch - update - - patch \ No newline at end of file + - patch +- apiGroups: + - "" + resources: + - secrets + - configmaps + verbs: + - get + - list + - watch \ No newline at end of file diff --git a/manifests/base/notification/argocd-notifications-controller-deployment.yaml b/manifests/base/notification/argocd-notifications-controller-deployment.yaml index 9cd1a068808b1..876a207c16e42 100644 --- a/manifests/base/notification/argocd-notifications-controller-deployment.yaml +++ b/manifests/base/notification/argocd-notifications-controller-deployment.yaml @@ -54,6 +54,12 @@ spec: key: application.namespaces name: argocd-cmd-params-cm optional: true + - name: ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED + valueFrom: + configMapKeyRef: + key: notificationscontroller.selfservice.enabled + name: argocd-cmd-params-cm + optional: true workingDir: /app livenessProbe: tcpSocket: diff --git a/manifests/ha/install.yaml b/manifests/ha/install.yaml index 50254b138d6ab..f693fd4eb887c 100644 --- a/manifests/ha/install.yaml +++ b/manifests/ha/install.yaml @@ -22472,6 +22472,12 @@ spec: key: application.namespaces name: argocd-cmd-params-cm optional: true + - name: ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED + valueFrom: + configMapKeyRef: + key: notificationscontroller.selfservice.enabled + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always livenessProbe: diff --git a/manifests/ha/namespace-install.yaml b/manifests/ha/namespace-install.yaml index 59aad0e49bda3..2ecd016092f1b 100644 --- a/manifests/ha/namespace-install.yaml +++ b/manifests/ha/namespace-install.yaml @@ -1859,6 +1859,12 @@ spec: key: application.namespaces name: argocd-cmd-params-cm optional: true + - name: ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED + valueFrom: + configMapKeyRef: + key: notificationscontroller.selfservice.enabled + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always livenessProbe: diff --git a/manifests/install.yaml b/manifests/install.yaml index 4fd267106eaf6..6e9ebcc8ea178 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -21567,6 +21567,12 @@ spec: key: application.namespaces name: argocd-cmd-params-cm optional: true + - name: ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED + valueFrom: + configMapKeyRef: + key: notificationscontroller.selfservice.enabled + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always livenessProbe: diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index fdd4eacd14efb..d74ca00b88e4c 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -954,6 +954,12 @@ spec: key: application.namespaces name: argocd-cmd-params-cm optional: true + - name: ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED + valueFrom: + configMapKeyRef: + key: notificationscontroller.selfservice.enabled + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always livenessProbe: diff --git a/notification_controller/controller/controller.go b/notification_controller/controller/controller.go index 32dfac2b75a3b..7d871af4c44a3 100644 --- a/notification_controller/controller/controller.go +++ b/notification_controller/controller/controller.go @@ -63,19 +63,27 @@ func NewController( registry *controller.MetricsRegistry, secretName string, configMapName string, + selfServiceNotificationEnabled bool, ) *notificationController { var appClient dynamic.ResourceInterface + namespaceableAppClient := client.Resource(applications) appClient = namespaceableAppClient + if len(applicationNamespaces) == 0 { appClient = namespaceableAppClient.Namespace(namespace) } - appInformer := newInformer(appClient, namespace, applicationNamespaces, appLabelSelector) appProjInformer := newInformer(newAppProjClient(client, namespace), namespace, []string{namespace}, "") - secretInformer := k8s.NewSecretInformer(k8sClient, namespace, secretName) - configMapInformer := k8s.NewConfigMapInformer(k8sClient, namespace, configMapName) - apiFactory := api.NewFactory(settings.GetFactorySettings(argocdService, secretName, configMapName), namespace, secretInformer, configMapInformer) + var notificationConfigNamespace string + if selfServiceNotificationEnabled { + notificationConfigNamespace = v1.NamespaceAll + } else { + notificationConfigNamespace = namespace + } + secretInformer := k8s.NewSecretInformer(k8sClient, notificationConfigNamespace, secretName) + configMapInformer := k8s.NewConfigMapInformer(k8sClient, notificationConfigNamespace, configMapName) + apiFactory := api.NewFactory(settings.GetFactorySettings(argocdService, secretName, configMapName, selfServiceNotificationEnabled), namespace, secretInformer, configMapInformer) res := ¬ificationController{ secretInformer: secretInformer, @@ -83,19 +91,30 @@ func NewController( appInformer: appInformer, appProjInformer: appProjInformer, apiFactory: apiFactory} - res.ctrl = controller.NewController(namespaceableAppClient, appInformer, apiFactory, - controller.WithSkipProcessing(func(obj v1.Object) (bool, string) { - app, ok := (obj).(*unstructured.Unstructured) - if !ok { - return false, "" - } - if checkAppNotInAdditionalNamespaces(app, namespace, applicationNamespaces) { - return true, "app is not in one of the application-namespaces, nor the notification controller namespace" - } - return !isAppSyncStatusRefreshed(app, log.WithField("app", obj.GetName())), "sync status out of date" - }), - controller.WithMetricsRegistry(registry), - controller.WithAlterDestinations(res.alterDestinations)) + skipProcessingOpt := controller.WithSkipProcessing(func(obj v1.Object) (bool, string) { + app, ok := (obj).(*unstructured.Unstructured) + if !ok { + return false, "" + } + if checkAppNotInAdditionalNamespaces(app, namespace, applicationNamespaces) { + return true, "app is not in one of the application-namespaces, nor the notification controller namespace" + } + return !isAppSyncStatusRefreshed(app, log.WithField("app", obj.GetName())), "sync status out of date" + }) + metricsRegistryOpt := controller.WithMetricsRegistry(registry) + alterDestinationsOpt := controller.WithAlterDestinations(res.alterDestinations) + + if !selfServiceNotificationEnabled { + res.ctrl = controller.NewController(namespaceableAppClient, appInformer, apiFactory, + skipProcessingOpt, + metricsRegistryOpt, + alterDestinationsOpt) + } else { + res.ctrl = controller.NewControllerWithNamespaceSupport(namespaceableAppClient, appInformer, apiFactory, + skipProcessingOpt, + metricsRegistryOpt, + alterDestinationsOpt) + } return res } @@ -118,6 +137,7 @@ func (c *notificationController) alterDestinations(obj v1.Object, destinations s } func newInformer(resClient dynamic.ResourceInterface, controllerNamespace string, applicationNamespaces []string, selector string) cache.SharedIndexInformer { + informer := cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func(options v1.ListOptions) (runtime.Object, error) { diff --git a/notification_controller/controller/controller_test.go b/notification_controller/controller/controller_test.go index 5ad1e520502a3..4eedb28f5e001 100644 --- a/notification_controller/controller/controller_test.go +++ b/notification_controller/controller/controller_test.go @@ -110,26 +110,30 @@ func TestInit(t *testing.T) { k8sClient := k8sfake.NewSimpleClientset() appLabelSelector := "app=test" - nc := NewController( - k8sClient, - dynamicClient, - nil, - "default", - []string{}, - appLabelSelector, - nil, - "my-secret", - "my-configmap", - ) - - assert.NotNil(t, nc) - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - err = nc.Init(ctx) - - assert.NoError(t, err) + selfServiceNotificationEnabledFlags := []bool{false, true} + for _, selfServiceNotificationEnabled := range selfServiceNotificationEnabledFlags { + nc := NewController( + k8sClient, + dynamicClient, + nil, + "default", + []string{}, + appLabelSelector, + nil, + "my-secret", + "my-configmap", + selfServiceNotificationEnabled, + ) + + assert.NotNil(t, nc) + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + err = nc.Init(ctx) + + assert.NoError(t, err) + } } func TestInitTimeout(t *testing.T) { @@ -152,6 +156,7 @@ func TestInitTimeout(t *testing.T) { nil, "my-secret", "my-configmap", + false, ) assert.NotNil(t, nc) diff --git a/server/notification/notification_test.go b/server/notification/notification_test.go index c1141e7ad6753..ee913926bc010 100644 --- a/server/notification/notification_test.go +++ b/server/notification/notification_test.go @@ -70,7 +70,7 @@ func TestNotificationServer(t *testing.T) { argocdService, err := service.NewArgoCDService(kubeclientset, testNamespace, mockRepoClient) require.NoError(t, err) defer argocdService.Close() - apiFactory := api.NewFactory(settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm"), testNamespace, secretInformer, configMapInformer) + apiFactory := api.NewFactory(settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm", false), testNamespace, secretInformer, configMapInformer) t.Run("TestListServices", func(t *testing.T) { server := NewServer(apiFactory) diff --git a/server/server.go b/server/server.go index d9f1638024c51..0f9b0ddadd800 100644 --- a/server/server.go +++ b/server/server.go @@ -288,7 +288,7 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer { secretInformer := k8s.NewSecretInformer(opts.KubeClientset, opts.Namespace, "argocd-notifications-secret") configMapInformer := k8s.NewConfigMapInformer(opts.KubeClientset, opts.Namespace, "argocd-notifications-cm") - apiFactory := api.NewFactory(settings_notif.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm"), opts.Namespace, secretInformer, configMapInformer) + apiFactory := api.NewFactory(settings_notif.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm", false), opts.Namespace, secretInformer, configMapInformer) dbInstance := db.NewDB(opts.Namespace, settingsMgr, opts.KubeClientset) logger := log.NewEntry(log.StandardLogger()) diff --git a/util/notification/settings/settings.go b/util/notification/settings/settings.go index ed6a44b60f365..79d70499aaea6 100644 --- a/util/notification/settings/settings.go +++ b/util/notification/settings/settings.go @@ -12,17 +12,20 @@ import ( service "github.com/argoproj/argo-cd/v2/util/notification/argocd" ) -func GetFactorySettings(argocdService service.Service, secretName, configMapName string) api.Settings { +func GetFactorySettings(argocdService service.Service, secretName, configMapName string, selfServiceNotificationEnabled bool) api.Settings { return api.Settings{ SecretName: secretName, ConfigMapName: configMapName, InitGetVars: func(cfg *api.Config, configMap *v1.ConfigMap, secret *v1.Secret) (api.GetVars, error) { + if selfServiceNotificationEnabled { + return initGetVarsWithoutSecret(argocdService, cfg, configMap, secret) + } return initGetVars(argocdService, cfg, configMap, secret) }, } } -func initGetVars(argocdService service.Service, cfg *api.Config, configMap *v1.ConfigMap, secret *v1.Secret) (api.GetVars, error) { +func getContext(cfg *api.Config, configMap *v1.ConfigMap, secret *v1.Secret) (map[string]string, error) { context := map[string]string{} if contextYaml, ok := configMap.Data["context"]; ok { if err := yaml.Unmarshal([]byte(contextYaml), &context); err != nil { @@ -32,6 +35,28 @@ func initGetVars(argocdService service.Service, cfg *api.Config, configMap *v1.C if err := ApplyLegacyConfig(cfg, context, configMap, secret); err != nil { return nil, err } + return context, nil +} + +func initGetVarsWithoutSecret(argocdService service.Service, cfg *api.Config, configMap *v1.ConfigMap, secret *v1.Secret) (api.GetVars, error) { + context, err := getContext(cfg, configMap, secret) + if err != nil { + return nil, err + } + + return func(obj map[string]interface{}, dest services.Destination) map[string]interface{} { + return expression.Spawn(&unstructured.Unstructured{Object: obj}, argocdService, map[string]interface{}{ + "app": obj, + "context": injectLegacyVar(context, dest.Service), + }) + }, nil +} + +func initGetVars(argocdService service.Service, cfg *api.Config, configMap *v1.ConfigMap, secret *v1.Secret) (api.GetVars, error) { + context, err := getContext(cfg, configMap, secret) + if err != nil { + return nil, err + } return func(obj map[string]interface{}, dest services.Destination) map[string]interface{} { return expression.Spawn(&unstructured.Unstructured{Object: obj}, argocdService, map[string]interface{}{ From 86565852a474347176880b21325a2ebc718658d2 Mon Sep 17 00:00:00 2001 From: Jessie Teng <101035990+JessieTeng89@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:07:34 +0800 Subject: [PATCH 16/66] fix: Tooltips point in wrong direction#11935 (#12578) * fix: Tooltips point in wrong direction#11935 Signed-off-by: Teng, Jessie * fix: Tooltips point in wrong direction#11935 Signed-off-by: Teng --------- Signed-off-by: Teng, Jessie Signed-off-by: Teng Co-authored-by: Teng, Jessie --- ui/src/app/sidebar/sidebar.scss | 7 +++++++ ui/src/app/sidebar/sidebar.tsx | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ui/src/app/sidebar/sidebar.scss b/ui/src/app/sidebar/sidebar.scss index 20ab71b969294..d41cbeed3f7cf 100644 --- a/ui/src/app/sidebar/sidebar.scss +++ b/ui/src/app/sidebar/sidebar.scss @@ -58,6 +58,13 @@ $deselected-text: #818d94; text-overflow: ellipsis; } + &__tooltip { + max-width: 300px; + @media screen and (max-width: 590px) { + max-width: 250px; + } + } + &__nav-item { cursor: pointer; display: flex; diff --git a/ui/src/app/sidebar/sidebar.tsx b/ui/src/app/sidebar/sidebar.tsx index c690565d01cb5..1aeb77c9112ee 100644 --- a/ui/src/app/sidebar/sidebar.tsx +++ b/ui/src/app/sidebar/sidebar.tsx @@ -64,7 +64,7 @@ export const Sidebar = (props: SidebarProps) => {
{(props.navItems || []).map(item => ( - + {item?.tooltip || item.title}
} {...tooltipProps}>
Date: Fri, 1 Dec 2023 11:13:33 -0500 Subject: [PATCH 17/66] feat(appset): Advanced Templating using templatePatch (#14893) * fix(11164): Advanced templating using patchTemplate Signed-off-by: gmuselli * small changes Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --------- Signed-off-by: gmuselli Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --- .../controllers/applicationset_controller.go | 31 + .../applicationset_controller_test.go | 12 + applicationset/controllers/templatePatch.go | 46 + .../controllers/templatePatch_test.go | 249 +++ applicationset/utils/utils.go | 1 + assets/swagger.json | 3 + .../applicationset/Template.md | 66 + manifests/core-install.yaml | 2 + manifests/crds/applicationset-crd.yaml | 2 + manifests/ha/install.yaml | 2 + manifests/install.yaml | 2 + .../v1alpha1/applicationset_types.go | 1 + pkg/apis/application/v1alpha1/generated.pb.go | 1388 +++++++++-------- pkg/apis/application/v1alpha1/generated.proto | 2 + .../application/v1alpha1/openapi_generated.go | 6 + .../v1alpha1/zz_generated.deepcopy.go | 5 + test/e2e/applicationset_test.go | 128 ++ 17 files changed, 1275 insertions(+), 671 deletions(-) create mode 100644 applicationset/controllers/templatePatch.go create mode 100644 applicationset/controllers/templatePatch_test.go diff --git a/applicationset/controllers/applicationset_controller.go b/applicationset/controllers/applicationset_controller.go index 5f1205caf384a..527a4ae1e3883 100644 --- a/applicationset/controllers/applicationset_controller.go +++ b/applicationset/controllers/applicationset_controller.go @@ -524,6 +524,7 @@ func (r *ApplicationSetReconciler) generateApplications(logCtx *log.Entry, appli for _, p := range a.Params { app, err := r.Renderer.RenderTemplateParams(tmplApplication, applicationSetInfo.Spec.SyncPolicy, p, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions) + if err != nil { logCtx.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator). Error("error generating application from params") @@ -534,6 +535,24 @@ func (r *ApplicationSetReconciler) generateApplications(logCtx *log.Entry, appli } continue } + + if applicationSetInfo.Spec.TemplatePatch != nil { + patchedApplication, err := r.applyTemplatePatch(app, applicationSetInfo, p) + + if err != nil { + log.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator). + Error("error generating application from params") + + if firstError == nil { + firstError = err + applicationSetReason = argov1alpha1.ApplicationSetReasonRenderTemplateParamsError + } + continue + } + + app = patchedApplication + } + res = append(res, *app) } } @@ -545,6 +564,16 @@ func (r *ApplicationSetReconciler) generateApplications(logCtx *log.Entry, appli return res, applicationSetReason, firstError } +func (r *ApplicationSetReconciler) applyTemplatePatch(app *argov1alpha1.Application, applicationSetInfo argov1alpha1.ApplicationSet, params map[string]interface{}) (*argov1alpha1.Application, error) { + replacedTemplate, err := r.Renderer.Replace(*applicationSetInfo.Spec.TemplatePatch, params, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions) + + if err != nil { + return nil, fmt.Errorf("error replacing values in templatePatch: %w", err) + } + + return applyTemplatePatch(app, replacedTemplate) +} + func ignoreNotAllowedNamespaces(namespaces []string) predicate.Predicate { return predicate.Funcs{ CreateFunc: func(e event.CreateEvent) bool { @@ -619,6 +648,8 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, var firstError error // Creates or updates the application in appList for _, generatedApp := range desiredApplications { + // The app's namespace must be the same as the AppSet's namespace to preserve the appsets-in-any-namespace + // security boundary. generatedApp.Namespace = applicationSet.Namespace appLog := logCtx.WithFields(log.Fields{"app": generatedApp.QualifiedName()}) diff --git a/applicationset/controllers/applicationset_controller_test.go b/applicationset/controllers/applicationset_controller_test.go index ce9dc485ba4a8..81fbad95ac50b 100644 --- a/applicationset/controllers/applicationset_controller_test.go +++ b/applicationset/controllers/applicationset_controller_test.go @@ -86,6 +86,12 @@ func (g *generatorMock) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetG return args.Get(0).([]map[string]interface{}), args.Error(1) } +func (g *generatorMock) Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) { + args := g.Called(tmpl, replaceMap, useGoTemplate, goTemplateOptions) + + return args.Get(0).(string), args.Error(1) +} + type rendererMock struct { mock.Mock } @@ -107,6 +113,12 @@ func (r *rendererMock) RenderTemplateParams(tmpl *v1alpha1.Application, syncPoli } +func (r *rendererMock) Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) { + args := r.Called(tmpl, replaceMap, useGoTemplate, goTemplateOptions) + + return args.Get(0).(string), args.Error(1) +} + func TestExtractApplications(t *testing.T) { scheme := runtime.NewScheme() err := v1alpha1.AddToScheme(scheme) diff --git a/applicationset/controllers/templatePatch.go b/applicationset/controllers/templatePatch.go new file mode 100644 index 0000000000000..f8efd9f376996 --- /dev/null +++ b/applicationset/controllers/templatePatch.go @@ -0,0 +1,46 @@ +package controllers + +import ( + "encoding/json" + "fmt" + + "k8s.io/apimachinery/pkg/util/strategicpatch" + + "github.com/argoproj/argo-cd/v2/applicationset/utils" + appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +func applyTemplatePatch(app *appv1.Application, templatePatch string) (*appv1.Application, error) { + + appString, err := json.Marshal(app) + if err != nil { + return nil, fmt.Errorf("error while marhsalling Application %w", err) + } + + convertedTemplatePatch, err := utils.ConvertYAMLToJSON(templatePatch) + + if err != nil { + return nil, fmt.Errorf("error while converting template to json %q: %w", convertedTemplatePatch, err) + } + + if err := json.Unmarshal([]byte(convertedTemplatePatch), &appv1.Application{}); err != nil { + return nil, fmt.Errorf("invalid templatePatch %q: %w", convertedTemplatePatch, err) + } + + data, err := strategicpatch.StrategicMergePatch(appString, []byte(convertedTemplatePatch), appv1.Application{}) + + if err != nil { + return nil, fmt.Errorf("error while applying templatePatch template to json %q: %w", convertedTemplatePatch, err) + } + + finalApp := appv1.Application{} + err = json.Unmarshal(data, &finalApp) + if err != nil { + return nil, fmt.Errorf("error while unmarhsalling patched application: %w", err) + } + + // Prevent changes to the `project` field. This helps prevent malicious template patches + finalApp.Spec.Project = app.Spec.Project + + return &finalApp, nil +} diff --git a/applicationset/controllers/templatePatch_test.go b/applicationset/controllers/templatePatch_test.go new file mode 100644 index 0000000000000..c1a794077c8ee --- /dev/null +++ b/applicationset/controllers/templatePatch_test.go @@ -0,0 +1,249 @@ +package controllers + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +func Test_ApplyTemplatePatch(t *testing.T) { + testCases := []struct { + name string + appTemplate *appv1.Application + templatePatch string + expectedApp *appv1.Application + }{ + { + name: "patch with JSON", + appTemplate: &appv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: "namespace", + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: appv1.ApplicationSpec{ + Project: "default", + Source: &appv1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: appv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + }, + templatePatch: `{ + "metadata": { + "annotations": { + "annotation-some-key": "annotation-some-value" + } + }, + "spec": { + "source": { + "helm": { + "valueFiles": [ + "values.test.yaml", + "values.big.yaml" + ] + } + }, + "syncPolicy": { + "automated": { + "prune": true + } + } + } + }`, + expectedApp: &appv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: "namespace", + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + Annotations: map[string]string{ + "annotation-some-key": "annotation-some-value", + }, + }, + Spec: appv1.ApplicationSpec{ + Project: "default", + Source: &appv1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + Helm: &appv1.ApplicationSourceHelm{ + ValueFiles: []string{ + "values.test.yaml", + "values.big.yaml", + }, + }, + }, + Destination: appv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + SyncPolicy: &appv1.SyncPolicy{ + Automated: &appv1.SyncPolicyAutomated{ + Prune: true, + }, + }, + }, + }, + }, + { + name: "patch with YAML", + appTemplate: &appv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: "namespace", + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + }, + Spec: appv1.ApplicationSpec{ + Project: "default", + Source: &appv1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: appv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + }, + templatePatch: ` +metadata: + annotations: + annotation-some-key: annotation-some-value +spec: + source: + helm: + valueFiles: + - values.test.yaml + - values.big.yaml + syncPolicy: + automated: + prune: true`, + expectedApp: &appv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: "namespace", + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + Annotations: map[string]string{ + "annotation-some-key": "annotation-some-value", + }, + }, + Spec: appv1.ApplicationSpec{ + Project: "default", + Source: &appv1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + Helm: &appv1.ApplicationSourceHelm{ + ValueFiles: []string{ + "values.test.yaml", + "values.big.yaml", + }, + }, + }, + Destination: appv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + SyncPolicy: &appv1.SyncPolicy{ + Automated: &appv1.SyncPolicyAutomated{ + Prune: true, + }, + }, + }, + }, + }, + { + name: "project field isn't overwritten", + appTemplate: &appv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: "namespace", + }, + Spec: appv1.ApplicationSpec{ + Project: "default", + Source: &appv1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: appv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + }, + templatePatch: ` +spec: + project: my-project`, + expectedApp: &appv1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: "namespace", + }, + Spec: appv1.ApplicationSpec{ + Project: "default", + Source: &appv1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: appv1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + }, + }, + }, + } + + for _, tc := range testCases { + tcc := tc + t.Run(tcc.name, func(t *testing.T) { + result, err := applyTemplatePatch(tcc.appTemplate, tcc.templatePatch) + require.NoError(t, err) + assert.Equal(t, *tcc.expectedApp, *result) + }) + } +} + +func TestError(t *testing.T) { + app := &appv1.Application{} + + result, err := applyTemplatePatch(app, "hello world") + require.Error(t, err) + require.Nil(t, result) +} diff --git a/applicationset/utils/utils.go b/applicationset/utils/utils.go index 3392d386f7426..2d128eb81a16c 100644 --- a/applicationset/utils/utils.go +++ b/applicationset/utils/utils.go @@ -41,6 +41,7 @@ func init() { type Renderer interface { RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *argoappsv1.ApplicationSetSyncPolicy, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (*argoappsv1.Application, error) + Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) } type Render struct { diff --git a/assets/swagger.json b/assets/swagger.json index b7f0cdefabe1c..44e67d0b3923e 100644 --- a/assets/swagger.json +++ b/assets/swagger.json @@ -6143,6 +6143,9 @@ }, "template": { "$ref": "#/definitions/v1alpha1ApplicationSetTemplate" + }, + "templatePatch": { + "type": "string" } } }, diff --git a/docs/operator-manual/applicationset/Template.md b/docs/operator-manual/applicationset/Template.md index f66a403586bbd..76ff9132802d5 100644 --- a/docs/operator-manual/applicationset/Template.md +++ b/docs/operator-manual/applicationset/Template.md @@ -108,3 +108,69 @@ spec: (*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/applicationset/examples/template-override).*) In this example, the ApplicationSet controller will generate an `Application` resource using the `path` generated by the List generator, rather than the `path` value defined in `.spec.template`. + +## Template Patch + +Templating is only available on string type. However, some uses cases may require to apply templating on other types. + +Example: + +- Set the automated sync policy +- Switch prune boolean to true +- Add multiple helm value files + +Argo CD has a `templatePatch` feature to allow advanced templating. It supports both json and yaml. + + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: guestbook +spec: + goTemplate: true + generators: + - list: + elements: + - cluster: engineering-dev + url: https://kubernetes.default.svc + autoSync: true + prune: true + valueFiles: + - values.large.yaml + - values.debug.yaml + template: + metadata: + name: '{{.cluster}}-deployment' + spec: + project: "default" + source: + repoURL: https://github.com/infra-team/cluster-deployments.git + targetRevision: HEAD + path: guestbook/{{ .cluster }} + destination: + server: '{{.url}}' + namespace: guestbook + templatePatch: | + spec: + source: + helm: + valueFiles: + {{- range $valueFile := .valueFiles }} + - {{ $valueFile | toJson }} + {{- end }} + {{- if .autoSync }} + syncPolicy: + automated: + prune: {{ .prune | toJson }} + {{- end }} +``` + +!!! important + The `templatePatch` can apply arbitrary changes to the template. If parameters include untrustworthy user input, it + may be possible to inject malicious changes into the template. It is recommended to use `templatePatch` only with + trusted input or to carefully escape the input before using it in the template. Piping input to `toJson` should help + prevent, for example, a user from successfully injecting a string with newlines. + + The `spec.project` field is not supported in `templatePatch`. If you need to change the project, you can use the + `spec.project` field in the `template` field. diff --git a/manifests/core-install.yaml b/manifests/core-install.yaml index beda1e8a5103f..5d2d225473452 100644 --- a/manifests/core-install.yaml +++ b/manifests/core-install.yaml @@ -20123,6 +20123,8 @@ spec: - metadata - spec type: object + templatePatch: + type: string required: - generators - template diff --git a/manifests/crds/applicationset-crd.yaml b/manifests/crds/applicationset-crd.yaml index c324134df927b..758785832ea78 100644 --- a/manifests/crds/applicationset-crd.yaml +++ b/manifests/crds/applicationset-crd.yaml @@ -15183,6 +15183,8 @@ spec: - metadata - spec type: object + templatePatch: + type: string required: - generators - template diff --git a/manifests/ha/install.yaml b/manifests/ha/install.yaml index f693fd4eb887c..19f8015b04945 100644 --- a/manifests/ha/install.yaml +++ b/manifests/ha/install.yaml @@ -20123,6 +20123,8 @@ spec: - metadata - spec type: object + templatePatch: + type: string required: - generators - template diff --git a/manifests/install.yaml b/manifests/install.yaml index 6e9ebcc8ea178..2f7da39bd9b12 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -20123,6 +20123,8 @@ spec: - metadata - spec type: object + templatePatch: + type: string required: - generators - template diff --git a/pkg/apis/application/v1alpha1/applicationset_types.go b/pkg/apis/application/v1alpha1/applicationset_types.go index 99db8124e51ea..41721d0c2287c 100644 --- a/pkg/apis/application/v1alpha1/applicationset_types.go +++ b/pkg/apis/application/v1alpha1/applicationset_types.go @@ -65,6 +65,7 @@ type ApplicationSetSpec struct { // ApplyNestedSelectors enables selectors defined within the generators of two level-nested matrix or merge generators ApplyNestedSelectors bool `json:"applyNestedSelectors,omitempty" protobuf:"bytes,8,name=applyNestedSelectors"` IgnoreApplicationDifferences ApplicationSetIgnoreDifferences `json:"ignoreApplicationDifferences,omitempty" protobuf:"bytes,9,name=ignoreApplicationDifferences"` + TemplatePatch *string `json:"templatePatch,omitempty" protobuf:"bytes,10,name=templatePatch"` } type ApplicationPreservedFields struct { diff --git a/pkg/apis/application/v1alpha1/generated.pb.go b/pkg/apis/application/v1alpha1/generated.pb.go index 777ef9fa49bcc..2a9c0f4789bb7 100644 --- a/pkg/apis/application/v1alpha1/generated.pb.go +++ b/pkg/apis/application/v1alpha1/generated.pb.go @@ -4448,693 +4448,694 @@ func init() { } var fileDescriptor_030104ce3b95bcac = []byte{ - // 10964 bytes of a gzipped FileDescriptorProto + // 10983 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x7d, 0x70, 0x1c, 0xc9, 0x75, 0x18, 0xae, 0xd9, 0x0f, 0x60, 0xf7, 0xe1, 0x83, 0x64, 0x93, 0xbc, 0x03, 0xa9, 0xbb, 0x03, - 0x3d, 0xf7, 0xf3, 0xe9, 0xfc, 0xd3, 0x1d, 0xe0, 0xa3, 0xee, 0x94, 0x8b, 0xce, 0x96, 0x8c, 0x0f, + 0x3d, 0xf7, 0xf3, 0xe9, 0xfc, 0xd3, 0x1d, 0xe0, 0xa3, 0xef, 0xe4, 0x8b, 0xce, 0x96, 0x8c, 0x0f, 0x12, 0x04, 0x09, 0x10, 0xb8, 0x06, 0x48, 0x4a, 0x27, 0x9f, 0x4e, 0x83, 0xdd, 0xc6, 0x62, 0x88, 0xd9, 0x99, 0xb9, 0x99, 0x59, 0x10, 0x38, 0x4b, 0xb2, 0x64, 0xc9, 0xb6, 0x12, 0x7d, 0x9c, 0x22, - 0x25, 0xe5, 0x73, 0x12, 0x39, 0xb2, 0xe5, 0xa4, 0xec, 0x4a, 0x54, 0x71, 0x92, 0x3f, 0xe2, 0xc4, + 0x25, 0xe5, 0x73, 0x12, 0x29, 0xb2, 0xe5, 0xa4, 0xec, 0x4a, 0x54, 0x71, 0x92, 0x3f, 0xe2, 0xc4, 0x49, 0xb9, 0x6c, 0xa7, 0x52, 0x4a, 0x29, 0x29, 0xbb, 0x52, 0x2e, 0xcb, 0x49, 0x6c, 0x44, 0x62, - 0xca, 0x95, 0x54, 0xaa, 0xe2, 0x2a, 0x27, 0xfe, 0x23, 0x61, 0xf2, 0x47, 0xaa, 0xbf, 0x7b, 0x66, + 0x2a, 0x95, 0x54, 0xaa, 0xe2, 0x2a, 0x27, 0xfe, 0x23, 0x61, 0xf2, 0x47, 0xaa, 0xbf, 0x7b, 0x66, 0x67, 0x81, 0x05, 0x30, 0x20, 0x29, 0xe5, 0xfe, 0xdb, 0xed, 0xf7, 0xe6, 0xbd, 0x9e, 0x9e, 0xee, 0xf7, 0x5e, 0xbf, 0x7e, 0xef, 0x35, 0x2c, 0xb4, 0xdc, 0x64, 0xa3, 0xb3, 0x36, 0xd1, 0x08, 0xda, 0x93, 0x4e, 0xd4, 0x0a, 0xc2, 0x28, 0xb8, 0xcd, 0x7e, 0x3c, 0xdb, 0x68, 0x4e, 0x6e, 0x5d, 0x9c, 0x0c, 0x37, 0x5b, 0x93, 0x4e, 0xe8, 0xc6, 0x93, 0x4e, 0x18, 0x7a, 0x6e, 0xc3, 0x49, 0xdc, 0xc0, 0x9f, 0xdc, 0x7a, 0xce, 0xf1, 0xc2, 0x0d, 0xe7, 0xb9, 0xc9, 0x16, 0xf1, 0x49, 0xe4, 0x24, 0xa4, - 0x39, 0x11, 0x46, 0x41, 0x12, 0xa0, 0x1f, 0xd1, 0xd4, 0x26, 0x24, 0x35, 0xf6, 0xe3, 0xb5, 0x46, + 0x39, 0x11, 0x46, 0x41, 0x12, 0xa0, 0x1f, 0xd3, 0xd4, 0x26, 0x24, 0x35, 0xf6, 0xe3, 0xb5, 0x46, 0x73, 0x62, 0xeb, 0xe2, 0x44, 0xb8, 0xd9, 0x9a, 0xa0, 0xd4, 0x26, 0x0c, 0x6a, 0x13, 0x92, 0xda, 0xf9, 0x67, 0x8d, 0xbe, 0xb4, 0x82, 0x56, 0x30, 0xc9, 0x88, 0xae, 0x75, 0xd6, 0xd9, 0x3f, 0xf6, 0x87, 0xfd, 0xe2, 0xcc, 0xce, 0xdb, 0x9b, 0x2f, 0xc6, 0x13, 0x6e, 0x40, 0xbb, 0x37, 0xd9, 0x08, 0x22, 0x32, 0xb9, 0xd5, 0xd5, 0xa1, 0xf3, 0x57, 0x34, 0x0e, 0xd9, 0x4e, 0x88, 0x1f, 0xbb, 0x81, 0x1f, 0x3f, 0x4b, 0xbb, 0x40, 0xa2, 0x2d, 0x12, 0x99, 0xaf, 0x67, 0x20, 0xe4, 0x51, 0x7a, 0x5e, 0x53, 0x6a, 0x3b, 0x8d, 0x0d, 0xd7, 0x27, 0xd1, 0x8e, 0x7e, 0xbc, 0x4d, 0x12, 0x27, 0xef, 0xa9, - 0xc9, 0x5e, 0x4f, 0x45, 0x1d, 0x3f, 0x71, 0xdb, 0xa4, 0xeb, 0x81, 0xf7, 0xee, 0xf7, 0x40, 0xdc, - 0xd8, 0x20, 0x6d, 0xa7, 0xeb, 0xb9, 0xf7, 0xf4, 0x7a, 0xae, 0x93, 0xb8, 0xde, 0xa4, 0xeb, 0x27, - 0x71, 0x12, 0x65, 0x1f, 0xb2, 0x5f, 0x87, 0x91, 0xa9, 0x5b, 0x2b, 0x53, 0x9d, 0x64, 0x63, 0x26, - 0xf0, 0xd7, 0xdd, 0x16, 0x7a, 0x01, 0x86, 0x1a, 0x5e, 0x27, 0x4e, 0x48, 0x74, 0xdd, 0x69, 0x93, - 0x31, 0xeb, 0x82, 0xf5, 0x74, 0x7d, 0xfa, 0xf4, 0x37, 0x77, 0xc7, 0xdf, 0x71, 0x77, 0x77, 0x7c, - 0x68, 0x46, 0x83, 0xb0, 0x89, 0x87, 0x7e, 0x08, 0x06, 0xa3, 0xc0, 0x23, 0x53, 0xf8, 0xfa, 0x58, - 0x89, 0x3d, 0x72, 0x42, 0x3c, 0x32, 0x88, 0x79, 0x33, 0x96, 0x70, 0xfb, 0x0f, 0x4a, 0x00, 0x53, - 0x61, 0xb8, 0x1c, 0x05, 0xb7, 0x49, 0x23, 0x41, 0x1f, 0x85, 0x1a, 0x1d, 0xba, 0xa6, 0x93, 0x38, - 0x8c, 0xdb, 0xd0, 0xc5, 0x1f, 0x9e, 0xe0, 0x6f, 0x32, 0x61, 0xbe, 0x89, 0x9e, 0x38, 0x14, 0x7b, - 0x62, 0xeb, 0xb9, 0x89, 0xa5, 0x35, 0xfa, 0xfc, 0x22, 0x49, 0x9c, 0x69, 0x24, 0x98, 0x81, 0x6e, - 0xc3, 0x8a, 0x2a, 0xf2, 0xa1, 0x12, 0x87, 0xa4, 0xc1, 0x3a, 0x36, 0x74, 0x71, 0x61, 0xe2, 0x28, - 0x33, 0x74, 0x42, 0xf7, 0x7c, 0x25, 0x24, 0x8d, 0xe9, 0x61, 0xc1, 0xb9, 0x42, 0xff, 0x61, 0xc6, - 0x07, 0x6d, 0xc1, 0x40, 0x9c, 0x38, 0x49, 0x27, 0x1e, 0x2b, 0x33, 0x8e, 0xd7, 0x0b, 0xe3, 0xc8, - 0xa8, 0x4e, 0x8f, 0x0a, 0x9e, 0x03, 0xfc, 0x3f, 0x16, 0xdc, 0xec, 0x3f, 0xb6, 0x60, 0x54, 0x23, - 0x2f, 0xb8, 0x71, 0x82, 0x7e, 0xbc, 0x6b, 0x70, 0x27, 0xfa, 0x1b, 0x5c, 0xfa, 0x34, 0x1b, 0xda, - 0x93, 0x82, 0x59, 0x4d, 0xb6, 0x18, 0x03, 0xdb, 0x86, 0xaa, 0x9b, 0x90, 0x76, 0x3c, 0x56, 0xba, - 0x50, 0x7e, 0x7a, 0xe8, 0xe2, 0x95, 0xa2, 0xde, 0x73, 0x7a, 0x44, 0x30, 0xad, 0xce, 0x53, 0xf2, - 0x98, 0x73, 0xb1, 0x7f, 0x75, 0xd8, 0x7c, 0x3f, 0x3a, 0xe0, 0xe8, 0x39, 0x18, 0x8a, 0x83, 0x4e, - 0xd4, 0x20, 0x98, 0x84, 0x41, 0x3c, 0x66, 0x5d, 0x28, 0xd3, 0xa9, 0x47, 0x67, 0xea, 0x8a, 0x6e, - 0xc6, 0x26, 0x0e, 0xfa, 0xa2, 0x05, 0xc3, 0x4d, 0x12, 0x27, 0xae, 0xcf, 0xf8, 0xcb, 0xce, 0xaf, - 0x1e, 0xb9, 0xf3, 0xb2, 0x71, 0x56, 0x13, 0x9f, 0x3e, 0x23, 0x5e, 0x64, 0xd8, 0x68, 0x8c, 0x71, - 0x8a, 0x3f, 0x5d, 0x71, 0x4d, 0x12, 0x37, 0x22, 0x37, 0xa4, 0xff, 0xd9, 0x9c, 0x31, 0x56, 0xdc, - 0xac, 0x06, 0x61, 0x13, 0x0f, 0xf9, 0x50, 0xa5, 0x2b, 0x2a, 0x1e, 0xab, 0xb0, 0xfe, 0xcf, 0x1f, - 0xad, 0xff, 0x62, 0x50, 0xe9, 0x62, 0xd5, 0xa3, 0x4f, 0xff, 0xc5, 0x98, 0xb3, 0x41, 0x5f, 0xb0, - 0x60, 0x4c, 0xac, 0x78, 0x4c, 0xf8, 0x80, 0xde, 0xda, 0x70, 0x13, 0xe2, 0xb9, 0x71, 0x32, 0x56, - 0x65, 0x7d, 0x98, 0xec, 0x6f, 0x6e, 0xcd, 0x45, 0x41, 0x27, 0xbc, 0xe6, 0xfa, 0xcd, 0xe9, 0x0b, - 0x82, 0xd3, 0xd8, 0x4c, 0x0f, 0xc2, 0xb8, 0x27, 0x4b, 0xf4, 0x15, 0x0b, 0xce, 0xfb, 0x4e, 0x9b, - 0xc4, 0xa1, 0x43, 0x3f, 0x2d, 0x07, 0x4f, 0x7b, 0x4e, 0x63, 0x93, 0xf5, 0x68, 0xe0, 0x70, 0x3d, - 0xb2, 0x45, 0x8f, 0xce, 0x5f, 0xef, 0x49, 0x1a, 0xef, 0xc1, 0x16, 0x7d, 0xdd, 0x82, 0x53, 0x41, - 0x14, 0x6e, 0x38, 0x3e, 0x69, 0x4a, 0x68, 0x3c, 0x36, 0xc8, 0x96, 0xde, 0x47, 0x8e, 0xf6, 0x89, - 0x96, 0xb2, 0x64, 0x17, 0x03, 0xdf, 0x4d, 0x82, 0x68, 0x85, 0x24, 0x89, 0xeb, 0xb7, 0xe2, 0xe9, - 0xb3, 0x77, 0x77, 0xc7, 0x4f, 0x75, 0x61, 0xe1, 0xee, 0xfe, 0xa0, 0x9f, 0x80, 0xa1, 0x78, 0xc7, - 0x6f, 0xdc, 0x72, 0xfd, 0x66, 0x70, 0x27, 0x1e, 0xab, 0x15, 0xb1, 0x7c, 0x57, 0x14, 0x41, 0xb1, - 0x00, 0x35, 0x03, 0x6c, 0x72, 0xcb, 0xff, 0x70, 0x7a, 0x2a, 0xd5, 0x8b, 0xfe, 0x70, 0x7a, 0x32, - 0xed, 0xc1, 0x16, 0xfd, 0xac, 0x05, 0x23, 0xb1, 0xdb, 0xf2, 0x9d, 0xa4, 0x13, 0x91, 0x6b, 0x64, - 0x27, 0x1e, 0x03, 0xd6, 0x91, 0xab, 0x47, 0x1c, 0x15, 0x83, 0xe4, 0xf4, 0x59, 0xd1, 0xc7, 0x11, - 0xb3, 0x35, 0xc6, 0x69, 0xbe, 0x79, 0x0b, 0x4d, 0x4f, 0xeb, 0xa1, 0x62, 0x17, 0x9a, 0x9e, 0xd4, - 0x3d, 0x59, 0xa2, 0x1f, 0x83, 0x93, 0xbc, 0x49, 0x8d, 0x6c, 0x3c, 0x36, 0xcc, 0x04, 0xed, 0x99, - 0xbb, 0xbb, 0xe3, 0x27, 0x57, 0x32, 0x30, 0xdc, 0x85, 0x8d, 0x5e, 0x87, 0xf1, 0x90, 0x44, 0x6d, - 0x37, 0x59, 0xf2, 0xbd, 0x1d, 0x29, 0xbe, 0x1b, 0x41, 0x48, 0x9a, 0xa2, 0x3b, 0xf1, 0xd8, 0xc8, - 0x05, 0xeb, 0xe9, 0xda, 0xf4, 0xbb, 0x44, 0x37, 0xc7, 0x97, 0xf7, 0x46, 0xc7, 0xfb, 0xd1, 0xb3, - 0xff, 0x55, 0x09, 0x4e, 0x66, 0x15, 0x27, 0xfa, 0x3b, 0x16, 0x9c, 0xb8, 0x7d, 0x27, 0x59, 0x0d, - 0x36, 0x89, 0x1f, 0x4f, 0xef, 0x50, 0xf1, 0xc6, 0x54, 0xc6, 0xd0, 0xc5, 0x46, 0xb1, 0x2a, 0x7a, - 0xe2, 0x6a, 0x9a, 0xcb, 0x25, 0x3f, 0x89, 0x76, 0xa6, 0x1f, 0x15, 0x6f, 0x77, 0xe2, 0xea, 0xad, - 0x55, 0x13, 0x8a, 0xb3, 0x9d, 0x3a, 0xff, 0x39, 0x0b, 0xce, 0xe4, 0x91, 0x40, 0x27, 0xa1, 0xbc, - 0x49, 0x76, 0xb8, 0x55, 0x86, 0xe9, 0x4f, 0xf4, 0x2a, 0x54, 0xb7, 0x1c, 0xaf, 0x43, 0x84, 0x75, - 0x33, 0x77, 0xb4, 0x17, 0x51, 0x3d, 0xc3, 0x9c, 0xea, 0xfb, 0x4a, 0x2f, 0x5a, 0xf6, 0xef, 0x96, - 0x61, 0xc8, 0xd0, 0x6f, 0xf7, 0xc1, 0x62, 0x0b, 0x52, 0x16, 0xdb, 0x62, 0x61, 0xaa, 0xb9, 0xa7, - 0xc9, 0x76, 0x27, 0x63, 0xb2, 0x2d, 0x15, 0xc7, 0x72, 0x4f, 0x9b, 0x0d, 0x25, 0x50, 0x0f, 0x42, - 0x6a, 0x91, 0x53, 0xd5, 0x5f, 0x29, 0xe2, 0x13, 0x2e, 0x49, 0x72, 0xd3, 0x23, 0x77, 0x77, 0xc7, - 0xeb, 0xea, 0x2f, 0xd6, 0x8c, 0xec, 0x6f, 0x5b, 0x70, 0xc6, 0xe8, 0xe3, 0x4c, 0xe0, 0x37, 0x5d, - 0xf6, 0x69, 0x2f, 0x40, 0x25, 0xd9, 0x09, 0xa5, 0xd9, 0xaf, 0x46, 0x6a, 0x75, 0x27, 0x24, 0x98, - 0x41, 0xa8, 0xa1, 0xdf, 0x26, 0x71, 0xec, 0xb4, 0x48, 0xd6, 0xd0, 0x5f, 0xe4, 0xcd, 0x58, 0xc2, - 0x51, 0x04, 0xc8, 0x73, 0xe2, 0x64, 0x35, 0x72, 0xfc, 0x98, 0x91, 0x5f, 0x75, 0xdb, 0x44, 0x0c, - 0xf0, 0xff, 0xdf, 0xdf, 0x8c, 0xa1, 0x4f, 0x4c, 0x3f, 0x72, 0x77, 0x77, 0x1c, 0x2d, 0x74, 0x51, - 0xc2, 0x39, 0xd4, 0xed, 0xaf, 0x58, 0xf0, 0x48, 0xbe, 0x2d, 0x86, 0x9e, 0x82, 0x01, 0xbe, 0xe5, - 0x13, 0x6f, 0xa7, 0x3f, 0x09, 0x6b, 0xc5, 0x02, 0x8a, 0x26, 0xa1, 0xae, 0xf4, 0x84, 0x78, 0xc7, - 0x53, 0x02, 0xb5, 0xae, 0x95, 0x8b, 0xc6, 0xa1, 0x83, 0x46, 0xff, 0x08, 0xcb, 0x4d, 0x0d, 0x1a, - 0xdb, 0x24, 0x31, 0x88, 0xfd, 0x1f, 0x2d, 0x38, 0x61, 0xf4, 0xea, 0x3e, 0x98, 0xe6, 0x7e, 0xda, - 0x34, 0x9f, 0x2f, 0x6c, 0x3e, 0xf7, 0xb0, 0xcd, 0xbf, 0x60, 0xc1, 0x79, 0x03, 0x6b, 0xd1, 0x49, - 0x1a, 0x1b, 0x97, 0xb6, 0xc3, 0x88, 0xc4, 0x74, 0x3b, 0x8d, 0x1e, 0x37, 0xe4, 0xd6, 0xf4, 0x90, - 0xa0, 0x50, 0xbe, 0x46, 0x76, 0xb8, 0x10, 0x7b, 0x06, 0x6a, 0x7c, 0x72, 0x06, 0x91, 0x18, 0x71, - 0xf5, 0x6e, 0x4b, 0xa2, 0x1d, 0x2b, 0x0c, 0x64, 0xc3, 0x00, 0x13, 0x4e, 0x74, 0xb1, 0x52, 0x35, - 0x04, 0xf4, 0x23, 0xde, 0x64, 0x2d, 0x58, 0x40, 0xec, 0x38, 0xd5, 0x9d, 0xe5, 0x88, 0xb0, 0x8f, - 0xdb, 0xbc, 0xec, 0x12, 0xaf, 0x19, 0xd3, 0x6d, 0x83, 0xe3, 0xfb, 0x41, 0x22, 0x76, 0x00, 0xc6, - 0xb6, 0x61, 0x4a, 0x37, 0x63, 0x13, 0x87, 0x32, 0xf5, 0x9c, 0x35, 0xe2, 0xf1, 0x11, 0x15, 0x4c, - 0x17, 0x58, 0x0b, 0x16, 0x10, 0xfb, 0x6e, 0x89, 0x6d, 0x50, 0xd4, 0xd2, 0x27, 0xf7, 0x63, 0x77, - 0x1b, 0xa5, 0x64, 0xe5, 0x72, 0x71, 0x82, 0x8b, 0xf4, 0xde, 0xe1, 0xbe, 0x91, 0x11, 0x97, 0xb8, - 0x50, 0xae, 0x7b, 0xef, 0x72, 0x7f, 0xab, 0x04, 0xe3, 0xe9, 0x07, 0xba, 0xa4, 0x2d, 0xdd, 0x52, - 0x19, 0x8c, 0xb2, 0x4e, 0x0c, 0x03, 0x1f, 0x9b, 0x78, 0x3d, 0x04, 0x56, 0xe9, 0x38, 0x05, 0x96, - 0x29, 0x4f, 0xcb, 0xfb, 0xc8, 0xd3, 0xa7, 0xd4, 0xa8, 0x57, 0x32, 0x02, 0x2c, 0xad, 0x53, 0x2e, - 0x40, 0x25, 0x4e, 0x48, 0x38, 0x56, 0x4d, 0xcb, 0xa3, 0x95, 0x84, 0x84, 0x98, 0x41, 0xec, 0xff, - 0x5a, 0x82, 0x47, 0xd3, 0x63, 0xa8, 0x55, 0xc0, 0x07, 0x52, 0x2a, 0xe0, 0xdd, 0xa6, 0x0a, 0xb8, - 0xb7, 0x3b, 0xfe, 0xce, 0x1e, 0x8f, 0x7d, 0xcf, 0x68, 0x08, 0x34, 0x97, 0x19, 0xc5, 0xc9, 0xf4, - 0x28, 0xde, 0xdb, 0x1d, 0x7f, 0xbc, 0xc7, 0x3b, 0x66, 0x86, 0xf9, 0x29, 0x18, 0x88, 0x88, 0x13, - 0x07, 0xbe, 0x18, 0x68, 0xf5, 0x39, 0x30, 0x6b, 0xc5, 0x02, 0x6a, 0xff, 0xdb, 0x7a, 0x76, 0xb0, - 0xe7, 0xb8, 0x13, 0x2e, 0x88, 0x90, 0x0b, 0x15, 0x66, 0xd6, 0x73, 0xd1, 0x70, 0xed, 0x68, 0xcb, - 0x88, 0xaa, 0x01, 0x45, 0x7a, 0xba, 0x46, 0xbf, 0x1a, 0x6d, 0xc2, 0x8c, 0x05, 0xda, 0x86, 0x5a, - 0x43, 0x5a, 0xdb, 0xa5, 0x22, 0xfc, 0x52, 0xc2, 0xd6, 0xd6, 0x1c, 0x87, 0xa9, 0xbc, 0x56, 0x26, - 0xba, 0xe2, 0x86, 0x08, 0x94, 0x5b, 0x6e, 0x22, 0x3e, 0xeb, 0x11, 0xf7, 0x53, 0x73, 0xae, 0xf1, - 0x8a, 0x83, 0x54, 0x89, 0xcc, 0xb9, 0x09, 0xa6, 0xf4, 0xd1, 0x4f, 0x5b, 0x30, 0x14, 0x37, 0xda, - 0xcb, 0x51, 0xb0, 0xe5, 0x36, 0x49, 0x24, 0xac, 0xa9, 0x23, 0x8a, 0xa6, 0x95, 0x99, 0x45, 0x49, - 0x50, 0xf3, 0xe5, 0xfb, 0x5b, 0x0d, 0xc1, 0x26, 0x5f, 0xba, 0xcb, 0x78, 0x54, 0xbc, 0xfb, 0x2c, - 0x69, 0xb8, 0x54, 0xff, 0xc9, 0x4d, 0x15, 0x9b, 0x29, 0x47, 0xb6, 0x2e, 0x67, 0x3b, 0x8d, 0x4d, - 0xba, 0xde, 0x74, 0x87, 0xde, 0x79, 0x77, 0x77, 0xfc, 0xd1, 0x99, 0x7c, 0x9e, 0xb8, 0x57, 0x67, - 0xd8, 0x80, 0x85, 0x1d, 0xcf, 0xc3, 0xe4, 0xf5, 0x0e, 0x61, 0x2e, 0x93, 0x02, 0x06, 0x6c, 0x59, - 0x13, 0xcc, 0x0c, 0x98, 0x01, 0xc1, 0x26, 0x5f, 0xf4, 0x3a, 0x0c, 0xb4, 0x9d, 0x24, 0x72, 0xb7, - 0x85, 0x9f, 0xe4, 0x88, 0xf6, 0xfe, 0x22, 0xa3, 0xa5, 0x99, 0x33, 0x4d, 0xcd, 0x1b, 0xb1, 0x60, - 0x84, 0xda, 0x50, 0x6d, 0x93, 0xa8, 0x45, 0xc6, 0x6a, 0x45, 0xf8, 0x84, 0x17, 0x29, 0x29, 0xcd, - 0xb0, 0x4e, 0xad, 0x23, 0xd6, 0x86, 0x39, 0x17, 0xf4, 0x2a, 0xd4, 0x62, 0xe2, 0x91, 0x06, 0xb5, - 0x6f, 0xea, 0x8c, 0xe3, 0x7b, 0xfa, 0xb4, 0xf5, 0xa8, 0x61, 0xb1, 0x22, 0x1e, 0xe5, 0x0b, 0x4c, - 0xfe, 0xc3, 0x8a, 0x24, 0x1d, 0xc0, 0xd0, 0xeb, 0xb4, 0x5c, 0x7f, 0x0c, 0x8a, 0x18, 0xc0, 0x65, - 0x46, 0x2b, 0x33, 0x80, 0xbc, 0x11, 0x0b, 0x46, 0xf6, 0x9f, 0x58, 0x80, 0xd2, 0x42, 0xed, 0x3e, - 0x18, 0xb5, 0xaf, 0xa7, 0x8d, 0xda, 0x85, 0x22, 0xad, 0x8e, 0x1e, 0x76, 0xed, 0x6f, 0xd4, 0x21, - 0xa3, 0x0e, 0xae, 0x93, 0x38, 0x21, 0xcd, 0xb7, 0x45, 0xf8, 0xdb, 0x22, 0xfc, 0x6d, 0x11, 0xae, - 0x44, 0xf8, 0x5a, 0x46, 0x84, 0xbf, 0xdf, 0x58, 0xf5, 0xfa, 0x50, 0xf5, 0x35, 0x75, 0xea, 0x6a, - 0xf6, 0xc0, 0x40, 0xa0, 0x92, 0xe0, 0xea, 0xca, 0xd2, 0xf5, 0x5c, 0x99, 0xfd, 0x5a, 0x5a, 0x66, - 0x1f, 0x95, 0xc5, 0xff, 0x0b, 0x52, 0xfa, 0x5f, 0x5a, 0xf0, 0xae, 0xb4, 0xf4, 0x92, 0x33, 0x67, - 0xbe, 0xe5, 0x07, 0x11, 0x99, 0x75, 0xd7, 0xd7, 0x49, 0x44, 0xfc, 0x06, 0x89, 0x95, 0x17, 0xc3, - 0xea, 0xe5, 0xc5, 0x40, 0xcf, 0xc3, 0xf0, 0xed, 0x38, 0xf0, 0x97, 0x03, 0xd7, 0x17, 0x22, 0x88, - 0x6e, 0x84, 0x4f, 0xde, 0xdd, 0x1d, 0x1f, 0xa6, 0x23, 0x2a, 0xdb, 0x71, 0x0a, 0x0b, 0xcd, 0xc0, - 0xa9, 0xdb, 0xaf, 0x2f, 0x3b, 0x89, 0xe1, 0x0e, 0x90, 0x1b, 0x77, 0x76, 0x60, 0x71, 0xf5, 0xe5, - 0x0c, 0x10, 0x77, 0xe3, 0xdb, 0x7f, 0xa3, 0x04, 0xe7, 0x32, 0x2f, 0x12, 0x78, 0x5e, 0xd0, 0x49, - 0xe8, 0xa6, 0x06, 0xfd, 0x82, 0x05, 0x27, 0xdb, 0x69, 0x8f, 0x43, 0x2c, 0x1c, 0xbb, 0x1f, 0x2c, - 0x4c, 0x47, 0x64, 0x5c, 0x1a, 0xd3, 0x63, 0x62, 0x84, 0x4e, 0x66, 0x00, 0x31, 0xee, 0xea, 0x0b, - 0x7a, 0x15, 0xea, 0x6d, 0x67, 0xfb, 0x46, 0xd8, 0x74, 0x12, 0xb9, 0x9f, 0xec, 0xed, 0x06, 0xe8, - 0x24, 0xae, 0x37, 0xc1, 0x8f, 0xeb, 0x27, 0xe6, 0xfd, 0x64, 0x29, 0x5a, 0x49, 0x22, 0xd7, 0x6f, - 0x71, 0x77, 0xde, 0xa2, 0x24, 0x83, 0x35, 0x45, 0xfb, 0xab, 0x56, 0x56, 0x49, 0xa9, 0xd1, 0x89, - 0x9c, 0x84, 0xb4, 0x76, 0xd0, 0xc7, 0xa0, 0x4a, 0x37, 0x7e, 0x72, 0x54, 0x6e, 0x15, 0xa9, 0x39, - 0x8d, 0x2f, 0xa1, 0x95, 0x28, 0xfd, 0x17, 0x63, 0xce, 0xd4, 0xfe, 0x93, 0x5a, 0xd6, 0x58, 0x60, - 0x87, 0xb7, 0x17, 0x01, 0x5a, 0xc1, 0x2a, 0x69, 0x87, 0x1e, 0x1d, 0x16, 0x8b, 0x9d, 0x00, 0x28, - 0x5f, 0xc7, 0x9c, 0x82, 0x60, 0x03, 0x0b, 0xfd, 0x25, 0x0b, 0xa0, 0x25, 0xe7, 0xbc, 0x34, 0x04, - 0x6e, 0x14, 0xf9, 0x3a, 0x7a, 0x45, 0xe9, 0xbe, 0x28, 0x86, 0xd8, 0x60, 0x8e, 0x7e, 0xca, 0x82, - 0x5a, 0x22, 0xbb, 0xcf, 0x55, 0xe3, 0x6a, 0x91, 0x3d, 0x91, 0x2f, 0xad, 0x6d, 0x22, 0x35, 0x24, - 0x8a, 0x2f, 0xfa, 0x19, 0x0b, 0x20, 0xde, 0xf1, 0x1b, 0xcb, 0x81, 0xe7, 0x36, 0x76, 0x84, 0xc6, - 0xbc, 0x59, 0xa8, 0x3f, 0x46, 0x51, 0x9f, 0x1e, 0xa5, 0xa3, 0xa1, 0xff, 0x63, 0x83, 0x33, 0xfa, - 0x04, 0xd4, 0x62, 0x31, 0xdd, 0x84, 0x8e, 0x5c, 0x2d, 0xd6, 0x2b, 0xc4, 0x69, 0x0b, 0xf1, 0x2a, - 0xfe, 0x61, 0xc5, 0x13, 0xfd, 0x9c, 0x05, 0x27, 0xc2, 0xb4, 0x9f, 0x4f, 0xa8, 0xc3, 0xe2, 0x64, - 0x40, 0xc6, 0x8f, 0x38, 0x7d, 0xfa, 0xee, 0xee, 0xf8, 0x89, 0x4c, 0x23, 0xce, 0xf6, 0x82, 0x4a, - 0x40, 0x3d, 0x83, 0x97, 0x42, 0xee, 0x73, 0x1c, 0xd4, 0x12, 0x70, 0x2e, 0x0b, 0xc4, 0xdd, 0xf8, - 0x68, 0x19, 0xce, 0xd0, 0xde, 0xed, 0x70, 0xf3, 0x53, 0xaa, 0x97, 0x98, 0x29, 0xc3, 0xda, 0xf4, - 0x63, 0x62, 0x86, 0x30, 0xaf, 0x7e, 0x16, 0x07, 0xe7, 0x3e, 0x89, 0x7e, 0xd7, 0x82, 0xc7, 0x5c, - 0xa6, 0x06, 0x4c, 0x87, 0xb9, 0xd6, 0x08, 0xe2, 0x24, 0x96, 0x14, 0x2a, 0x2b, 0x7a, 0xa9, 0x9f, - 0xe9, 0xff, 0x4f, 0xbc, 0xc1, 0x63, 0xf3, 0x7b, 0x74, 0x09, 0xef, 0xd9, 0x61, 0xfb, 0x5b, 0xa5, - 0xd4, 0xb1, 0x86, 0xf2, 0x25, 0x32, 0xa9, 0xd1, 0x90, 0x6e, 0x1c, 0x29, 0x04, 0x0b, 0x95, 0x1a, - 0xca, 0x49, 0xa4, 0xa5, 0x86, 0x6a, 0x8a, 0xb1, 0xc1, 0x9c, 0xda, 0x96, 0xa7, 0x9c, 0xac, 0xc7, - 0x52, 0x08, 0xb2, 0x57, 0x8b, 0xec, 0x52, 0xf7, 0x21, 0xd4, 0x39, 0xd1, 0xb5, 0x53, 0x5d, 0x20, - 0xdc, 0xdd, 0x25, 0xfb, 0x5b, 0xe9, 0xa3, 0x14, 0x63, 0x0d, 0xf6, 0x71, 0x4c, 0xf4, 0x45, 0x0b, - 0x86, 0xa2, 0xc0, 0xf3, 0x5c, 0xbf, 0x45, 0xe5, 0x85, 0x50, 0x7a, 0x1f, 0x3e, 0x16, 0xbd, 0x23, - 0x04, 0x03, 0xb3, 0x50, 0xb1, 0xe6, 0x89, 0xcd, 0x0e, 0xd8, 0x7f, 0x6c, 0xc1, 0x58, 0x2f, 0xb9, - 0x86, 0x08, 0xbc, 0x53, 0x2e, 0x5a, 0x15, 0x24, 0xb1, 0xe4, 0xcf, 0x12, 0x8f, 0x28, 0xff, 0x71, - 0x6d, 0xfa, 0x49, 0xf1, 0x9a, 0xef, 0x5c, 0xee, 0x8d, 0x8a, 0xf7, 0xa2, 0x83, 0x5e, 0x81, 0x93, - 0xc6, 0x7b, 0xc5, 0x6a, 0x60, 0xea, 0xd3, 0x13, 0xd4, 0x90, 0x98, 0xca, 0xc0, 0xee, 0xed, 0x8e, - 0x3f, 0x92, 0x6d, 0x13, 0x82, 0xb7, 0x8b, 0x8e, 0xfd, 0xcb, 0xa5, 0xec, 0xd7, 0x52, 0x3a, 0xf3, - 0x2d, 0xab, 0x6b, 0x57, 0xfe, 0xc1, 0xe3, 0xd0, 0x53, 0x6c, 0xff, 0xae, 0xe2, 0x30, 0x7a, 0xe3, - 0x3c, 0xc0, 0x83, 0x5e, 0xfb, 0x5f, 0x57, 0x60, 0x8f, 0x9e, 0xf5, 0x61, 0x04, 0x1f, 0xf8, 0x74, - 0xf0, 0xf3, 0x96, 0x3a, 0x39, 0x2a, 0xb3, 0x45, 0xde, 0x3c, 0xae, 0xb1, 0xe7, 0xfb, 0x90, 0x98, - 0x07, 0x1b, 0x28, 0x6f, 0x74, 0xfa, 0x8c, 0x0a, 0x7d, 0xcd, 0x4a, 0x9f, 0x7d, 0xf1, 0xe8, 0x31, - 0xf7, 0xd8, 0xfa, 0x64, 0x1c, 0xa8, 0xf1, 0x8e, 0xe9, 0x63, 0x98, 0x5e, 0x47, 0x6d, 0x13, 0x00, - 0xeb, 0xae, 0xef, 0x78, 0xee, 0x1b, 0x74, 0x97, 0x51, 0x65, 0x8a, 0x92, 0x59, 0x1e, 0x97, 0x55, - 0x2b, 0x36, 0x30, 0xce, 0xff, 0x45, 0x18, 0x32, 0xde, 0x3c, 0x27, 0x46, 0xe2, 0x8c, 0x19, 0x23, - 0x51, 0x37, 0x42, 0x1b, 0xce, 0xbf, 0x1f, 0x4e, 0x66, 0x3b, 0x78, 0x90, 0xe7, 0xed, 0xff, 0x39, - 0x98, 0x3d, 0x8c, 0x5a, 0x25, 0x51, 0x9b, 0x76, 0xed, 0x6d, 0x07, 0xd1, 0xdb, 0x0e, 0xa2, 0xb7, - 0x1d, 0x44, 0xa6, 0x8f, 0x5f, 0x38, 0x3f, 0x06, 0xef, 0x93, 0xf3, 0x23, 0xe5, 0xce, 0xa9, 0x15, - 0xee, 0xce, 0xb1, 0xef, 0x56, 0x21, 0x65, 0x47, 0xf1, 0xf1, 0xfe, 0x21, 0x18, 0x8c, 0x48, 0x18, - 0xdc, 0xc0, 0x0b, 0x42, 0x87, 0xe8, 0x38, 0x78, 0xde, 0x8c, 0x25, 0x9c, 0xea, 0x9a, 0xd0, 0x49, - 0x36, 0x84, 0x12, 0x51, 0xba, 0x66, 0xd9, 0x49, 0x36, 0x30, 0x83, 0xa0, 0xf7, 0xc3, 0x68, 0xe2, - 0x44, 0x2d, 0x6a, 0x36, 0x6f, 0xb1, 0xcf, 0x2a, 0x8e, 0x2c, 0x1f, 0x11, 0xb8, 0xa3, 0xab, 0x29, - 0x28, 0xce, 0x60, 0xa3, 0xd7, 0xa1, 0xb2, 0x41, 0xbc, 0xb6, 0x18, 0xf2, 0x95, 0xe2, 0x64, 0x3c, - 0x7b, 0xd7, 0x2b, 0xc4, 0x6b, 0x73, 0x09, 0x44, 0x7f, 0x61, 0xc6, 0x8a, 0xce, 0xb7, 0xfa, 0x66, - 0x27, 0x4e, 0x82, 0xb6, 0xfb, 0x86, 0xf4, 0xd4, 0x7d, 0xb0, 0x60, 0xc6, 0xd7, 0x24, 0x7d, 0xee, - 0x12, 0x51, 0x7f, 0xb1, 0xe6, 0xcc, 0xfa, 0xd1, 0x74, 0x23, 0xf6, 0xa9, 0x76, 0x84, 0xc3, 0xad, - 0xe8, 0x7e, 0xcc, 0x4a, 0xfa, 0xbc, 0x1f, 0xea, 0x2f, 0xd6, 0x9c, 0xd1, 0x8e, 0x9a, 0xf7, 0x43, - 0xac, 0x0f, 0x37, 0x0a, 0xee, 0x03, 0x9f, 0xf3, 0xb9, 0xf3, 0xff, 0x49, 0xa8, 0x36, 0x36, 0x9c, - 0x28, 0x19, 0x1b, 0x66, 0x93, 0x46, 0xb9, 0x66, 0x66, 0x68, 0x23, 0xe6, 0x30, 0xf4, 0x38, 0x94, - 0x23, 0xb2, 0xce, 0xc2, 0x2f, 0x8d, 0xc0, 0x1c, 0x4c, 0xd6, 0x31, 0x6d, 0xb7, 0x7f, 0xb1, 0x94, - 0x36, 0x97, 0xd2, 0xef, 0xcd, 0x67, 0x7b, 0xa3, 0x13, 0xc5, 0xd2, 0x7d, 0x63, 0xcc, 0x76, 0xd6, - 0x8c, 0x25, 0x1c, 0x7d, 0xca, 0x82, 0xc1, 0xdb, 0x71, 0xe0, 0xfb, 0x24, 0x11, 0xaa, 0xe9, 0x66, - 0xc1, 0x43, 0x71, 0x95, 0x53, 0xd7, 0x7d, 0x10, 0x0d, 0x58, 0xf2, 0xa5, 0xdd, 0x25, 0xdb, 0x0d, - 0xaf, 0xd3, 0xec, 0x8a, 0xb5, 0xb8, 0xc4, 0x9b, 0xb1, 0x84, 0x53, 0x54, 0xd7, 0xe7, 0xa8, 0x95, - 0x34, 0xea, 0xbc, 0x2f, 0x50, 0x05, 0xdc, 0xfe, 0x6b, 0x03, 0x70, 0x36, 0x77, 0x71, 0x50, 0x43, - 0x86, 0x99, 0x0a, 0x97, 0x5d, 0x8f, 0xc8, 0x28, 0x23, 0x66, 0xc8, 0xdc, 0x54, 0xad, 0xd8, 0xc0, - 0x40, 0x3f, 0x09, 0x10, 0x3a, 0x91, 0xd3, 0x26, 0xca, 0xbd, 0x7a, 0x64, 0x7b, 0x81, 0xf6, 0x63, - 0x59, 0xd2, 0xd4, 0x7b, 0x53, 0xd5, 0x14, 0x63, 0x83, 0x25, 0x7a, 0x01, 0x86, 0x22, 0xe2, 0x11, - 0x27, 0x66, 0xd1, 0xbb, 0xd9, 0x54, 0x04, 0xac, 0x41, 0xd8, 0xc4, 0x43, 0x4f, 0xa9, 0x80, 0xac, - 0x4c, 0x60, 0x4a, 0x3a, 0x28, 0x0b, 0xbd, 0x69, 0xc1, 0xe8, 0xba, 0xeb, 0x11, 0xcd, 0x5d, 0x24, - 0x0e, 0x2c, 0x1d, 0xfd, 0x25, 0x2f, 0x9b, 0x74, 0xb5, 0x84, 0x4c, 0x35, 0xc7, 0x38, 0xc3, 0x9e, - 0x7e, 0xe6, 0x2d, 0x12, 0x31, 0xd1, 0x3a, 0x90, 0xfe, 0xcc, 0x37, 0x79, 0x33, 0x96, 0x70, 0x34, - 0x05, 0x27, 0x42, 0x27, 0x8e, 0x67, 0x22, 0xd2, 0x24, 0x7e, 0xe2, 0x3a, 0x1e, 0x0f, 0xeb, 0xaf, - 0xe9, 0xb0, 0xde, 0xe5, 0x34, 0x18, 0x67, 0xf1, 0xd1, 0x87, 0xe0, 0x51, 0xee, 0xbf, 0x58, 0x74, - 0xe3, 0xd8, 0xf5, 0x5b, 0x7a, 0x1a, 0x08, 0x37, 0xce, 0xb8, 0x20, 0xf5, 0xe8, 0x7c, 0x3e, 0x1a, - 0xee, 0xf5, 0x3c, 0x7a, 0x06, 0x6a, 0xf1, 0xa6, 0x1b, 0xce, 0x44, 0xcd, 0x98, 0x9d, 0x5d, 0xd4, - 0xb4, 0xd3, 0x70, 0x45, 0xb4, 0x63, 0x85, 0x81, 0x1a, 0x30, 0xcc, 0x3f, 0x09, 0x8f, 0x28, 0x13, - 0xf2, 0xf1, 0xd9, 0x9e, 0xea, 0x51, 0x64, 0x9e, 0x4d, 0x60, 0xe7, 0xce, 0x25, 0x79, 0x92, 0xc2, - 0x1d, 0xff, 0x37, 0x0d, 0x32, 0x38, 0x45, 0xd4, 0xfe, 0xf9, 0x52, 0x7a, 0xc7, 0x6d, 0x2e, 0x52, - 0x14, 0xd3, 0xa5, 0x98, 0xdc, 0x74, 0x22, 0xe9, 0x8d, 0x39, 0x62, 0xf6, 0x81, 0xa0, 0x7b, 0xd3, - 0x89, 0xcc, 0x45, 0xcd, 0x18, 0x60, 0xc9, 0x09, 0xdd, 0x86, 0x4a, 0xe2, 0x39, 0x05, 0xa5, 0x2b, - 0x19, 0x1c, 0xb5, 0x03, 0x64, 0x61, 0x2a, 0xc6, 0x8c, 0x07, 0x7a, 0x8c, 0x5a, 0xfd, 0x6b, 0xf2, - 0xa4, 0x43, 0x18, 0xea, 0x6b, 0x31, 0x66, 0xad, 0xf6, 0xaf, 0x40, 0x8e, 0x5c, 0x55, 0x8a, 0x0c, - 0x5d, 0x04, 0xa0, 0x1b, 0xc8, 0xe5, 0x88, 0xac, 0xbb, 0xdb, 0xc2, 0x90, 0x50, 0x6b, 0xf7, 0xba, - 0x82, 0x60, 0x03, 0x4b, 0x3e, 0xb3, 0xd2, 0x59, 0xa7, 0xcf, 0x94, 0xba, 0x9f, 0xe1, 0x10, 0x6c, - 0x60, 0xa1, 0xe7, 0x61, 0xc0, 0x6d, 0x3b, 0x2d, 0x15, 0x49, 0xf9, 0x18, 0x5d, 0xb4, 0xf3, 0xac, - 0xe5, 0xde, 0xee, 0xf8, 0xa8, 0xea, 0x10, 0x6b, 0xc2, 0x02, 0x17, 0xfd, 0xb2, 0x05, 0xc3, 0x8d, - 0xa0, 0xdd, 0x0e, 0x7c, 0xbe, 0xed, 0x12, 0x7b, 0xc8, 0xdb, 0xc7, 0xa5, 0xe6, 0x27, 0x66, 0x0c, - 0x66, 0x7c, 0x13, 0xa9, 0xf2, 0xaa, 0x4c, 0x10, 0x4e, 0xf5, 0xca, 0x5c, 0xdb, 0xd5, 0x7d, 0xd6, - 0xf6, 0xaf, 0x5b, 0x70, 0x8a, 0x3f, 0x6b, 0xec, 0x06, 0x45, 0x0a, 0x51, 0x70, 0xcc, 0xaf, 0xd5, - 0xb5, 0x41, 0x56, 0x5e, 0xba, 0x2e, 0x38, 0xee, 0xee, 0x24, 0x9a, 0x83, 0x53, 0xeb, 0x41, 0xd4, - 0x20, 0xe6, 0x40, 0x08, 0xc1, 0xa4, 0x08, 0x5d, 0xce, 0x22, 0xe0, 0xee, 0x67, 0xd0, 0x4d, 0x78, - 0xc4, 0x68, 0x34, 0xc7, 0x81, 0xcb, 0xa6, 0x27, 0x04, 0xb5, 0x47, 0x2e, 0xe7, 0x62, 0xe1, 0x1e, - 0x4f, 0xa7, 0x1d, 0x26, 0xf5, 0x3e, 0x1c, 0x26, 0xaf, 0xc1, 0xb9, 0x46, 0xf7, 0xc8, 0x6c, 0xc5, - 0x9d, 0xb5, 0x98, 0x4b, 0xaa, 0xda, 0xf4, 0x0f, 0x08, 0x02, 0xe7, 0x66, 0x7a, 0x21, 0xe2, 0xde, - 0x34, 0xd0, 0xc7, 0xa0, 0x16, 0x11, 0xf6, 0x55, 0x62, 0x91, 0x4f, 0x73, 0xc4, 0x5d, 0xb2, 0xb6, - 0x40, 0x39, 0x59, 0x2d, 0x7b, 0x45, 0x43, 0x8c, 0x15, 0x47, 0x74, 0x07, 0x06, 0x43, 0x27, 0x69, - 0x6c, 0x88, 0x2c, 0x9a, 0x23, 0x87, 0xb1, 0x28, 0xe6, 0xcb, 0x94, 0xaa, 0x9e, 0xe4, 0xcb, 0x9c, - 0x09, 0x96, 0xdc, 0xa8, 0x35, 0xd2, 0x08, 0xda, 0x61, 0xe0, 0x13, 0x3f, 0x89, 0xc7, 0x46, 0xb4, - 0x35, 0x32, 0xa3, 0x5a, 0xb1, 0x81, 0x71, 0xfe, 0x03, 0x70, 0xaa, 0x6b, 0xe1, 0x1d, 0xc8, 0xb9, - 0x32, 0x0b, 0x8f, 0xe4, 0x4f, 0xf1, 0x03, 0xb9, 0x58, 0xfe, 0x51, 0x26, 0x56, 0xd5, 0x30, 0x7b, - 0xfb, 0x70, 0xd7, 0x39, 0x50, 0x26, 0xfe, 0x96, 0x90, 0xf8, 0x97, 0x8f, 0x36, 0xd2, 0x97, 0xfc, - 0x2d, 0xbe, 0x42, 0x99, 0x4f, 0xe2, 0x92, 0xbf, 0x85, 0x29, 0x6d, 0xf4, 0x65, 0x2b, 0x65, 0xb6, - 0x71, 0x27, 0xdf, 0x47, 0x8e, 0xc5, 0xce, 0xef, 0xdb, 0x92, 0xb3, 0xff, 0x4d, 0x09, 0x2e, 0xec, - 0x47, 0xa4, 0x8f, 0xe1, 0x7b, 0x12, 0x06, 0x62, 0x76, 0xfa, 0x2c, 0x44, 0xe8, 0x10, 0x9d, 0x59, - 0xfc, 0x3c, 0xfa, 0x35, 0x2c, 0x40, 0xc8, 0x83, 0x72, 0xdb, 0x09, 0x85, 0xef, 0x67, 0xfe, 0xa8, - 0xd9, 0x2b, 0xf4, 0xbf, 0xe3, 0x2d, 0x3a, 0x21, 0xf7, 0x28, 0x18, 0x0d, 0x98, 0xb2, 0x41, 0x09, - 0x54, 0x9d, 0x28, 0x72, 0xe4, 0x51, 0xe7, 0xb5, 0x62, 0xf8, 0x4d, 0x51, 0x92, 0xd3, 0xa7, 0xee, - 0xee, 0x8e, 0x8f, 0xa4, 0x9a, 0x30, 0x67, 0x66, 0x7f, 0x7e, 0x30, 0x95, 0xc1, 0xc1, 0xce, 0xaf, - 0x63, 0x18, 0x10, 0x2e, 0x1f, 0xab, 0xe8, 0xa4, 0x21, 0x9e, 0x82, 0xc7, 0x76, 0x75, 0x22, 0x91, - 0x59, 0xb0, 0x42, 0x9f, 0xb3, 0x58, 0xba, 0xb0, 0xcc, 0x6a, 0x11, 0x7b, 0xa9, 0xe3, 0xc9, 0x5e, - 0x36, 0x93, 0x90, 0x65, 0x23, 0x36, 0xb9, 0x53, 0x1d, 0x1b, 0xf2, 0xc4, 0xb7, 0xec, 0x8e, 0x4a, - 0x26, 0x14, 0x4b, 0x38, 0xda, 0xce, 0x39, 0xa7, 0x2e, 0x20, 0xe5, 0xb4, 0x8f, 0x93, 0xe9, 0xaf, - 0x59, 0x70, 0xca, 0xcd, 0x1e, 0x38, 0x8a, 0x9d, 0xc7, 0x11, 0x23, 0x21, 0x7a, 0x9f, 0x67, 0x2a, - 0xe5, 0xdb, 0x05, 0xc2, 0xdd, 0x9d, 0x41, 0x4d, 0xa8, 0xb8, 0xfe, 0x7a, 0x20, 0x4c, 0x8e, 0xe9, - 0xa3, 0x75, 0x6a, 0xde, 0x5f, 0x0f, 0xf4, 0x6a, 0xa6, 0xff, 0x30, 0xa3, 0x8e, 0x16, 0xe0, 0x4c, - 0x24, 0x7c, 0x43, 0x57, 0xdc, 0x98, 0xee, 0xe0, 0x17, 0xdc, 0xb6, 0x9b, 0x30, 0x73, 0xa1, 0x3c, - 0x3d, 0x76, 0x77, 0x77, 0xfc, 0x0c, 0xce, 0x81, 0xe3, 0xdc, 0xa7, 0xd0, 0x1b, 0x30, 0x28, 0xf3, - 0x9b, 0x6b, 0x45, 0xec, 0xe2, 0xba, 0xe7, 0xbf, 0x9a, 0x4c, 0x2b, 0x22, 0x95, 0x59, 0x32, 0xb4, - 0xdf, 0x1c, 0x82, 0xee, 0x43, 0x4c, 0xf4, 0x71, 0xa8, 0x47, 0x2a, 0xe7, 0xda, 0x2a, 0x42, 0xb9, - 0xca, 0xef, 0x2b, 0x0e, 0x50, 0x95, 0xe1, 0xa2, 0xb3, 0xab, 0x35, 0x47, 0xba, 0xbd, 0x88, 0xf5, - 0x59, 0x67, 0x01, 0x73, 0x5b, 0x70, 0xd5, 0xe7, 0x58, 0x3b, 0x7e, 0x03, 0x33, 0x1e, 0x28, 0x82, - 0x81, 0x0d, 0xe2, 0x78, 0xc9, 0x46, 0x31, 0x2e, 0xf7, 0x2b, 0x8c, 0x56, 0x36, 0xf3, 0x86, 0xb7, - 0x62, 0xc1, 0x09, 0x6d, 0xc3, 0xe0, 0x06, 0x9f, 0x00, 0xc2, 0xe2, 0x5f, 0x3c, 0xea, 0xe0, 0xa6, - 0x66, 0x95, 0xfe, 0xdc, 0xa2, 0x01, 0x4b, 0x76, 0x2c, 0xc8, 0xc5, 0x38, 0xbf, 0xe7, 0x4b, 0xb7, - 0xb8, 0xa4, 0xa3, 0xfe, 0x0f, 0xef, 0x3f, 0x0a, 0xc3, 0x11, 0x69, 0x04, 0x7e, 0xc3, 0xf5, 0x48, - 0x73, 0x4a, 0xba, 0xd3, 0x0f, 0x92, 0xaa, 0xc2, 0x76, 0xcd, 0xd8, 0xa0, 0x81, 0x53, 0x14, 0xd1, - 0x67, 0x2d, 0x18, 0x55, 0x89, 0x9a, 0xf4, 0x83, 0x10, 0xe1, 0xbe, 0x5d, 0x28, 0x28, 0x2d, 0x94, - 0xd1, 0x9c, 0x46, 0x77, 0x77, 0xc7, 0x47, 0xd3, 0x6d, 0x38, 0xc3, 0x17, 0xbd, 0x02, 0x10, 0xac, - 0xf1, 0x48, 0x96, 0xa9, 0x44, 0xf8, 0x72, 0x0f, 0xf2, 0xaa, 0xa3, 0x3c, 0x67, 0x4d, 0x52, 0xc0, - 0x06, 0x35, 0x74, 0x0d, 0x80, 0x2f, 0x9b, 0xd5, 0x9d, 0x50, 0x6e, 0x0b, 0x64, 0xae, 0x11, 0xac, - 0x28, 0xc8, 0xbd, 0xdd, 0xf1, 0x6e, 0xdf, 0x1a, 0x0b, 0x33, 0x30, 0x1e, 0x47, 0x3f, 0x01, 0x83, - 0x71, 0xa7, 0xdd, 0x76, 0x94, 0xa7, 0xb7, 0xc0, 0x2c, 0x38, 0x4e, 0xd7, 0x10, 0x45, 0xbc, 0x01, - 0x4b, 0x8e, 0xe8, 0x36, 0x15, 0xaa, 0xb1, 0x70, 0xfa, 0xb1, 0x55, 0xc4, 0x6d, 0x82, 0x21, 0xf6, - 0x4e, 0xef, 0x95, 0x81, 0x39, 0x38, 0x07, 0xe7, 0xde, 0xee, 0xf8, 0x23, 0xe9, 0xf6, 0x85, 0x40, - 0xe4, 0xa5, 0xe5, 0xd2, 0x44, 0x57, 0x65, 0xb9, 0x13, 0xfa, 0xda, 0x32, 0x0b, 0xff, 0x69, 0x5d, - 0xee, 0x84, 0x35, 0xf7, 0x1e, 0x33, 0xf3, 0x61, 0xb4, 0x08, 0xa7, 0x1b, 0x81, 0x9f, 0x44, 0x81, - 0xe7, 0xf1, 0x1a, 0x3e, 0x7c, 0x87, 0xc6, 0x3d, 0xc1, 0xef, 0x14, 0xdd, 0x3e, 0x3d, 0xd3, 0x8d, - 0x82, 0xf3, 0x9e, 0xb3, 0xfd, 0x74, 0x88, 0x9f, 0x18, 0x9c, 0xe7, 0x61, 0x98, 0x6c, 0x27, 0x24, - 0xf2, 0x1d, 0xef, 0x06, 0x5e, 0x90, 0x3e, 0x50, 0xb6, 0x06, 0x2e, 0x19, 0xed, 0x38, 0x85, 0x85, - 0x6c, 0xe5, 0x96, 0x30, 0x72, 0x2d, 0xb9, 0x5b, 0x42, 0x3a, 0x21, 0xec, 0xff, 0x55, 0x4a, 0x19, - 0x64, 0xab, 0x11, 0x21, 0x28, 0x80, 0xaa, 0x1f, 0x34, 0x95, 0xec, 0xbf, 0x5a, 0x8c, 0xec, 0xbf, - 0x1e, 0x34, 0x8d, 0x9a, 0x28, 0xf4, 0x5f, 0x8c, 0x39, 0x1f, 0x56, 0x34, 0x42, 0x56, 0xd7, 0x60, - 0x00, 0xb1, 0xd1, 0x28, 0x92, 0xb3, 0x2a, 0x1a, 0xb1, 0x64, 0x32, 0xc2, 0x69, 0xbe, 0x68, 0x13, - 0xaa, 0x1b, 0x41, 0x9c, 0xc8, 0xed, 0xc7, 0x11, 0x77, 0x3a, 0x57, 0x82, 0x38, 0x61, 0x56, 0x84, - 0x7a, 0x6d, 0xda, 0x12, 0x63, 0xce, 0xc3, 0xfe, 0xcf, 0x56, 0xca, 0xe3, 0x7d, 0x8b, 0x85, 0xbb, - 0x6e, 0x11, 0x9f, 0x2e, 0x6b, 0x33, 0x30, 0xe8, 0x2f, 0x64, 0x92, 0x07, 0xdf, 0xd5, 0xab, 0x42, - 0xd5, 0x1d, 0x4a, 0x61, 0x82, 0x91, 0x30, 0x62, 0x88, 0x3e, 0x69, 0xa5, 0xd3, 0x38, 0x4b, 0x45, - 0x6c, 0x30, 0xcc, 0x54, 0xe6, 0x7d, 0x33, 0x42, 0xed, 0x2f, 0x5b, 0x30, 0x38, 0xed, 0x34, 0x36, - 0x83, 0xf5, 0x75, 0xf4, 0x0c, 0xd4, 0x9a, 0x9d, 0xc8, 0xcc, 0x28, 0x55, 0xdb, 0xfc, 0x59, 0xd1, - 0x8e, 0x15, 0x06, 0x9d, 0xc3, 0xeb, 0x4e, 0x43, 0x26, 0x34, 0x97, 0xf9, 0x1c, 0xbe, 0xcc, 0x5a, - 0xb0, 0x80, 0xa0, 0x17, 0x60, 0xa8, 0xed, 0x6c, 0xcb, 0x87, 0xb3, 0xee, 0xf6, 0x45, 0x0d, 0xc2, - 0x26, 0x9e, 0xfd, 0x2f, 0x2c, 0x18, 0x9b, 0x76, 0x62, 0xb7, 0x31, 0xd5, 0x49, 0x36, 0xa6, 0xdd, - 0x64, 0xad, 0xd3, 0xd8, 0x24, 0x09, 0xcf, 0x62, 0xa7, 0xbd, 0xec, 0xc4, 0x74, 0x29, 0xa9, 0x7d, - 0x9d, 0xea, 0xe5, 0x0d, 0xd1, 0x8e, 0x15, 0x06, 0x7a, 0x03, 0x86, 0x42, 0x27, 0x8e, 0xef, 0x04, - 0x51, 0x13, 0x93, 0xf5, 0x62, 0x6a, 0x48, 0xac, 0x90, 0x46, 0x44, 0x12, 0x4c, 0xd6, 0xc5, 0x91, - 0xb0, 0xa6, 0x8f, 0x4d, 0x66, 0xf6, 0x17, 0x2d, 0x38, 0x37, 0x4d, 0x9c, 0x88, 0x44, 0xac, 0xe4, - 0x84, 0x7a, 0x91, 0x19, 0x2f, 0xe8, 0x34, 0xd1, 0xeb, 0x50, 0x4b, 0x68, 0x33, 0xed, 0x96, 0x55, - 0x6c, 0xb7, 0xd8, 0x89, 0xee, 0xaa, 0x20, 0x8e, 0x15, 0x1b, 0xfb, 0xaf, 0x5b, 0x30, 0xcc, 0x0e, - 0xc7, 0x66, 0x49, 0xe2, 0xb8, 0x5e, 0x57, 0x65, 0x26, 0xab, 0xcf, 0xca, 0x4c, 0x17, 0xa0, 0xb2, - 0x11, 0xb4, 0x49, 0xf6, 0x60, 0xf7, 0x4a, 0x40, 0xb7, 0xd5, 0x14, 0x82, 0x9e, 0xa3, 0x1f, 0xde, - 0xf5, 0x13, 0x87, 0x2e, 0x01, 0xe9, 0x7c, 0x3d, 0xc1, 0x3f, 0xba, 0x6a, 0xc6, 0x26, 0x8e, 0xfd, - 0x5b, 0x75, 0x18, 0x14, 0xa7, 0xff, 0x7d, 0x57, 0x32, 0x90, 0xfb, 0xfb, 0x52, 0xcf, 0xfd, 0x7d, - 0x0c, 0x03, 0x0d, 0x56, 0xf7, 0x4d, 0x98, 0x91, 0xd7, 0x0a, 0x09, 0x17, 0xe1, 0xa5, 0xe4, 0x74, - 0xb7, 0xf8, 0x7f, 0x2c, 0x58, 0xa1, 0x2f, 0x59, 0x70, 0xa2, 0x11, 0xf8, 0x3e, 0x69, 0x68, 0x1b, - 0xa7, 0x52, 0x44, 0x54, 0xc0, 0x4c, 0x9a, 0xa8, 0x3e, 0x99, 0xc9, 0x00, 0x70, 0x96, 0x3d, 0x7a, - 0x09, 0x46, 0xf8, 0x98, 0xdd, 0x4c, 0x79, 0x8c, 0x75, 0xc1, 0x1e, 0x13, 0x88, 0xd3, 0xb8, 0x68, - 0x82, 0x7b, 0xde, 0x45, 0x69, 0x9c, 0x01, 0xed, 0x58, 0x33, 0x8a, 0xe2, 0x18, 0x18, 0x28, 0x02, - 0x14, 0x91, 0xf5, 0x88, 0xc4, 0x1b, 0x22, 0x3a, 0x82, 0xd9, 0x57, 0x83, 0x87, 0xcb, 0x7a, 0xc6, - 0x5d, 0x94, 0x70, 0x0e, 0x75, 0xb4, 0x29, 0x36, 0x98, 0xb5, 0x22, 0x64, 0xa8, 0xf8, 0xcc, 0x3d, - 0xf7, 0x99, 0xe3, 0x50, 0x8d, 0x37, 0x9c, 0xa8, 0xc9, 0xec, 0xba, 0x32, 0xcf, 0xb4, 0x59, 0xa1, - 0x0d, 0x98, 0xb7, 0xa3, 0x59, 0x38, 0x99, 0x29, 0x37, 0x14, 0x0b, 0xcf, 0xae, 0xca, 0xaa, 0xc8, - 0x14, 0x2a, 0x8a, 0x71, 0xd7, 0x13, 0xa6, 0xf3, 0x61, 0x68, 0x1f, 0xe7, 0xc3, 0x8e, 0x8a, 0xc1, - 0xe3, 0x3e, 0xd7, 0x97, 0x0b, 0x19, 0x80, 0xbe, 0x02, 0xee, 0xbe, 0x90, 0x09, 0xb8, 0x1b, 0x61, - 0x1d, 0xb8, 0x59, 0x4c, 0x07, 0x0e, 0x1e, 0x5d, 0xf7, 0x20, 0xa3, 0xe5, 0xfe, 0xdc, 0x02, 0xf9, - 0x5d, 0x67, 0x9c, 0xc6, 0x06, 0xa1, 0x53, 0x06, 0xbd, 0x1f, 0x46, 0xd5, 0x16, 0x7a, 0x26, 0xe8, - 0xf8, 0x3c, 0x50, 0xae, 0xac, 0x8f, 0x70, 0x71, 0x0a, 0x8a, 0x33, 0xd8, 0x68, 0x12, 0xea, 0x74, - 0x9c, 0xf8, 0xa3, 0x5c, 0xd7, 0xaa, 0x6d, 0xfa, 0xd4, 0xf2, 0xbc, 0x78, 0x4a, 0xe3, 0xa0, 0x00, - 0x4e, 0x79, 0x4e, 0x9c, 0xb0, 0x1e, 0xd0, 0x1d, 0xf5, 0x21, 0x6b, 0x0e, 0xb0, 0xd0, 0xfd, 0x85, - 0x2c, 0x21, 0xdc, 0x4d, 0xdb, 0xfe, 0x76, 0x05, 0x46, 0x52, 0x92, 0xf1, 0x80, 0x4a, 0xfa, 0x19, - 0xa8, 0x49, 0xbd, 0x99, 0xad, 0x8e, 0xa2, 0x94, 0xab, 0xc2, 0xa0, 0x4a, 0x6b, 0x4d, 0x6b, 0xd5, - 0xac, 0x51, 0x61, 0x28, 0x5c, 0x6c, 0xe2, 0x31, 0xa1, 0x9c, 0x78, 0xf1, 0x8c, 0xe7, 0x12, 0x3f, - 0xe1, 0xdd, 0x2c, 0x46, 0x28, 0xaf, 0x2e, 0xac, 0x98, 0x44, 0xb5, 0x50, 0xce, 0x00, 0x70, 0x96, - 0x3d, 0xfa, 0x8c, 0x05, 0x23, 0xce, 0x9d, 0x58, 0x17, 0x27, 0x15, 0xa1, 0x75, 0x47, 0x54, 0x52, - 0xa9, 0x7a, 0xa7, 0xdc, 0xe5, 0x9b, 0x6a, 0xc2, 0x69, 0xa6, 0xe8, 0x2d, 0x0b, 0x10, 0xd9, 0x26, - 0x0d, 0x19, 0xfc, 0x27, 0xfa, 0x32, 0x50, 0xc4, 0x4e, 0xf3, 0x52, 0x17, 0x5d, 0x2e, 0xd5, 0xbb, - 0xdb, 0x71, 0x4e, 0x1f, 0xec, 0x7f, 0x5a, 0x56, 0x0b, 0x4a, 0xc7, 0x9b, 0x3a, 0x46, 0xdc, 0x9b, - 0x75, 0xf8, 0xb8, 0x37, 0x1d, 0x3f, 0xd0, 0x9d, 0xca, 0x98, 0xca, 0x7c, 0x2a, 0x3d, 0xa0, 0xcc, - 0xa7, 0x9f, 0xb2, 0x52, 0x75, 0x80, 0x86, 0x2e, 0xbe, 0x52, 0x6c, 0xac, 0xeb, 0x04, 0x8f, 0x6d, - 0xc8, 0x48, 0xf7, 0x74, 0x48, 0x0b, 0x95, 0xa6, 0x06, 0xda, 0x81, 0xa4, 0xe1, 0xbf, 0x2f, 0xc3, - 0x90, 0xa1, 0x49, 0x73, 0xcd, 0x22, 0xeb, 0x21, 0x33, 0x8b, 0x4a, 0x07, 0x30, 0x8b, 0x7e, 0x12, - 0xea, 0x0d, 0x29, 0xe5, 0x8b, 0xa9, 0x84, 0x9b, 0xd5, 0x1d, 0x5a, 0xd0, 0xab, 0x26, 0xac, 0x79, - 0xa2, 0xb9, 0x54, 0xa2, 0x8d, 0xd0, 0x10, 0x15, 0xa6, 0x21, 0xf2, 0x32, 0x61, 0x84, 0xa6, 0xe8, - 0x7e, 0x86, 0x95, 0x8b, 0x0a, 0x5d, 0xf1, 0x5e, 0x32, 0x22, 0x9d, 0x97, 0x8b, 0x5a, 0x9e, 0x97, - 0xcd, 0xd8, 0xc4, 0xb1, 0xbf, 0x6d, 0xa9, 0x8f, 0x7b, 0x1f, 0x0a, 0x23, 0xdc, 0x4e, 0x17, 0x46, - 0xb8, 0x54, 0xc8, 0x30, 0xf7, 0xa8, 0x88, 0x70, 0x1d, 0x06, 0x67, 0x82, 0x76, 0xdb, 0xf1, 0x9b, - 0xe8, 0x07, 0x61, 0xb0, 0xc1, 0x7f, 0x0a, 0xc7, 0x0e, 0x3b, 0x1e, 0x14, 0x50, 0x2c, 0x61, 0xe8, - 0x31, 0xa8, 0x38, 0x51, 0x4b, 0x3a, 0x73, 0x58, 0x28, 0xcc, 0x54, 0xd4, 0x8a, 0x31, 0x6b, 0xb5, - 0xff, 0x61, 0x05, 0xd8, 0x09, 0xb4, 0x13, 0x91, 0xe6, 0x6a, 0xc0, 0x2a, 0xf1, 0x1d, 0xeb, 0xa1, - 0x9a, 0xde, 0x2c, 0x3d, 0xcc, 0x07, 0x6b, 0xc6, 0xe1, 0x4a, 0xf9, 0x3e, 0x1f, 0xae, 0xf4, 0x38, - 0x2f, 0xab, 0x3c, 0x44, 0xe7, 0x65, 0xf6, 0xe7, 0x2d, 0x40, 0x2a, 0x6c, 0x41, 0x1f, 0x68, 0x4f, - 0x42, 0x5d, 0x05, 0x30, 0x08, 0xc3, 0x4a, 0x8b, 0x08, 0x09, 0xc0, 0x1a, 0xa7, 0x8f, 0x1d, 0xf2, - 0x93, 0x52, 0x7e, 0x97, 0xd3, 0x51, 0xb4, 0x4c, 0xea, 0x0b, 0x71, 0x6e, 0xff, 0x76, 0x09, 0x1e, - 0xe1, 0x2a, 0x79, 0xd1, 0xf1, 0x9d, 0x16, 0x69, 0xd3, 0x5e, 0xf5, 0x1b, 0xa2, 0xd0, 0xa0, 0x5b, - 0x33, 0x57, 0x46, 0xc5, 0x1e, 0x75, 0xed, 0xf2, 0x35, 0xc7, 0x57, 0xd9, 0xbc, 0xef, 0x26, 0x98, - 0x11, 0x47, 0x31, 0xd4, 0x64, 0xe9, 0x77, 0x21, 0x8b, 0x0b, 0x62, 0xa4, 0xc4, 0x92, 0xd0, 0x9b, - 0x04, 0x2b, 0x46, 0xd4, 0x70, 0xf5, 0x82, 0xc6, 0x26, 0x26, 0x61, 0xc0, 0xe4, 0xae, 0x11, 0x94, - 0xb8, 0x20, 0xda, 0xb1, 0xc2, 0xb0, 0x7f, 0xdb, 0x82, 0xac, 0x46, 0x32, 0x4a, 0x9e, 0x59, 0x7b, - 0x96, 0x3c, 0x3b, 0x40, 0xcd, 0xb1, 0x1f, 0x87, 0x21, 0x27, 0xa1, 0x46, 0x04, 0xdf, 0x76, 0x97, - 0x0f, 0x77, 0xac, 0xb1, 0x18, 0x34, 0xdd, 0x75, 0x97, 0x6d, 0xb7, 0x4d, 0x72, 0xf6, 0x7f, 0xaf, - 0xc0, 0xa9, 0xae, 0xdc, 0x0d, 0xf4, 0x22, 0x0c, 0x37, 0xc4, 0xf4, 0x08, 0xa5, 0x43, 0xab, 0x6e, - 0x06, 0xb1, 0x69, 0x18, 0x4e, 0x61, 0xf6, 0x31, 0x41, 0xe7, 0xe1, 0x74, 0x44, 0x37, 0xfa, 0x1d, - 0x32, 0xb5, 0x9e, 0x90, 0x68, 0x85, 0x34, 0x02, 0xbf, 0xc9, 0x0b, 0xf3, 0x95, 0xa7, 0x1f, 0xbd, - 0xbb, 0x3b, 0x7e, 0x1a, 0x77, 0x83, 0x71, 0xde, 0x33, 0x28, 0x84, 0x11, 0xcf, 0xb4, 0x01, 0xc5, - 0x06, 0xe0, 0x50, 0xe6, 0xa3, 0xb2, 0x11, 0x52, 0xcd, 0x38, 0xcd, 0x20, 0x6d, 0x48, 0x56, 0x1f, - 0x90, 0x21, 0xf9, 0x69, 0x6d, 0x48, 0xf2, 0xf3, 0xf7, 0x0f, 0x17, 0x9c, 0xbb, 0x73, 0xdc, 0x96, - 0xe4, 0xcb, 0x50, 0x93, 0xb1, 0x49, 0x7d, 0xc5, 0xf4, 0x98, 0x74, 0x7a, 0x48, 0xb4, 0x7b, 0x25, - 0xc8, 0xd9, 0x84, 0xd0, 0x75, 0xa6, 0x35, 0x7e, 0x6a, 0x9d, 0x1d, 0x4c, 0xeb, 0xa3, 0x6d, 0x1e, - 0x97, 0xc5, 0x75, 0xdb, 0x87, 0x8a, 0xde, 0x44, 0xe9, 0x50, 0x2d, 0x95, 0xd2, 0xa0, 0xc2, 0xb5, - 0x2e, 0x02, 0x68, 0x43, 0x4d, 0x04, 0xac, 0xab, 0x63, 0x5f, 0x6d, 0xcf, 0x61, 0x03, 0x8b, 0xee, - 0xa9, 0x5d, 0x3f, 0x4e, 0x1c, 0xcf, 0xbb, 0xe2, 0xfa, 0x89, 0x70, 0x0e, 0x2a, 0x25, 0x3e, 0xaf, - 0x41, 0xd8, 0xc4, 0x3b, 0xff, 0x5e, 0xe3, 0xbb, 0x1c, 0xe4, 0x7b, 0x6e, 0xc0, 0xb9, 0x39, 0x37, - 0x51, 0x69, 0x16, 0x6a, 0x1e, 0x51, 0x3b, 0x4c, 0xa5, 0x0d, 0x59, 0x3d, 0xd3, 0x86, 0x8c, 0x34, - 0x87, 0x52, 0x3a, 0x2b, 0x23, 0x9b, 0xe6, 0x60, 0xbf, 0x08, 0x67, 0xe6, 0xdc, 0xe4, 0xb2, 0xeb, - 0x91, 0x03, 0x32, 0xb1, 0x7f, 0x73, 0x00, 0x86, 0xcd, 0x44, 0xbd, 0x83, 0x64, 0x3e, 0x7d, 0x91, - 0x9a, 0x5a, 0xe2, 0xed, 0x5c, 0x75, 0x68, 0x76, 0xeb, 0xc8, 0x59, 0x83, 0xf9, 0x23, 0x66, 0x58, - 0x5b, 0x9a, 0x27, 0x36, 0x3b, 0x80, 0xee, 0x40, 0x75, 0x9d, 0x85, 0xe1, 0x97, 0x8b, 0x88, 0x2c, - 0xc8, 0x1b, 0x51, 0xbd, 0xcc, 0x78, 0x20, 0x3f, 0xe7, 0x47, 0x35, 0x64, 0x94, 0xce, 0xed, 0x32, - 0x42, 0x47, 0x45, 0x56, 0x97, 0xc2, 0xe8, 0x25, 0xea, 0xab, 0x87, 0x10, 0xf5, 0x29, 0xc1, 0x3b, - 0xf0, 0x80, 0x04, 0x2f, 0x4b, 0xa9, 0x48, 0x36, 0x98, 0xfd, 0x26, 0x62, 0xdd, 0x07, 0xd9, 0x20, - 0x18, 0x29, 0x15, 0x29, 0x30, 0xce, 0xe2, 0xa3, 0x4f, 0x28, 0xd1, 0x5d, 0x2b, 0xc2, 0xaf, 0x6a, - 0xce, 0xe8, 0xe3, 0x96, 0xda, 0x9f, 0x2f, 0xc1, 0xe8, 0x9c, 0xdf, 0x59, 0x9e, 0x5b, 0xee, 0xac, - 0x79, 0x6e, 0xe3, 0x1a, 0xd9, 0xa1, 0xa2, 0x79, 0x93, 0xec, 0xcc, 0xcf, 0x8a, 0x15, 0xa4, 0xe6, - 0xcc, 0x35, 0xda, 0x88, 0x39, 0x8c, 0x0a, 0xa3, 0x75, 0xd7, 0x6f, 0x91, 0x28, 0x8c, 0x5c, 0xe1, - 0xf2, 0x34, 0x84, 0xd1, 0x65, 0x0d, 0xc2, 0x26, 0x1e, 0xa5, 0x1d, 0xdc, 0xf1, 0x49, 0x94, 0x35, - 0x64, 0x97, 0x68, 0x23, 0xe6, 0x30, 0x8a, 0x94, 0x44, 0x9d, 0x38, 0x11, 0x93, 0x51, 0x21, 0xad, - 0xd2, 0x46, 0xcc, 0x61, 0x74, 0xa5, 0xc7, 0x9d, 0x35, 0x16, 0xb8, 0x91, 0x09, 0xac, 0x5f, 0xe1, - 0xcd, 0x58, 0xc2, 0x29, 0xea, 0x26, 0xd9, 0x99, 0xa5, 0xbb, 0xde, 0x4c, 0x7e, 0xcd, 0x35, 0xde, - 0x8c, 0x25, 0x9c, 0x55, 0x14, 0x4c, 0x0f, 0xc7, 0xf7, 0x5c, 0x45, 0xc1, 0x74, 0xf7, 0x7b, 0xec, - 0x9f, 0x7f, 0xc9, 0x82, 0x61, 0x33, 0xdc, 0x0a, 0xb5, 0x32, 0x36, 0xee, 0x52, 0x57, 0x41, 0xda, - 0x1f, 0xcd, 0xbb, 0xa1, 0xab, 0xe5, 0x26, 0x41, 0x18, 0x3f, 0x4b, 0xfc, 0x96, 0xeb, 0x13, 0x76, - 0x8a, 0xce, 0xc3, 0xb4, 0x52, 0xb1, 0x5c, 0x33, 0x41, 0x93, 0x1c, 0xc2, 0x48, 0xb6, 0x6f, 0xc1, - 0xa9, 0xae, 0xa4, 0xaa, 0x3e, 0x4c, 0x8b, 0x7d, 0x53, 0x5a, 0x6d, 0x0c, 0x43, 0x94, 0xb0, 0xac, - 0x6a, 0x33, 0x03, 0xa7, 0xf8, 0x42, 0xa2, 0x9c, 0x56, 0x1a, 0x1b, 0xa4, 0xad, 0x12, 0xe5, 0x98, - 0x7f, 0xfd, 0x66, 0x16, 0x88, 0xbb, 0xf1, 0xed, 0x2f, 0x58, 0x30, 0x92, 0xca, 0x73, 0x2b, 0xc8, - 0x08, 0x62, 0x2b, 0x2d, 0x60, 0xd1, 0x7f, 0x2c, 0x04, 0xba, 0xcc, 0x94, 0xa9, 0x5e, 0x69, 0x1a, - 0x84, 0x4d, 0x3c, 0xfb, 0xcb, 0x25, 0xa8, 0xc9, 0x08, 0x8a, 0x3e, 0xba, 0xf2, 0x39, 0x0b, 0x46, - 0xd4, 0x99, 0x06, 0x73, 0x96, 0x95, 0x8a, 0x48, 0x4a, 0xa0, 0x3d, 0x50, 0xdb, 0x6d, 0x7f, 0x3d, - 0xd0, 0x16, 0x39, 0x36, 0x99, 0xe1, 0x34, 0x6f, 0x74, 0x13, 0x20, 0xde, 0x89, 0x13, 0xd2, 0x36, - 0xdc, 0x76, 0xb6, 0xb1, 0xe2, 0x26, 0x1a, 0x41, 0x44, 0xe8, 0xfa, 0xba, 0x1e, 0x34, 0xc9, 0x8a, - 0xc2, 0xd4, 0x26, 0x94, 0x6e, 0xc3, 0x06, 0x25, 0xfb, 0xef, 0x97, 0xe0, 0x64, 0xb6, 0x4b, 0xe8, - 0xc3, 0x30, 0x2c, 0xb9, 0x1b, 0xb7, 0x8d, 0xc9, 0xb0, 0x91, 0x61, 0x6c, 0xc0, 0xee, 0xed, 0x8e, - 0x8f, 0x77, 0xdf, 0xf6, 0x36, 0x61, 0xa2, 0xe0, 0x14, 0x31, 0x7e, 0xb0, 0x24, 0x4e, 0x40, 0xa7, - 0x77, 0xa6, 0xc2, 0x50, 0x9c, 0x0e, 0x19, 0x07, 0x4b, 0x26, 0x14, 0x67, 0xb0, 0xd1, 0x32, 0x9c, - 0x31, 0x5a, 0xae, 0x13, 0xb7, 0xb5, 0xb1, 0x16, 0x44, 0x72, 0x67, 0xf5, 0x98, 0x0e, 0xec, 0xea, - 0xc6, 0xc1, 0xb9, 0x4f, 0x52, 0x6d, 0xdf, 0x70, 0x42, 0xa7, 0xe1, 0x26, 0x3b, 0xc2, 0x0f, 0xa9, - 0x64, 0xd3, 0x8c, 0x68, 0xc7, 0x0a, 0xc3, 0x5e, 0x84, 0x4a, 0x9f, 0x33, 0xa8, 0x2f, 0x8b, 0xfe, - 0x65, 0xa8, 0x51, 0x72, 0xd2, 0xbc, 0x2b, 0x82, 0x64, 0x00, 0x35, 0x79, 0x61, 0x08, 0xb2, 0xa1, - 0xec, 0x3a, 0xf2, 0xec, 0x4e, 0xbd, 0xd6, 0x7c, 0x1c, 0x77, 0xd8, 0x26, 0x99, 0x02, 0xd1, 0x93, - 0x50, 0x26, 0xdb, 0x61, 0xf6, 0x90, 0xee, 0xd2, 0x76, 0xe8, 0x46, 0x24, 0xa6, 0x48, 0x64, 0x3b, - 0x44, 0xe7, 0xa1, 0xe4, 0x36, 0x85, 0x92, 0x02, 0x81, 0x53, 0x9a, 0x9f, 0xc5, 0x25, 0xb7, 0x69, - 0x6f, 0x43, 0x5d, 0xdd, 0x50, 0x82, 0x36, 0xa5, 0xec, 0xb6, 0x8a, 0x08, 0x79, 0x92, 0x74, 0x7b, - 0x48, 0xed, 0x0e, 0x80, 0x4e, 0xf8, 0x2b, 0x4a, 0xbe, 0x5c, 0x80, 0x4a, 0x23, 0x10, 0xc9, 0xc8, - 0x35, 0x4d, 0x86, 0x09, 0x6d, 0x06, 0xb1, 0x6f, 0xc1, 0xe8, 0x35, 0x3f, 0xb8, 0xc3, 0xca, 0xab, - 0xb3, 0x6a, 0x62, 0x94, 0xf0, 0x3a, 0xfd, 0x91, 0x35, 0x11, 0x18, 0x14, 0x73, 0x98, 0xaa, 0xcf, - 0x54, 0xea, 0x55, 0x9f, 0xc9, 0xfe, 0xa4, 0x05, 0xc3, 0x2a, 0x73, 0x68, 0x6e, 0x6b, 0x93, 0xd2, - 0x6d, 0x45, 0x41, 0x27, 0xcc, 0xd2, 0x65, 0x77, 0x08, 0x61, 0x0e, 0x33, 0x53, 0xea, 0x4a, 0xfb, - 0xa4, 0xd4, 0x5d, 0x80, 0xca, 0xa6, 0xeb, 0x37, 0xb3, 0x97, 0x62, 0x5c, 0x73, 0xfd, 0x26, 0x66, - 0x10, 0xda, 0x85, 0x93, 0xaa, 0x0b, 0x52, 0x21, 0xbc, 0x08, 0xc3, 0x6b, 0x1d, 0xd7, 0x6b, 0xca, - 0x32, 0x69, 0x19, 0x4f, 0xc9, 0xb4, 0x01, 0xc3, 0x29, 0x4c, 0xba, 0xaf, 0x5b, 0x73, 0x7d, 0x27, - 0xda, 0x59, 0xd6, 0x1a, 0x48, 0x09, 0xa5, 0x69, 0x05, 0xc1, 0x06, 0x96, 0xfd, 0x66, 0x19, 0x46, - 0xd3, 0xf9, 0x53, 0x7d, 0x6c, 0xaf, 0x9e, 0x84, 0x2a, 0x4b, 0xa9, 0xca, 0x7e, 0x5a, 0xf6, 0x3c, - 0xe6, 0x30, 0x14, 0xc3, 0x00, 0x2f, 0xc6, 0x50, 0xcc, 0x85, 0x32, 0xaa, 0x93, 0xca, 0xbf, 0xc2, - 0xe2, 0xc9, 0x44, 0xfd, 0x07, 0xc1, 0x0a, 0x7d, 0xc6, 0x82, 0xc1, 0x20, 0x34, 0xeb, 0xfa, 0x7c, - 0xa8, 0xc8, 0xdc, 0x32, 0x91, 0x2c, 0x23, 0x2c, 0x62, 0xf5, 0xe9, 0xe5, 0xe7, 0x90, 0xac, 0xcf, - 0xbf, 0x0f, 0x86, 0x4d, 0xcc, 0xfd, 0x8c, 0xe2, 0x9a, 0x69, 0x14, 0x7f, 0xce, 0x9c, 0x14, 0x22, - 0x7b, 0xae, 0x8f, 0xe5, 0x76, 0x03, 0xaa, 0x0d, 0x15, 0x00, 0x70, 0xa8, 0xe2, 0x9a, 0xaa, 0x3a, - 0x02, 0x3b, 0x04, 0xe2, 0xd4, 0xec, 0x6f, 0x5b, 0xc6, 0xfc, 0xc0, 0x24, 0x9e, 0x6f, 0xa2, 0x08, - 0xca, 0xad, 0xad, 0x4d, 0x61, 0x8a, 0x5e, 0x2d, 0x68, 0x78, 0xe7, 0xb6, 0x36, 0xf5, 0x1c, 0x37, - 0x5b, 0x31, 0x65, 0xd6, 0x87, 0x13, 0x30, 0x95, 0x64, 0x59, 0xde, 0x3f, 0xc9, 0xd2, 0x7e, 0xab, - 0x04, 0xa7, 0xba, 0x26, 0x15, 0x7a, 0x03, 0xaa, 0x11, 0x7d, 0x4b, 0xf1, 0x7a, 0x0b, 0x85, 0xa5, - 0x45, 0xc6, 0xf3, 0x4d, 0xad, 0x77, 0xd3, 0xed, 0x98, 0xb3, 0x44, 0x57, 0x01, 0xe9, 0x30, 0x15, - 0xe5, 0x81, 0xe4, 0xaf, 0x7c, 0x5e, 0x3c, 0x8a, 0xa6, 0xba, 0x30, 0x70, 0xce, 0x53, 0xe8, 0xa5, - 0xac, 0x23, 0xb3, 0x9c, 0x3e, 0xb7, 0xdc, 0xcb, 0x27, 0x69, 0xff, 0xb3, 0x12, 0x8c, 0xa4, 0xca, - 0x2c, 0x21, 0x0f, 0x6a, 0xc4, 0x63, 0x4e, 0x7d, 0xa9, 0x6c, 0x8e, 0x5a, 0x7c, 0x58, 0x29, 0xc8, - 0x4b, 0x82, 0x2e, 0x56, 0x1c, 0x1e, 0x8e, 0xc3, 0xf5, 0x17, 0x61, 0x58, 0x76, 0xe8, 0x43, 0x4e, - 0xdb, 0x13, 0x03, 0xa8, 0xe6, 0xe8, 0x25, 0x03, 0x86, 0x53, 0x98, 0xf6, 0xef, 0x94, 0x61, 0x8c, - 0x9f, 0x82, 0x34, 0xd5, 0xcc, 0x5b, 0x94, 0xfb, 0xad, 0xbf, 0xac, 0x8b, 0xa1, 0xf1, 0x81, 0x5c, - 0x3b, 0x6a, 0xad, 0xff, 0x7c, 0x46, 0x7d, 0x45, 0x66, 0xfd, 0x42, 0x26, 0x32, 0x8b, 0x9b, 0xdd, - 0xad, 0x63, 0xea, 0xd1, 0xf7, 0x56, 0xa8, 0xd6, 0xaf, 0x94, 0xe0, 0x44, 0xe6, 0x22, 0x05, 0xf4, - 0x66, 0xba, 0xf6, 0xae, 0x55, 0x84, 0xaf, 0x7c, 0xcf, 0xda, 0xfa, 0x07, 0xab, 0xc0, 0xfb, 0x80, - 0x96, 0x8a, 0xfd, 0xfb, 0x25, 0x18, 0x4d, 0xdf, 0x00, 0xf1, 0x10, 0x8e, 0xd4, 0xbb, 0xa1, 0xce, - 0x8a, 0x9c, 0xb3, 0x9b, 0x2d, 0xb9, 0x4b, 0x9e, 0xd7, 0x93, 0x96, 0x8d, 0x58, 0xc3, 0x1f, 0x8a, - 0xc2, 0xc6, 0xf6, 0xdf, 0xb5, 0xe0, 0x2c, 0x7f, 0xcb, 0xec, 0x3c, 0xfc, 0x2b, 0x79, 0xa3, 0xfb, - 0x6a, 0xb1, 0x1d, 0xcc, 0x14, 0xf1, 0xdb, 0x6f, 0x7c, 0xd9, 0x8d, 0x7a, 0xa2, 0xb7, 0xe9, 0xa9, - 0xf0, 0x10, 0x76, 0xf6, 0x40, 0x93, 0xc1, 0xfe, 0xfd, 0x32, 0xe8, 0x4b, 0x04, 0x91, 0x2b, 0x72, - 0x1c, 0x0b, 0x29, 0x66, 0xb8, 0xb2, 0xe3, 0x37, 0xf4, 0x75, 0x85, 0xb5, 0x4c, 0x8a, 0xe3, 0xcf, - 0x5a, 0x30, 0xe4, 0xfa, 0x6e, 0xe2, 0x3a, 0x6c, 0x1b, 0x5d, 0xcc, 0x05, 0x67, 0x8a, 0xdd, 0x3c, - 0xa7, 0x1c, 0x44, 0xe6, 0x39, 0x8e, 0x62, 0x86, 0x4d, 0xce, 0xe8, 0xa3, 0x22, 0x78, 0xba, 0x5c, - 0x58, 0x76, 0x6e, 0x2d, 0x13, 0x31, 0x1d, 0x52, 0xc3, 0x2b, 0x89, 0x0a, 0x4a, 0x6a, 0xc7, 0x94, - 0x94, 0xaa, 0x8b, 0xab, 0xaf, 0x73, 0xa6, 0xcd, 0x98, 0x33, 0xb2, 0x63, 0x40, 0xdd, 0x63, 0x71, - 0xc0, 0xc0, 0xd4, 0x49, 0xa8, 0x3b, 0x9d, 0x24, 0x68, 0xd3, 0x61, 0x12, 0x47, 0x4d, 0x3a, 0xf4, - 0x56, 0x02, 0xb0, 0xc6, 0xb1, 0xdf, 0xac, 0x42, 0x26, 0xe9, 0x10, 0x6d, 0x9b, 0x17, 0x60, 0x5a, - 0xc5, 0x5e, 0x80, 0xa9, 0x3a, 0x93, 0x77, 0x09, 0x26, 0x6a, 0x41, 0x35, 0xdc, 0x70, 0x62, 0x69, - 0x56, 0xbf, 0xac, 0xf6, 0x71, 0xb4, 0xf1, 0xde, 0xee, 0xf8, 0x8f, 0xf5, 0xe7, 0x75, 0xa5, 0x73, - 0x75, 0x92, 0x17, 0x1b, 0xd1, 0xac, 0x19, 0x0d, 0xcc, 0xe9, 0x1f, 0xe4, 0x8a, 0xb7, 0x4f, 0x89, - 0x6a, 0xee, 0x98, 0xc4, 0x1d, 0x2f, 0x11, 0xb3, 0xe1, 0xe5, 0x02, 0x57, 0x19, 0x27, 0xac, 0xd3, - 0xe5, 0xf9, 0x7f, 0x6c, 0x30, 0x45, 0x1f, 0x86, 0x7a, 0x9c, 0x38, 0x51, 0x72, 0xc8, 0x04, 0x57, - 0x35, 0xe8, 0x2b, 0x92, 0x08, 0xd6, 0xf4, 0xd0, 0x2b, 0xac, 0xb6, 0xab, 0x1b, 0x6f, 0x1c, 0x32, - 0xe7, 0x41, 0xd6, 0x81, 0x15, 0x14, 0xb0, 0x41, 0x0d, 0x5d, 0x04, 0x60, 0x73, 0x9b, 0x07, 0xfa, - 0xd5, 0x98, 0x97, 0x49, 0x89, 0x42, 0xac, 0x20, 0xd8, 0xc0, 0xb2, 0x7f, 0x18, 0xd2, 0xf5, 0x1e, - 0xd0, 0xb8, 0x2c, 0x2f, 0xc1, 0xbd, 0xd0, 0x2c, 0x77, 0x21, 0x55, 0x09, 0xe2, 0xd7, 0x2d, 0x30, - 0x8b, 0x52, 0xa0, 0xd7, 0x79, 0xf5, 0x0b, 0xab, 0x88, 0x93, 0x43, 0x83, 0xee, 0xc4, 0xa2, 0x13, - 0x66, 0x8e, 0xb0, 0x65, 0x09, 0x8c, 0xf3, 0xef, 0x85, 0x9a, 0x84, 0x1e, 0xc8, 0xa8, 0xfb, 0x04, - 0x9c, 0xce, 0x5e, 0x0f, 0x2e, 0x4e, 0x9d, 0xf6, 0x77, 0xfd, 0x48, 0x7f, 0x4e, 0xa9, 0x97, 0x3f, - 0xa7, 0x8f, 0x6b, 0x50, 0x7f, 0xc3, 0x82, 0x0b, 0xfb, 0xdd, 0x62, 0x8e, 0x1e, 0x83, 0xca, 0x1d, - 0x27, 0x92, 0x45, 0xb7, 0x99, 0xa0, 0xbc, 0xe5, 0x44, 0x3e, 0x66, 0xad, 0x68, 0x07, 0x06, 0x78, - 0x34, 0x98, 0xb0, 0xd6, 0x5f, 0x2e, 0xf6, 0x4e, 0xf5, 0x6b, 0xc4, 0xd8, 0x2e, 0xf0, 0x48, 0x34, - 0x2c, 0x18, 0xda, 0xdf, 0xb1, 0x00, 0x2d, 0x6d, 0x91, 0x28, 0x72, 0x9b, 0x46, 0xfc, 0x1a, 0xbb, - 0x15, 0xc5, 0xb8, 0xfd, 0xc4, 0x4c, 0x71, 0xcd, 0xdc, 0x8a, 0x62, 0xfc, 0xcb, 0xbf, 0x15, 0xa5, - 0x74, 0xb0, 0x5b, 0x51, 0xd0, 0x12, 0x9c, 0x6d, 0xf3, 0xed, 0x06, 0xbf, 0x69, 0x80, 0xef, 0x3d, - 0x54, 0x42, 0xd9, 0xb9, 0xbb, 0xbb, 0xe3, 0x67, 0x17, 0xf3, 0x10, 0x70, 0xfe, 0x73, 0xf6, 0x7b, - 0x01, 0xf1, 0xb0, 0xb5, 0x99, 0xbc, 0x18, 0xa4, 0x9e, 0xee, 0x17, 0xfb, 0xab, 0x55, 0x38, 0x91, - 0x29, 0xc9, 0x4a, 0xb7, 0x7a, 0xdd, 0x41, 0x4f, 0x47, 0xd6, 0xdf, 0xdd, 0xdd, 0xeb, 0x2b, 0x8c, - 0xca, 0x87, 0xaa, 0xeb, 0x87, 0x9d, 0xa4, 0x98, 0x1c, 0x52, 0xde, 0x89, 0x79, 0x4a, 0xd0, 0x70, - 0x17, 0xd3, 0xbf, 0x98, 0xb3, 0x29, 0x32, 0x28, 0x2b, 0x65, 0x8c, 0x57, 0x1e, 0x90, 0x3b, 0xe0, - 0x53, 0x3a, 0x44, 0xaa, 0x5a, 0x84, 0x63, 0x31, 0x33, 0x59, 0x8e, 0xfb, 0xa8, 0xfd, 0xd7, 0x4a, - 0x30, 0x64, 0x7c, 0x34, 0xf4, 0x8b, 0xe9, 0x92, 0x4d, 0x56, 0x71, 0xaf, 0xc4, 0xe8, 0x4f, 0xe8, - 0xa2, 0x4c, 0xfc, 0x95, 0x9e, 0xea, 0xae, 0xd6, 0x74, 0x6f, 0x77, 0xfc, 0x64, 0xa6, 0x1e, 0x53, - 0xaa, 0x82, 0xd3, 0xf9, 0x8f, 0xc3, 0x89, 0x0c, 0x99, 0x9c, 0x57, 0x5e, 0x4d, 0xdf, 0xfe, 0x7e, - 0x44, 0xb7, 0x94, 0x39, 0x64, 0xdf, 0xa0, 0x43, 0x26, 0xd2, 0xe8, 0x02, 0x8f, 0xf4, 0xe1, 0x83, - 0xcd, 0x64, 0xcb, 0x96, 0xfa, 0xcc, 0x96, 0x7d, 0x1a, 0x6a, 0x61, 0xe0, 0xb9, 0x0d, 0x57, 0x55, - 0x21, 0x64, 0xf9, 0xb9, 0xcb, 0xa2, 0x0d, 0x2b, 0x28, 0xba, 0x03, 0x75, 0x75, 0x51, 0xbe, 0xf0, - 0x6f, 0x17, 0x75, 0xe8, 0xa3, 0x8c, 0x16, 0x7d, 0x01, 0xbe, 0xe6, 0x85, 0x6c, 0x18, 0x60, 0x4a, - 0x50, 0x86, 0xfe, 0x33, 0xdf, 0x3b, 0xd3, 0x8e, 0x31, 0x16, 0x10, 0xfb, 0xeb, 0x75, 0x38, 0x93, - 0x57, 0x17, 0x1b, 0x7d, 0x0c, 0x06, 0x78, 0x1f, 0x8b, 0xb9, 0x7a, 0x21, 0x8f, 0xc7, 0x1c, 0x23, - 0x28, 0xba, 0xc5, 0x7e, 0x63, 0xc1, 0x53, 0x70, 0xf7, 0x9c, 0x35, 0x31, 0x43, 0x8e, 0x87, 0xfb, - 0x82, 0xa3, 0xb9, 0x2f, 0x38, 0x9c, 0xbb, 0xe7, 0xac, 0xa1, 0x6d, 0xa8, 0xb6, 0xdc, 0x84, 0x38, - 0xc2, 0x89, 0x70, 0xeb, 0x58, 0x98, 0x13, 0x87, 0x5b, 0x69, 0xec, 0x27, 0xe6, 0x0c, 0xd1, 0xd7, - 0x2c, 0x38, 0xb1, 0x96, 0x4e, 0x8d, 0x17, 0xc2, 0xd3, 0x39, 0x86, 0xda, 0xe7, 0x69, 0x46, 0xfc, - 0x5a, 0xa0, 0x4c, 0x23, 0xce, 0x76, 0x07, 0x7d, 0xda, 0x82, 0xc1, 0x75, 0xd7, 0x33, 0xca, 0xe0, - 0x1e, 0xc3, 0xc7, 0xb9, 0xcc, 0x18, 0xe8, 0x1d, 0x07, 0xff, 0x1f, 0x63, 0xc9, 0xb9, 0x97, 0xa6, - 0x1a, 0x38, 0xaa, 0xa6, 0x1a, 0x7c, 0x40, 0x9a, 0xea, 0xb3, 0x16, 0xd4, 0xd5, 0x48, 0x8b, 0x74, - 0xe7, 0x0f, 0x1f, 0xe3, 0x27, 0xe7, 0x9e, 0x13, 0xf5, 0x17, 0x6b, 0xe6, 0xe8, 0x4b, 0x16, 0x0c, - 0x39, 0x6f, 0x74, 0x22, 0xd2, 0x24, 0x5b, 0x41, 0x18, 0x8b, 0x3b, 0x05, 0x5f, 0x2d, 0xbe, 0x33, - 0x53, 0x94, 0xc9, 0x2c, 0xd9, 0x5a, 0x0a, 0x63, 0x91, 0x96, 0xa4, 0x1b, 0xb0, 0xd9, 0x05, 0x7b, - 0xb7, 0x04, 0xe3, 0xfb, 0x50, 0x40, 0x2f, 0xc2, 0x70, 0x10, 0xb5, 0x1c, 0xdf, 0x7d, 0xc3, 0xac, - 0x75, 0xa1, 0xac, 0xac, 0x25, 0x03, 0x86, 0x53, 0x98, 0x66, 0x42, 0x76, 0x69, 0x9f, 0x84, 0xec, - 0x0b, 0x50, 0x89, 0x48, 0x18, 0x64, 0x37, 0x0b, 0x2c, 0x25, 0x80, 0x41, 0xd0, 0xe3, 0x50, 0x76, - 0x42, 0x57, 0x04, 0xa2, 0xa9, 0x3d, 0xd0, 0xd4, 0xf2, 0x3c, 0xa6, 0xed, 0xa9, 0xfa, 0x10, 0xd5, - 0xfb, 0x52, 0x1f, 0x82, 0xaa, 0x01, 0x71, 0x76, 0x31, 0xa0, 0xd5, 0x40, 0xfa, 0x4c, 0xc1, 0x7e, - 0xab, 0x0c, 0x8f, 0xef, 0x39, 0x5f, 0x74, 0x1c, 0x9e, 0xb5, 0x47, 0x1c, 0x9e, 0x1c, 0x9e, 0xd2, - 0x7e, 0xc3, 0x53, 0xee, 0x31, 0x3c, 0x9f, 0xa6, 0xcb, 0x40, 0xd6, 0x08, 0x29, 0xe6, 0x56, 0xb8, - 0x5e, 0x25, 0x47, 0xc4, 0x0a, 0x90, 0x50, 0xac, 0xf9, 0xd2, 0x3d, 0x40, 0x2a, 0x19, 0xb9, 0x5a, - 0x84, 0x1a, 0xe8, 0x59, 0x33, 0x84, 0xcf, 0xfd, 0x5e, 0x19, 0xce, 0xf6, 0xcf, 0x95, 0xe0, 0xc9, - 0x3e, 0xa4, 0xb7, 0x39, 0x8b, 0xad, 0x3e, 0x67, 0xf1, 0xf7, 0xf6, 0x67, 0xb2, 0xff, 0xaa, 0x05, - 0xe7, 0x7b, 0x2b, 0x0f, 0xf4, 0x1c, 0x0c, 0xad, 0x45, 0x8e, 0xdf, 0xd8, 0x60, 0x37, 0x5d, 0xca, - 0x41, 0x61, 0x63, 0xad, 0x9b, 0xb1, 0x89, 0x43, 0xb7, 0xb7, 0x3c, 0x26, 0xc1, 0xc0, 0x90, 0xc9, - 0xa3, 0x74, 0x7b, 0xbb, 0x9a, 0x05, 0xe2, 0x6e, 0x7c, 0xfb, 0xcf, 0x4a, 0xf9, 0xdd, 0xe2, 0x46, - 0xc6, 0x41, 0xbe, 0x93, 0xf8, 0x0a, 0xa5, 0x3e, 0x64, 0x49, 0xf9, 0x7e, 0xcb, 0x92, 0x4a, 0x2f, - 0x59, 0x82, 0x66, 0xe1, 0xa4, 0x71, 0x85, 0x0a, 0x4f, 0x08, 0xe6, 0x01, 0xb7, 0xaa, 0x4a, 0xc6, - 0x72, 0x06, 0x8e, 0xbb, 0x9e, 0x40, 0xcf, 0x40, 0xcd, 0xf5, 0x63, 0xd2, 0xe8, 0x44, 0x3c, 0xd0, - 0xdb, 0x48, 0xc2, 0x9a, 0x17, 0xed, 0x58, 0x61, 0xd8, 0xbf, 0x54, 0x82, 0x73, 0x3d, 0xed, 0xac, - 0xfb, 0x24, 0xbb, 0xcc, 0xcf, 0x51, 0xb9, 0x3f, 0x9f, 0xc3, 0x1c, 0xa4, 0xea, 0xbe, 0x83, 0xf4, - 0x07, 0xbd, 0x27, 0x26, 0xb5, 0xb9, 0xbf, 0x6f, 0x47, 0xe9, 0x25, 0x18, 0x71, 0xc2, 0x90, 0xe3, - 0xb1, 0x78, 0xcd, 0x4c, 0x95, 0x9c, 0x29, 0x13, 0x88, 0xd3, 0xb8, 0x7d, 0x69, 0xcf, 0x3f, 0xb2, - 0xa0, 0x8e, 0xc9, 0x3a, 0x97, 0x0e, 0xe8, 0xb6, 0x18, 0x22, 0xab, 0x88, 0x7a, 0x9a, 0x74, 0x60, - 0x63, 0x97, 0xd5, 0x99, 0xcc, 0x1b, 0xec, 0xee, 0xab, 0x76, 0x4a, 0x07, 0xba, 0x6a, 0x47, 0x5d, - 0xb6, 0x52, 0xee, 0x7d, 0xd9, 0x8a, 0xfd, 0x8d, 0x41, 0xfa, 0x7a, 0x61, 0x30, 0x13, 0x91, 0x66, - 0x4c, 0xbf, 0x6f, 0x27, 0xf2, 0xc4, 0x24, 0x51, 0xdf, 0xf7, 0x06, 0x5e, 0xc0, 0xb4, 0x3d, 0x75, - 0x14, 0x53, 0x3a, 0x50, 0x8d, 0x90, 0xf2, 0xbe, 0x35, 0x42, 0x5e, 0x82, 0x91, 0x38, 0xde, 0x58, - 0x8e, 0xdc, 0x2d, 0x27, 0x21, 0xd7, 0xc8, 0x8e, 0xb0, 0xb2, 0x74, 0x5e, 0xff, 0xca, 0x15, 0x0d, - 0xc4, 0x69, 0x5c, 0x34, 0x07, 0xa7, 0x74, 0xa5, 0x0e, 0x12, 0x25, 0x2c, 0xba, 0x9f, 0xcf, 0x04, - 0x95, 0xc4, 0xab, 0x6b, 0x7b, 0x08, 0x04, 0xdc, 0xfd, 0x0c, 0x95, 0x6f, 0xa9, 0x46, 0xda, 0x91, - 0x81, 0xb4, 0x7c, 0x4b, 0xd1, 0xa1, 0x7d, 0xe9, 0x7a, 0x02, 0x2d, 0xc2, 0x69, 0x3e, 0x31, 0xa6, - 0xc2, 0xd0, 0x78, 0xa3, 0xc1, 0x74, 0x1d, 0xc3, 0xb9, 0x6e, 0x14, 0x9c, 0xf7, 0x1c, 0x7a, 0x01, - 0x86, 0x54, 0xf3, 0xfc, 0xac, 0x38, 0x45, 0x50, 0x5e, 0x0c, 0x45, 0x66, 0xbe, 0x89, 0x4d, 0x3c, - 0xf4, 0x21, 0x78, 0x54, 0xff, 0xe5, 0x29, 0x60, 0xfc, 0x68, 0x6d, 0x56, 0x14, 0x41, 0x52, 0x57, - 0x7b, 0xcc, 0xe5, 0xa2, 0x35, 0x71, 0xaf, 0xe7, 0xd1, 0x1a, 0x9c, 0x57, 0xa0, 0x4b, 0x7e, 0xc2, - 0xf2, 0x39, 0x62, 0x32, 0xed, 0xc4, 0xe4, 0x46, 0xe4, 0xb1, 0xb2, 0x49, 0x75, 0x7d, 0xeb, 0xe2, - 0x9c, 0x9b, 0x5c, 0xc9, 0xc3, 0xc4, 0x0b, 0x78, 0x0f, 0x2a, 0x68, 0x12, 0xea, 0xc4, 0x77, 0xd6, - 0x3c, 0xb2, 0x34, 0x33, 0xcf, 0x8a, 0x29, 0x19, 0x27, 0x79, 0x97, 0x24, 0x00, 0x6b, 0x1c, 0x15, - 0x61, 0x3a, 0xdc, 0xf3, 0x06, 0xd0, 0x65, 0x38, 0xd3, 0x6a, 0x84, 0xd4, 0xf6, 0x70, 0x1b, 0x64, - 0xaa, 0xc1, 0x02, 0xea, 0xe8, 0x87, 0xe1, 0x05, 0x26, 0x55, 0xf8, 0xf4, 0xdc, 0xcc, 0x72, 0x17, - 0x0e, 0xce, 0x7d, 0x92, 0x05, 0x5e, 0x46, 0xc1, 0xf6, 0xce, 0xd8, 0xe9, 0x4c, 0xe0, 0x25, 0x6d, - 0xc4, 0x1c, 0x86, 0xae, 0x02, 0x62, 0xb1, 0xf8, 0x57, 0x92, 0x24, 0x54, 0xc6, 0xce, 0xd8, 0x19, - 0xf6, 0x4a, 0x2a, 0x8c, 0xec, 0x72, 0x17, 0x06, 0xce, 0x79, 0xca, 0xfe, 0x0f, 0x16, 0x8c, 0xa8, - 0xf5, 0x7a, 0x1f, 0xb2, 0x51, 0xbc, 0x74, 0x36, 0xca, 0xdc, 0xd1, 0x25, 0x1e, 0xeb, 0x79, 0x8f, - 0x90, 0xe6, 0x9f, 0x1e, 0x02, 0xd0, 0x52, 0x51, 0x29, 0x24, 0xab, 0xa7, 0x42, 0x7a, 0x68, 0x25, - 0x52, 0x5e, 0xe5, 0x94, 0xea, 0x83, 0xad, 0x9c, 0xb2, 0x02, 0x67, 0xa5, 0xb9, 0xc0, 0xcf, 0x8a, - 0xae, 0x04, 0xb1, 0x12, 0x70, 0xb5, 0xe9, 0xc7, 0x05, 0xa1, 0xb3, 0xf3, 0x79, 0x48, 0x38, 0xff, - 0xd9, 0x94, 0x95, 0x32, 0xb8, 0x9f, 0x95, 0xa2, 0xd7, 0xf4, 0xc2, 0xba, 0xbc, 0xc3, 0x23, 0xb3, - 0xa6, 0x17, 0x2e, 0xaf, 0x60, 0x8d, 0x93, 0x2f, 0xd8, 0xeb, 0x05, 0x09, 0x76, 0x38, 0xb0, 0x60, - 0x97, 0x22, 0x66, 0xa8, 0xa7, 0x88, 0x91, 0x3e, 0xe9, 0xe1, 0x9e, 0x3e, 0xe9, 0xf7, 0xc3, 0xa8, - 0xeb, 0x6f, 0x90, 0xc8, 0x4d, 0x48, 0x93, 0xad, 0x05, 0x26, 0x7e, 0x6a, 0x5a, 0xad, 0xcf, 0xa7, - 0xa0, 0x38, 0x83, 0x9d, 0x96, 0x8b, 0xa3, 0x7d, 0xc8, 0xc5, 0x1e, 0xda, 0xe8, 0x44, 0x31, 0xda, - 0xe8, 0xe4, 0xd1, 0xb5, 0xd1, 0xa9, 0x63, 0xd5, 0x46, 0xa8, 0x10, 0x6d, 0xd4, 0x97, 0xa0, 0x37, - 0xb6, 0x7f, 0x67, 0xf6, 0xd9, 0xfe, 0xf5, 0x52, 0x45, 0x67, 0x0f, 0xad, 0x8a, 0xf2, 0xb5, 0xcc, - 0x23, 0x87, 0xd2, 0x32, 0x9f, 0x2d, 0xc1, 0x59, 0x2d, 0x87, 0xe9, 0xec, 0x77, 0xd7, 0xa9, 0x24, - 0x62, 0xd7, 0x40, 0xf1, 0x73, 0x1b, 0x23, 0x39, 0x4a, 0xe7, 0x59, 0x29, 0x08, 0x36, 0xb0, 0x58, - 0x8e, 0x11, 0x89, 0x58, 0x19, 0xdd, 0xac, 0x90, 0x9e, 0x11, 0xed, 0x58, 0x61, 0xd0, 0xf9, 0x45, - 0x7f, 0x8b, 0xbc, 0xcd, 0x6c, 0xb1, 0xb8, 0x19, 0x0d, 0xc2, 0x26, 0x1e, 0x7a, 0x9a, 0x33, 0x61, - 0x02, 0x82, 0x0a, 0xea, 0x61, 0x71, 0x2f, 0xac, 0x94, 0x09, 0x0a, 0x2a, 0xbb, 0xc3, 0x92, 0xc9, - 0xaa, 0xdd, 0xdd, 0x61, 0x21, 0x50, 0x0a, 0xc3, 0xfe, 0x1f, 0x16, 0x9c, 0xcb, 0x1d, 0x8a, 0xfb, - 0xa0, 0x7c, 0xb7, 0xd3, 0xca, 0x77, 0xa5, 0xa8, 0xed, 0x86, 0xf1, 0x16, 0x3d, 0x14, 0xf1, 0xbf, - 0xb3, 0x60, 0x54, 0xe3, 0xdf, 0x87, 0x57, 0x75, 0xd3, 0xaf, 0x5a, 0xdc, 0xce, 0xaa, 0xde, 0xf5, - 0x6e, 0xbf, 0x53, 0x02, 0x55, 0xc0, 0x71, 0xaa, 0x21, 0xcb, 0xe3, 0xee, 0x73, 0x92, 0xb8, 0x03, - 0x03, 0xec, 0x20, 0x34, 0x2e, 0x26, 0xc8, 0x23, 0xcd, 0x9f, 0x1d, 0xaa, 0xea, 0x43, 0x66, 0xf6, - 0x37, 0xc6, 0x82, 0x21, 0x2b, 0xf2, 0xec, 0xc6, 0x54, 0x9a, 0x37, 0x45, 0x5a, 0x96, 0x2e, 0xf2, - 0x2c, 0xda, 0xb1, 0xc2, 0xa0, 0xea, 0xc1, 0x6d, 0x04, 0xfe, 0x8c, 0xe7, 0xc4, 0xf2, 0xee, 0x43, - 0xa5, 0x1e, 0xe6, 0x25, 0x00, 0x6b, 0x1c, 0x76, 0x46, 0xea, 0xc6, 0xa1, 0xe7, 0xec, 0x18, 0xfb, - 0x67, 0xa3, 0x3e, 0x81, 0x02, 0x61, 0x13, 0xcf, 0x6e, 0xc3, 0x58, 0xfa, 0x25, 0x66, 0xc9, 0x3a, - 0x0b, 0x50, 0xec, 0x6b, 0x38, 0x27, 0xa1, 0xee, 0xb0, 0xa7, 0x16, 0x3a, 0x4e, 0xf6, 0xca, 0xf2, - 0x29, 0x09, 0xc0, 0x1a, 0xc7, 0xfe, 0x55, 0x0b, 0x4e, 0xe7, 0x0c, 0x5a, 0x81, 0x69, 0x6f, 0x89, - 0x96, 0x36, 0x79, 0x8a, 0xfd, 0x87, 0x60, 0xb0, 0x49, 0xd6, 0x1d, 0x19, 0x02, 0x67, 0xc8, 0xf6, - 0x59, 0xde, 0x8c, 0x25, 0xdc, 0xfe, 0x6f, 0x16, 0x9c, 0x48, 0xf7, 0x35, 0x66, 0xa9, 0x24, 0x7c, - 0x98, 0xdc, 0xb8, 0x11, 0x6c, 0x91, 0x68, 0x87, 0xbe, 0xb9, 0x95, 0x49, 0x25, 0xe9, 0xc2, 0xc0, - 0x39, 0x4f, 0xb1, 0xf2, 0xad, 0x4d, 0x35, 0xda, 0x72, 0x46, 0xde, 0x2c, 0x72, 0x46, 0xea, 0x8f, - 0x69, 0x1e, 0x97, 0x2b, 0x96, 0xd8, 0xe4, 0x6f, 0x7f, 0xa7, 0x02, 0x2a, 0x2f, 0x96, 0xc5, 0x1f, - 0x15, 0x14, 0xbd, 0x75, 0xd0, 0x0c, 0x22, 0x35, 0x19, 0x2a, 0x7b, 0x05, 0x04, 0x70, 0x2f, 0x89, - 0xe9, 0xba, 0x54, 0x6f, 0xb8, 0xaa, 0x41, 0xd8, 0xc4, 0xa3, 0x3d, 0xf1, 0xdc, 0x2d, 0xc2, 0x1f, - 0x1a, 0x48, 0xf7, 0x64, 0x41, 0x02, 0xb0, 0xc6, 0xa1, 0x3d, 0x69, 0xba, 0xeb, 0xeb, 0x62, 0xcb, - 0xaf, 0x7a, 0x42, 0x47, 0x07, 0x33, 0x08, 0xaf, 0xc8, 0x1d, 0x6c, 0x0a, 0x2b, 0xd8, 0xa8, 0xc8, - 0x1d, 0x6c, 0x62, 0x06, 0xa1, 0x76, 0x9b, 0x1f, 0x44, 0x6d, 0x76, 0xa5, 0x7c, 0x53, 0x71, 0x11, - 0xd6, 0xaf, 0xb2, 0xdb, 0xae, 0x77, 0xa3, 0xe0, 0xbc, 0xe7, 0xe8, 0x0c, 0x0c, 0x23, 0xd2, 0x74, - 0x1b, 0x89, 0x49, 0x0d, 0xd2, 0x33, 0x70, 0xb9, 0x0b, 0x03, 0xe7, 0x3c, 0x85, 0xa6, 0xe0, 0x84, - 0xcc, 0x6b, 0x96, 0x55, 0x6b, 0x86, 0xd2, 0x55, 0x32, 0x70, 0x1a, 0x8c, 0xb3, 0xf8, 0x54, 0xaa, - 0xb5, 0x45, 0xc1, 0x2a, 0x66, 0x2c, 0x1b, 0x52, 0x4d, 0x16, 0xb2, 0xc2, 0x0a, 0xc3, 0xfe, 0x54, - 0x99, 0x6a, 0xe1, 0x1e, 0x85, 0xda, 0xee, 0x5b, 0xb4, 0x60, 0x7a, 0x46, 0x56, 0xfa, 0x98, 0x91, - 0xcf, 0xc3, 0xf0, 0xed, 0x38, 0xf0, 0x55, 0x24, 0x5e, 0xb5, 0x67, 0x24, 0x9e, 0x81, 0x95, 0x1f, - 0x89, 0x37, 0x50, 0x54, 0x24, 0xde, 0xe0, 0x21, 0x23, 0xf1, 0xbe, 0x55, 0x05, 0x75, 0x35, 0xc8, - 0x75, 0x92, 0xdc, 0x09, 0xa2, 0x4d, 0xd7, 0x6f, 0xb1, 0x7c, 0xf0, 0xaf, 0x59, 0x30, 0xcc, 0xd7, - 0xcb, 0x82, 0x99, 0x49, 0xb5, 0x5e, 0xd0, 0x9d, 0x13, 0x29, 0x66, 0x13, 0xab, 0x06, 0xa3, 0xcc, - 0xd5, 0x9b, 0x26, 0x08, 0xa7, 0x7a, 0x84, 0x3e, 0x0e, 0x20, 0xfd, 0xa3, 0xeb, 0x52, 0x64, 0xce, - 0x17, 0xd3, 0x3f, 0x4c, 0xd6, 0xb5, 0x0d, 0xbc, 0xaa, 0x98, 0x60, 0x83, 0x21, 0xfa, 0xac, 0xce, - 0x32, 0xe3, 0x21, 0xfb, 0x1f, 0x3d, 0x96, 0xb1, 0xe9, 0x27, 0xc7, 0x0c, 0xc3, 0xa0, 0xeb, 0xb7, - 0xe8, 0x3c, 0x11, 0x11, 0x4b, 0xef, 0xca, 0xab, 0xa5, 0xb0, 0x10, 0x38, 0xcd, 0x69, 0xc7, 0x73, - 0xfc, 0x06, 0x89, 0xe6, 0x39, 0xba, 0x79, 0xe1, 0x34, 0x6b, 0xc0, 0x92, 0x50, 0xd7, 0xa5, 0x2a, - 0xd5, 0x7e, 0x2e, 0x55, 0x39, 0xff, 0x01, 0x38, 0xd5, 0xf5, 0x31, 0x0f, 0x94, 0x52, 0x76, 0xf8, - 0x6c, 0x34, 0xfb, 0x9f, 0x0f, 0x68, 0xa5, 0x75, 0x3d, 0x68, 0xf2, 0xab, 0x3d, 0x22, 0xfd, 0x45, - 0x85, 0x8d, 0x5b, 0xe0, 0x14, 0x31, 0x2e, 0xad, 0x56, 0x8d, 0xd8, 0x64, 0x49, 0xe7, 0x68, 0xe8, - 0x44, 0xc4, 0x3f, 0xee, 0x39, 0xba, 0xac, 0x98, 0x60, 0x83, 0x21, 0xda, 0x48, 0xe5, 0x94, 0x5c, - 0x3e, 0x7a, 0x4e, 0x09, 0xab, 0x32, 0x95, 0x57, 0x8d, 0xff, 0x4b, 0x16, 0x8c, 0xfa, 0xa9, 0x99, - 0x5b, 0x4c, 0x18, 0x69, 0xfe, 0xaa, 0xe0, 0x37, 0x4b, 0xa5, 0xdb, 0x70, 0x86, 0x7f, 0x9e, 0x4a, - 0xab, 0x1e, 0x50, 0xa5, 0xe9, 0x3b, 0x82, 0x06, 0x7a, 0xdd, 0x11, 0x84, 0x7c, 0x75, 0x49, 0xda, - 0x60, 0xe1, 0x97, 0xa4, 0x41, 0xce, 0x05, 0x69, 0xb7, 0xa0, 0xde, 0x88, 0x88, 0x93, 0x1c, 0xf2, - 0xbe, 0x2c, 0x76, 0x40, 0x3f, 0x23, 0x09, 0x60, 0x4d, 0xcb, 0xfe, 0xdf, 0x15, 0x38, 0x29, 0x47, - 0x44, 0x86, 0xa0, 0x53, 0xfd, 0xc8, 0xf9, 0x6a, 0xe3, 0x56, 0xe9, 0xc7, 0x2b, 0x12, 0x80, 0x35, - 0x0e, 0xb5, 0xc7, 0x3a, 0x31, 0x59, 0x0a, 0x89, 0xbf, 0xe0, 0xae, 0xc5, 0xe2, 0x9c, 0x53, 0x2d, - 0x94, 0x1b, 0x1a, 0x84, 0x4d, 0x3c, 0x6a, 0x8c, 0x73, 0xbb, 0x38, 0xce, 0xa6, 0xaf, 0x08, 0x7b, - 0x1b, 0x4b, 0x38, 0xfa, 0xf9, 0xdc, 0xca, 0xb1, 0xc5, 0x24, 0x6e, 0x75, 0x45, 0xde, 0x1f, 0xf0, - 0x8a, 0xc5, 0xbf, 0x6d, 0xc1, 0x59, 0xde, 0x2a, 0x47, 0xf2, 0x46, 0xd8, 0x74, 0x12, 0x12, 0x17, - 0x53, 0xc9, 0x3d, 0xa7, 0x7f, 0xda, 0xc9, 0x9b, 0xc7, 0x16, 0xe7, 0xf7, 0x06, 0xbd, 0x69, 0xc1, - 0x89, 0xcd, 0x54, 0xcd, 0x0f, 0xa9, 0x3a, 0x8e, 0x9a, 0x8e, 0x9f, 0x22, 0xaa, 0x97, 0x5a, 0xba, - 0x3d, 0xc6, 0x59, 0xee, 0xf6, 0x9f, 0x59, 0x60, 0x8a, 0xd1, 0xfb, 0x5f, 0x2a, 0xe4, 0xe0, 0xa6, - 0xa0, 0xb4, 0x2e, 0xab, 0x3d, 0xad, 0xcb, 0xc7, 0xa1, 0xdc, 0x71, 0x9b, 0x62, 0x7f, 0xa1, 0x4f, - 0x5f, 0xe7, 0x67, 0x31, 0x6d, 0xb7, 0xff, 0x49, 0x55, 0xfb, 0x2d, 0x44, 0x5e, 0xd4, 0xf7, 0xc5, - 0x6b, 0xaf, 0xab, 0x62, 0x63, 0xfc, 0xcd, 0xaf, 0x77, 0x15, 0x1b, 0xfb, 0x91, 0x83, 0xa7, 0xbd, - 0xf1, 0x01, 0xea, 0x55, 0x6b, 0x6c, 0x70, 0x9f, 0x9c, 0xb7, 0xdb, 0x50, 0xa3, 0x5b, 0x30, 0xe6, - 0x80, 0xac, 0xa5, 0x3a, 0x55, 0xbb, 0x22, 0xda, 0xef, 0xed, 0x8e, 0xbf, 0xef, 0xe0, 0xdd, 0x92, - 0x4f, 0x63, 0x45, 0x1f, 0xc5, 0x50, 0xa7, 0xbf, 0x59, 0x7a, 0x9e, 0xd8, 0xdc, 0xdd, 0x50, 0x32, - 0x53, 0x02, 0x0a, 0xc9, 0xfd, 0xd3, 0x7c, 0x90, 0x0f, 0x75, 0x76, 0x1b, 0x2d, 0x63, 0xca, 0xf7, - 0x80, 0xcb, 0x2a, 0x49, 0x4e, 0x02, 0xee, 0xed, 0x8e, 0xbf, 0x74, 0x70, 0xa6, 0xea, 0x71, 0xac, - 0x59, 0xd8, 0x5f, 0xae, 0xe8, 0xb9, 0x2b, 0x6a, 0xcc, 0x7d, 0x5f, 0xcc, 0xdd, 0x17, 0x33, 0x73, - 0xf7, 0x42, 0xd7, 0xdc, 0x1d, 0xd5, 0xb7, 0xa6, 0xa6, 0x66, 0xe3, 0xfd, 0x36, 0x04, 0xf6, 0xf7, - 0x37, 0x30, 0x0b, 0xe8, 0xf5, 0x8e, 0x1b, 0x91, 0x78, 0x39, 0xea, 0xf8, 0xae, 0xdf, 0x62, 0xd3, - 0xb1, 0x66, 0x5a, 0x40, 0x29, 0x30, 0xce, 0xe2, 0xd3, 0x4d, 0x3d, 0xfd, 0xe6, 0xb7, 0x9c, 0x2d, - 0x3e, 0xab, 0x8c, 0xb2, 0x5b, 0x2b, 0xa2, 0x1d, 0x2b, 0x0c, 0xfb, 0x1b, 0xec, 0x2c, 0xdb, 0xc8, - 0x0b, 0xa6, 0x73, 0xc2, 0x63, 0xd7, 0xff, 0xf2, 0x9a, 0x5d, 0x6a, 0x4e, 0xf0, 0x3b, 0x7f, 0x39, - 0x0c, 0xdd, 0x81, 0xc1, 0x35, 0x7e, 0xff, 0x5d, 0x31, 0xf5, 0xc9, 0xc5, 0x65, 0x7a, 0xec, 0x96, - 0x13, 0x79, 0xb3, 0xde, 0x3d, 0xfd, 0x13, 0x4b, 0x6e, 0xf6, 0x37, 0x2b, 0x70, 0x22, 0x73, 0x41, - 0x6c, 0xaa, 0x5a, 0x6a, 0x69, 0xdf, 0x6a, 0xa9, 0x1f, 0x01, 0x68, 0x92, 0xd0, 0x0b, 0x76, 0x98, - 0x39, 0x56, 0x39, 0xb0, 0x39, 0xa6, 0x2c, 0xf8, 0x59, 0x45, 0x05, 0x1b, 0x14, 0x45, 0xa1, 0x32, - 0x5e, 0x7c, 0x35, 0x53, 0xa8, 0xcc, 0xb8, 0xc5, 0x60, 0xe0, 0xfe, 0xde, 0x62, 0xe0, 0xc2, 0x09, - 0xde, 0x45, 0x95, 0x7d, 0x7b, 0x88, 0x24, 0x5b, 0x96, 0xbf, 0x30, 0x9b, 0x26, 0x83, 0xb3, 0x74, - 0x1f, 0xe4, 0xfd, 0xcf, 0xe8, 0xdd, 0x50, 0x97, 0xdf, 0x39, 0x1e, 0xab, 0xeb, 0x0a, 0x06, 0x72, - 0x1a, 0xb0, 0x7b, 0x99, 0xc5, 0x4f, 0xfb, 0x8b, 0x25, 0x6a, 0x3d, 0xf3, 0x7f, 0xaa, 0x12, 0xcd, - 0x53, 0x30, 0xe0, 0x74, 0x92, 0x8d, 0xa0, 0xeb, 0x0e, 0xbd, 0x29, 0xd6, 0x8a, 0x05, 0x14, 0x2d, - 0x40, 0xa5, 0xa9, 0xab, 0x8b, 0x1c, 0x64, 0x14, 0xb5, 0x23, 0xd2, 0x49, 0x08, 0x66, 0x54, 0xd0, - 0x63, 0x50, 0x49, 0x9c, 0x96, 0x4c, 0x74, 0x62, 0xc9, 0xad, 0xab, 0x4e, 0x2b, 0xc6, 0xac, 0xd5, - 0x54, 0x9a, 0x95, 0x7d, 0x94, 0xe6, 0x4b, 0x30, 0x12, 0xbb, 0x2d, 0xdf, 0x49, 0x3a, 0x11, 0x31, - 0x0e, 0xd7, 0x74, 0xbc, 0x84, 0x09, 0xc4, 0x69, 0x5c, 0xfb, 0x37, 0x87, 0xe1, 0xcc, 0xca, 0xcc, - 0xa2, 0xac, 0x99, 0x7d, 0x6c, 0xb9, 0x4a, 0x79, 0x3c, 0xee, 0x5f, 0xae, 0x52, 0x0f, 0xee, 0x9e, - 0x91, 0xab, 0xe4, 0x19, 0xb9, 0x4a, 0xe9, 0xc4, 0x91, 0x72, 0x11, 0x89, 0x23, 0x79, 0x3d, 0xe8, - 0x27, 0x71, 0xe4, 0xd8, 0x92, 0x97, 0xf6, 0xec, 0xd0, 0x81, 0x92, 0x97, 0x54, 0x66, 0x57, 0x21, - 0x21, 0xfd, 0x3d, 0x3e, 0x55, 0x6e, 0x66, 0x97, 0xca, 0xaa, 0xe1, 0xe9, 0x2a, 0x42, 0xc0, 0xbe, - 0x5a, 0x7c, 0x07, 0xfa, 0xc8, 0xaa, 0x11, 0x19, 0x33, 0x66, 0x26, 0xd7, 0x60, 0x11, 0x99, 0x5c, - 0x79, 0xdd, 0xd9, 0x37, 0x93, 0xeb, 0x25, 0x18, 0x69, 0x78, 0x81, 0x4f, 0x96, 0xa3, 0x20, 0x09, - 0x1a, 0x81, 0x27, 0x8c, 0x69, 0x25, 0x12, 0x66, 0x4c, 0x20, 0x4e, 0xe3, 0xf6, 0x4a, 0x03, 0xab, - 0x1f, 0x35, 0x0d, 0x0c, 0x1e, 0x50, 0x1a, 0xd8, 0xcf, 0xe8, 0x84, 0xe5, 0x21, 0xf6, 0x45, 0x3e, - 0x52, 0xfc, 0x17, 0xe9, 0x27, 0x6b, 0x19, 0xbd, 0xc5, 0x2f, 0xb1, 0xa3, 0xe6, 0xe8, 0x4c, 0xd0, - 0xa6, 0xe6, 0xd6, 0x30, 0x1b, 0x92, 0xd7, 0x8e, 0x61, 0xc2, 0xde, 0x5a, 0xd1, 0x6c, 0xd4, 0xc5, - 0x76, 0xba, 0x09, 0xa7, 0x3b, 0x72, 0x94, 0x84, 0xea, 0xaf, 0x96, 0xe0, 0x07, 0xf6, 0xed, 0x02, - 0xba, 0x03, 0x90, 0x38, 0x2d, 0x31, 0x51, 0xc5, 0x31, 0xc5, 0x11, 0x83, 0x1a, 0x57, 0x25, 0x3d, - 0x5e, 0x09, 0x44, 0xfd, 0x65, 0x07, 0x00, 0xf2, 0x37, 0x8b, 0x65, 0x0c, 0xbc, 0xae, 0x82, 0x89, - 0x38, 0xf0, 0x08, 0x66, 0x10, 0xaa, 0xfe, 0x23, 0xd2, 0xd2, 0xb7, 0x2e, 0xab, 0xcf, 0x87, 0x59, - 0x2b, 0x16, 0x50, 0xf4, 0x02, 0x0c, 0x39, 0x9e, 0xc7, 0xb3, 0x52, 0x48, 0x2c, 0x6e, 0xb1, 0xd1, - 0x95, 0xdb, 0x34, 0x08, 0x9b, 0x78, 0xf6, 0x9f, 0x96, 0x60, 0x7c, 0x1f, 0x99, 0xd2, 0x95, 0x67, - 0x57, 0xed, 0x3b, 0xcf, 0x4e, 0x64, 0x06, 0x0c, 0xf4, 0xc8, 0x0c, 0x78, 0x01, 0x86, 0x12, 0xe2, - 0xb4, 0x45, 0x18, 0x94, 0xd8, 0x7f, 0xeb, 0x73, 0x57, 0x0d, 0xc2, 0x26, 0x1e, 0x95, 0x62, 0xa3, - 0x4e, 0xa3, 0x41, 0xe2, 0x58, 0x86, 0xfe, 0x0b, 0x1f, 0x66, 0x61, 0x79, 0x05, 0xcc, 0x35, 0x3c, - 0x95, 0x62, 0x81, 0x33, 0x2c, 0xb3, 0x03, 0x5e, 0xef, 0x73, 0xc0, 0xbf, 0x5e, 0x82, 0xc7, 0xf7, - 0xd4, 0x6e, 0x7d, 0x67, 0x65, 0x74, 0x62, 0x12, 0x65, 0x27, 0xce, 0x8d, 0x98, 0x44, 0x98, 0x41, - 0xf8, 0x28, 0x85, 0xa1, 0x71, 0xab, 0x75, 0xd1, 0x29, 0x43, 0x7c, 0x94, 0x52, 0x2c, 0x70, 0x86, - 0xe5, 0x61, 0xa7, 0xe5, 0xdf, 0x2b, 0xc1, 0x93, 0x7d, 0xd8, 0x00, 0x05, 0xa6, 0x56, 0xa5, 0x13, - 0xdc, 0xca, 0x0f, 0x28, 0x0f, 0xf1, 0x90, 0xc3, 0xf5, 0x8d, 0x12, 0x9c, 0xef, 0xad, 0x8a, 0xd1, - 0x8f, 0xd2, 0x3d, 0xbc, 0x8c, 0x7d, 0x32, 0x73, 0xe3, 0x4e, 0xf3, 0xfd, 0x7b, 0x0a, 0x84, 0xb3, - 0xb8, 0x68, 0x02, 0x20, 0x74, 0x92, 0x8d, 0xf8, 0xd2, 0xb6, 0x1b, 0x27, 0xa2, 0xf6, 0xcb, 0x28, - 0x3f, 0x31, 0x92, 0xad, 0xd8, 0xc0, 0xa0, 0xec, 0xd8, 0xbf, 0xd9, 0xe0, 0x7a, 0x90, 0xf0, 0x87, - 0xf8, 0x36, 0xe2, 0xb4, 0xbc, 0x29, 0xc3, 0x00, 0xe1, 0x2c, 0x2e, 0x65, 0xc7, 0xce, 0x24, 0x79, - 0x47, 0xf9, 0xfe, 0x82, 0xb1, 0x5b, 0x50, 0xad, 0xd8, 0xc0, 0xc8, 0x66, 0xfd, 0x55, 0xf7, 0xcf, - 0xfa, 0xb3, 0xff, 0x71, 0x09, 0xce, 0xf5, 0x34, 0xe5, 0xfa, 0x5b, 0x80, 0x0f, 0x5f, 0xa6, 0xde, - 0xe1, 0xe6, 0xce, 0x01, 0x33, 0xca, 0xfe, 0xa8, 0xc7, 0x4c, 0x13, 0x19, 0x65, 0x87, 0x4f, 0xc9, - 0x7e, 0xf8, 0xc6, 0xb3, 0x2b, 0x89, 0xac, 0x72, 0x80, 0x24, 0xb2, 0xcc, 0xc7, 0xa8, 0xf6, 0xb9, - 0x90, 0xff, 0xbc, 0xdc, 0x73, 0x78, 0xe9, 0xd6, 0xaf, 0x2f, 0xef, 0xe8, 0x2c, 0x9c, 0x74, 0x7d, - 0x76, 0x6b, 0xd2, 0x4a, 0x67, 0x4d, 0x94, 0x03, 0x29, 0xa5, 0xef, 0x2c, 0x9f, 0xcf, 0xc0, 0x71, - 0xd7, 0x13, 0x0f, 0x61, 0x52, 0xdf, 0xe1, 0x86, 0xf4, 0x60, 0x69, 0xa5, 0x68, 0x09, 0xce, 0xca, - 0xa1, 0xd8, 0x70, 0x22, 0xd2, 0x14, 0x6a, 0x24, 0x16, 0x69, 0x0c, 0xe7, 0x78, 0x2a, 0x44, 0x0e, - 0x02, 0xce, 0x7f, 0x8e, 0x5d, 0x54, 0x13, 0x84, 0x6e, 0x43, 0x6c, 0x72, 0xf4, 0x45, 0x35, 0xb4, - 0x11, 0x73, 0x98, 0xfd, 0x11, 0xa8, 0xab, 0xf7, 0xe7, 0xc1, 0xd4, 0x6a, 0xd2, 0x75, 0x05, 0x53, - 0xab, 0x19, 0x67, 0x60, 0xd1, 0xaf, 0x45, 0x4d, 0xe2, 0xcc, 0xea, 0xb9, 0x46, 0x76, 0x98, 0x7d, - 0x6c, 0xbf, 0x07, 0x86, 0x95, 0x9f, 0xa5, 0xdf, 0xeb, 0x7b, 0xec, 0x2f, 0x0f, 0xc0, 0x48, 0xaa, - 0x24, 0x5f, 0xca, 0xad, 0x69, 0xed, 0xeb, 0xd6, 0x64, 0xc1, 0xf1, 0x1d, 0x5f, 0xde, 0xed, 0x65, - 0x04, 0xc7, 0x77, 0x7c, 0x82, 0x39, 0x8c, 0x9a, 0xb7, 0xcd, 0x68, 0x07, 0x77, 0x7c, 0x11, 0xc4, - 0xaa, 0xcc, 0xdb, 0x59, 0xd6, 0x8a, 0x05, 0x14, 0x7d, 0xd2, 0x82, 0xe1, 0x98, 0xf9, 0xcc, 0xb9, - 0x53, 0x58, 0x4c, 0xba, 0xab, 0x47, 0xaf, 0x38, 0xa8, 0xca, 0x4f, 0xb2, 0xb8, 0x14, 0xb3, 0x05, - 0xa7, 0x38, 0xa2, 0xcf, 0x58, 0x50, 0x57, 0x57, 0x90, 0x88, 0x0b, 0xf8, 0x56, 0x8a, 0xad, 0x78, - 0xc8, 0xbd, 0x89, 0xea, 0xf8, 0x41, 0x95, 0x9e, 0xc3, 0x9a, 0x31, 0x8a, 0x95, 0xc7, 0x76, 0xf0, - 0x78, 0x3c, 0xb6, 0x90, 0xe3, 0xad, 0x7d, 0x37, 0xd4, 0xdb, 0x8e, 0xef, 0xae, 0x93, 0x38, 0xe1, - 0x4e, 0x54, 0x59, 0x88, 0x55, 0x36, 0x62, 0x0d, 0xa7, 0x0a, 0x39, 0x66, 0x2f, 0x96, 0x18, 0x5e, - 0x4f, 0xa6, 0x90, 0x57, 0x74, 0x33, 0x36, 0x71, 0x4c, 0x17, 0x2d, 0x3c, 0x50, 0x17, 0xed, 0xd0, - 0x3e, 0x2e, 0xda, 0x7f, 0x60, 0xc1, 0xd9, 0xdc, 0xaf, 0xf6, 0xf0, 0x86, 0x1b, 0xda, 0x5f, 0xa9, - 0xc2, 0xe9, 0x9c, 0xda, 0x9a, 0x68, 0xc7, 0x9c, 0xcf, 0x56, 0x11, 0x27, 0xf7, 0xe9, 0x83, 0x68, - 0x39, 0x8c, 0x39, 0x93, 0xf8, 0x60, 0x07, 0x24, 0xfa, 0x90, 0xa2, 0x7c, 0x7f, 0x0f, 0x29, 0x8c, - 0x69, 0x59, 0x79, 0xa0, 0xd3, 0xb2, 0xba, 0xf7, 0xb4, 0x44, 0xbf, 0x66, 0xc1, 0x58, 0xbb, 0x47, - 0x41, 0x77, 0xe1, 0x78, 0xbc, 0x79, 0x3c, 0xe5, 0xe2, 0xa7, 0x1f, 0xbb, 0xbb, 0x3b, 0xde, 0xb3, - 0x8e, 0x3e, 0xee, 0xd9, 0x2b, 0xfb, 0x3b, 0x65, 0x60, 0x85, 0x5d, 0x59, 0xfd, 0xb4, 0x1d, 0xf4, - 0x09, 0xb3, 0x44, 0xaf, 0x55, 0x54, 0x39, 0x59, 0x4e, 0x5c, 0x95, 0xf8, 0xe5, 0x23, 0x98, 0x57, - 0xf1, 0x37, 0x2b, 0xb4, 0x4a, 0x7d, 0x08, 0x2d, 0x4f, 0xd6, 0x42, 0x2e, 0x17, 0x5f, 0x0b, 0xb9, - 0x9e, 0xad, 0x83, 0xbc, 0xf7, 0x27, 0xae, 0x3c, 0x94, 0x9f, 0xf8, 0x6f, 0x5a, 0x5c, 0xf0, 0x64, - 0xbe, 0x82, 0xb6, 0x0c, 0xac, 0x3d, 0x2c, 0x83, 0x67, 0xa0, 0x16, 0x13, 0x6f, 0xfd, 0x0a, 0x71, - 0x3c, 0x61, 0x41, 0xe8, 0x53, 0x63, 0xd1, 0x8e, 0x15, 0x06, 0xbb, 0x2c, 0xd5, 0xf3, 0x82, 0x3b, - 0x97, 0xda, 0x61, 0xb2, 0x23, 0x6c, 0x09, 0x7d, 0x59, 0xaa, 0x82, 0x60, 0x03, 0xcb, 0xfe, 0x5b, - 0x25, 0x3e, 0x03, 0x45, 0xe8, 0xc1, 0x8b, 0x99, 0xeb, 0xed, 0xfa, 0x3f, 0xb5, 0xff, 0x18, 0x40, - 0x43, 0x5d, 0x0c, 0x2f, 0xce, 0x84, 0xae, 0x1c, 0xf9, 0xd6, 0x6a, 0x41, 0x4f, 0xbf, 0x86, 0x6e, - 0xc3, 0x06, 0xbf, 0x94, 0x2c, 0x2d, 0xef, 0x2b, 0x4b, 0x53, 0x62, 0xa5, 0xb2, 0x8f, 0xb6, 0xfb, - 0x53, 0x0b, 0x52, 0x16, 0x11, 0x0a, 0xa1, 0x4a, 0xbb, 0xbb, 0x53, 0xcc, 0x9d, 0xf7, 0x26, 0x69, - 0x2a, 0x1a, 0xc5, 0xb4, 0x67, 0x3f, 0x31, 0x67, 0x84, 0x3c, 0x11, 0xa1, 0xc0, 0x47, 0xf5, 0x7a, - 0x71, 0x0c, 0xaf, 0x04, 0xc1, 0x26, 0x3f, 0xd8, 0xd4, 0xd1, 0x0e, 0xf6, 0x8b, 0x70, 0xaa, 0xab, - 0x53, 0xec, 0x26, 0xab, 0x40, 0x5e, 0xf4, 0x6f, 0x4c, 0x57, 0x96, 0x36, 0x89, 0x39, 0xcc, 0xfe, - 0x86, 0x05, 0x27, 0xb3, 0xe4, 0xd1, 0x5b, 0x16, 0x9c, 0x8a, 0xb3, 0xf4, 0x8e, 0x6b, 0xec, 0x54, - 0x94, 0x61, 0x17, 0x08, 0x77, 0x77, 0xc2, 0xfe, 0x3f, 0x62, 0xf2, 0xdf, 0x72, 0xfd, 0x66, 0x70, - 0x47, 0x19, 0x26, 0x56, 0x4f, 0xc3, 0x84, 0xae, 0xc7, 0xc6, 0x06, 0x69, 0x76, 0xbc, 0xae, 0x7c, - 0xcd, 0x15, 0xd1, 0x8e, 0x15, 0x06, 0x4b, 0x4f, 0xeb, 0x88, 0x62, 0xe9, 0x99, 0x49, 0x39, 0x2b, - 0xda, 0xb1, 0xc2, 0x40, 0xcf, 0xc3, 0xb0, 0xf1, 0x92, 0x72, 0x5e, 0x32, 0x83, 0xdc, 0x50, 0x99, - 0x31, 0x4e, 0x61, 0xa1, 0x09, 0x00, 0x65, 0xe4, 0x48, 0x15, 0xc9, 0x1c, 0x45, 0x4a, 0x12, 0xc5, - 0xd8, 0xc0, 0x60, 0xc9, 0xa0, 0x5e, 0x27, 0x66, 0x3e, 0xfe, 0x01, 0x5d, 0xc0, 0x73, 0x46, 0xb4, - 0x61, 0x05, 0xa5, 0xd2, 0xa4, 0xed, 0xf8, 0x1d, 0xc7, 0xa3, 0x23, 0x24, 0xb6, 0x7e, 0x6a, 0x19, - 0x2e, 0x2a, 0x08, 0x36, 0xb0, 0xe8, 0x1b, 0x27, 0x6e, 0x9b, 0xbc, 0x12, 0xf8, 0x32, 0x3a, 0x4c, - 0x1f, 0xfb, 0x88, 0x76, 0xac, 0x30, 0xec, 0xff, 0x62, 0xc1, 0x09, 0x9d, 0x5a, 0xce, 0xef, 0xac, - 0x36, 0x77, 0xaa, 0xd6, 0xbe, 0x3b, 0xd5, 0x74, 0xce, 0x6d, 0xa9, 0xaf, 0x9c, 0x5b, 0x33, 0x1d, - 0xb6, 0xbc, 0x67, 0x3a, 0xec, 0x0f, 0xea, 0xfb, 0x50, 0x79, 0xde, 0xec, 0x50, 0xde, 0x5d, 0xa8, - 0xc8, 0x86, 0x81, 0x86, 0xa3, 0xea, 0xaa, 0x0c, 0xf3, 0xbd, 0xc3, 0xcc, 0x14, 0x43, 0x12, 0x10, - 0x7b, 0x09, 0xea, 0xea, 0xf4, 0x43, 0x6e, 0x54, 0xad, 0xfc, 0x8d, 0x6a, 0x5f, 0x69, 0x79, 0xd3, - 0x6b, 0xdf, 0xfc, 0xee, 0x13, 0xef, 0xf8, 0xbd, 0xef, 0x3e, 0xf1, 0x8e, 0x3f, 0xfc, 0xee, 0x13, - 0xef, 0xf8, 0xe4, 0xdd, 0x27, 0xac, 0x6f, 0xde, 0x7d, 0xc2, 0xfa, 0xbd, 0xbb, 0x4f, 0x58, 0x7f, - 0x78, 0xf7, 0x09, 0xeb, 0x3b, 0x77, 0x9f, 0xb0, 0xbe, 0xf4, 0x9f, 0x9e, 0x78, 0xc7, 0x2b, 0xb9, - 0xe1, 0x81, 0xf4, 0xc7, 0xb3, 0x8d, 0xe6, 0xe4, 0xd6, 0x45, 0x16, 0xa1, 0x46, 0x97, 0xd7, 0xa4, - 0x31, 0xa7, 0x26, 0xe5, 0xf2, 0xfa, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa7, 0x24, 0x1a, 0xa3, - 0x6c, 0xe0, 0x00, 0x00, + 0xc9, 0x5e, 0x4f, 0x45, 0x1d, 0x3f, 0x71, 0xdb, 0xa4, 0xeb, 0x81, 0xf7, 0xec, 0xf7, 0x40, 0xdc, + 0xd8, 0x20, 0x6d, 0xa7, 0xeb, 0xb9, 0x1f, 0xe9, 0xf5, 0x5c, 0x27, 0x71, 0xbd, 0x49, 0xd7, 0x4f, + 0xe2, 0x24, 0xca, 0x3e, 0x64, 0xbf, 0x0e, 0x23, 0x53, 0xb7, 0x56, 0xa6, 0x3a, 0xc9, 0xc6, 0x4c, + 0xe0, 0xaf, 0xbb, 0x2d, 0xf4, 0x02, 0x0c, 0x35, 0xbc, 0x4e, 0x9c, 0x90, 0xe8, 0xba, 0xd3, 0x26, + 0x63, 0xd6, 0x05, 0xeb, 0xe9, 0xfa, 0xf4, 0xe9, 0x6f, 0xee, 0x8e, 0xbf, 0xe3, 0xee, 0xee, 0xf8, + 0xd0, 0x8c, 0x06, 0x61, 0x13, 0x0f, 0xfd, 0x10, 0x0c, 0x46, 0x81, 0x47, 0xa6, 0xf0, 0xf5, 0xb1, + 0x12, 0x7b, 0xe4, 0x84, 0x78, 0x64, 0x10, 0xf3, 0x66, 0x2c, 0xe1, 0xf6, 0x1f, 0x96, 0x00, 0xa6, + 0xc2, 0x70, 0x39, 0x0a, 0x6e, 0x93, 0x46, 0x82, 0x3e, 0x02, 0x35, 0x3a, 0x74, 0x4d, 0x27, 0x71, + 0x18, 0xb7, 0xa1, 0x8b, 0x3f, 0x3c, 0xc1, 0xdf, 0x64, 0xc2, 0x7c, 0x13, 0x3d, 0x71, 0x28, 0xf6, + 0xc4, 0xd6, 0x73, 0x13, 0x4b, 0x6b, 0xf4, 0xf9, 0x45, 0x92, 0x38, 0xd3, 0x48, 0x30, 0x03, 0xdd, + 0x86, 0x15, 0x55, 0xe4, 0x43, 0x25, 0x0e, 0x49, 0x83, 0x75, 0x6c, 0xe8, 0xe2, 0xc2, 0xc4, 0x51, + 0x66, 0xe8, 0x84, 0xee, 0xf9, 0x4a, 0x48, 0x1a, 0xd3, 0xc3, 0x82, 0x73, 0x85, 0xfe, 0xc3, 0x8c, + 0x0f, 0xda, 0x82, 0x81, 0x38, 0x71, 0x92, 0x4e, 0x3c, 0x56, 0x66, 0x1c, 0xaf, 0x17, 0xc6, 0x91, + 0x51, 0x9d, 0x1e, 0x15, 0x3c, 0x07, 0xf8, 0x7f, 0x2c, 0xb8, 0xd9, 0x7f, 0x62, 0xc1, 0xa8, 0x46, + 0x5e, 0x70, 0xe3, 0x04, 0xfd, 0x64, 0xd7, 0xe0, 0x4e, 0xf4, 0x37, 0xb8, 0xf4, 0x69, 0x36, 0xb4, + 0x27, 0x05, 0xb3, 0x9a, 0x6c, 0x31, 0x06, 0xb6, 0x0d, 0x55, 0x37, 0x21, 0xed, 0x78, 0xac, 0x74, + 0xa1, 0xfc, 0xf4, 0xd0, 0xc5, 0x2b, 0x45, 0xbd, 0xe7, 0xf4, 0x88, 0x60, 0x5a, 0x9d, 0xa7, 0xe4, + 0x31, 0xe7, 0x62, 0xff, 0xda, 0xb0, 0xf9, 0x7e, 0x74, 0xc0, 0xd1, 0x73, 0x30, 0x14, 0x07, 0x9d, + 0xa8, 0x41, 0x30, 0x09, 0x83, 0x78, 0xcc, 0xba, 0x50, 0xa6, 0x53, 0x8f, 0xce, 0xd4, 0x15, 0xdd, + 0x8c, 0x4d, 0x1c, 0xf4, 0x05, 0x0b, 0x86, 0x9b, 0x24, 0x4e, 0x5c, 0x9f, 0xf1, 0x97, 0x9d, 0x5f, + 0x3d, 0x72, 0xe7, 0x65, 0xe3, 0xac, 0x26, 0x3e, 0x7d, 0x46, 0xbc, 0xc8, 0xb0, 0xd1, 0x18, 0xe3, + 0x14, 0x7f, 0xba, 0xe2, 0x9a, 0x24, 0x6e, 0x44, 0x6e, 0x48, 0xff, 0xb3, 0x39, 0x63, 0xac, 0xb8, + 0x59, 0x0d, 0xc2, 0x26, 0x1e, 0xf2, 0xa1, 0x4a, 0x57, 0x54, 0x3c, 0x56, 0x61, 0xfd, 0x9f, 0x3f, + 0x5a, 0xff, 0xc5, 0xa0, 0xd2, 0xc5, 0xaa, 0x47, 0x9f, 0xfe, 0x8b, 0x31, 0x67, 0x83, 0x3e, 0x6f, + 0xc1, 0x98, 0x58, 0xf1, 0x98, 0xf0, 0x01, 0xbd, 0xb5, 0xe1, 0x26, 0xc4, 0x73, 0xe3, 0x64, 0xac, + 0xca, 0xfa, 0x30, 0xd9, 0xdf, 0xdc, 0x9a, 0x8b, 0x82, 0x4e, 0x78, 0xcd, 0xf5, 0x9b, 0xd3, 0x17, + 0x04, 0xa7, 0xb1, 0x99, 0x1e, 0x84, 0x71, 0x4f, 0x96, 0xe8, 0xcb, 0x16, 0x9c, 0xf7, 0x9d, 0x36, + 0x89, 0x43, 0x87, 0x7e, 0x5a, 0x0e, 0x9e, 0xf6, 0x9c, 0xc6, 0x26, 0xeb, 0xd1, 0xc0, 0xe1, 0x7a, + 0x64, 0x8b, 0x1e, 0x9d, 0xbf, 0xde, 0x93, 0x34, 0xde, 0x83, 0x2d, 0xfa, 0xba, 0x05, 0xa7, 0x82, + 0x28, 0xdc, 0x70, 0x7c, 0xd2, 0x94, 0xd0, 0x78, 0x6c, 0x90, 0x2d, 0xbd, 0x0f, 0x1f, 0xed, 0x13, + 0x2d, 0x65, 0xc9, 0x2e, 0x06, 0xbe, 0x9b, 0x04, 0xd1, 0x0a, 0x49, 0x12, 0xd7, 0x6f, 0xc5, 0xd3, + 0x67, 0xef, 0xee, 0x8e, 0x9f, 0xea, 0xc2, 0xc2, 0xdd, 0xfd, 0x41, 0x3f, 0x05, 0x43, 0xf1, 0x8e, + 0xdf, 0xb8, 0xe5, 0xfa, 0xcd, 0xe0, 0x4e, 0x3c, 0x56, 0x2b, 0x62, 0xf9, 0xae, 0x28, 0x82, 0x62, + 0x01, 0x6a, 0x06, 0xd8, 0xe4, 0x96, 0xff, 0xe1, 0xf4, 0x54, 0xaa, 0x17, 0xfd, 0xe1, 0xf4, 0x64, + 0xda, 0x83, 0x2d, 0xfa, 0x79, 0x0b, 0x46, 0x62, 0xb7, 0xe5, 0x3b, 0x49, 0x27, 0x22, 0xd7, 0xc8, + 0x4e, 0x3c, 0x06, 0xac, 0x23, 0x57, 0x8f, 0x38, 0x2a, 0x06, 0xc9, 0xe9, 0xb3, 0xa2, 0x8f, 0x23, + 0x66, 0x6b, 0x8c, 0xd3, 0x7c, 0xf3, 0x16, 0x9a, 0x9e, 0xd6, 0x43, 0xc5, 0x2e, 0x34, 0x3d, 0xa9, + 0x7b, 0xb2, 0x44, 0x3f, 0x01, 0x27, 0x79, 0x93, 0x1a, 0xd9, 0x78, 0x6c, 0x98, 0x09, 0xda, 0x33, + 0x77, 0x77, 0xc7, 0x4f, 0xae, 0x64, 0x60, 0xb8, 0x0b, 0x1b, 0xbd, 0x0e, 0xe3, 0x21, 0x89, 0xda, + 0x6e, 0xb2, 0xe4, 0x7b, 0x3b, 0x52, 0x7c, 0x37, 0x82, 0x90, 0x34, 0x45, 0x77, 0xe2, 0xb1, 0x91, + 0x0b, 0xd6, 0xd3, 0xb5, 0xe9, 0x77, 0x89, 0x6e, 0x8e, 0x2f, 0xef, 0x8d, 0x8e, 0xf7, 0xa3, 0x67, + 0xff, 0xcb, 0x12, 0x9c, 0xcc, 0x2a, 0x4e, 0xf4, 0xb7, 0x2d, 0x38, 0x71, 0xfb, 0x4e, 0xb2, 0x1a, + 0x6c, 0x12, 0x3f, 0x9e, 0xde, 0xa1, 0xe2, 0x8d, 0xa9, 0x8c, 0xa1, 0x8b, 0x8d, 0x62, 0x55, 0xf4, + 0xc4, 0xd5, 0x34, 0x97, 0x4b, 0x7e, 0x12, 0xed, 0x4c, 0x3f, 0x2a, 0xde, 0xee, 0xc4, 0xd5, 0x5b, + 0xab, 0x26, 0x14, 0x67, 0x3b, 0x75, 0xfe, 0xb3, 0x16, 0x9c, 0xc9, 0x23, 0x81, 0x4e, 0x42, 0x79, + 0x93, 0xec, 0x70, 0xab, 0x0c, 0xd3, 0x9f, 0xe8, 0x55, 0xa8, 0x6e, 0x39, 0x5e, 0x87, 0x08, 0xeb, + 0x66, 0xee, 0x68, 0x2f, 0xa2, 0x7a, 0x86, 0x39, 0xd5, 0xf7, 0x96, 0x5e, 0xb4, 0xec, 0xdf, 0x2b, + 0xc3, 0x90, 0xa1, 0xdf, 0xee, 0x83, 0xc5, 0x16, 0xa4, 0x2c, 0xb6, 0xc5, 0xc2, 0x54, 0x73, 0x4f, + 0x93, 0xed, 0x4e, 0xc6, 0x64, 0x5b, 0x2a, 0x8e, 0xe5, 0x9e, 0x36, 0x1b, 0x4a, 0xa0, 0x1e, 0x84, + 0xd4, 0x22, 0xa7, 0xaa, 0xbf, 0x52, 0xc4, 0x27, 0x5c, 0x92, 0xe4, 0xa6, 0x47, 0xee, 0xee, 0x8e, + 0xd7, 0xd5, 0x5f, 0xac, 0x19, 0xd9, 0xdf, 0xb6, 0xe0, 0x8c, 0xd1, 0xc7, 0x99, 0xc0, 0x6f, 0xba, + 0xec, 0xd3, 0x5e, 0x80, 0x4a, 0xb2, 0x13, 0x4a, 0xb3, 0x5f, 0x8d, 0xd4, 0xea, 0x4e, 0x48, 0x30, + 0x83, 0x50, 0x43, 0xbf, 0x4d, 0xe2, 0xd8, 0x69, 0x91, 0xac, 0xa1, 0xbf, 0xc8, 0x9b, 0xb1, 0x84, + 0xa3, 0x08, 0x90, 0xe7, 0xc4, 0xc9, 0x6a, 0xe4, 0xf8, 0x31, 0x23, 0xbf, 0xea, 0xb6, 0x89, 0x18, + 0xe0, 0xff, 0xbf, 0xbf, 0x19, 0x43, 0x9f, 0x98, 0x7e, 0xe4, 0xee, 0xee, 0x38, 0x5a, 0xe8, 0xa2, + 0x84, 0x73, 0xa8, 0xdb, 0x5f, 0xb6, 0xe0, 0x91, 0x7c, 0x5b, 0x0c, 0x3d, 0x05, 0x03, 0x7c, 0xcb, + 0x27, 0xde, 0x4e, 0x7f, 0x12, 0xd6, 0x8a, 0x05, 0x14, 0x4d, 0x42, 0x5d, 0xe9, 0x09, 0xf1, 0x8e, + 0xa7, 0x04, 0x6a, 0x5d, 0x2b, 0x17, 0x8d, 0x43, 0x07, 0x8d, 0xfe, 0x11, 0x96, 0x9b, 0x1a, 0x34, + 0xb6, 0x49, 0x62, 0x10, 0xfb, 0x3f, 0x58, 0x70, 0xc2, 0xe8, 0xd5, 0x7d, 0x30, 0xcd, 0xfd, 0xb4, + 0x69, 0x3e, 0x5f, 0xd8, 0x7c, 0xee, 0x61, 0x9b, 0x7f, 0xde, 0x82, 0xf3, 0x06, 0xd6, 0xa2, 0x93, + 0x34, 0x36, 0x2e, 0x6d, 0x87, 0x11, 0x89, 0xe9, 0x76, 0x1a, 0x3d, 0x6e, 0xc8, 0xad, 0xe9, 0x21, + 0x41, 0xa1, 0x7c, 0x8d, 0xec, 0x70, 0x21, 0xf6, 0x0c, 0xd4, 0xf8, 0xe4, 0x0c, 0x22, 0x31, 0xe2, + 0xea, 0xdd, 0x96, 0x44, 0x3b, 0x56, 0x18, 0xc8, 0x86, 0x01, 0x26, 0x9c, 0xe8, 0x62, 0xa5, 0x6a, + 0x08, 0xe8, 0x47, 0xbc, 0xc9, 0x5a, 0xb0, 0x80, 0xd8, 0x71, 0xaa, 0x3b, 0xcb, 0x11, 0x61, 0x1f, + 0xb7, 0x79, 0xd9, 0x25, 0x5e, 0x33, 0xa6, 0xdb, 0x06, 0xc7, 0xf7, 0x83, 0x44, 0xec, 0x00, 0x8c, + 0x6d, 0xc3, 0x94, 0x6e, 0xc6, 0x26, 0x0e, 0x65, 0xea, 0x39, 0x6b, 0xc4, 0xe3, 0x23, 0x2a, 0x98, + 0x2e, 0xb0, 0x16, 0x2c, 0x20, 0xf6, 0xdd, 0x12, 0xdb, 0xa0, 0xa8, 0xa5, 0x4f, 0xee, 0xc7, 0xee, + 0x36, 0x4a, 0xc9, 0xca, 0xe5, 0xe2, 0x04, 0x17, 0xe9, 0xbd, 0xc3, 0x7d, 0x23, 0x23, 0x2e, 0x71, + 0xa1, 0x5c, 0xf7, 0xde, 0xe5, 0xfe, 0x76, 0x09, 0xc6, 0xd3, 0x0f, 0x74, 0x49, 0x5b, 0xba, 0xa5, + 0x32, 0x18, 0x65, 0x9d, 0x18, 0x06, 0x3e, 0x36, 0xf1, 0x7a, 0x08, 0xac, 0xd2, 0x71, 0x0a, 0x2c, + 0x53, 0x9e, 0x96, 0xf7, 0x91, 0xa7, 0x4f, 0xa9, 0x51, 0xaf, 0x64, 0x04, 0x58, 0x5a, 0xa7, 0x5c, + 0x80, 0x4a, 0x9c, 0x90, 0x70, 0xac, 0x9a, 0x96, 0x47, 0x2b, 0x09, 0x09, 0x31, 0x83, 0xd8, 0xff, + 0xb5, 0x04, 0x8f, 0xa6, 0xc7, 0x50, 0xab, 0x80, 0xf7, 0xa7, 0x54, 0xc0, 0xbb, 0x4d, 0x15, 0x70, + 0x6f, 0x77, 0xfc, 0x9d, 0x3d, 0x1e, 0xfb, 0x9e, 0xd1, 0x10, 0x68, 0x2e, 0x33, 0x8a, 0x93, 0xe9, + 0x51, 0xbc, 0xb7, 0x3b, 0xfe, 0x78, 0x8f, 0x77, 0xcc, 0x0c, 0xf3, 0x53, 0x30, 0x10, 0x11, 0x27, + 0x0e, 0x7c, 0x31, 0xd0, 0xea, 0x73, 0x60, 0xd6, 0x8a, 0x05, 0xd4, 0xfe, 0x37, 0xf5, 0xec, 0x60, + 0xcf, 0x71, 0x27, 0x5c, 0x10, 0x21, 0x17, 0x2a, 0xcc, 0xac, 0xe7, 0xa2, 0xe1, 0xda, 0xd1, 0x96, + 0x11, 0x55, 0x03, 0x8a, 0xf4, 0x74, 0x8d, 0x7e, 0x35, 0xda, 0x84, 0x19, 0x0b, 0xb4, 0x0d, 0xb5, + 0x86, 0xb4, 0xb6, 0x4b, 0x45, 0xf8, 0xa5, 0x84, 0xad, 0xad, 0x39, 0x0e, 0x53, 0x79, 0xad, 0x4c, + 0x74, 0xc5, 0x0d, 0x11, 0x28, 0xb7, 0xdc, 0x44, 0x7c, 0xd6, 0x23, 0xee, 0xa7, 0xe6, 0x5c, 0xe3, + 0x15, 0x07, 0xa9, 0x12, 0x99, 0x73, 0x13, 0x4c, 0xe9, 0xa3, 0x9f, 0xb5, 0x60, 0x28, 0x6e, 0xb4, + 0x97, 0xa3, 0x60, 0xcb, 0x6d, 0x92, 0x48, 0x58, 0x53, 0x47, 0x14, 0x4d, 0x2b, 0x33, 0x8b, 0x92, + 0xa0, 0xe6, 0xcb, 0xf7, 0xb7, 0x1a, 0x82, 0x4d, 0xbe, 0x74, 0x97, 0xf1, 0xa8, 0x78, 0xf7, 0x59, + 0xd2, 0x70, 0xa9, 0xfe, 0x93, 0x9b, 0x2a, 0x36, 0x53, 0x8e, 0x6c, 0x5d, 0xce, 0x76, 0x1a, 0x9b, + 0x74, 0xbd, 0xe9, 0x0e, 0xbd, 0xf3, 0xee, 0xee, 0xf8, 0xa3, 0x33, 0xf9, 0x3c, 0x71, 0xaf, 0xce, + 0xb0, 0x01, 0x0b, 0x3b, 0x9e, 0x87, 0xc9, 0xeb, 0x1d, 0xc2, 0x5c, 0x26, 0x05, 0x0c, 0xd8, 0xb2, + 0x26, 0x98, 0x19, 0x30, 0x03, 0x82, 0x4d, 0xbe, 0xe8, 0x75, 0x18, 0x68, 0x3b, 0x49, 0xe4, 0x6e, + 0x0b, 0x3f, 0xc9, 0x11, 0xed, 0xfd, 0x45, 0x46, 0x4b, 0x33, 0x67, 0x9a, 0x9a, 0x37, 0x62, 0xc1, + 0x08, 0xb5, 0xa1, 0xda, 0x26, 0x51, 0x8b, 0x8c, 0xd5, 0x8a, 0xf0, 0x09, 0x2f, 0x52, 0x52, 0x9a, + 0x61, 0x9d, 0x5a, 0x47, 0xac, 0x0d, 0x73, 0x2e, 0xe8, 0x55, 0xa8, 0xc5, 0xc4, 0x23, 0x0d, 0x6a, + 0xdf, 0xd4, 0x19, 0xc7, 0x1f, 0xe9, 0xd3, 0xd6, 0xa3, 0x86, 0xc5, 0x8a, 0x78, 0x94, 0x2f, 0x30, + 0xf9, 0x0f, 0x2b, 0x92, 0x74, 0x00, 0x43, 0xaf, 0xd3, 0x72, 0xfd, 0x31, 0x28, 0x62, 0x00, 0x97, + 0x19, 0xad, 0xcc, 0x00, 0xf2, 0x46, 0x2c, 0x18, 0xd9, 0xff, 0xc9, 0x02, 0x94, 0x16, 0x6a, 0xf7, + 0xc1, 0xa8, 0x7d, 0x3d, 0x6d, 0xd4, 0x2e, 0x14, 0x69, 0x75, 0xf4, 0xb0, 0x6b, 0x7f, 0xb3, 0x0e, + 0x19, 0x75, 0x70, 0x9d, 0xc4, 0x09, 0x69, 0xbe, 0x2d, 0xc2, 0xdf, 0x16, 0xe1, 0x6f, 0x8b, 0x70, + 0x25, 0xc2, 0xd7, 0x32, 0x22, 0xfc, 0x7d, 0xc6, 0xaa, 0xd7, 0x87, 0xaa, 0xaf, 0xa9, 0x53, 0x57, + 0xb3, 0x07, 0x06, 0x02, 0x95, 0x04, 0x57, 0x57, 0x96, 0xae, 0xe7, 0xca, 0xec, 0xd7, 0xd2, 0x32, + 0xfb, 0xa8, 0x2c, 0xfe, 0x5f, 0x90, 0xd2, 0xff, 0xc2, 0x82, 0x77, 0xa5, 0xa5, 0x97, 0x9c, 0x39, + 0xf3, 0x2d, 0x3f, 0x88, 0xc8, 0xac, 0xbb, 0xbe, 0x4e, 0x22, 0xe2, 0x37, 0x48, 0xac, 0xbc, 0x18, + 0x56, 0x2f, 0x2f, 0x06, 0x7a, 0x1e, 0x86, 0x6f, 0xc7, 0x81, 0xbf, 0x1c, 0xb8, 0xbe, 0x10, 0x41, + 0x74, 0x23, 0x7c, 0xf2, 0xee, 0xee, 0xf8, 0x30, 0x1d, 0x51, 0xd9, 0x8e, 0x53, 0x58, 0x68, 0x06, + 0x4e, 0xdd, 0x7e, 0x7d, 0xd9, 0x49, 0x0c, 0x77, 0x80, 0xdc, 0xb8, 0xb3, 0x03, 0x8b, 0xab, 0x2f, + 0x67, 0x80, 0xb8, 0x1b, 0xdf, 0xfe, 0xeb, 0x25, 0x38, 0x97, 0x79, 0x91, 0xc0, 0xf3, 0x82, 0x4e, + 0x42, 0x37, 0x35, 0xe8, 0xab, 0x16, 0x9c, 0x6c, 0xa7, 0x3d, 0x0e, 0xb1, 0x70, 0xec, 0x7e, 0xa0, + 0x30, 0x1d, 0x91, 0x71, 0x69, 0x4c, 0x8f, 0x89, 0x11, 0x3a, 0x99, 0x01, 0xc4, 0xb8, 0xab, 0x2f, + 0xe8, 0x55, 0xa8, 0xb7, 0x9d, 0xed, 0x1b, 0x61, 0xd3, 0x49, 0xe4, 0x7e, 0xb2, 0xb7, 0x1b, 0xa0, + 0x93, 0xb8, 0xde, 0x04, 0x3f, 0xae, 0x9f, 0x98, 0xf7, 0x93, 0xa5, 0x68, 0x25, 0x89, 0x5c, 0xbf, + 0xc5, 0xdd, 0x79, 0x8b, 0x92, 0x0c, 0xd6, 0x14, 0xed, 0xaf, 0x58, 0x59, 0x25, 0xa5, 0x46, 0x27, + 0x72, 0x12, 0xd2, 0xda, 0x41, 0x1f, 0x85, 0x2a, 0xdd, 0xf8, 0xc9, 0x51, 0xb9, 0x55, 0xa4, 0xe6, + 0x34, 0xbe, 0x84, 0x56, 0xa2, 0xf4, 0x5f, 0x8c, 0x39, 0x53, 0xfb, 0xab, 0xf5, 0xac, 0xb1, 0xc0, + 0x0e, 0x6f, 0x2f, 0x02, 0xb4, 0x82, 0x55, 0xd2, 0x0e, 0x3d, 0x3a, 0x2c, 0x16, 0x3b, 0x01, 0x50, + 0xbe, 0x8e, 0x39, 0x05, 0xc1, 0x06, 0x16, 0xfa, 0x8b, 0x16, 0x40, 0x4b, 0xce, 0x79, 0x69, 0x08, + 0xdc, 0x28, 0xf2, 0x75, 0xf4, 0x8a, 0xd2, 0x7d, 0x51, 0x0c, 0xb1, 0xc1, 0x1c, 0xfd, 0x8c, 0x05, + 0xb5, 0x44, 0x76, 0x9f, 0xab, 0xc6, 0xd5, 0x22, 0x7b, 0x22, 0x5f, 0x5a, 0xdb, 0x44, 0x6a, 0x48, + 0x14, 0x5f, 0xf4, 0x73, 0x16, 0x40, 0xbc, 0xe3, 0x37, 0x96, 0x03, 0xcf, 0x6d, 0xec, 0x08, 0x8d, + 0x79, 0xb3, 0x50, 0x7f, 0x8c, 0xa2, 0x3e, 0x3d, 0x4a, 0x47, 0x43, 0xff, 0xc7, 0x06, 0x67, 0xf4, + 0x71, 0xa8, 0xc5, 0x62, 0xba, 0x09, 0x1d, 0xb9, 0x5a, 0xac, 0x57, 0x88, 0xd3, 0x16, 0xe2, 0x55, + 0xfc, 0xc3, 0x8a, 0x27, 0xfa, 0x05, 0x0b, 0x4e, 0x84, 0x69, 0x3f, 0x9f, 0x50, 0x87, 0xc5, 0xc9, + 0x80, 0x8c, 0x1f, 0x71, 0xfa, 0xf4, 0xdd, 0xdd, 0xf1, 0x13, 0x99, 0x46, 0x9c, 0xed, 0x05, 0x95, + 0x80, 0x7a, 0x06, 0x2f, 0x85, 0xdc, 0xe7, 0x38, 0xa8, 0x25, 0xe0, 0x5c, 0x16, 0x88, 0xbb, 0xf1, + 0xd1, 0x32, 0x9c, 0xa1, 0xbd, 0xdb, 0xe1, 0xe6, 0xa7, 0x54, 0x2f, 0x31, 0x53, 0x86, 0xb5, 0xe9, + 0xc7, 0xc4, 0x0c, 0x61, 0x5e, 0xfd, 0x2c, 0x0e, 0xce, 0x7d, 0x12, 0xfd, 0x9e, 0x05, 0x8f, 0xb9, + 0x4c, 0x0d, 0x98, 0x0e, 0x73, 0xad, 0x11, 0xc4, 0x49, 0x2c, 0x29, 0x54, 0x56, 0xf4, 0x52, 0x3f, + 0xd3, 0xff, 0x9f, 0x78, 0x83, 0xc7, 0xe6, 0xf7, 0xe8, 0x12, 0xde, 0xb3, 0xc3, 0xe8, 0x47, 0x61, + 0x44, 0xae, 0x8b, 0x65, 0x2a, 0x82, 0x99, 0xa2, 0xad, 0x4f, 0x9f, 0xba, 0xbb, 0x3b, 0x3e, 0xb2, + 0x6a, 0x02, 0x70, 0x1a, 0xcf, 0xfe, 0x56, 0x29, 0x75, 0x1e, 0xa2, 0x9c, 0x90, 0x4c, 0xdc, 0x34, + 0xa4, 0xff, 0x47, 0x4a, 0xcf, 0x42, 0xc5, 0x8d, 0xf2, 0x2e, 0x69, 0x71, 0xa3, 0x9a, 0x62, 0x6c, + 0x30, 0xa7, 0x46, 0xe9, 0x29, 0x27, 0xeb, 0xea, 0x14, 0x12, 0xf0, 0xd5, 0x22, 0xbb, 0xd4, 0x7d, + 0x7a, 0x75, 0x4e, 0x74, 0xed, 0x54, 0x17, 0x08, 0x77, 0x77, 0xc9, 0xfe, 0x56, 0xfa, 0x0c, 0xc6, + 0x58, 0xbc, 0x7d, 0x9c, 0x2f, 0x7d, 0xc1, 0x82, 0xa1, 0x28, 0xf0, 0x3c, 0xd7, 0x6f, 0x51, 0x41, + 0x23, 0xb4, 0xe5, 0x87, 0x8e, 0x45, 0x61, 0x09, 0x89, 0xc2, 0x4c, 0x5b, 0xac, 0x79, 0x62, 0xb3, + 0x03, 0xf6, 0x9f, 0x58, 0x30, 0xd6, 0x4b, 0x20, 0x22, 0x02, 0xef, 0x94, 0xab, 0x5d, 0x45, 0x57, + 0x2c, 0xf9, 0xb3, 0xc4, 0x23, 0xca, 0xf1, 0x5c, 0x9b, 0x7e, 0x52, 0xbc, 0xe6, 0x3b, 0x97, 0x7b, + 0xa3, 0xe2, 0xbd, 0xe8, 0xa0, 0x57, 0xe0, 0xa4, 0xf1, 0x5e, 0xb1, 0x1a, 0x98, 0xfa, 0xf4, 0x04, + 0xb5, 0x40, 0xa6, 0x32, 0xb0, 0x7b, 0xbb, 0xe3, 0x8f, 0x64, 0xdb, 0x84, 0xc4, 0xee, 0xa2, 0x63, + 0xff, 0x4a, 0x29, 0xfb, 0xb5, 0x94, 0xb2, 0x7d, 0xcb, 0xea, 0xda, 0xce, 0x7f, 0xe0, 0x38, 0x14, + 0x1c, 0xdb, 0xf8, 0xab, 0x00, 0x8e, 0xde, 0x38, 0x0f, 0xf0, 0x84, 0xd8, 0xfe, 0x57, 0x15, 0xd8, + 0xa3, 0x67, 0x7d, 0x58, 0xcf, 0x07, 0x3e, 0x56, 0xfc, 0x9c, 0xa5, 0x8e, 0x9c, 0xca, 0x6c, 0x91, + 0x37, 0x8f, 0x6b, 0xec, 0xf9, 0x06, 0x26, 0xe6, 0x51, 0x0a, 0xca, 0x8d, 0x9d, 0x3e, 0xdc, 0x42, + 0x5f, 0xb3, 0xd2, 0x87, 0x66, 0x3c, 0xec, 0xcc, 0x3d, 0xb6, 0x3e, 0x19, 0x27, 0x71, 0xbc, 0x63, + 0xfa, 0xfc, 0xa6, 0xd7, 0x19, 0xdd, 0x04, 0xc0, 0xba, 0xeb, 0x3b, 0x9e, 0xfb, 0x06, 0xdd, 0x9e, + 0x54, 0x99, 0x86, 0x65, 0x26, 0xcb, 0x65, 0xd5, 0x8a, 0x0d, 0x8c, 0xf3, 0x7f, 0x01, 0x86, 0x8c, + 0x37, 0xcf, 0x09, 0xae, 0x38, 0x63, 0x06, 0x57, 0xd4, 0x8d, 0x98, 0x88, 0xf3, 0xef, 0x83, 0x93, + 0xd9, 0x0e, 0x1e, 0xe4, 0x79, 0xfb, 0x7f, 0x0e, 0x66, 0x4f, 0xb1, 0x56, 0x49, 0xd4, 0xa6, 0x5d, + 0x7b, 0xdb, 0xb3, 0xf4, 0xb6, 0x67, 0xe9, 0x6d, 0xcf, 0x92, 0x79, 0x38, 0x20, 0xbc, 0x26, 0x83, + 0xf7, 0xc9, 0x6b, 0x92, 0xf2, 0x03, 0xd5, 0x0a, 0xf7, 0x03, 0xd9, 0x77, 0xab, 0x90, 0xb2, 0xa3, + 0xf8, 0x78, 0xff, 0x10, 0x0c, 0x46, 0x24, 0x0c, 0x6e, 0xe0, 0x05, 0xa1, 0x43, 0x74, 0x00, 0x3d, + 0x6f, 0xc6, 0x12, 0x4e, 0x75, 0x4d, 0xe8, 0x24, 0x1b, 0x42, 0x89, 0x28, 0x5d, 0xb3, 0xec, 0x24, + 0x1b, 0x98, 0x41, 0xd0, 0xfb, 0x60, 0x34, 0x71, 0xa2, 0x16, 0xb5, 0xb7, 0xb7, 0xd8, 0x67, 0x15, + 0x67, 0x9d, 0x8f, 0x08, 0xdc, 0xd1, 0xd5, 0x14, 0x14, 0x67, 0xb0, 0xd1, 0xeb, 0x50, 0xd9, 0x20, + 0x5e, 0x5b, 0x0c, 0xf9, 0x4a, 0x71, 0x32, 0x9e, 0xbd, 0xeb, 0x15, 0xe2, 0xb5, 0xb9, 0x04, 0xa2, + 0xbf, 0x30, 0x63, 0x45, 0xe7, 0x5b, 0x7d, 0xb3, 0x13, 0x27, 0x41, 0xdb, 0x7d, 0x43, 0xba, 0xf8, + 0x3e, 0x50, 0x30, 0xe3, 0x6b, 0x92, 0x3e, 0xf7, 0xa5, 0xa8, 0xbf, 0x58, 0x73, 0x66, 0xfd, 0x68, + 0xba, 0x11, 0xfb, 0x54, 0x3b, 0xc2, 0x53, 0x57, 0x74, 0x3f, 0x66, 0x25, 0x7d, 0xde, 0x0f, 0xf5, + 0x17, 0x6b, 0xce, 0x68, 0x47, 0xcd, 0xfb, 0x21, 0xd6, 0x87, 0x1b, 0x05, 0xf7, 0x81, 0xcf, 0xf9, + 0xdc, 0xf9, 0xff, 0x24, 0x54, 0x1b, 0x1b, 0x4e, 0x94, 0x8c, 0x0d, 0xb3, 0x49, 0xa3, 0x7c, 0x3a, + 0x33, 0xb4, 0x11, 0x73, 0x18, 0x7a, 0x1c, 0xca, 0x11, 0x59, 0x67, 0x71, 0x9b, 0x46, 0x44, 0x0f, + 0x26, 0xeb, 0x98, 0xb6, 0xdb, 0xbf, 0x54, 0x4a, 0x9b, 0x4b, 0xe9, 0xf7, 0xe6, 0xb3, 0xbd, 0xd1, + 0x89, 0x62, 0xe9, 0xf7, 0x31, 0x66, 0x3b, 0x6b, 0xc6, 0x12, 0x8e, 0x3e, 0x69, 0xc1, 0xe0, 0xed, + 0x38, 0xf0, 0x7d, 0x92, 0x08, 0xd5, 0x74, 0xb3, 0xe0, 0xa1, 0xb8, 0xca, 0xa9, 0xeb, 0x3e, 0x88, + 0x06, 0x2c, 0xf9, 0xd2, 0xee, 0x92, 0xed, 0x86, 0xd7, 0x69, 0x76, 0x05, 0x69, 0x5c, 0xe2, 0xcd, + 0x58, 0xc2, 0x29, 0xaa, 0xeb, 0x73, 0xd4, 0x4a, 0x1a, 0x75, 0xde, 0x17, 0xa8, 0x02, 0x6e, 0xff, + 0xd5, 0x01, 0x38, 0x9b, 0xbb, 0x38, 0xa8, 0x21, 0xc3, 0x4c, 0x85, 0xcb, 0xae, 0x47, 0x64, 0x78, + 0x12, 0x33, 0x64, 0x6e, 0xaa, 0x56, 0x6c, 0x60, 0xa0, 0x9f, 0x06, 0x08, 0x9d, 0xc8, 0x69, 0x13, + 0xe5, 0x97, 0x3d, 0xb2, 0xbd, 0x40, 0xfb, 0xb1, 0x2c, 0x69, 0xea, 0xbd, 0xa9, 0x6a, 0x8a, 0xb1, + 0xc1, 0x12, 0xbd, 0x00, 0x43, 0x11, 0xf1, 0x88, 0x13, 0xb3, 0xb0, 0xdf, 0x6c, 0x0e, 0x03, 0xd6, + 0x20, 0x6c, 0xe2, 0xa1, 0xa7, 0x54, 0x24, 0x57, 0x26, 0xa2, 0x25, 0x1d, 0xcd, 0x85, 0xde, 0xb4, + 0x60, 0x74, 0xdd, 0xf5, 0x88, 0xe6, 0x2e, 0x32, 0x0e, 0x96, 0x8e, 0xfe, 0x92, 0x97, 0x4d, 0xba, + 0x5a, 0x42, 0xa6, 0x9a, 0x63, 0x9c, 0x61, 0x4f, 0x3f, 0xf3, 0x16, 0x89, 0x98, 0x68, 0x1d, 0x48, + 0x7f, 0xe6, 0x9b, 0xbc, 0x19, 0x4b, 0x38, 0x9a, 0x82, 0x13, 0xa1, 0x13, 0xc7, 0x33, 0x11, 0x69, + 0x12, 0x3f, 0x71, 0x1d, 0x8f, 0xe7, 0x03, 0xd4, 0x74, 0x3c, 0xf0, 0x72, 0x1a, 0x8c, 0xb3, 0xf8, + 0xe8, 0x83, 0xf0, 0x28, 0x77, 0x7c, 0x2c, 0xba, 0x71, 0xec, 0xfa, 0x2d, 0x3d, 0x0d, 0x84, 0xff, + 0x67, 0x5c, 0x90, 0x7a, 0x74, 0x3e, 0x1f, 0x0d, 0xf7, 0x7a, 0x1e, 0x3d, 0x03, 0xb5, 0x78, 0xd3, + 0x0d, 0x67, 0xa2, 0x66, 0xcc, 0x0e, 0x3d, 0x6a, 0xda, 0xdb, 0xb8, 0x22, 0xda, 0xb1, 0xc2, 0x40, + 0x0d, 0x18, 0xe6, 0x9f, 0x84, 0x87, 0xa2, 0x09, 0xf9, 0xf8, 0x6c, 0x4f, 0xf5, 0x28, 0x52, 0xd6, + 0x26, 0xb0, 0x73, 0xe7, 0x92, 0x3c, 0x82, 0xe1, 0x27, 0x06, 0x37, 0x0d, 0x32, 0x38, 0x45, 0xd4, + 0xfe, 0xc5, 0x52, 0x7a, 0xc7, 0x6d, 0x2e, 0x52, 0x14, 0xd3, 0xa5, 0x98, 0xdc, 0x74, 0x22, 0xe9, + 0x8d, 0x39, 0x62, 0xda, 0x82, 0xa0, 0x7b, 0xd3, 0x89, 0xcc, 0x45, 0xcd, 0x18, 0x60, 0xc9, 0x09, + 0xdd, 0x86, 0x4a, 0xe2, 0x39, 0x05, 0xe5, 0x39, 0x19, 0x1c, 0xb5, 0x03, 0x64, 0x61, 0x2a, 0xc6, + 0x8c, 0x07, 0x7a, 0x8c, 0x5a, 0xfd, 0x6b, 0xf2, 0x88, 0x44, 0x18, 0xea, 0x6b, 0x31, 0x66, 0xad, + 0xf6, 0xaf, 0x42, 0x8e, 0x5c, 0x55, 0x8a, 0x0c, 0x5d, 0x04, 0xa0, 0x1b, 0xc8, 0xe5, 0x88, 0xac, + 0xbb, 0xdb, 0xc2, 0x90, 0x50, 0x6b, 0xf7, 0xba, 0x82, 0x60, 0x03, 0x4b, 0x3e, 0xb3, 0xd2, 0x59, + 0xa7, 0xcf, 0x94, 0xba, 0x9f, 0xe1, 0x10, 0x6c, 0x60, 0xa1, 0xe7, 0x61, 0xc0, 0x6d, 0x3b, 0x2d, + 0x15, 0x82, 0xf9, 0x18, 0x5d, 0xb4, 0xf3, 0xac, 0xe5, 0xde, 0xee, 0xf8, 0xa8, 0xea, 0x10, 0x6b, + 0xc2, 0x02, 0x17, 0xfd, 0x8a, 0x05, 0xc3, 0x8d, 0xa0, 0xdd, 0x0e, 0x7c, 0xbe, 0xed, 0x12, 0x7b, + 0xc8, 0xdb, 0xc7, 0xa5, 0xe6, 0x27, 0x66, 0x0c, 0x66, 0x7c, 0x13, 0xa9, 0x12, 0xb2, 0x4c, 0x10, + 0x4e, 0xf5, 0xca, 0x5c, 0xdb, 0xd5, 0x7d, 0xd6, 0xf6, 0x6f, 0x58, 0x70, 0x8a, 0x3f, 0x6b, 0xec, + 0x06, 0x45, 0xee, 0x51, 0x70, 0xcc, 0xaf, 0xd5, 0xb5, 0x41, 0x56, 0x5e, 0xba, 0x2e, 0x38, 0xee, + 0xee, 0x24, 0x9a, 0x83, 0x53, 0xeb, 0x41, 0xd4, 0x20, 0xe6, 0x40, 0x08, 0xc1, 0xa4, 0x08, 0x5d, + 0xce, 0x22, 0xe0, 0xee, 0x67, 0xd0, 0x4d, 0x78, 0xc4, 0x68, 0x34, 0xc7, 0x81, 0xcb, 0xa6, 0x27, + 0x04, 0xb5, 0x47, 0x2e, 0xe7, 0x62, 0xe1, 0x1e, 0x4f, 0xa7, 0x1d, 0x26, 0xf5, 0x3e, 0x1c, 0x26, + 0xaf, 0xc1, 0xb9, 0x46, 0xf7, 0xc8, 0x6c, 0xc5, 0x9d, 0xb5, 0x98, 0x4b, 0xaa, 0xda, 0xf4, 0x0f, + 0x08, 0x02, 0xe7, 0x66, 0x7a, 0x21, 0xe2, 0xde, 0x34, 0xd0, 0x47, 0xa1, 0x16, 0x11, 0xf6, 0x55, + 0x62, 0x91, 0x88, 0x73, 0xc4, 0x5d, 0xb2, 0xb6, 0x40, 0x39, 0x59, 0x2d, 0x7b, 0x45, 0x43, 0x8c, + 0x15, 0x47, 0x74, 0x07, 0x06, 0x43, 0x27, 0x69, 0x6c, 0x88, 0xf4, 0x9b, 0x23, 0xc7, 0xbf, 0x28, + 0xe6, 0xcc, 0x07, 0xae, 0x27, 0xf9, 0x32, 0x67, 0x82, 0x25, 0x37, 0x6a, 0x8d, 0x34, 0x82, 0x76, + 0x18, 0xf8, 0xc4, 0x4f, 0xe2, 0xb1, 0x11, 0x6d, 0x8d, 0xcc, 0xa8, 0x56, 0x6c, 0x60, 0x9c, 0x7f, + 0x3f, 0x9c, 0xea, 0x5a, 0x78, 0x07, 0x72, 0xae, 0xcc, 0xc2, 0x23, 0xf9, 0x53, 0xfc, 0x40, 0x2e, + 0x96, 0x7f, 0x98, 0x09, 0x72, 0x35, 0xcc, 0xde, 0x3e, 0xdc, 0x75, 0x0e, 0x94, 0x89, 0xbf, 0x25, + 0x24, 0xfe, 0xe5, 0xa3, 0x8d, 0xf4, 0x25, 0x7f, 0x8b, 0xaf, 0x50, 0xe6, 0x93, 0xb8, 0xe4, 0x6f, + 0x61, 0x4a, 0x1b, 0x7d, 0xc9, 0x4a, 0x99, 0x6d, 0xdc, 0xc9, 0xf7, 0xe1, 0x63, 0xb1, 0xf3, 0xfb, + 0xb6, 0xe4, 0xec, 0x7f, 0x5d, 0x82, 0x0b, 0xfb, 0x11, 0xe9, 0x63, 0xf8, 0x9e, 0x84, 0x81, 0x98, + 0x1d, 0x5b, 0x0b, 0x11, 0x3a, 0x44, 0x67, 0x16, 0x3f, 0xc8, 0x7e, 0x0d, 0x0b, 0x10, 0xf2, 0xa0, + 0xdc, 0x76, 0x42, 0xe1, 0xfb, 0x99, 0x3f, 0x6a, 0xda, 0x0b, 0xfd, 0xef, 0x78, 0x8b, 0x4e, 0xc8, + 0x3d, 0x0a, 0x46, 0x03, 0xa6, 0x6c, 0x50, 0x02, 0x55, 0x27, 0x8a, 0x1c, 0x79, 0x46, 0x7a, 0xad, + 0x18, 0x7e, 0x53, 0x94, 0x24, 0x3f, 0x62, 0x4a, 0x35, 0x61, 0xce, 0xcc, 0xfe, 0xdc, 0x60, 0x2a, + 0xf5, 0x83, 0x1d, 0x7c, 0xc7, 0x30, 0x20, 0x5c, 0x3e, 0x56, 0xd1, 0xd9, 0x46, 0x3c, 0x77, 0x8f, + 0xed, 0xea, 0x44, 0x06, 0xb4, 0x60, 0x85, 0x3e, 0x6b, 0xb1, 0x3c, 0x63, 0x99, 0x0e, 0x23, 0xf6, + 0x52, 0xc7, 0x93, 0xf6, 0x6c, 0x66, 0x2f, 0xcb, 0x46, 0x6c, 0x72, 0xa7, 0x3a, 0x36, 0xe4, 0x19, + 0x73, 0xd9, 0x1d, 0x95, 0xcc, 0x44, 0x96, 0x70, 0xb4, 0x9d, 0x73, 0xc0, 0x5d, 0x40, 0xae, 0x6a, + 0x1f, 0x47, 0xda, 0x5f, 0xb3, 0xe0, 0x94, 0x9b, 0x3d, 0xa9, 0x14, 0x3b, 0x8f, 0x23, 0x86, 0x50, + 0xf4, 0x3e, 0x08, 0x55, 0xca, 0xb7, 0x0b, 0x84, 0xbb, 0x3b, 0x83, 0x9a, 0x50, 0x71, 0xfd, 0xf5, + 0x40, 0x98, 0x1c, 0xd3, 0x47, 0xeb, 0xd4, 0xbc, 0xbf, 0x1e, 0xe8, 0xd5, 0x4c, 0xff, 0x61, 0x46, + 0x1d, 0x2d, 0xc0, 0x99, 0x48, 0xf8, 0x86, 0xae, 0xb8, 0x31, 0xdd, 0xc1, 0x2f, 0xb8, 0x6d, 0x37, + 0x61, 0xe6, 0x42, 0x79, 0x7a, 0xec, 0xee, 0xee, 0xf8, 0x19, 0x9c, 0x03, 0xc7, 0xb9, 0x4f, 0xa1, + 0x37, 0x60, 0x50, 0x26, 0x46, 0xd7, 0x8a, 0xd8, 0xc5, 0x75, 0xcf, 0x7f, 0x35, 0x99, 0x56, 0x44, + 0x0e, 0xb4, 0x64, 0x68, 0xbf, 0x39, 0x04, 0xdd, 0x87, 0x98, 0xe8, 0x63, 0x50, 0x8f, 0x54, 0xb2, + 0xb6, 0x55, 0x84, 0x72, 0x95, 0xdf, 0x57, 0x1c, 0xa0, 0x2a, 0xc3, 0x45, 0xa7, 0x65, 0x6b, 0x8e, + 0x74, 0x7b, 0x11, 0xeb, 0xb3, 0xce, 0x02, 0xe6, 0xb6, 0xe0, 0xaa, 0xcf, 0xb1, 0x76, 0xfc, 0x06, + 0x66, 0x3c, 0x50, 0x04, 0x03, 0x1b, 0xc4, 0xf1, 0x92, 0x8d, 0x62, 0x5c, 0xee, 0x57, 0x18, 0xad, + 0x6c, 0xca, 0x0e, 0x6f, 0xc5, 0x82, 0x13, 0xda, 0x86, 0xc1, 0x0d, 0x3e, 0x01, 0x84, 0xc5, 0xbf, + 0x78, 0xd4, 0xc1, 0x4d, 0xcd, 0x2a, 0xfd, 0xb9, 0x45, 0x03, 0x96, 0xec, 0x58, 0x74, 0x8c, 0x71, + 0x7e, 0xcf, 0x97, 0x6e, 0x71, 0xd9, 0x4a, 0xfd, 0x1f, 0xde, 0x7f, 0x04, 0x86, 0x23, 0xd2, 0x08, + 0xfc, 0x86, 0xeb, 0x91, 0xe6, 0x94, 0x74, 0xa7, 0x1f, 0x24, 0xc7, 0x85, 0xed, 0x9a, 0xb1, 0x41, + 0x03, 0xa7, 0x28, 0xa2, 0xcf, 0x58, 0x30, 0xaa, 0x32, 0x3c, 0xe9, 0x07, 0x21, 0xc2, 0x7d, 0xbb, + 0x50, 0x50, 0x3e, 0x29, 0xa3, 0x39, 0x8d, 0xee, 0xee, 0x8e, 0x8f, 0xa6, 0xdb, 0x70, 0x86, 0x2f, + 0x7a, 0x05, 0x20, 0x58, 0xe3, 0x21, 0x30, 0x53, 0x89, 0xf0, 0xe5, 0x1e, 0xe4, 0x55, 0x47, 0x79, + 0xb2, 0x9b, 0xa4, 0x80, 0x0d, 0x6a, 0xe8, 0x1a, 0x00, 0x5f, 0x36, 0xab, 0x3b, 0xa1, 0xdc, 0x16, + 0xc8, 0x24, 0x25, 0x58, 0x51, 0x90, 0x7b, 0xbb, 0xe3, 0xdd, 0xbe, 0x35, 0x16, 0x66, 0x60, 0x3c, + 0x8e, 0x7e, 0x0a, 0x06, 0xe3, 0x4e, 0xbb, 0xed, 0x28, 0x4f, 0x6f, 0x81, 0xe9, 0x73, 0x9c, 0xae, + 0x21, 0x8a, 0x78, 0x03, 0x96, 0x1c, 0xd1, 0x6d, 0x2a, 0x54, 0x63, 0xe1, 0xf4, 0x63, 0xab, 0x88, + 0xdb, 0x04, 0x43, 0xec, 0x9d, 0xde, 0x23, 0x23, 0x7a, 0x70, 0x0e, 0xce, 0xbd, 0xdd, 0xf1, 0x47, + 0xd2, 0xed, 0x0b, 0x81, 0x48, 0x68, 0xcb, 0xa5, 0x89, 0xae, 0xca, 0x3a, 0x29, 0xf4, 0xb5, 0x65, + 0xfa, 0xfe, 0xd3, 0xba, 0x4e, 0x0a, 0x6b, 0xee, 0x3d, 0x66, 0xe6, 0xc3, 0x68, 0x11, 0x4e, 0x37, + 0x02, 0x3f, 0x89, 0x02, 0xcf, 0xe3, 0xc5, 0x7f, 0xf8, 0x0e, 0x8d, 0x7b, 0x82, 0xdf, 0x29, 0xba, + 0x7d, 0x7a, 0xa6, 0x1b, 0x05, 0xe7, 0x3d, 0x67, 0xfb, 0xe9, 0xd8, 0x40, 0x31, 0x38, 0xcf, 0xc3, + 0x30, 0xd9, 0x4e, 0x48, 0xe4, 0x3b, 0xde, 0x0d, 0xbc, 0x20, 0x7d, 0xa0, 0x6c, 0x0d, 0x5c, 0x32, + 0xda, 0x71, 0x0a, 0x0b, 0xd9, 0xca, 0x2d, 0x61, 0x24, 0x69, 0x72, 0xb7, 0x84, 0x74, 0x42, 0xd8, + 0xff, 0xab, 0x94, 0x32, 0xc8, 0x56, 0x23, 0x42, 0x50, 0x00, 0x55, 0x3f, 0x68, 0x2a, 0xd9, 0x7f, + 0xb5, 0x18, 0xd9, 0x7f, 0x3d, 0x68, 0x1a, 0xc5, 0x54, 0xe8, 0xbf, 0x18, 0x73, 0x3e, 0xac, 0xda, + 0x84, 0x2c, 0xcb, 0xc1, 0x00, 0x62, 0xa3, 0x51, 0x24, 0x67, 0x55, 0x6d, 0x62, 0xc9, 0x64, 0x84, + 0xd3, 0x7c, 0xd1, 0x26, 0x54, 0x37, 0x82, 0x38, 0x91, 0xdb, 0x8f, 0x23, 0xee, 0x74, 0xae, 0x04, + 0x71, 0xc2, 0xac, 0x08, 0xf5, 0xda, 0xb4, 0x25, 0xc6, 0x9c, 0x87, 0xfd, 0x9f, 0xad, 0x94, 0xc7, + 0xfb, 0x16, 0x8b, 0x93, 0xdd, 0x22, 0x3e, 0x5d, 0xd6, 0x66, 0x60, 0xd0, 0x8f, 0x66, 0xb2, 0x0e, + 0xdf, 0xd5, 0xab, 0xb4, 0xd5, 0x1d, 0x4a, 0x61, 0x82, 0x91, 0x30, 0x62, 0x88, 0x3e, 0x61, 0xa5, + 0xf3, 0x3f, 0x4b, 0x45, 0x6c, 0x30, 0xcc, 0x1c, 0xe8, 0x7d, 0x53, 0x49, 0xed, 0x2f, 0x59, 0x30, + 0x38, 0xed, 0x34, 0x36, 0x83, 0xf5, 0x75, 0xf4, 0x0c, 0xd4, 0x9a, 0x9d, 0xc8, 0x4c, 0x45, 0x55, + 0xdb, 0xfc, 0x59, 0xd1, 0x8e, 0x15, 0x06, 0x9d, 0xc3, 0xeb, 0x4e, 0x43, 0x66, 0x42, 0x97, 0xf9, + 0x1c, 0xbe, 0xcc, 0x5a, 0xb0, 0x80, 0xa0, 0x17, 0x60, 0xa8, 0xed, 0x6c, 0xcb, 0x87, 0xb3, 0xee, + 0xf6, 0x45, 0x0d, 0xc2, 0x26, 0x9e, 0xfd, 0xcf, 0x2d, 0x18, 0x9b, 0x76, 0x62, 0xb7, 0x31, 0xd5, + 0x49, 0x36, 0xa6, 0xdd, 0x64, 0xad, 0xd3, 0xd8, 0x24, 0x09, 0x4f, 0x7f, 0xa7, 0xbd, 0xec, 0xc4, + 0x74, 0x29, 0xa9, 0x7d, 0x9d, 0xea, 0xe5, 0x0d, 0xd1, 0x8e, 0x15, 0x06, 0x7a, 0x03, 0x86, 0x42, + 0x27, 0x8e, 0xef, 0x04, 0x51, 0x13, 0x93, 0xf5, 0x62, 0x8a, 0x4f, 0xac, 0x90, 0x46, 0x44, 0x12, + 0x4c, 0xd6, 0xc5, 0x91, 0xb0, 0xa6, 0x8f, 0x4d, 0x66, 0xf6, 0x17, 0x2c, 0x38, 0x37, 0x4d, 0x9c, + 0x88, 0x44, 0xac, 0x56, 0x85, 0x7a, 0x91, 0x19, 0x2f, 0xe8, 0x34, 0xd1, 0xeb, 0x50, 0x4b, 0x68, + 0x33, 0xed, 0x96, 0x55, 0x6c, 0xb7, 0xd8, 0x89, 0xee, 0xaa, 0x20, 0x8e, 0x15, 0x1b, 0xfb, 0xaf, + 0x59, 0x30, 0xcc, 0x0e, 0xc7, 0x66, 0x49, 0xe2, 0xb8, 0x5e, 0x57, 0x49, 0x27, 0xab, 0xcf, 0x92, + 0x4e, 0x17, 0xa0, 0xb2, 0x11, 0xb4, 0x49, 0xf6, 0x60, 0xf7, 0x4a, 0x40, 0xb7, 0xd5, 0x14, 0x82, + 0x9e, 0xa3, 0x1f, 0xde, 0xf5, 0x13, 0x87, 0x2e, 0x01, 0xe9, 0x7c, 0x3d, 0xc1, 0x3f, 0xba, 0x6a, + 0xc6, 0x26, 0x8e, 0xfd, 0xdb, 0x75, 0x18, 0x14, 0xa7, 0xff, 0x7d, 0x97, 0x40, 0x90, 0xfb, 0xfb, + 0x52, 0xcf, 0xfd, 0x7d, 0x0c, 0x03, 0x0d, 0x56, 0x30, 0x4e, 0x98, 0x91, 0xd7, 0x0a, 0x09, 0x17, + 0xe1, 0x35, 0xe8, 0x74, 0xb7, 0xf8, 0x7f, 0x2c, 0x58, 0xa1, 0x2f, 0x5a, 0x70, 0xa2, 0x11, 0xf8, + 0x3e, 0x69, 0x68, 0x1b, 0xa7, 0x52, 0x44, 0x54, 0xc0, 0x4c, 0x9a, 0xa8, 0x3e, 0x99, 0xc9, 0x00, + 0x70, 0x96, 0x3d, 0x7a, 0x09, 0x46, 0xf8, 0x98, 0xdd, 0x4c, 0x79, 0x8c, 0x75, 0xa5, 0x1f, 0x13, + 0x88, 0xd3, 0xb8, 0x68, 0x82, 0x7b, 0xde, 0x45, 0x4d, 0x9d, 0x01, 0xed, 0x58, 0x33, 0xaa, 0xe9, + 0x18, 0x18, 0x28, 0x02, 0x14, 0x91, 0xf5, 0x88, 0xc4, 0x1b, 0x22, 0x3a, 0x82, 0xd9, 0x57, 0x83, + 0x87, 0x4b, 0x97, 0xc6, 0x5d, 0x94, 0x70, 0x0e, 0x75, 0xb4, 0x29, 0x36, 0x98, 0xb5, 0x22, 0x64, + 0xa8, 0xf8, 0xcc, 0x3d, 0xf7, 0x99, 0xe3, 0x50, 0x8d, 0x37, 0x9c, 0xa8, 0xc9, 0xec, 0xba, 0x32, + 0x4f, 0xd1, 0x59, 0xa1, 0x0d, 0x98, 0xb7, 0xa3, 0x59, 0x38, 0x99, 0xa9, 0x53, 0x14, 0x0b, 0xcf, + 0xae, 0x4a, 0xc7, 0xc8, 0x54, 0x38, 0x8a, 0x71, 0xd7, 0x13, 0xa6, 0xf3, 0x61, 0x68, 0x1f, 0xe7, + 0xc3, 0x8e, 0x8a, 0xc1, 0xe3, 0x3e, 0xd7, 0x97, 0x0b, 0x19, 0x80, 0xbe, 0x02, 0xee, 0x3e, 0x9f, + 0x09, 0xb8, 0x1b, 0x61, 0x1d, 0xb8, 0x59, 0x4c, 0x07, 0x0e, 0x1e, 0x5d, 0xf7, 0x20, 0xa3, 0xe5, + 0xfe, 0xdc, 0x02, 0xf9, 0x5d, 0x67, 0x9c, 0xc6, 0x06, 0xa1, 0x53, 0x06, 0xbd, 0x0f, 0x46, 0xd5, + 0x16, 0x7a, 0x26, 0xe8, 0xf8, 0x3c, 0x50, 0xae, 0xac, 0x8f, 0x70, 0x71, 0x0a, 0x8a, 0x33, 0xd8, + 0x68, 0x12, 0xea, 0x74, 0x9c, 0xf8, 0xa3, 0x5c, 0xd7, 0xaa, 0x6d, 0xfa, 0xd4, 0xf2, 0xbc, 0x78, + 0x4a, 0xe3, 0xa0, 0x00, 0x4e, 0x79, 0x4e, 0x9c, 0xb0, 0x1e, 0xd0, 0x1d, 0xf5, 0x21, 0x8b, 0x15, + 0xb0, 0x98, 0xff, 0x85, 0x2c, 0x21, 0xdc, 0x4d, 0xdb, 0xfe, 0x76, 0x05, 0x46, 0x52, 0x92, 0xf1, + 0x80, 0x4a, 0xfa, 0x19, 0xa8, 0x49, 0xbd, 0x99, 0x2d, 0xab, 0xa2, 0x94, 0xab, 0xc2, 0xa0, 0x4a, + 0x6b, 0x4d, 0x6b, 0xd5, 0xac, 0x51, 0x61, 0x28, 0x5c, 0x6c, 0xe2, 0x31, 0xa1, 0x9c, 0x78, 0xf1, + 0x8c, 0xe7, 0x12, 0x3f, 0xe1, 0xdd, 0x2c, 0x46, 0x28, 0xaf, 0x2e, 0xac, 0x98, 0x44, 0xb5, 0x50, + 0xce, 0x00, 0x70, 0x96, 0x3d, 0xfa, 0xb4, 0x05, 0x23, 0xce, 0x9d, 0x58, 0x57, 0x35, 0x15, 0xa1, + 0x75, 0x47, 0x54, 0x52, 0xa9, 0x42, 0xa9, 0xdc, 0xe5, 0x9b, 0x6a, 0xc2, 0x69, 0xa6, 0xe8, 0x2d, + 0x0b, 0x10, 0xd9, 0x26, 0x0d, 0x19, 0xfc, 0x27, 0xfa, 0x32, 0x50, 0xc4, 0x4e, 0xf3, 0x52, 0x17, + 0x5d, 0x2e, 0xd5, 0xbb, 0xdb, 0x71, 0x4e, 0x1f, 0xec, 0x7f, 0x52, 0x56, 0x0b, 0x4a, 0xc7, 0x9b, + 0x3a, 0x46, 0xdc, 0x9b, 0x75, 0xf8, 0xb8, 0x37, 0x1d, 0x3f, 0xd0, 0x9d, 0x03, 0x99, 0x4a, 0x99, + 0x2a, 0x3d, 0xa0, 0x94, 0xa9, 0x9f, 0xb1, 0x52, 0x05, 0x84, 0x86, 0x2e, 0xbe, 0x52, 0x6c, 0xac, + 0xeb, 0x04, 0x8f, 0x6d, 0xc8, 0x48, 0xf7, 0x74, 0x48, 0x0b, 0x95, 0xa6, 0x06, 0xda, 0x81, 0xa4, + 0xe1, 0xbf, 0x2b, 0xc3, 0x90, 0xa1, 0x49, 0x73, 0xcd, 0x22, 0xeb, 0x21, 0x33, 0x8b, 0x4a, 0x07, + 0x30, 0x8b, 0x7e, 0x1a, 0xea, 0x0d, 0x29, 0xe5, 0x8b, 0x29, 0xa1, 0x9b, 0xd5, 0x1d, 0x5a, 0xd0, + 0xab, 0x26, 0xac, 0x79, 0xa2, 0xb9, 0x54, 0xa2, 0x8d, 0xd0, 0x10, 0x15, 0xa6, 0x21, 0xf2, 0x32, + 0x61, 0x84, 0xa6, 0xe8, 0x7e, 0x86, 0xd5, 0x99, 0x0a, 0x5d, 0xf1, 0x5e, 0x32, 0x22, 0x9d, 0xd7, + 0x99, 0x5a, 0x9e, 0x97, 0xcd, 0xd8, 0xc4, 0xb1, 0xbf, 0x6d, 0xa9, 0x8f, 0x7b, 0x1f, 0x2a, 0x2a, + 0xdc, 0x4e, 0x57, 0x54, 0xb8, 0x54, 0xc8, 0x30, 0xf7, 0x28, 0xa5, 0x70, 0x1d, 0x06, 0x67, 0x82, + 0x76, 0xdb, 0xf1, 0x9b, 0xe8, 0x07, 0x61, 0xb0, 0xc1, 0x7f, 0x0a, 0xc7, 0x0e, 0x3b, 0x1e, 0x14, + 0x50, 0x2c, 0x61, 0xe8, 0x31, 0xa8, 0x38, 0x51, 0x4b, 0x3a, 0x73, 0x58, 0x28, 0xcc, 0x54, 0xd4, + 0x8a, 0x31, 0x6b, 0xb5, 0xff, 0x41, 0x05, 0xd8, 0x09, 0xb4, 0x13, 0x91, 0xe6, 0x6a, 0xc0, 0x4a, + 0xf8, 0x1d, 0xeb, 0xa1, 0x9a, 0xde, 0x2c, 0x3d, 0xcc, 0x07, 0x6b, 0xc6, 0xe1, 0x4a, 0xf9, 0x3e, + 0x1f, 0xae, 0xf4, 0x38, 0x2f, 0xab, 0x3c, 0x44, 0xe7, 0x65, 0xf6, 0xe7, 0x2c, 0x40, 0x2a, 0x6c, + 0x41, 0x1f, 0x68, 0x4f, 0x42, 0x5d, 0x05, 0x30, 0x08, 0xc3, 0x4a, 0x8b, 0x08, 0x09, 0xc0, 0x1a, + 0xa7, 0x8f, 0x1d, 0xf2, 0x93, 0x52, 0x7e, 0x97, 0xd3, 0x51, 0xb4, 0x4c, 0xea, 0x0b, 0x71, 0x6e, + 0xff, 0x4e, 0x09, 0x1e, 0xe1, 0x2a, 0x79, 0xd1, 0xf1, 0x9d, 0x16, 0x69, 0xd3, 0x5e, 0xf5, 0x1b, + 0xa2, 0xd0, 0xa0, 0x5b, 0x33, 0x57, 0x46, 0xc5, 0x1e, 0x75, 0xed, 0xf2, 0x35, 0xc7, 0x57, 0xd9, + 0xbc, 0xef, 0x26, 0x98, 0x11, 0x47, 0x31, 0xd4, 0x64, 0xcd, 0x78, 0x21, 0x8b, 0x0b, 0x62, 0xa4, + 0xc4, 0x92, 0xd0, 0x9b, 0x04, 0x2b, 0x46, 0xd4, 0x70, 0xf5, 0x82, 0xc6, 0x26, 0x26, 0x61, 0xc0, + 0xe4, 0xae, 0x11, 0x94, 0xb8, 0x20, 0xda, 0xb1, 0xc2, 0xb0, 0x7f, 0xc7, 0x82, 0xac, 0x46, 0x32, + 0x6a, 0xa5, 0x59, 0x7b, 0xd6, 0x4a, 0x3b, 0x40, 0xb1, 0xb2, 0x9f, 0x84, 0x21, 0x27, 0xa1, 0x46, + 0x04, 0xdf, 0x76, 0x97, 0x0f, 0x77, 0xac, 0xb1, 0x18, 0x34, 0xdd, 0x75, 0x97, 0x6d, 0xb7, 0x4d, + 0x72, 0xf6, 0x7f, 0xaf, 0xc0, 0xa9, 0xae, 0xdc, 0x0d, 0xf4, 0x22, 0x0c, 0x37, 0xc4, 0xf4, 0x08, + 0xa5, 0x43, 0xab, 0x6e, 0x06, 0xb1, 0x69, 0x18, 0x4e, 0x61, 0xf6, 0x31, 0x41, 0xe7, 0xe1, 0x74, + 0x44, 0x37, 0xfa, 0x1d, 0x32, 0xb5, 0x9e, 0x90, 0x68, 0x85, 0x34, 0x02, 0xbf, 0xc9, 0x2b, 0xfa, + 0x95, 0xa7, 0x1f, 0xbd, 0xbb, 0x3b, 0x7e, 0x1a, 0x77, 0x83, 0x71, 0xde, 0x33, 0x28, 0x84, 0x11, + 0xcf, 0xb4, 0x01, 0xc5, 0x06, 0xe0, 0x50, 0xe6, 0xa3, 0xb2, 0x11, 0x52, 0xcd, 0x38, 0xcd, 0x20, + 0x6d, 0x48, 0x56, 0x1f, 0x90, 0x21, 0xf9, 0x29, 0x6d, 0x48, 0xf2, 0xf3, 0xf7, 0x0f, 0x15, 0x9c, + 0xbb, 0x73, 0xdc, 0x96, 0xe4, 0xcb, 0x50, 0x93, 0xb1, 0x49, 0x7d, 0xc5, 0xf4, 0x98, 0x74, 0x7a, + 0x48, 0xb4, 0x7b, 0x25, 0xc8, 0xd9, 0x84, 0xd0, 0x75, 0xa6, 0x35, 0x7e, 0x6a, 0x9d, 0x1d, 0x4c, + 0xeb, 0xa3, 0x6d, 0x1e, 0x97, 0xc5, 0x75, 0xdb, 0x07, 0x8b, 0xde, 0x44, 0xe9, 0x50, 0x2d, 0x95, + 0xd2, 0xa0, 0xc2, 0xb5, 0x2e, 0x02, 0x68, 0x43, 0x4d, 0x04, 0xac, 0xab, 0x63, 0x5f, 0x6d, 0xcf, + 0x61, 0x03, 0x8b, 0xee, 0xa9, 0x5d, 0x3f, 0x4e, 0x1c, 0xcf, 0xbb, 0xe2, 0xfa, 0x89, 0x70, 0x0e, + 0x2a, 0x25, 0x3e, 0xaf, 0x41, 0xd8, 0xc4, 0x3b, 0xff, 0x1e, 0xe3, 0xbb, 0x1c, 0xe4, 0x7b, 0x6e, + 0xc0, 0xb9, 0x39, 0x37, 0x51, 0x69, 0x16, 0x6a, 0x1e, 0x51, 0x3b, 0x4c, 0xa5, 0x0d, 0x59, 0x3d, + 0xd3, 0x86, 0x8c, 0x34, 0x87, 0x52, 0x3a, 0x2b, 0x23, 0x9b, 0xe6, 0x60, 0xbf, 0x08, 0x67, 0xe6, + 0xdc, 0xe4, 0xb2, 0xeb, 0x91, 0x03, 0x32, 0xb1, 0x7f, 0x6b, 0x00, 0x86, 0xcd, 0x44, 0xbd, 0x83, + 0x64, 0x3e, 0x7d, 0x81, 0x9a, 0x5a, 0xe2, 0xed, 0x5c, 0x75, 0x68, 0x76, 0xeb, 0xc8, 0x59, 0x83, + 0xf9, 0x23, 0x66, 0x58, 0x5b, 0x9a, 0x27, 0x36, 0x3b, 0x80, 0xee, 0x40, 0x75, 0x9d, 0x85, 0xe1, + 0x97, 0x8b, 0x88, 0x2c, 0xc8, 0x1b, 0x51, 0xbd, 0xcc, 0x78, 0x20, 0x3f, 0xe7, 0x47, 0x35, 0x64, + 0x94, 0xce, 0xed, 0x32, 0x42, 0x47, 0x45, 0x56, 0x97, 0xc2, 0xe8, 0x25, 0xea, 0xab, 0x87, 0x10, + 0xf5, 0x29, 0xc1, 0x3b, 0xf0, 0x80, 0x04, 0x2f, 0x4b, 0xa9, 0x48, 0x36, 0x98, 0xfd, 0x26, 0x62, + 0xdd, 0x07, 0xd9, 0x20, 0x18, 0x29, 0x15, 0x29, 0x30, 0xce, 0xe2, 0xa3, 0x8f, 0x2b, 0xd1, 0x5d, + 0x2b, 0xc2, 0xaf, 0x6a, 0xce, 0xe8, 0xe3, 0x96, 0xda, 0x9f, 0x2b, 0xc1, 0xe8, 0x9c, 0xdf, 0x59, + 0x9e, 0x5b, 0xee, 0xac, 0x79, 0x6e, 0xe3, 0x1a, 0xd9, 0xa1, 0xa2, 0x79, 0x93, 0xec, 0xcc, 0xcf, + 0x8a, 0x15, 0xa4, 0xe6, 0xcc, 0x35, 0xda, 0x88, 0x39, 0x8c, 0x0a, 0xa3, 0x75, 0xd7, 0x6f, 0x91, + 0x28, 0x8c, 0x5c, 0xe1, 0xf2, 0x34, 0x84, 0xd1, 0x65, 0x0d, 0xc2, 0x26, 0x1e, 0xa5, 0x1d, 0xdc, + 0xf1, 0x49, 0x94, 0x35, 0x64, 0x97, 0x68, 0x23, 0xe6, 0x30, 0x8a, 0x94, 0x44, 0x9d, 0x38, 0x11, + 0x93, 0x51, 0x21, 0xad, 0xd2, 0x46, 0xcc, 0x61, 0x74, 0xa5, 0xc7, 0x9d, 0x35, 0x16, 0xb8, 0x91, + 0x09, 0xac, 0x5f, 0xe1, 0xcd, 0x58, 0xc2, 0x29, 0xea, 0x26, 0xd9, 0x99, 0xa5, 0xbb, 0xde, 0x4c, + 0x7e, 0xcd, 0x35, 0xde, 0x8c, 0x25, 0x9c, 0x95, 0x22, 0x4c, 0x0f, 0xc7, 0xf7, 0x5c, 0x29, 0xc2, + 0x74, 0xf7, 0x7b, 0xec, 0x9f, 0x7f, 0xd9, 0x82, 0x61, 0x33, 0xdc, 0x0a, 0xb5, 0x32, 0x36, 0xee, + 0x52, 0x57, 0x25, 0xdb, 0x1f, 0xcf, 0xbb, 0xda, 0xab, 0xe5, 0x26, 0x41, 0x18, 0x3f, 0x4b, 0xfc, + 0x96, 0xeb, 0x13, 0x76, 0x8a, 0xce, 0xc3, 0xb4, 0x52, 0xb1, 0x5c, 0x33, 0x41, 0x93, 0x1c, 0xc2, + 0x48, 0xb6, 0x6f, 0xc1, 0xa9, 0xae, 0xa4, 0xaa, 0x3e, 0x4c, 0x8b, 0x7d, 0x53, 0x5a, 0x6d, 0x0c, + 0x43, 0x94, 0xb0, 0x2c, 0x87, 0x33, 0x03, 0xa7, 0xf8, 0x42, 0xa2, 0x9c, 0x56, 0x1a, 0x1b, 0xa4, + 0xad, 0x12, 0xe5, 0x98, 0x7f, 0xfd, 0x66, 0x16, 0x88, 0xbb, 0xf1, 0xed, 0xcf, 0x5b, 0x30, 0x92, + 0xca, 0x73, 0x2b, 0xc8, 0x08, 0x62, 0x2b, 0x2d, 0x60, 0xd1, 0x7f, 0x2c, 0x04, 0xba, 0xcc, 0x94, + 0xa9, 0x5e, 0x69, 0x1a, 0x84, 0x4d, 0x3c, 0xfb, 0x4b, 0x25, 0xa8, 0xc9, 0x08, 0x8a, 0x3e, 0xba, + 0xf2, 0x59, 0x0b, 0x46, 0xd4, 0x99, 0x06, 0x73, 0x96, 0x95, 0x8a, 0x48, 0x4a, 0xa0, 0x3d, 0x50, + 0xdb, 0x6d, 0x7f, 0x3d, 0xd0, 0x16, 0x39, 0x36, 0x99, 0xe1, 0x34, 0x6f, 0x74, 0x13, 0x20, 0xde, + 0x89, 0x13, 0xd2, 0x36, 0xdc, 0x76, 0xb6, 0xb1, 0xe2, 0x26, 0x1a, 0x41, 0x44, 0xe8, 0xfa, 0xba, + 0x1e, 0x34, 0xc9, 0x8a, 0xc2, 0xd4, 0x26, 0x94, 0x6e, 0xc3, 0x06, 0x25, 0xfb, 0xef, 0x95, 0xe0, + 0x64, 0xb6, 0x4b, 0xe8, 0x43, 0x30, 0x2c, 0xb9, 0x1b, 0xd7, 0x94, 0xc9, 0xb0, 0x91, 0x61, 0x6c, + 0xc0, 0xee, 0xed, 0x8e, 0x8f, 0x77, 0x5f, 0x13, 0x37, 0x61, 0xa2, 0xe0, 0x14, 0x31, 0x7e, 0xb0, + 0x24, 0x4e, 0x40, 0xa7, 0x77, 0xa6, 0xc2, 0x50, 0x9c, 0x0e, 0x19, 0x07, 0x4b, 0x26, 0x14, 0x67, + 0xb0, 0xd1, 0x32, 0x9c, 0x31, 0x5a, 0xae, 0x13, 0xb7, 0xb5, 0xb1, 0x16, 0x44, 0x72, 0x67, 0xf5, + 0x98, 0x0e, 0xec, 0xea, 0xc6, 0xc1, 0xb9, 0x4f, 0x52, 0x6d, 0xdf, 0x70, 0x42, 0xa7, 0xe1, 0x26, + 0x3b, 0xc2, 0x0f, 0xa9, 0x64, 0xd3, 0x8c, 0x68, 0xc7, 0x0a, 0xc3, 0x5e, 0x84, 0x4a, 0x9f, 0x33, + 0xa8, 0x2f, 0x8b, 0xfe, 0x65, 0xa8, 0x51, 0x72, 0xd2, 0xbc, 0x2b, 0x82, 0x64, 0x00, 0x35, 0x79, + 0xd3, 0x08, 0xb2, 0xa1, 0xec, 0x3a, 0xf2, 0xec, 0x4e, 0xbd, 0xd6, 0x7c, 0x1c, 0x77, 0xd8, 0x26, + 0x99, 0x02, 0xd1, 0x93, 0x50, 0x26, 0xdb, 0x61, 0xf6, 0x90, 0xee, 0xd2, 0x76, 0xe8, 0x46, 0x24, + 0xa6, 0x48, 0x64, 0x3b, 0x44, 0xe7, 0xa1, 0xe4, 0x36, 0x85, 0x92, 0x02, 0x81, 0x53, 0x9a, 0x9f, + 0xc5, 0x25, 0xb7, 0x69, 0x6f, 0x43, 0x5d, 0x5d, 0x6d, 0x82, 0x36, 0xa5, 0xec, 0xb6, 0x8a, 0x08, + 0x79, 0x92, 0x74, 0x7b, 0x48, 0xed, 0x0e, 0x80, 0x4e, 0xf8, 0x2b, 0x4a, 0xbe, 0x5c, 0x80, 0x4a, + 0x23, 0x10, 0xc9, 0xc8, 0x35, 0x4d, 0x86, 0x09, 0x6d, 0x06, 0xb1, 0x6f, 0xc1, 0xe8, 0x35, 0x3f, + 0xb8, 0xc3, 0xea, 0xb2, 0xb3, 0x32, 0x64, 0x94, 0xf0, 0x3a, 0xfd, 0x91, 0x35, 0x11, 0x18, 0x14, + 0x73, 0x98, 0xaa, 0xcf, 0x54, 0xea, 0x55, 0x9f, 0xc9, 0xfe, 0x84, 0x05, 0xc3, 0x2a, 0x73, 0x68, + 0x6e, 0x6b, 0x93, 0xd2, 0x6d, 0x45, 0x41, 0x27, 0xcc, 0xd2, 0x65, 0x97, 0x0f, 0x61, 0x0e, 0x33, + 0x53, 0xea, 0x4a, 0xfb, 0xa4, 0xd4, 0x5d, 0x80, 0xca, 0xa6, 0xeb, 0x37, 0xb3, 0xb7, 0x69, 0x5c, + 0x73, 0xfd, 0x26, 0x66, 0x10, 0xda, 0x85, 0x93, 0xaa, 0x0b, 0x52, 0x21, 0xbc, 0x08, 0xc3, 0x6b, + 0x1d, 0xd7, 0x6b, 0xca, 0xfa, 0x6a, 0x19, 0x4f, 0xc9, 0xb4, 0x01, 0xc3, 0x29, 0x4c, 0xba, 0xaf, + 0x5b, 0x73, 0x7d, 0x27, 0xda, 0x59, 0xd6, 0x1a, 0x48, 0x09, 0xa5, 0x69, 0x05, 0xc1, 0x06, 0x96, + 0xfd, 0x66, 0x19, 0x46, 0xd3, 0xf9, 0x53, 0x7d, 0x6c, 0xaf, 0x9e, 0x84, 0x2a, 0x4b, 0xa9, 0xca, + 0x7e, 0x5a, 0x5e, 0x92, 0x8c, 0xc3, 0x50, 0x0c, 0x03, 0xbc, 0x18, 0x43, 0x31, 0x37, 0xd1, 0xa8, + 0x4e, 0x2a, 0xff, 0x0a, 0x8b, 0x27, 0x13, 0xf5, 0x1f, 0x04, 0x2b, 0xf4, 0x69, 0x0b, 0x06, 0x83, + 0xd0, 0xac, 0xeb, 0xf3, 0xc1, 0x22, 0x73, 0xcb, 0x44, 0xb2, 0x8c, 0xb0, 0x88, 0xd5, 0xa7, 0x97, + 0x9f, 0x43, 0xb2, 0x3e, 0xff, 0x5e, 0x18, 0x36, 0x31, 0xf7, 0x33, 0x8a, 0x6b, 0xa6, 0x51, 0xfc, + 0x59, 0x73, 0x52, 0x88, 0xec, 0xb9, 0x3e, 0x96, 0xdb, 0x0d, 0xa8, 0x36, 0x54, 0x00, 0xc0, 0xa1, + 0xaa, 0x72, 0xaa, 0xea, 0x08, 0xec, 0x10, 0x88, 0x53, 0xb3, 0xbf, 0x6d, 0x19, 0xf3, 0x03, 0x93, + 0x78, 0xbe, 0x89, 0x22, 0x28, 0xb7, 0xb6, 0x36, 0x85, 0x29, 0x7a, 0xb5, 0xa0, 0xe1, 0x9d, 0xdb, + 0xda, 0xd4, 0x73, 0xdc, 0x6c, 0xc5, 0x94, 0x59, 0x1f, 0x4e, 0xc0, 0x54, 0x92, 0x65, 0x79, 0xff, + 0x24, 0x4b, 0xfb, 0xad, 0x12, 0x9c, 0xea, 0x9a, 0x54, 0xe8, 0x0d, 0xa8, 0x46, 0xf4, 0x2d, 0xc5, + 0xeb, 0x2d, 0x14, 0x96, 0x16, 0x19, 0xcf, 0x37, 0xb5, 0xde, 0x4d, 0xb7, 0x63, 0xce, 0x12, 0x5d, + 0x05, 0xa4, 0xc3, 0x54, 0x94, 0x07, 0x92, 0xbf, 0xf2, 0x79, 0xf1, 0x28, 0x9a, 0xea, 0xc2, 0xc0, + 0x39, 0x4f, 0xa1, 0x97, 0xb2, 0x8e, 0xcc, 0x72, 0xfa, 0xdc, 0x72, 0x2f, 0x9f, 0xa4, 0xfd, 0x4f, + 0x4b, 0x30, 0x92, 0x2a, 0xb3, 0x84, 0x3c, 0xa8, 0x11, 0x8f, 0x39, 0xf5, 0xa5, 0xb2, 0x39, 0x6a, + 0xd5, 0x62, 0xa5, 0x20, 0x2f, 0x09, 0xba, 0x58, 0x71, 0x78, 0x38, 0x0e, 0xd7, 0x5f, 0x84, 0x61, + 0xd9, 0xa1, 0x0f, 0x3a, 0x6d, 0x4f, 0x0c, 0xa0, 0x9a, 0xa3, 0x97, 0x0c, 0x18, 0x4e, 0x61, 0xda, + 0xbf, 0x5b, 0x86, 0x31, 0x7e, 0x0a, 0xd2, 0x54, 0x33, 0x6f, 0x51, 0xee, 0xb7, 0xfe, 0x92, 0x2e, + 0x86, 0xc6, 0x07, 0x72, 0xed, 0xa8, 0x97, 0x04, 0xe4, 0x33, 0xea, 0x2b, 0x32, 0xeb, 0xab, 0x99, + 0xc8, 0x2c, 0x6e, 0x76, 0xb7, 0x8e, 0xa9, 0x47, 0xdf, 0x5b, 0xa1, 0x5a, 0xbf, 0x5a, 0x82, 0x13, + 0x99, 0x1b, 0x18, 0xd0, 0x9b, 0xe9, 0xa2, 0xbd, 0x56, 0x11, 0xbe, 0xf2, 0x3d, 0x8b, 0xf2, 0x1f, + 0xac, 0x74, 0xef, 0x03, 0x5a, 0x2a, 0xf6, 0x1f, 0x94, 0x60, 0x34, 0x7d, 0x75, 0xc4, 0x43, 0x38, + 0x52, 0xef, 0x86, 0x3a, 0xab, 0x8e, 0xce, 0xae, 0xc4, 0xe4, 0x2e, 0x79, 0x5e, 0x88, 0x5a, 0x36, + 0x62, 0x0d, 0x7f, 0x28, 0x2a, 0x22, 0xdb, 0x7f, 0xc7, 0x82, 0xb3, 0xfc, 0x2d, 0xb3, 0xf3, 0xf0, + 0x2f, 0xe7, 0x8d, 0xee, 0xab, 0xc5, 0x76, 0x30, 0x53, 0xc4, 0x6f, 0xbf, 0xf1, 0x65, 0x57, 0xf1, + 0x89, 0xde, 0xa6, 0xa7, 0xc2, 0x43, 0xd8, 0xd9, 0x03, 0x4d, 0x06, 0xfb, 0x0f, 0xca, 0xa0, 0x6f, + 0x1f, 0x44, 0xae, 0xc8, 0x71, 0x2c, 0xa4, 0x98, 0xe1, 0xca, 0x8e, 0xdf, 0xd0, 0xf7, 0x1c, 0xd6, + 0x32, 0x29, 0x8e, 0x3f, 0x6f, 0xc1, 0x90, 0xeb, 0xbb, 0x89, 0xeb, 0xb0, 0x6d, 0x74, 0x31, 0x37, + 0xa3, 0x29, 0x76, 0xf3, 0x9c, 0x72, 0x10, 0x99, 0xe7, 0x38, 0x8a, 0x19, 0x36, 0x39, 0xa3, 0x8f, + 0x88, 0xe0, 0xe9, 0x72, 0x61, 0xd9, 0xb9, 0xb5, 0x4c, 0xc4, 0x74, 0x48, 0x0d, 0xaf, 0x24, 0x2a, + 0x28, 0xa9, 0x1d, 0x53, 0x52, 0xaa, 0x2e, 0xae, 0xbe, 0x07, 0x9a, 0x36, 0x63, 0xce, 0xc8, 0x8e, + 0x01, 0x75, 0x8f, 0xc5, 0x01, 0x03, 0x53, 0x27, 0xa1, 0xee, 0x74, 0x92, 0xa0, 0x4d, 0x87, 0x49, + 0x1c, 0x35, 0xe9, 0xd0, 0x5b, 0x09, 0xc0, 0x1a, 0xc7, 0x7e, 0xb3, 0x0a, 0x99, 0xa4, 0x43, 0xb4, + 0x6d, 0xde, 0x9c, 0x69, 0x15, 0x7b, 0x73, 0xa6, 0xea, 0x4c, 0xde, 0xed, 0x99, 0xa8, 0x05, 0xd5, + 0x70, 0xc3, 0x89, 0xa5, 0x59, 0xfd, 0xb2, 0xda, 0xc7, 0xd1, 0xc6, 0x7b, 0xbb, 0xe3, 0x3f, 0xd1, + 0x9f, 0xd7, 0x95, 0xce, 0xd5, 0x49, 0x5e, 0x6c, 0x44, 0xb3, 0x66, 0x34, 0x30, 0xa7, 0x7f, 0x90, + 0xbb, 0xe1, 0x3e, 0x29, 0xca, 0xc0, 0x63, 0x12, 0x77, 0xbc, 0x44, 0xcc, 0x86, 0x97, 0x0b, 0x5c, + 0x65, 0x9c, 0xb0, 0x4e, 0x97, 0xe7, 0xff, 0xb1, 0xc1, 0x14, 0x7d, 0x08, 0xea, 0x71, 0xe2, 0x44, + 0xc9, 0x21, 0x13, 0x5c, 0xd5, 0xa0, 0xaf, 0x48, 0x22, 0x58, 0xd3, 0x43, 0xaf, 0xb0, 0xda, 0xae, + 0x6e, 0xbc, 0x71, 0xc8, 0x9c, 0x07, 0x59, 0x07, 0x56, 0x50, 0xc0, 0x06, 0x35, 0x74, 0x11, 0x80, + 0xcd, 0x6d, 0x1e, 0xe8, 0x57, 0x63, 0x5e, 0x26, 0x25, 0x0a, 0xb1, 0x82, 0x60, 0x03, 0xcb, 0xfe, + 0x61, 0x48, 0xd7, 0x7b, 0x40, 0xe3, 0xb2, 0xbc, 0x04, 0xf7, 0x42, 0xb3, 0xdc, 0x85, 0x54, 0x25, + 0x88, 0xdf, 0xb0, 0xc0, 0x2c, 0x4a, 0x81, 0x5e, 0xe7, 0xd5, 0x2f, 0xac, 0x22, 0x4e, 0x0e, 0x0d, + 0xba, 0x13, 0x8b, 0x4e, 0x98, 0x39, 0xc2, 0x96, 0x25, 0x30, 0xce, 0xbf, 0x07, 0x6a, 0x12, 0x7a, + 0x20, 0xa3, 0xee, 0xe3, 0x70, 0x3a, 0x7b, 0xaf, 0xb8, 0x38, 0x75, 0xda, 0xdf, 0xf5, 0x23, 0xfd, + 0x39, 0xa5, 0x5e, 0xfe, 0x9c, 0x3e, 0xee, 0x4f, 0xfd, 0x4d, 0x0b, 0x2e, 0xec, 0x77, 0xfd, 0x39, + 0x7a, 0x0c, 0x2a, 0x77, 0x9c, 0x48, 0x16, 0xdd, 0x66, 0x82, 0xf2, 0x96, 0x13, 0xf9, 0x98, 0xb5, + 0xa2, 0x1d, 0x18, 0xe0, 0xd1, 0x60, 0xc2, 0x5a, 0x7f, 0xb9, 0xd8, 0xcb, 0xd8, 0xaf, 0x11, 0x63, + 0xbb, 0xc0, 0x23, 0xd1, 0xb0, 0x60, 0x68, 0x7f, 0xc7, 0x02, 0xb4, 0xb4, 0x45, 0xa2, 0xc8, 0x6d, + 0x1a, 0xf1, 0x6b, 0xec, 0x3a, 0x15, 0xe3, 0xda, 0x14, 0x33, 0xc5, 0x35, 0x73, 0x9d, 0x8a, 0xf1, + 0x2f, 0xff, 0x3a, 0x95, 0xd2, 0xc1, 0xae, 0x53, 0x41, 0x4b, 0x70, 0xb6, 0xcd, 0xb7, 0x1b, 0xfc, + 0x8a, 0x02, 0xbe, 0xf7, 0x50, 0x09, 0x65, 0xe7, 0xee, 0xee, 0x8e, 0x9f, 0x5d, 0xcc, 0x43, 0xc0, + 0xf9, 0xcf, 0xd9, 0xef, 0x01, 0xc4, 0xc3, 0xd6, 0x66, 0xf2, 0x62, 0x90, 0x7a, 0xba, 0x5f, 0xec, + 0xaf, 0x54, 0xe1, 0x44, 0xa6, 0x24, 0x2b, 0xdd, 0xea, 0x75, 0x07, 0x3d, 0x1d, 0x59, 0x7f, 0x77, + 0x77, 0xaf, 0xaf, 0x30, 0x2a, 0x1f, 0xaa, 0xae, 0x1f, 0x76, 0x92, 0x62, 0x72, 0x48, 0x79, 0x27, + 0xe6, 0x29, 0x41, 0xc3, 0x5d, 0x4c, 0xff, 0x62, 0xce, 0xa6, 0xc8, 0xa0, 0xac, 0x94, 0x31, 0x5e, + 0x79, 0x40, 0xee, 0x80, 0x4f, 0xea, 0x10, 0xa9, 0x6a, 0x11, 0x8e, 0xc5, 0xcc, 0x64, 0x39, 0xee, + 0xa3, 0xf6, 0x5f, 0x2f, 0xc1, 0x90, 0xf1, 0xd1, 0xd0, 0x2f, 0xa5, 0x4b, 0x36, 0x59, 0xc5, 0xbd, + 0x12, 0xa3, 0x3f, 0xa1, 0x8b, 0x32, 0xf1, 0x57, 0x7a, 0xaa, 0xbb, 0x5a, 0xd3, 0xbd, 0xdd, 0xf1, + 0x93, 0x99, 0x7a, 0x4c, 0xa9, 0x0a, 0x4e, 0xe7, 0x3f, 0x06, 0x27, 0x32, 0x64, 0x72, 0x5e, 0x79, + 0x35, 0x7d, 0x6d, 0xfc, 0x11, 0xdd, 0x52, 0xe6, 0x90, 0x7d, 0x83, 0x0e, 0x99, 0x48, 0xa3, 0x0b, + 0x3c, 0xd2, 0x87, 0x0f, 0x36, 0x93, 0x2d, 0x5b, 0xea, 0x33, 0x5b, 0xf6, 0x69, 0xa8, 0x85, 0x81, + 0xe7, 0x36, 0x5c, 0x55, 0x85, 0x90, 0xe5, 0xe7, 0x2e, 0x8b, 0x36, 0xac, 0xa0, 0xe8, 0x0e, 0xd4, + 0xd5, 0x0d, 0xfb, 0xc2, 0xbf, 0x5d, 0xd4, 0xa1, 0x8f, 0x32, 0x5a, 0xf4, 0xcd, 0xf9, 0x9a, 0x17, + 0xb2, 0x61, 0x80, 0x29, 0x41, 0x19, 0xfa, 0xcf, 0x7c, 0xef, 0x4c, 0x3b, 0xc6, 0x58, 0x40, 0xec, + 0xaf, 0xd7, 0xe1, 0x4c, 0x5e, 0x5d, 0x6c, 0xf4, 0x51, 0x18, 0xe0, 0x7d, 0x2c, 0xe6, 0xea, 0x85, + 0x3c, 0x1e, 0x73, 0x8c, 0xa0, 0xe8, 0x16, 0xfb, 0x8d, 0x05, 0x4f, 0xc1, 0xdd, 0x73, 0xd6, 0xc4, + 0x0c, 0x39, 0x1e, 0xee, 0x0b, 0x8e, 0xe6, 0xbe, 0xe0, 0x70, 0xee, 0x9e, 0xb3, 0x86, 0xb6, 0xa1, + 0xda, 0x72, 0x13, 0xe2, 0x08, 0x27, 0xc2, 0xad, 0x63, 0x61, 0x4e, 0x1c, 0x6e, 0xa5, 0xb1, 0x9f, + 0x98, 0x33, 0x44, 0x5f, 0xb3, 0xe0, 0xc4, 0x5a, 0x3a, 0x35, 0x5e, 0x08, 0x4f, 0xe7, 0x18, 0x6a, + 0x9f, 0xa7, 0x19, 0xf1, 0xfb, 0x84, 0x32, 0x8d, 0x38, 0xdb, 0x1d, 0xf4, 0x29, 0x0b, 0x06, 0xd7, + 0x5d, 0xcf, 0x28, 0x83, 0x7b, 0x0c, 0x1f, 0xe7, 0x32, 0x63, 0xa0, 0x77, 0x1c, 0xfc, 0x7f, 0x8c, + 0x25, 0xe7, 0x5e, 0x9a, 0x6a, 0xe0, 0xa8, 0x9a, 0x6a, 0xf0, 0x01, 0x69, 0xaa, 0xcf, 0x58, 0x50, + 0x57, 0x23, 0x2d, 0xd2, 0x9d, 0x3f, 0x74, 0x8c, 0x9f, 0x9c, 0x7b, 0x4e, 0xd4, 0x5f, 0xac, 0x99, + 0xa3, 0x2f, 0x5a, 0x30, 0xe4, 0xbc, 0xd1, 0x89, 0x48, 0x93, 0x6c, 0x05, 0x61, 0x2c, 0x2e, 0x23, + 0x7c, 0xb5, 0xf8, 0xce, 0x4c, 0x51, 0x26, 0xb3, 0x64, 0x6b, 0x29, 0x8c, 0x45, 0x5a, 0x92, 0x6e, + 0xc0, 0x66, 0x17, 0xec, 0xdd, 0x12, 0x8c, 0xef, 0x43, 0x01, 0xbd, 0x08, 0xc3, 0x41, 0xd4, 0x72, + 0x7c, 0xf7, 0x0d, 0xb3, 0xd6, 0x85, 0xb2, 0xb2, 0x96, 0x0c, 0x18, 0x4e, 0x61, 0x9a, 0x09, 0xd9, + 0xa5, 0x7d, 0x12, 0xb2, 0x2f, 0x40, 0x25, 0x22, 0x61, 0x90, 0xdd, 0x2c, 0xb0, 0x94, 0x00, 0x06, + 0x41, 0x8f, 0x43, 0xd9, 0x09, 0x5d, 0x11, 0x88, 0xa6, 0xf6, 0x40, 0x53, 0xcb, 0xf3, 0x98, 0xb6, + 0xa7, 0xea, 0x43, 0x54, 0xef, 0x4b, 0x7d, 0x08, 0xaa, 0x06, 0xc4, 0xd9, 0xc5, 0x80, 0x56, 0x03, + 0xe9, 0x33, 0x05, 0xfb, 0xad, 0x32, 0x3c, 0xbe, 0xe7, 0x7c, 0xd1, 0x71, 0x78, 0xd6, 0x1e, 0x71, + 0x78, 0x72, 0x78, 0x4a, 0xfb, 0x0d, 0x4f, 0xb9, 0xc7, 0xf0, 0x7c, 0x8a, 0x2e, 0x03, 0x59, 0x23, + 0xa4, 0x98, 0xeb, 0xe4, 0x7a, 0x95, 0x1c, 0x11, 0x2b, 0x40, 0x42, 0xb1, 0xe6, 0x4b, 0xf7, 0x00, + 0xa9, 0x64, 0xe4, 0x6a, 0x11, 0x6a, 0xa0, 0x67, 0xcd, 0x10, 0x3e, 0xf7, 0x7b, 0x65, 0x38, 0xdb, + 0xbf, 0x50, 0x82, 0x27, 0xfb, 0x90, 0xde, 0xe6, 0x2c, 0xb6, 0xfa, 0x9c, 0xc5, 0xdf, 0xdb, 0x9f, + 0xc9, 0xfe, 0x2b, 0x16, 0x9c, 0xef, 0xad, 0x3c, 0xd0, 0x73, 0x30, 0xb4, 0x16, 0x39, 0x7e, 0x63, + 0x83, 0x5d, 0x91, 0x29, 0x07, 0x85, 0x8d, 0xb5, 0x6e, 0xc6, 0x26, 0x0e, 0xdd, 0xde, 0xf2, 0x98, + 0x04, 0x03, 0x43, 0x26, 0x8f, 0xd2, 0xed, 0xed, 0x6a, 0x16, 0x88, 0xbb, 0xf1, 0xed, 0x3f, 0x2b, + 0xe5, 0x77, 0x8b, 0x1b, 0x19, 0x07, 0xf9, 0x4e, 0xe2, 0x2b, 0x94, 0xfa, 0x90, 0x25, 0xe5, 0xfb, + 0x2d, 0x4b, 0x2a, 0xbd, 0x64, 0x09, 0x9a, 0x85, 0x93, 0xc6, 0x15, 0x2a, 0x3c, 0x21, 0x98, 0x07, + 0xdc, 0xaa, 0x2a, 0x19, 0xcb, 0x19, 0x38, 0xee, 0x7a, 0x02, 0x3d, 0x03, 0x35, 0xd7, 0x8f, 0x49, + 0xa3, 0x13, 0xf1, 0x40, 0x6f, 0x23, 0x09, 0x6b, 0x5e, 0xb4, 0x63, 0x85, 0x61, 0xff, 0x72, 0x09, + 0xce, 0xf5, 0xb4, 0xb3, 0xee, 0x93, 0xec, 0x32, 0x3f, 0x47, 0xe5, 0xfe, 0x7c, 0x0e, 0x73, 0x90, + 0xaa, 0xfb, 0x0e, 0xd2, 0x1f, 0xf6, 0x9e, 0x98, 0xd4, 0xe6, 0xfe, 0xbe, 0x1d, 0xa5, 0x97, 0x60, + 0xc4, 0x09, 0x43, 0x8e, 0xc7, 0xe2, 0x35, 0x33, 0x55, 0x72, 0xa6, 0x4c, 0x20, 0x4e, 0xe3, 0xf6, + 0xa5, 0x3d, 0xff, 0xd8, 0x82, 0x3a, 0x26, 0xeb, 0x5c, 0x3a, 0xa0, 0xdb, 0x62, 0x88, 0xac, 0x22, + 0xea, 0x69, 0xd2, 0x81, 0x8d, 0x5d, 0x56, 0x67, 0x32, 0x6f, 0xb0, 0xbb, 0xaf, 0xda, 0x29, 0x1d, + 0xe8, 0xaa, 0x1d, 0x75, 0xd9, 0x4a, 0xb9, 0xf7, 0x65, 0x2b, 0xf6, 0x37, 0x06, 0xe9, 0xeb, 0x85, + 0xc1, 0x4c, 0x44, 0x9a, 0x31, 0xfd, 0xbe, 0x9d, 0xc8, 0x13, 0x93, 0x44, 0x7d, 0xdf, 0x1b, 0x78, + 0x01, 0xd3, 0xf6, 0xd4, 0x51, 0x4c, 0xe9, 0x40, 0x35, 0x42, 0xca, 0xfb, 0xd6, 0x08, 0x79, 0x09, + 0x46, 0xe2, 0x78, 0x63, 0x39, 0x72, 0xb7, 0x9c, 0x84, 0x5c, 0x23, 0x3b, 0xc2, 0xca, 0xd2, 0x79, + 0xfd, 0x2b, 0x57, 0x34, 0x10, 0xa7, 0x71, 0xd1, 0x1c, 0x9c, 0xd2, 0x95, 0x3a, 0x48, 0x94, 0xb0, + 0xe8, 0x7e, 0x3e, 0x13, 0x54, 0x12, 0xaf, 0xae, 0xed, 0x21, 0x10, 0x70, 0xf7, 0x33, 0x54, 0xbe, + 0xa5, 0x1a, 0x69, 0x47, 0x06, 0xd2, 0xf2, 0x2d, 0x45, 0x87, 0xf6, 0xa5, 0xeb, 0x09, 0xb4, 0x08, + 0xa7, 0xf9, 0xc4, 0x98, 0x0a, 0x43, 0xe3, 0x8d, 0x06, 0xd3, 0x75, 0x0c, 0xe7, 0xba, 0x51, 0x70, + 0xde, 0x73, 0xe8, 0x05, 0x18, 0x52, 0xcd, 0xf3, 0xb3, 0xe2, 0x14, 0x41, 0x79, 0x31, 0x14, 0x99, + 0xf9, 0x26, 0x36, 0xf1, 0xd0, 0x07, 0xe1, 0x51, 0xfd, 0x97, 0xa7, 0x80, 0xf1, 0xa3, 0xb5, 0x59, + 0x51, 0x04, 0x49, 0x5d, 0xed, 0x31, 0x97, 0x8b, 0xd6, 0xc4, 0xbd, 0x9e, 0x47, 0x6b, 0x70, 0x5e, + 0x81, 0x2e, 0xf9, 0x09, 0xcb, 0xe7, 0x88, 0xc9, 0xb4, 0x13, 0x93, 0x1b, 0x91, 0x27, 0xee, 0x46, + 0x55, 0xb7, 0x2e, 0xce, 0xb9, 0xc9, 0x95, 0x3c, 0x4c, 0xbc, 0x80, 0xf7, 0xa0, 0x82, 0x26, 0xa1, + 0x4e, 0x7c, 0x67, 0xcd, 0x23, 0x4b, 0x33, 0xf3, 0xac, 0x98, 0x92, 0x71, 0x92, 0x77, 0x49, 0x02, + 0xb0, 0xc6, 0x51, 0x11, 0xa6, 0xc3, 0x3d, 0x6f, 0x00, 0x5d, 0x86, 0x33, 0xad, 0x46, 0x48, 0x6d, + 0x0f, 0xb7, 0x41, 0xa6, 0x1a, 0x2c, 0xa0, 0x8e, 0x7e, 0x18, 0x5e, 0x60, 0x52, 0x85, 0x4f, 0xcf, + 0xcd, 0x2c, 0x77, 0xe1, 0xe0, 0xdc, 0x27, 0x59, 0xe0, 0x65, 0x14, 0x6c, 0xef, 0x8c, 0x9d, 0xce, + 0x04, 0x5e, 0xd2, 0x46, 0xcc, 0x61, 0xe8, 0x2a, 0x20, 0x16, 0x8b, 0x7f, 0x25, 0x49, 0x42, 0x65, + 0xec, 0x8c, 0x9d, 0x61, 0xaf, 0xa4, 0xc2, 0xc8, 0x2e, 0x77, 0x61, 0xe0, 0x9c, 0xa7, 0xec, 0x7f, + 0x6f, 0xc1, 0x88, 0x5a, 0xaf, 0xf7, 0x21, 0x1b, 0xc5, 0x4b, 0x67, 0xa3, 0xcc, 0x1d, 0x5d, 0xe2, + 0xb1, 0x9e, 0xf7, 0x08, 0x69, 0xfe, 0xd9, 0x21, 0x00, 0x2d, 0x15, 0x95, 0x42, 0xb2, 0x7a, 0x2a, + 0xa4, 0x87, 0x56, 0x22, 0xe5, 0x55, 0x4e, 0xa9, 0x3e, 0xd8, 0xca, 0x29, 0x2b, 0x70, 0x56, 0x9a, + 0x0b, 0xfc, 0xac, 0xe8, 0x4a, 0x10, 0x2b, 0x01, 0x57, 0x9b, 0x7e, 0x5c, 0x10, 0x3a, 0x3b, 0x9f, + 0x87, 0x84, 0xf3, 0x9f, 0x4d, 0x59, 0x29, 0x83, 0xfb, 0x59, 0x29, 0x7a, 0x4d, 0x2f, 0xac, 0xcb, + 0x3b, 0x3c, 0x32, 0x6b, 0x7a, 0xe1, 0xf2, 0x0a, 0xd6, 0x38, 0xf9, 0x82, 0xbd, 0x5e, 0x90, 0x60, + 0x87, 0x03, 0x0b, 0x76, 0x29, 0x62, 0x86, 0x7a, 0x8a, 0x18, 0xe9, 0x93, 0x1e, 0xee, 0xe9, 0x93, + 0x7e, 0x1f, 0x8c, 0xba, 0xfe, 0x06, 0x89, 0xdc, 0x84, 0x34, 0xd9, 0x5a, 0x60, 0xe2, 0xa7, 0xa6, + 0xd5, 0xfa, 0x7c, 0x0a, 0x8a, 0x33, 0xd8, 0x69, 0xb9, 0x38, 0xda, 0x87, 0x5c, 0xec, 0xa1, 0x8d, + 0x4e, 0x14, 0xa3, 0x8d, 0x4e, 0x1e, 0x5d, 0x1b, 0x9d, 0x3a, 0x56, 0x6d, 0x84, 0x0a, 0xd1, 0x46, + 0x7d, 0x09, 0x7a, 0x63, 0xfb, 0x77, 0x66, 0x9f, 0xed, 0x5f, 0x2f, 0x55, 0x74, 0xf6, 0xd0, 0xaa, + 0x28, 0x5f, 0xcb, 0x3c, 0x72, 0x28, 0x2d, 0xf3, 0x99, 0x12, 0x9c, 0xd5, 0x72, 0x98, 0xce, 0x7e, + 0x77, 0x9d, 0x4a, 0x22, 0x76, 0x0d, 0x14, 0x3f, 0xb7, 0x31, 0x92, 0xa3, 0x74, 0x9e, 0x95, 0x82, + 0x60, 0x03, 0x8b, 0xe5, 0x18, 0x91, 0x88, 0x95, 0xd1, 0xcd, 0x0a, 0xe9, 0x19, 0xd1, 0x8e, 0x15, + 0x06, 0x9d, 0x5f, 0xf4, 0xb7, 0xc8, 0xdb, 0xcc, 0x16, 0x8b, 0x9b, 0xd1, 0x20, 0x6c, 0xe2, 0xa1, + 0xa7, 0x39, 0x13, 0x26, 0x20, 0xa8, 0xa0, 0x1e, 0x16, 0xf7, 0xc2, 0x4a, 0x99, 0xa0, 0xa0, 0xb2, + 0x3b, 0x2c, 0x99, 0xac, 0xda, 0xdd, 0x1d, 0x16, 0x02, 0xa5, 0x30, 0xec, 0xff, 0x61, 0xc1, 0xb9, + 0xdc, 0xa1, 0xb8, 0x0f, 0xca, 0x77, 0x3b, 0xad, 0x7c, 0x57, 0x8a, 0xda, 0x6e, 0x18, 0x6f, 0xd1, + 0x43, 0x11, 0xff, 0x5b, 0x0b, 0x46, 0x35, 0xfe, 0x7d, 0x78, 0x55, 0x37, 0xfd, 0xaa, 0xc5, 0xed, + 0xac, 0xea, 0x5d, 0xef, 0xf6, 0xbb, 0x25, 0x50, 0x05, 0x1c, 0xa7, 0x1a, 0xb2, 0x3c, 0xee, 0x3e, + 0x27, 0x89, 0x3b, 0x30, 0xc0, 0x0e, 0x42, 0xe3, 0x62, 0x82, 0x3c, 0xd2, 0xfc, 0xd9, 0xa1, 0xaa, + 0x3e, 0x64, 0x66, 0x7f, 0x63, 0x2c, 0x18, 0xb2, 0x22, 0xcf, 0x6e, 0x4c, 0xa5, 0x79, 0x53, 0xa4, + 0x65, 0xe9, 0x22, 0xcf, 0xa2, 0x1d, 0x2b, 0x0c, 0xaa, 0x1e, 0xdc, 0x46, 0xe0, 0xcf, 0x78, 0x4e, + 0x2c, 0xef, 0x3e, 0x54, 0xea, 0x61, 0x5e, 0x02, 0xb0, 0xc6, 0x61, 0x67, 0xa4, 0x6e, 0x1c, 0x7a, + 0xce, 0x8e, 0xb1, 0x7f, 0x36, 0xea, 0x13, 0x28, 0x10, 0x36, 0xf1, 0xec, 0x36, 0x8c, 0xa5, 0x5f, + 0x62, 0x96, 0xac, 0xb3, 0x00, 0xc5, 0xbe, 0x86, 0x73, 0x12, 0xea, 0x0e, 0x7b, 0x6a, 0xa1, 0xe3, + 0x64, 0xaf, 0x2c, 0x9f, 0x92, 0x00, 0xac, 0x71, 0xec, 0x5f, 0xb3, 0xe0, 0x74, 0xce, 0xa0, 0x15, + 0x98, 0xf6, 0x96, 0x68, 0x69, 0x93, 0xa7, 0xd8, 0x7f, 0x08, 0x06, 0x9b, 0x64, 0xdd, 0x91, 0x21, + 0x70, 0x86, 0x6c, 0x9f, 0xe5, 0xcd, 0x58, 0xc2, 0xed, 0xff, 0x66, 0xc1, 0x89, 0x74, 0x5f, 0x63, + 0x96, 0x4a, 0xc2, 0x87, 0xc9, 0x8d, 0x1b, 0xc1, 0x16, 0x89, 0x76, 0xe8, 0x9b, 0x5b, 0x99, 0x54, + 0x92, 0x2e, 0x0c, 0x9c, 0xf3, 0x14, 0x2b, 0xdf, 0xda, 0x54, 0xa3, 0x2d, 0x67, 0xe4, 0xcd, 0x22, + 0x67, 0xa4, 0xfe, 0x98, 0xe6, 0x71, 0xb9, 0x62, 0x89, 0x4d, 0xfe, 0xf6, 0x77, 0x2a, 0xa0, 0xf2, + 0x62, 0x59, 0xfc, 0x51, 0x41, 0xd1, 0x5b, 0x07, 0xcd, 0x20, 0x52, 0x93, 0xa1, 0xb2, 0x57, 0x40, + 0x00, 0xf7, 0x92, 0x98, 0xae, 0x4b, 0xf5, 0x86, 0xab, 0x1a, 0x84, 0x4d, 0x3c, 0xda, 0x13, 0xcf, + 0xdd, 0x22, 0xfc, 0xa1, 0x81, 0x74, 0x4f, 0x16, 0x24, 0x00, 0x6b, 0x1c, 0xda, 0x93, 0xa6, 0xbb, + 0xbe, 0x2e, 0xb6, 0xfc, 0xaa, 0x27, 0x74, 0x74, 0x30, 0x83, 0xf0, 0x8a, 0xdc, 0xc1, 0xa6, 0xb0, + 0x82, 0x8d, 0x8a, 0xdc, 0xc1, 0x26, 0x66, 0x10, 0x6a, 0xb7, 0xf9, 0x41, 0xd4, 0x66, 0x57, 0xca, + 0x37, 0x15, 0x17, 0x61, 0xfd, 0x2a, 0xbb, 0xed, 0x7a, 0x37, 0x0a, 0xce, 0x7b, 0x8e, 0xce, 0xc0, + 0x30, 0x22, 0x4d, 0xb7, 0x91, 0x98, 0xd4, 0x20, 0x3d, 0x03, 0x97, 0xbb, 0x30, 0x70, 0xce, 0x53, + 0x68, 0x0a, 0x4e, 0xc8, 0xbc, 0x66, 0x59, 0xb5, 0x66, 0x28, 0x5d, 0x25, 0x03, 0xa7, 0xc1, 0x38, + 0x8b, 0x4f, 0xa5, 0x5a, 0x5b, 0x14, 0xac, 0x62, 0xc6, 0xb2, 0x21, 0xd5, 0x64, 0x21, 0x2b, 0xac, + 0x30, 0xec, 0x4f, 0x96, 0xa9, 0x16, 0xee, 0x51, 0xa8, 0xed, 0xbe, 0x45, 0x0b, 0xa6, 0x67, 0x64, + 0xa5, 0x8f, 0x19, 0xf9, 0x3c, 0x0c, 0xdf, 0x8e, 0x03, 0x5f, 0x45, 0xe2, 0x55, 0x7b, 0x46, 0xe2, + 0x19, 0x58, 0xf9, 0x91, 0x78, 0x03, 0x45, 0x45, 0xe2, 0x0d, 0x1e, 0x32, 0x12, 0xef, 0x5b, 0x55, + 0x50, 0x57, 0x83, 0x5c, 0x27, 0xc9, 0x9d, 0x20, 0xda, 0x74, 0xfd, 0x16, 0xcb, 0x07, 0xff, 0x9a, + 0x05, 0xc3, 0x7c, 0xbd, 0x2c, 0x98, 0x99, 0x54, 0xeb, 0x05, 0xdd, 0x39, 0x91, 0x62, 0x36, 0xb1, + 0x6a, 0x30, 0xca, 0x5c, 0xbd, 0x69, 0x82, 0x70, 0xaa, 0x47, 0xe8, 0x63, 0x00, 0xd2, 0x3f, 0xba, + 0x2e, 0x45, 0xe6, 0x7c, 0x31, 0xfd, 0xc3, 0x64, 0x5d, 0xdb, 0xc0, 0xab, 0x8a, 0x09, 0x36, 0x18, + 0xa2, 0xcf, 0xe8, 0x2c, 0x33, 0x1e, 0xb2, 0xff, 0x91, 0x63, 0x19, 0x9b, 0x7e, 0x72, 0xcc, 0x30, + 0x0c, 0xba, 0x7e, 0x8b, 0xce, 0x13, 0x11, 0xb1, 0xf4, 0xae, 0xbc, 0x5a, 0x0a, 0x0b, 0x81, 0xd3, + 0x9c, 0x76, 0x3c, 0xc7, 0x6f, 0x90, 0x68, 0x9e, 0xa3, 0x9b, 0x17, 0x4e, 0xb3, 0x06, 0x2c, 0x09, + 0x75, 0x5d, 0xaa, 0x52, 0xed, 0xe7, 0x52, 0x95, 0xf3, 0xef, 0x87, 0x53, 0x5d, 0x1f, 0xf3, 0x40, + 0x29, 0x65, 0x87, 0xcf, 0x46, 0xb3, 0xff, 0xd9, 0x80, 0x56, 0x5a, 0xd7, 0x83, 0x26, 0xbf, 0xda, + 0x23, 0xd2, 0x5f, 0x54, 0xd8, 0xb8, 0x05, 0x4e, 0x11, 0xe3, 0xd2, 0x6a, 0xd5, 0x88, 0x4d, 0x96, + 0x74, 0x8e, 0x86, 0x4e, 0x44, 0xfc, 0xe3, 0x9e, 0xa3, 0xcb, 0x8a, 0x09, 0x36, 0x18, 0xa2, 0x8d, + 0x54, 0x4e, 0xc9, 0xe5, 0xa3, 0xe7, 0x94, 0xb0, 0x2a, 0x53, 0x79, 0xd5, 0xf8, 0xbf, 0x68, 0xc1, + 0xa8, 0x9f, 0x9a, 0xb9, 0xc5, 0x84, 0x91, 0xe6, 0xaf, 0x0a, 0x7e, 0xb3, 0x54, 0xba, 0x0d, 0x67, + 0xf8, 0xe7, 0xa9, 0xb4, 0xea, 0x01, 0x55, 0x9a, 0xbe, 0x23, 0x68, 0xa0, 0xd7, 0x1d, 0x41, 0xc8, + 0x57, 0x97, 0xa4, 0x0d, 0x16, 0x7e, 0x49, 0x1a, 0xe4, 0x5c, 0x90, 0x76, 0x0b, 0xea, 0x8d, 0x88, + 0x38, 0xc9, 0x21, 0xef, 0xcb, 0x62, 0x07, 0xf4, 0x33, 0x92, 0x00, 0xd6, 0xb4, 0xec, 0xff, 0x5d, + 0x81, 0x93, 0x72, 0x44, 0x64, 0x08, 0x3a, 0xd5, 0x8f, 0x9c, 0xaf, 0x36, 0x6e, 0x95, 0x7e, 0xbc, + 0x22, 0x01, 0x58, 0xe3, 0x50, 0x7b, 0xac, 0x13, 0x93, 0xa5, 0x90, 0xf8, 0x0b, 0xee, 0x5a, 0x2c, + 0xce, 0x39, 0xd5, 0x42, 0xb9, 0xa1, 0x41, 0xd8, 0xc4, 0xa3, 0xc6, 0x38, 0xb7, 0x8b, 0xe3, 0x6c, + 0xfa, 0x8a, 0xb0, 0xb7, 0xb1, 0x84, 0xa3, 0x5f, 0xcc, 0xad, 0x1c, 0x5b, 0x4c, 0xe2, 0x56, 0x57, + 0xe4, 0xfd, 0x01, 0xaf, 0x58, 0xfc, 0x5b, 0x16, 0x9c, 0xe5, 0xad, 0x72, 0x24, 0x6f, 0x84, 0x4d, + 0x27, 0x21, 0x71, 0x31, 0x95, 0xdc, 0x73, 0xfa, 0xa7, 0x9d, 0xbc, 0x79, 0x6c, 0x71, 0x7e, 0x6f, + 0xd0, 0x9b, 0x16, 0x9c, 0xd8, 0x4c, 0xd5, 0xfc, 0x90, 0xaa, 0xe3, 0xa8, 0xe9, 0xf8, 0x29, 0xa2, + 0x7a, 0xa9, 0xa5, 0xdb, 0x63, 0x9c, 0xe5, 0x6e, 0xff, 0x99, 0x05, 0xa6, 0x18, 0xbd, 0xff, 0xa5, + 0x42, 0x0e, 0x6e, 0x0a, 0x4a, 0xeb, 0xb2, 0xda, 0xd3, 0xba, 0x7c, 0x1c, 0xca, 0x1d, 0xb7, 0x29, + 0xf6, 0x17, 0xfa, 0xf4, 0x75, 0x7e, 0x16, 0xd3, 0x76, 0xfb, 0x1f, 0x57, 0xb5, 0xdf, 0x42, 0xe4, + 0x45, 0x7d, 0x5f, 0xbc, 0xf6, 0xba, 0x2a, 0x36, 0xc6, 0xdf, 0xfc, 0x7a, 0x57, 0xb1, 0xb1, 0x1f, + 0x3b, 0x78, 0xda, 0x1b, 0x1f, 0xa0, 0x5e, 0xb5, 0xc6, 0x06, 0xf7, 0xc9, 0x79, 0xbb, 0x0d, 0x35, + 0xba, 0x05, 0x63, 0x0e, 0xc8, 0x5a, 0xaa, 0x53, 0xb5, 0x2b, 0xa2, 0xfd, 0xde, 0xee, 0xf8, 0x7b, + 0x0f, 0xde, 0x2d, 0xf9, 0x34, 0x56, 0xf4, 0x51, 0x0c, 0x75, 0xfa, 0x9b, 0xa5, 0xe7, 0x89, 0xcd, + 0xdd, 0x0d, 0x25, 0x33, 0x25, 0xa0, 0x90, 0xdc, 0x3f, 0xcd, 0x07, 0xf9, 0x50, 0x67, 0xb7, 0xd1, + 0x32, 0xa6, 0x7c, 0x0f, 0xb8, 0xac, 0x92, 0xe4, 0x24, 0xe0, 0xde, 0xee, 0xf8, 0x4b, 0x07, 0x67, + 0xaa, 0x1e, 0xc7, 0x9a, 0x85, 0xfd, 0xa5, 0x8a, 0x9e, 0xbb, 0xa2, 0xc6, 0xdc, 0xf7, 0xc5, 0xdc, + 0x7d, 0x31, 0x33, 0x77, 0x2f, 0x74, 0xcd, 0xdd, 0x51, 0x7d, 0x6b, 0x6a, 0x6a, 0x36, 0xde, 0x6f, + 0x43, 0x60, 0x7f, 0x7f, 0x03, 0xb3, 0x80, 0x5e, 0xef, 0xb8, 0x11, 0x89, 0x97, 0xa3, 0x8e, 0xef, + 0xfa, 0x2d, 0x36, 0x1d, 0x6b, 0xa6, 0x05, 0x94, 0x02, 0xe3, 0x2c, 0x3e, 0xdd, 0xd4, 0xd3, 0x6f, + 0x7e, 0xcb, 0xd9, 0xe2, 0xb3, 0xca, 0x28, 0xbb, 0xb5, 0x22, 0xda, 0xb1, 0xc2, 0xb0, 0xbf, 0xc1, + 0xce, 0xb2, 0x8d, 0xbc, 0x60, 0x3a, 0x27, 0x3c, 0x76, 0xfd, 0x2f, 0xaf, 0xd9, 0xa5, 0xe6, 0x04, + 0xbf, 0xf3, 0x97, 0xc3, 0xd0, 0x1d, 0x18, 0x5c, 0xe3, 0xf7, 0xdf, 0x15, 0x53, 0x9f, 0x5c, 0x5c, + 0xa6, 0xc7, 0x6e, 0x39, 0x91, 0x37, 0xeb, 0xdd, 0xd3, 0x3f, 0xb1, 0xe4, 0x66, 0x7f, 0xb3, 0x02, + 0x27, 0x32, 0x17, 0xc4, 0xa6, 0xaa, 0xa5, 0x96, 0xf6, 0xad, 0x96, 0xfa, 0x61, 0x80, 0x26, 0x09, + 0xbd, 0x60, 0x87, 0x99, 0x63, 0x95, 0x03, 0x9b, 0x63, 0xca, 0x82, 0x9f, 0x55, 0x54, 0xb0, 0x41, + 0x51, 0x14, 0x2a, 0xe3, 0xc5, 0x57, 0x33, 0x85, 0xca, 0x8c, 0x5b, 0x0c, 0x06, 0xee, 0xef, 0x2d, + 0x06, 0x2e, 0x9c, 0xe0, 0x5d, 0x54, 0xd9, 0xb7, 0x87, 0x48, 0xb2, 0x65, 0xf9, 0x0b, 0xb3, 0x69, + 0x32, 0x38, 0x4b, 0xf7, 0x41, 0xde, 0xff, 0x8c, 0xde, 0x0d, 0x75, 0xf9, 0x9d, 0xe3, 0xb1, 0xba, + 0xae, 0x60, 0x20, 0xa7, 0x01, 0xbb, 0x97, 0x59, 0xfc, 0xb4, 0xbf, 0x50, 0xa2, 0xd6, 0x33, 0xff, + 0xa7, 0x2a, 0xd1, 0x3c, 0x05, 0x03, 0x4e, 0x27, 0xd9, 0x08, 0xba, 0xee, 0xd0, 0x9b, 0x62, 0xad, + 0x58, 0x40, 0xd1, 0x02, 0x54, 0x9a, 0xba, 0xba, 0xc8, 0x41, 0x46, 0x51, 0x3b, 0x22, 0x9d, 0x84, + 0x60, 0x46, 0x05, 0x3d, 0x06, 0x95, 0xc4, 0x69, 0xc9, 0x44, 0x27, 0x96, 0xdc, 0xba, 0xea, 0xb4, + 0x62, 0xcc, 0x5a, 0x4d, 0xa5, 0x59, 0xd9, 0x47, 0x69, 0xbe, 0x04, 0x23, 0xb1, 0xdb, 0xf2, 0x9d, + 0xa4, 0x13, 0x11, 0xe3, 0x70, 0x4d, 0xc7, 0x4b, 0x98, 0x40, 0x9c, 0xc6, 0xb5, 0x7f, 0x6b, 0x18, + 0xce, 0xac, 0xcc, 0x2c, 0xca, 0x9a, 0xd9, 0xc7, 0x96, 0xab, 0x94, 0xc7, 0xe3, 0xfe, 0xe5, 0x2a, + 0xf5, 0xe0, 0xee, 0x19, 0xb9, 0x4a, 0x9e, 0x91, 0xab, 0x94, 0x4e, 0x1c, 0x29, 0x17, 0x91, 0x38, + 0x92, 0xd7, 0x83, 0x7e, 0x12, 0x47, 0x8e, 0x2d, 0x79, 0x69, 0xcf, 0x0e, 0x1d, 0x28, 0x79, 0x49, + 0x65, 0x76, 0x15, 0x12, 0xd2, 0xdf, 0xe3, 0x53, 0xe5, 0x66, 0x76, 0xa9, 0xac, 0x1a, 0x9e, 0xae, + 0x22, 0x04, 0xec, 0xab, 0xc5, 0x77, 0xa0, 0x8f, 0xac, 0x1a, 0x91, 0x31, 0x63, 0x66, 0x72, 0x0d, + 0x16, 0x91, 0xc9, 0x95, 0xd7, 0x9d, 0x7d, 0x33, 0xb9, 0x5e, 0x82, 0x91, 0x86, 0x17, 0xf8, 0x64, + 0x39, 0x0a, 0x92, 0xa0, 0x11, 0x78, 0xc2, 0x98, 0x56, 0x22, 0x61, 0xc6, 0x04, 0xe2, 0x34, 0x6e, + 0xaf, 0x34, 0xb0, 0xfa, 0x51, 0xd3, 0xc0, 0xe0, 0x01, 0xa5, 0x81, 0xfd, 0x9c, 0x4e, 0x58, 0x1e, + 0x62, 0x5f, 0xe4, 0xc3, 0xc5, 0x7f, 0x91, 0x7e, 0xb2, 0x96, 0xd1, 0x5b, 0xfc, 0x12, 0x3b, 0x6a, + 0x8e, 0xce, 0x04, 0x6d, 0x6a, 0x6e, 0x0d, 0xb3, 0x21, 0x79, 0xed, 0x18, 0x26, 0xec, 0xad, 0x15, + 0xcd, 0x46, 0x5d, 0x6c, 0xa7, 0x9b, 0x70, 0xba, 0x23, 0x47, 0x49, 0xa8, 0xfe, 0x4a, 0x09, 0x7e, + 0x60, 0xdf, 0x2e, 0xa0, 0x3b, 0x00, 0x89, 0xd3, 0x12, 0x13, 0x55, 0x1c, 0x53, 0x1c, 0x31, 0xa8, + 0x71, 0x55, 0xd2, 0xe3, 0x95, 0x40, 0xd4, 0x5f, 0x76, 0x00, 0x20, 0x7f, 0xb3, 0x58, 0xc6, 0xc0, + 0xeb, 0x2a, 0x98, 0x88, 0x03, 0x8f, 0x60, 0x06, 0xa1, 0xea, 0x3f, 0x22, 0x2d, 0x7d, 0xeb, 0xb2, + 0xfa, 0x7c, 0x98, 0xb5, 0x62, 0x01, 0x45, 0x2f, 0xc0, 0x90, 0xe3, 0x79, 0x3c, 0x2b, 0x85, 0xc4, + 0xe2, 0x16, 0x1b, 0x5d, 0xb9, 0x4d, 0x83, 0xb0, 0x89, 0x67, 0xff, 0x69, 0x09, 0xc6, 0xf7, 0x91, + 0x29, 0x5d, 0x79, 0x76, 0xd5, 0xbe, 0xf3, 0xec, 0x44, 0x66, 0xc0, 0x40, 0x8f, 0xcc, 0x80, 0x17, + 0x60, 0x28, 0x21, 0x4e, 0x5b, 0x84, 0x41, 0x89, 0xfd, 0xb7, 0x3e, 0x77, 0xd5, 0x20, 0x6c, 0xe2, + 0x51, 0x29, 0x36, 0xea, 0x34, 0x1a, 0x24, 0x8e, 0x65, 0xe8, 0xbf, 0xf0, 0x61, 0x16, 0x96, 0x57, + 0xc0, 0x5c, 0xc3, 0x53, 0x29, 0x16, 0x38, 0xc3, 0x32, 0x3b, 0xe0, 0xf5, 0x3e, 0x07, 0xfc, 0xeb, + 0x25, 0x78, 0x7c, 0x4f, 0xed, 0xd6, 0x77, 0x56, 0x46, 0x27, 0x26, 0x51, 0x76, 0xe2, 0xdc, 0x88, + 0x49, 0x84, 0x19, 0x84, 0x8f, 0x52, 0x18, 0x1a, 0xb7, 0x5a, 0x17, 0x9d, 0x32, 0xc4, 0x47, 0x29, + 0xc5, 0x02, 0x67, 0x58, 0x1e, 0x76, 0x5a, 0xfe, 0xdd, 0x12, 0x3c, 0xd9, 0x87, 0x0d, 0x50, 0x60, + 0x6a, 0x55, 0x3a, 0xc1, 0xad, 0xfc, 0x80, 0xf2, 0x10, 0x0f, 0x39, 0x5c, 0xdf, 0x28, 0xc1, 0xf9, + 0xde, 0xaa, 0x18, 0xfd, 0x38, 0xdd, 0xc3, 0xcb, 0xd8, 0x27, 0x33, 0x37, 0xee, 0x34, 0xdf, 0xbf, + 0xa7, 0x40, 0x38, 0x8b, 0x8b, 0x26, 0x00, 0x42, 0x27, 0xd9, 0x88, 0x2f, 0x6d, 0xbb, 0x71, 0x22, + 0x6a, 0xbf, 0x8c, 0xf2, 0x13, 0x23, 0xd9, 0x8a, 0x0d, 0x0c, 0xca, 0x8e, 0xfd, 0x9b, 0x0d, 0xae, + 0x07, 0x09, 0x7f, 0x88, 0x6f, 0x23, 0x4e, 0xcb, 0x9b, 0x32, 0x0c, 0x10, 0xce, 0xe2, 0x52, 0x76, + 0xec, 0x4c, 0x92, 0x77, 0x94, 0xef, 0x2f, 0x18, 0xbb, 0x05, 0xd5, 0x8a, 0x0d, 0x8c, 0x6c, 0xd6, + 0x5f, 0x75, 0xff, 0xac, 0x3f, 0xfb, 0x1f, 0x95, 0xe0, 0x5c, 0x4f, 0x53, 0xae, 0xbf, 0x05, 0xf8, + 0xf0, 0x65, 0xea, 0x1d, 0x6e, 0xee, 0x1c, 0x30, 0xa3, 0xec, 0x8f, 0x7b, 0xcc, 0x34, 0x91, 0x51, + 0x76, 0xf8, 0x94, 0xec, 0x87, 0x6f, 0x3c, 0xbb, 0x92, 0xc8, 0x2a, 0x07, 0x48, 0x22, 0xcb, 0x7c, + 0x8c, 0x6a, 0x9f, 0x0b, 0xf9, 0xcf, 0xcb, 0x3d, 0x87, 0x97, 0x6e, 0xfd, 0xfa, 0xf2, 0x8e, 0xce, + 0xc2, 0x49, 0xd7, 0x67, 0xb7, 0x26, 0xad, 0x74, 0xd6, 0x44, 0x39, 0x90, 0x52, 0xfa, 0xce, 0xf2, + 0xf9, 0x0c, 0x1c, 0x77, 0x3d, 0xf1, 0x10, 0x26, 0xf5, 0x1d, 0x6e, 0x48, 0x0f, 0x96, 0x56, 0x8a, + 0x96, 0xe0, 0xac, 0x1c, 0x8a, 0x0d, 0x27, 0x22, 0x4d, 0xa1, 0x46, 0x62, 0x91, 0xc6, 0x70, 0x8e, + 0xa7, 0x42, 0xe4, 0x20, 0xe0, 0xfc, 0xe7, 0xd8, 0x45, 0x35, 0x41, 0xe8, 0x36, 0xc4, 0x26, 0x47, + 0x5f, 0x54, 0x43, 0x1b, 0x31, 0x87, 0xd9, 0x1f, 0x86, 0xba, 0x7a, 0x7f, 0x1e, 0x4c, 0xad, 0x26, + 0x5d, 0x57, 0x30, 0xb5, 0x9a, 0x71, 0x06, 0x16, 0xfd, 0x5a, 0xd4, 0x24, 0xce, 0xac, 0x9e, 0x6b, + 0x64, 0x87, 0xd9, 0xc7, 0xf6, 0x8f, 0xc0, 0xb0, 0xf2, 0xb3, 0xf4, 0x7b, 0x7d, 0x8f, 0xfd, 0xa5, + 0x01, 0x18, 0x49, 0x95, 0xe4, 0x4b, 0xb9, 0x35, 0xad, 0x7d, 0xdd, 0x9a, 0x2c, 0x38, 0xbe, 0xe3, + 0xcb, 0xbb, 0xbd, 0x8c, 0xe0, 0xf8, 0x8e, 0x4f, 0x30, 0x87, 0x51, 0xf3, 0xb6, 0x19, 0xed, 0xe0, + 0x8e, 0x2f, 0x82, 0x58, 0x95, 0x79, 0x3b, 0xcb, 0x5a, 0xb1, 0x80, 0xa2, 0x4f, 0x58, 0x30, 0x1c, + 0x33, 0x9f, 0x39, 0x77, 0x0a, 0x8b, 0x49, 0x77, 0xf5, 0xe8, 0x15, 0x07, 0x55, 0xf9, 0x49, 0x16, + 0x97, 0x62, 0xb6, 0xe0, 0x14, 0x47, 0xf4, 0x69, 0x0b, 0xea, 0xea, 0x0a, 0x12, 0x71, 0x01, 0xdf, + 0x4a, 0xb1, 0x15, 0x0f, 0xb9, 0x37, 0x51, 0x1d, 0x3f, 0xa8, 0xd2, 0x73, 0x58, 0x33, 0x46, 0xb1, + 0xf2, 0xd8, 0x0e, 0x1e, 0x8f, 0xc7, 0x16, 0x72, 0xbc, 0xb5, 0xef, 0x86, 0x7a, 0xdb, 0xf1, 0xdd, + 0x75, 0x12, 0x27, 0xdc, 0x89, 0x2a, 0x0b, 0xb1, 0xca, 0x46, 0xac, 0xe1, 0x54, 0x21, 0xc7, 0xec, + 0xc5, 0x12, 0xc3, 0xeb, 0xc9, 0x14, 0xf2, 0x8a, 0x6e, 0xc6, 0x26, 0x8e, 0xe9, 0xa2, 0x85, 0x07, + 0xea, 0xa2, 0x1d, 0xda, 0xc7, 0x45, 0xfb, 0xf7, 0x2d, 0x38, 0x9b, 0xfb, 0xd5, 0x1e, 0xde, 0x70, + 0x43, 0xfb, 0xcb, 0x55, 0x38, 0x9d, 0x53, 0x5b, 0x13, 0xed, 0x98, 0xf3, 0xd9, 0x2a, 0xe2, 0xe4, + 0x3e, 0x7d, 0x10, 0x2d, 0x87, 0x31, 0x67, 0x12, 0x1f, 0xec, 0x80, 0x44, 0x1f, 0x52, 0x94, 0xef, + 0xef, 0x21, 0x85, 0x31, 0x2d, 0x2b, 0x0f, 0x74, 0x5a, 0x56, 0xf7, 0x9e, 0x96, 0xe8, 0xd7, 0x2d, + 0x18, 0x6b, 0xf7, 0x28, 0xe8, 0x2e, 0x1c, 0x8f, 0x37, 0x8f, 0xa7, 0x5c, 0xfc, 0xf4, 0x63, 0x77, + 0x77, 0xc7, 0x7b, 0xd6, 0xd1, 0xc7, 0x3d, 0x7b, 0x65, 0x7f, 0xa7, 0x0c, 0xac, 0xb0, 0x2b, 0xab, + 0x9f, 0xb6, 0x83, 0x3e, 0x6e, 0x96, 0xe8, 0xb5, 0x8a, 0x2a, 0x27, 0xcb, 0x89, 0xab, 0x12, 0xbf, + 0x7c, 0x04, 0xf3, 0x2a, 0xfe, 0x66, 0x85, 0x56, 0xa9, 0x0f, 0xa1, 0xe5, 0xc9, 0x5a, 0xc8, 0xe5, + 0xe2, 0x6b, 0x21, 0xd7, 0xb3, 0x75, 0x90, 0xf7, 0xfe, 0xc4, 0x95, 0x87, 0xf2, 0x13, 0xff, 0x0d, + 0x8b, 0x0b, 0x9e, 0xcc, 0x57, 0xd0, 0x96, 0x81, 0xb5, 0x87, 0x65, 0xf0, 0x0c, 0xd4, 0x62, 0xe2, + 0xad, 0x5f, 0x21, 0x8e, 0x27, 0x2c, 0x08, 0x7d, 0x6a, 0x2c, 0xda, 0xb1, 0xc2, 0x60, 0x97, 0xa5, + 0x7a, 0x5e, 0x70, 0xe7, 0x52, 0x3b, 0x4c, 0x76, 0x84, 0x2d, 0xa1, 0x2f, 0x4b, 0x55, 0x10, 0x6c, + 0x60, 0xd9, 0x7f, 0xb3, 0xc4, 0x67, 0xa0, 0x08, 0x3d, 0x78, 0x31, 0x73, 0xbd, 0x5d, 0xff, 0xa7, + 0xf6, 0x1f, 0x05, 0x68, 0xa8, 0x8b, 0xe1, 0xc5, 0x99, 0xd0, 0x95, 0x23, 0xdf, 0x5a, 0x2d, 0xe8, + 0xe9, 0xd7, 0xd0, 0x6d, 0xd8, 0xe0, 0x97, 0x92, 0xa5, 0xe5, 0x7d, 0x65, 0x69, 0x4a, 0xac, 0x54, + 0xf6, 0xd1, 0x76, 0x7f, 0x6a, 0x41, 0xca, 0x22, 0x42, 0x21, 0x54, 0x69, 0x77, 0x77, 0x8a, 0xb9, + 0xf3, 0xde, 0x24, 0x4d, 0x45, 0xa3, 0x98, 0xf6, 0xec, 0x27, 0xe6, 0x8c, 0x90, 0x27, 0x22, 0x14, + 0xf8, 0xa8, 0x5e, 0x2f, 0x8e, 0xe1, 0x95, 0x20, 0xd8, 0xe4, 0x07, 0x9b, 0x3a, 0xda, 0xc1, 0x7e, + 0x11, 0x4e, 0x75, 0x75, 0x8a, 0xdd, 0x64, 0x15, 0xc8, 0x8b, 0xfe, 0x8d, 0xe9, 0xca, 0xd2, 0x26, + 0x31, 0x87, 0xd9, 0xdf, 0xb0, 0xe0, 0x64, 0x96, 0x3c, 0x7a, 0xcb, 0x82, 0x53, 0x71, 0x96, 0xde, + 0x71, 0x8d, 0x9d, 0x8a, 0x32, 0xec, 0x02, 0xe1, 0xee, 0x4e, 0xd8, 0xff, 0x47, 0x4c, 0xfe, 0x5b, + 0xae, 0xdf, 0x0c, 0xee, 0x28, 0xc3, 0xc4, 0xea, 0x69, 0x98, 0xd0, 0xf5, 0xd8, 0xd8, 0x20, 0xcd, + 0x8e, 0xd7, 0x95, 0xaf, 0xb9, 0x22, 0xda, 0xb1, 0xc2, 0x60, 0xe9, 0x69, 0x1d, 0x51, 0x2c, 0x3d, + 0x33, 0x29, 0x67, 0x45, 0x3b, 0x56, 0x18, 0xe8, 0x79, 0x18, 0x36, 0x5e, 0x52, 0xce, 0x4b, 0x66, + 0x90, 0x1b, 0x2a, 0x33, 0xc6, 0x29, 0x2c, 0x34, 0x01, 0xa0, 0x8c, 0x1c, 0xa9, 0x22, 0x99, 0xa3, + 0x48, 0x49, 0xa2, 0x18, 0x1b, 0x18, 0x2c, 0x19, 0xd4, 0xeb, 0xc4, 0xcc, 0xc7, 0x3f, 0xa0, 0x0b, + 0x78, 0xce, 0x88, 0x36, 0xac, 0xa0, 0x54, 0x9a, 0xb4, 0x1d, 0xbf, 0xe3, 0x78, 0x74, 0x84, 0xc4, + 0xd6, 0x4f, 0x2d, 0xc3, 0x45, 0x05, 0xc1, 0x06, 0x16, 0x7d, 0xe3, 0xc4, 0x6d, 0x93, 0x57, 0x02, + 0x5f, 0x46, 0x87, 0xe9, 0x63, 0x1f, 0xd1, 0x8e, 0x15, 0x86, 0xfd, 0x5f, 0x2c, 0x38, 0xa1, 0x53, + 0xcb, 0xf9, 0x9d, 0xd5, 0xe6, 0x4e, 0xd5, 0xda, 0x77, 0xa7, 0x9a, 0xce, 0xb9, 0x2d, 0xf5, 0x95, + 0x73, 0x6b, 0xa6, 0xc3, 0x96, 0xf7, 0x4c, 0x87, 0xfd, 0x41, 0x7d, 0x1f, 0x2a, 0xcf, 0x9b, 0x1d, + 0xca, 0xbb, 0x0b, 0x15, 0xd9, 0x30, 0xd0, 0x70, 0x54, 0x5d, 0x95, 0x61, 0xbe, 0x77, 0x98, 0x99, + 0x62, 0x48, 0x02, 0x62, 0x2f, 0x41, 0x5d, 0x9d, 0x7e, 0xc8, 0x8d, 0xaa, 0x95, 0xbf, 0x51, 0xed, + 0x2b, 0x2d, 0x6f, 0x7a, 0xed, 0x9b, 0xdf, 0x7d, 0xe2, 0x1d, 0xbf, 0xff, 0xdd, 0x27, 0xde, 0xf1, + 0x47, 0xdf, 0x7d, 0xe2, 0x1d, 0x9f, 0xb8, 0xfb, 0x84, 0xf5, 0xcd, 0xbb, 0x4f, 0x58, 0xbf, 0x7f, + 0xf7, 0x09, 0xeb, 0x8f, 0xee, 0x3e, 0x61, 0x7d, 0xe7, 0xee, 0x13, 0xd6, 0x17, 0xff, 0xe3, 0x13, + 0xef, 0x78, 0x25, 0x37, 0x3c, 0x90, 0xfe, 0x78, 0xb6, 0xd1, 0x9c, 0xdc, 0xba, 0xc8, 0x22, 0xd4, + 0xe8, 0xf2, 0x9a, 0x34, 0xe6, 0xd4, 0xa4, 0x5c, 0x5e, 0xff, 0x37, 0x00, 0x00, 0xff, 0xff, 0x90, + 0x85, 0x5a, 0x45, 0xa5, 0xe0, 0x00, 0x00, } func (m *AWSAuthConfig) Marshal() (dAtA []byte, err error) { @@ -6426,6 +6427,13 @@ func (m *ApplicationSetSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.TemplatePatch != nil { + i -= len(*m.TemplatePatch) + copy(dAtA[i:], *m.TemplatePatch) + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.TemplatePatch))) + i-- + dAtA[i] = 0x52 + } if len(m.IgnoreApplicationDifferences) > 0 { for iNdEx := len(m.IgnoreApplicationDifferences) - 1; iNdEx >= 0; iNdEx-- { { @@ -14845,6 +14853,10 @@ func (m *ApplicationSetSpec) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } + if m.TemplatePatch != nil { + l = len(*m.TemplatePatch) + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -18133,6 +18145,7 @@ func (this *ApplicationSetSpec) String() string { `GoTemplateOptions:` + fmt.Sprintf("%v", this.GoTemplateOptions) + `,`, `ApplyNestedSelectors:` + fmt.Sprintf("%v", this.ApplyNestedSelectors) + `,`, `IgnoreApplicationDifferences:` + repeatedStringForIgnoreApplicationDifferences + `,`, + `TemplatePatch:` + valueToStringGenerated(this.TemplatePatch) + `,`, `}`, }, "") return s @@ -24402,6 +24415,39 @@ func (m *ApplicationSetSpec) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TemplatePatch", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.TemplatePatch = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/pkg/apis/application/v1alpha1/generated.proto b/pkg/apis/application/v1alpha1/generated.proto index ac1415786d032..c080c04c3a088 100644 --- a/pkg/apis/application/v1alpha1/generated.proto +++ b/pkg/apis/application/v1alpha1/generated.proto @@ -316,6 +316,8 @@ message ApplicationSetSpec { optional bool applyNestedSelectors = 8; repeated ApplicationSetResourceIgnoreDifferences ignoreApplicationDifferences = 9; + + optional string templatePatch = 10; } // ApplicationSetStatus defines the observed state of ApplicationSet diff --git a/pkg/apis/application/v1alpha1/openapi_generated.go b/pkg/apis/application/v1alpha1/openapi_generated.go index 5dff99db45d12..723ea884cb75b 100644 --- a/pkg/apis/application/v1alpha1/openapi_generated.go +++ b/pkg/apis/application/v1alpha1/openapi_generated.go @@ -1281,6 +1281,12 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSetSpec(ref common.Referenc }, }, }, + "templatePatch": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, }, Required: []string{"generators", "template"}, }, diff --git a/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go index 48e7e601670be..c10b610cbd5a7 100644 --- a/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go @@ -733,6 +733,11 @@ func (in *ApplicationSetSpec) DeepCopyInto(out *ApplicationSetSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.TemplatePatch != nil { + in, out := &in.TemplatePatch, &out.TemplatePatch + *out = new(string) + **out = **in + } return } diff --git a/test/e2e/applicationset_test.go b/test/e2e/applicationset_test.go index 2fef0ace55583..5b9b8190c5437 100644 --- a/test/e2e/applicationset_test.go +++ b/test/e2e/applicationset_test.go @@ -599,6 +599,134 @@ func TestRenderHelmValuesObject(t *testing.T) { } +func TestTemplatePatch(t *testing.T) { + + expectedApp := argov1alpha1.Application{ + TypeMeta: metav1.TypeMeta{ + Kind: application.ApplicationKind, + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "my-cluster-guestbook", + Namespace: fixture.TestNamespace(), + Finalizers: []string{"resources-finalizer.argocd.argoproj.io"}, + Annotations: map[string]string{ + "annotation-some-key": "annotation-some-value", + }, + }, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "https://kubernetes.default.svc", + Namespace: "guestbook", + }, + SyncPolicy: &argov1alpha1.SyncPolicy{ + SyncOptions: argov1alpha1.SyncOptions{"CreateNamespace=true"}, + }, + }, + } + + templatePatch := `{ + "metadata": { + "annotations": { + {{- range $k, $v := .annotations }} + "{{ $k }}": "{{ $v }}" + {{- end }} + } + }, + {{- if .createNamespace }} + "spec": { + "syncPolicy": { + "syncOptions": [ + "CreateNamespace=true" + ] + } + } + {{- end }} + } + ` + + var expectedAppNewNamespace *argov1alpha1.Application + var expectedAppNewMetadata *argov1alpha1.Application + + Given(t). + // Create a ListGenerator-based ApplicationSet + When().Create(v1alpha1.ApplicationSet{ObjectMeta: metav1.ObjectMeta{ + Name: "patch-template", + }, + Spec: v1alpha1.ApplicationSetSpec{ + GoTemplate: true, + Template: v1alpha1.ApplicationSetTemplate{ + ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{Name: "{{.cluster}}-guestbook"}, + Spec: argov1alpha1.ApplicationSpec{ + Project: "default", + Source: &argov1alpha1.ApplicationSource{ + RepoURL: "https://github.com/argoproj/argocd-example-apps.git", + TargetRevision: "HEAD", + Path: "guestbook", + }, + Destination: argov1alpha1.ApplicationDestination{ + Server: "{{.url}}", + Namespace: "guestbook", + }, + }, + }, + TemplatePatch: &templatePatch, + Generators: []v1alpha1.ApplicationSetGenerator{ + { + List: &v1alpha1.ListGenerator{ + Elements: []apiextensionsv1.JSON{{ + Raw: []byte(`{ + "cluster": "my-cluster", + "url": "https://kubernetes.default.svc", + "createNamespace": true, + "annotations": { + "annotation-some-key": "annotation-some-value" + } + }`), + }}, + }, + }, + }, + }, + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{expectedApp})). + + // Update the ApplicationSet template namespace, and verify it updates the Applications + When(). + And(func() { + expectedAppNewNamespace = expectedApp.DeepCopy() + expectedAppNewNamespace.Spec.Destination.Namespace = "guestbook2" + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Spec.Destination.Namespace = "guestbook2" + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewNamespace})). + + // Update the metadata fields in the appset template, and make sure it propagates to the apps + When(). + And(func() { + expectedAppNewMetadata = expectedAppNewNamespace.DeepCopy() + expectedAppNewMetadata.ObjectMeta.Labels = map[string]string{ + "label-key": "label-value", + } + }). + Update(func(appset *v1alpha1.ApplicationSet) { + appset.Spec.Template.Labels = map[string]string{"label-key": "label-value"} + }).Then().Expect(ApplicationsExist([]argov1alpha1.Application{*expectedAppNewMetadata})). + + // verify the ApplicationSet status conditions were set correctly + Expect(ApplicationSetHasConditions("patch-template", ExpectedConditions)). + + // Delete the ApplicationSet, and verify it deletes the Applications + When(). + Delete().Then().Expect(ApplicationsDoNotExist([]argov1alpha1.Application{*expectedAppNewMetadata})) + +} + func TestSyncPolicyCreateUpdate(t *testing.T) { expectedApp := argov1alpha1.Application{ From 017b08a61cb989deae7502d2e148e08107e824fd Mon Sep 17 00:00:00 2001 From: Jonas Eilers <133217951+jdvgh@users.noreply.github.com> Date: Fri, 1 Dec 2023 21:15:42 +0100 Subject: [PATCH 18/66] feat(ui): Add sourceNamespaces in Projects UI and only show it if AppsInAnyNamespaceEnabled flag is set (#16249) Signed-off-by: Eilers, Jonas <133217951+jdvgh@users.noreply.github.com> --- .../project-details/project-details.tsx | 59 ++++++++++++++++++- .../project-details/resource-lists-panel.tsx | 32 ++++++++++ ui/src/app/shared/models.ts | 1 + 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/ui/src/app/settings/components/project-details/project-details.tsx b/ui/src/app/settings/components/project-details/project-details.tsx index 224c2e1e45e12..8b00c8590edb7 100644 --- a/ui/src/app/settings/components/project-details/project-details.tsx +++ b/ui/src/app/settings/components/project-details/project-details.tsx @@ -6,7 +6,7 @@ import {FormApi, Text} from 'react-form'; import {RouteComponentProps} from 'react-router'; import {BadgePanel, CheckboxField, DataLoader, EditablePanel, ErrorNotification, MapInputField, Page, Query} from '../../../shared/components'; -import {AppContext, Consumer} from '../../../shared/context'; +import {AppContext, Consumer, AuthSettingsCtx} from '../../../shared/context'; import {GroupKind, Groups, Project, DetailedProjectsResponse, ProjectSpec, ResourceKinds} from '../../../shared/models'; import {CreateJWTTokenParams, DeleteJWTTokenParams, ProjectRoleParams, services} from '../../../shared/services'; @@ -52,6 +52,7 @@ function reduceGlobal(projs: Project[]): ProjectSpec & {count: number} { merged.namespaceResourceWhitelist = merged.namespaceResourceWhitelist.concat(proj.spec.namespaceResourceWhitelist || []); merged.sourceRepos = merged.sourceRepos.concat(proj.spec.sourceRepos || []); merged.destinations = merged.destinations.concat(proj.spec.destinations || []); + merged.sourceNamespaces = merged.sourceNamespaces.concat(proj.spec.sourceNamespaces || []); merged.sourceRepos = merged.sourceRepos.filter((item, index) => { return ( @@ -106,6 +107,15 @@ function reduceGlobal(projs: Project[]): ProjectSpec & {count: number} { }) ); }); + + merged.sourceNamespaces = merged.sourceNamespaces.filter((item, index) => { + return ( + index === + merged.sourceNamespaces.findIndex(obj => { + return obj === item; + }) + ); + }); merged.count += 1; return merged; @@ -116,6 +126,7 @@ function reduceGlobal(projs: Project[]): ProjectSpec & {count: number} { namespaceResourceWhitelist: new Array(), clusterResourceWhitelist: new Array(), sourceRepos: [], + sourceNamespaces: [], signatureKeys: [], destinations: [], description: '', @@ -648,7 +659,51 @@ export class ProjectDetails extends React.Component - + + {authCtx => + authCtx.appsInAnyNamespaceEnabled && ( + this.saveProject(item)} + values={proj} + title={ + SOURCE NAMESPACES {helpTip('Kubernetes namespaces where application resources are allowed to be created in')} + } + view={ + + {proj.spec.sourceNamespaces + ? proj.spec.sourceNamespaces.map((namespace, i) => ( +
+
{namespace}
+
+ )) + : emptyMessage('source namespaces')} +
+ } + edit={formApi => ( + + {(formApi.values.spec.sourceNamespaces || []).map((_: Project, i: number) => ( +
+
+ + formApi.setValue('spec.sourceNamespaces', removeEl(formApi.values.spec.sourceNamespaces, i))} + /> +
+
+ ))} + +
+ )} + items={[]} + /> + ) + } +
this.saveProject(item)} values={proj} diff --git a/ui/src/app/settings/components/project-details/resource-lists-panel.tsx b/ui/src/app/settings/components/project-details/resource-lists-panel.tsx index 2e5c1d1fedd72..ec9e617ac9122 100644 --- a/ui/src/app/settings/components/project-details/resource-lists-panel.tsx +++ b/ui/src/app/settings/components/project-details/resource-lists-panel.tsx @@ -99,6 +99,36 @@ function viewSourceReposInfoList(type: field, proj: Project) { ); } +const sourceNamespacesInfoByField: {[type: string]: {title: string; helpText: string}} = { + sourceNamespaces: { + title: 'source namespaces', + helpText: 'Kubernetes namespaces where application resources are allowed to be created in' + } +}; + +function viewSourceNamespacesInfoList(type: field, proj: Project) { + const info = sourceNamespacesInfoByField[type]; + const list = proj.spec[type] as Array; + return ( + +

+ {info.title} {helpTip(info.helpText)} +

+ {(list || []).length > 0 ? ( + + {list.map((namespace, i) => ( +
+
{namespace}
+
+ ))} +
+ ) : ( +

The {info.title} is empty

+ )} +
+ ); +} + const destinationsInfoByField: {[type: string]: {title: string; helpText: string}} = { destinations: { title: 'destinations', @@ -180,6 +210,8 @@ export const ResourceListsPanel = ({proj, saveProject, title}: {proj: Project; t {viewList(key as field, proj)} ))} {!proj.metadata && Object.keys(sourceReposInfoByField).map(key => {viewSourceReposInfoList(key as field, proj)})} + {!proj.metadata && + Object.keys(sourceNamespacesInfoByField).map(key => {viewSourceNamespacesInfoList(key as field, proj)})} {!proj.metadata && Object.keys(destinationsInfoByField).map(key => {viewDestinationsInfoList(key as field, proj)})} } diff --git a/ui/src/app/shared/models.ts b/ui/src/app/shared/models.ts index b3f4c284dcfc4..861513523c7a3 100644 --- a/ui/src/app/shared/models.ts +++ b/ui/src/app/shared/models.ts @@ -714,6 +714,7 @@ export interface ProjectSignatureKey { export interface ProjectSpec { sourceRepos: string[]; + sourceNamespaces: string[]; destinations: ApplicationDestination[]; description: string; roles: ProjectRole[]; From 7408292bb058fcb1feb62b27cc7b348b2a4b2c68 Mon Sep 17 00:00:00 2001 From: Dhruvang Makadia Date: Sun, 3 Dec 2023 17:54:55 -0800 Subject: [PATCH 19/66] fix(appset): Don't use revision cache when reconciling after webhook (#16062) (#16241) * fix(appset): store sha from webhook to get latest change during reconcile (#16062) Signed-off-by: dhruvang1 * fix(appset): Don't use revision cache when reconciling after webhook(#16062) Signed-off-by: dhruvang1 --------- Signed-off-by: dhruvang1 --- applicationset/generators/git.go | 14 +- applicationset/generators/git_test.go | 8 +- applicationset/generators/matrix_test.go | 2 +- applicationset/services/mocks/Repos.go | 36 +- applicationset/services/repo_service.go | 14 +- applicationset/services/repo_service_test.go | 28 +- reposerver/apiclient/repository.pb.go | 348 ++++++++++++------- reposerver/repository/repository.go | 7 +- reposerver/repository/repository.proto | 2 + 9 files changed, 277 insertions(+), 182 deletions(-) diff --git a/applicationset/generators/git.go b/applicationset/generators/git.go index 07c1b11849cd0..57fe2835b8df0 100644 --- a/applicationset/generators/git.go +++ b/applicationset/generators/git.go @@ -56,12 +56,14 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic return nil, EmptyAppSetGeneratorError } + noRevisionCache := appSet.RefreshRequired() + var err error var res []map[string]interface{} if len(appSetGenerator.Git.Directories) != 0 { - res, err = g.generateParamsForGitDirectories(appSetGenerator, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) + res, err = g.generateParamsForGitDirectories(appSetGenerator, noRevisionCache, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) } else if len(appSetGenerator.Git.Files) != 0 { - res, err = g.generateParamsForGitFiles(appSetGenerator, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) + res, err = g.generateParamsForGitFiles(appSetGenerator, noRevisionCache, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) } else { return nil, EmptyAppSetGeneratorError } @@ -72,10 +74,10 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic return res, nil } -func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) { +func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache bool, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) { // Directories, not files - allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision) + allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, noRevisionCache) if err != nil { return nil, fmt.Errorf("error getting directories from repo: %w", err) } @@ -98,12 +100,12 @@ func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoproj return res, nil } -func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) { +func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache bool, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) { // Get all files that match the requested path string, removing duplicates allFiles := make(map[string][]byte) for _, requestedPath := range appSetGenerator.Git.Files { - files, err := g.repos.GetFiles(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, requestedPath.Path) + files, err := g.repos.GetFiles(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, requestedPath.Path, noRevisionCache) if err != nil { return nil, err } diff --git a/applicationset/generators/git_test.go b/applicationset/generators/git_test.go index f0d1d29bca6ec..d3fd4965057f8 100644 --- a/applicationset/generators/git_test.go +++ b/applicationset/generators/git_test.go @@ -317,7 +317,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) { argoCDServiceMock := mocks.Repos{} - argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError) + argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError) var gitGenerator = NewGitGenerator(&argoCDServiceMock) applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ @@ -613,7 +613,7 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) { argoCDServiceMock := mocks.Repos{} - argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError) + argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError) var gitGenerator = NewGitGenerator(&argoCDServiceMock) applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ @@ -972,7 +972,7 @@ cluster: t.Parallel() argoCDServiceMock := mocks.Repos{} - argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError) var gitGenerator = NewGitGenerator(&argoCDServiceMock) @@ -1322,7 +1322,7 @@ cluster: t.Parallel() argoCDServiceMock := mocks.Repos{} - argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError) var gitGenerator = NewGitGenerator(&argoCDServiceMock) diff --git a/applicationset/generators/matrix_test.go b/applicationset/generators/matrix_test.go index 35748b98bcf19..21e88710ae618 100644 --- a/applicationset/generators/matrix_test.go +++ b/applicationset/generators/matrix_test.go @@ -1108,7 +1108,7 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) { } repoServiceMock := &mocks.Repos{} - repoServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{ + repoServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{ "some/path.json": []byte("test: content"), }, nil) gitGenerator := NewGitGenerator(repoServiceMock) diff --git a/applicationset/services/mocks/Repos.go b/applicationset/services/mocks/Repos.go index 776b104cae284..b7620b22f08bb 100644 --- a/applicationset/services/mocks/Repos.go +++ b/applicationset/services/mocks/Repos.go @@ -13,25 +13,25 @@ type Repos struct { mock.Mock } -// GetDirectories provides a mock function with given fields: ctx, repoURL, revision -func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision string) ([]string, error) { - ret := _m.Called(ctx, repoURL, revision) +// GetDirectories provides a mock function with given fields: ctx, repoURL, revision, noRevisionCache +func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error) { + ret := _m.Called(ctx, repoURL, revision, noRevisionCache) var r0 []string var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]string, error)); ok { - return rf(ctx, repoURL, revision) + if rf, ok := ret.Get(0).(func(context.Context, string, string, bool) ([]string, error)); ok { + return rf(ctx, repoURL, revision, noRevisionCache) } - if rf, ok := ret.Get(0).(func(context.Context, string, string) []string); ok { - r0 = rf(ctx, repoURL, revision) + if rf, ok := ret.Get(0).(func(context.Context, string, string, bool) []string); ok { + r0 = rf(ctx, repoURL, revision, noRevisionCache) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]string) } } - if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, repoURL, revision) + if rf, ok := ret.Get(1).(func(context.Context, string, string, bool) error); ok { + r1 = rf(ctx, repoURL, revision, noRevisionCache) } else { r1 = ret.Error(1) } @@ -39,25 +39,25 @@ func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision st return r0, r1 } -// GetFiles provides a mock function with given fields: ctx, repoURL, revision, pattern -func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string, pattern string) (map[string][]byte, error) { - ret := _m.Called(ctx, repoURL, revision, pattern) +// GetFiles provides a mock function with given fields: ctx, repoURL, revision, pattern, noRevisionCache +func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool) (map[string][]byte, error) { + ret := _m.Called(ctx, repoURL, revision, pattern, noRevisionCache) var r0 map[string][]byte var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (map[string][]byte, error)); ok { - return rf(ctx, repoURL, revision, pattern) + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool) (map[string][]byte, error)); ok { + return rf(ctx, repoURL, revision, pattern, noRevisionCache) } - if rf, ok := ret.Get(0).(func(context.Context, string, string, string) map[string][]byte); ok { - r0 = rf(ctx, repoURL, revision, pattern) + if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool) map[string][]byte); ok { + r0 = rf(ctx, repoURL, revision, pattern, noRevisionCache) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(map[string][]byte) } } - if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { - r1 = rf(ctx, repoURL, revision, pattern) + if rf, ok := ret.Get(1).(func(context.Context, string, string, string, bool) error); ok { + r1 = rf(ctx, repoURL, revision, pattern, noRevisionCache) } else { r1 = ret.Error(1) } diff --git a/applicationset/services/repo_service.go b/applicationset/services/repo_service.go index 8ad261fda11cd..64fedc34390b8 100644 --- a/applicationset/services/repo_service.go +++ b/applicationset/services/repo_service.go @@ -11,6 +11,8 @@ import ( "github.com/argoproj/argo-cd/v2/util/io" ) +//go:generate go run github.com/vektra/mockery/v2@v2.25.1 --name=RepositoryDB + // RepositoryDB Is a lean facade for ArgoDB, // Using a lean interface makes it easier to test the functionality of the git generator type RepositoryDB interface { @@ -25,13 +27,15 @@ type argoCDService struct { newFileGlobbingEnabled bool } +//go:generate go run github.com/vektra/mockery/v2@v2.25.1 --name=Repos + type Repos interface { // GetFiles returns content of files (not directories) within the target repo - GetFiles(ctx context.Context, repoURL string, revision string, pattern string) (map[string][]byte, error) + GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool) (map[string][]byte, error) // GetDirectories returns a list of directories (not files) within the target repo - GetDirectories(ctx context.Context, repoURL string, revision string) ([]string, error) + GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error) } func NewArgoCDService(db db.ArgoDB, submoduleEnabled bool, repoClientset apiclient.Clientset, newFileGlobbingEnabled bool) (Repos, error) { @@ -43,7 +47,7 @@ func NewArgoCDService(db db.ArgoDB, submoduleEnabled bool, repoClientset apiclie }, nil } -func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision string, pattern string) (map[string][]byte, error) { +func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool) (map[string][]byte, error) { repo, err := a.repositoriesDB.GetRepository(ctx, repoURL) if err != nil { return nil, fmt.Errorf("error in GetRepository: %w", err) @@ -55,6 +59,7 @@ func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision s Revision: revision, Path: pattern, NewGitFileGlobbingEnabled: a.newFileGlobbingEnabled, + NoRevisionCache: noRevisionCache, } closer, client, err := a.repoServerClientSet.NewRepoServerClient() if err != nil { @@ -69,7 +74,7 @@ func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision s return fileResponse.GetMap(), nil } -func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revision string) ([]string, error) { +func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error) { repo, err := a.repositoriesDB.GetRepository(ctx, repoURL) if err != nil { return nil, fmt.Errorf("error in GetRepository: %w", err) @@ -79,6 +84,7 @@ func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revi Repo: repo, SubmoduleEnabled: a.submoduleEnabled, Revision: revision, + NoRevisionCache: noRevisionCache, } closer, client, err := a.repoServerClientSet.NewRepoServerClient() diff --git a/applicationset/services/repo_service_test.go b/applicationset/services/repo_service_test.go index 62f8c11c172d0..040fe57f96958 100644 --- a/applicationset/services/repo_service_test.go +++ b/applicationset/services/repo_service_test.go @@ -25,9 +25,10 @@ func TestGetDirectories(t *testing.T) { repoServerClientFuncs []func(*repo_mocks.RepoServerServiceClient) } type args struct { - ctx context.Context - repoURL string - revision string + ctx context.Context + repoURL string + revision string + noRevisionCache bool } tests := []struct { name string @@ -88,11 +89,11 @@ func TestGetDirectories(t *testing.T) { submoduleEnabled: tt.fields.submoduleEnabled, repoServerClientSet: &repo_mocks.Clientset{RepoServerServiceClient: mockRepoClient}, } - got, err := a.GetDirectories(tt.args.ctx, tt.args.repoURL, tt.args.revision) - if !tt.wantErr(t, err, fmt.Sprintf("GetDirectories(%v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision)) { + got, err := a.GetDirectories(tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.noRevisionCache) + if !tt.wantErr(t, err, fmt.Sprintf("GetDirectories(%v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.noRevisionCache)) { return } - assert.Equalf(t, tt.want, got, "GetDirectories(%v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision) + assert.Equalf(t, tt.want, got, "GetDirectories(%v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.noRevisionCache) }) } } @@ -105,10 +106,11 @@ func TestGetFiles(t *testing.T) { repoServerClientFuncs []func(*repo_mocks.RepoServerServiceClient) } type args struct { - ctx context.Context - repoURL string - revision string - pattern string + ctx context.Context + repoURL string + revision string + pattern string + noRevisionCache bool } tests := []struct { name string @@ -175,11 +177,11 @@ func TestGetFiles(t *testing.T) { submoduleEnabled: tt.fields.submoduleEnabled, repoServerClientSet: &repo_mocks.Clientset{RepoServerServiceClient: mockRepoClient}, } - got, err := a.GetFiles(tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern) - if !tt.wantErr(t, err, fmt.Sprintf("GetFiles(%v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern)) { + got, err := a.GetFiles(tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern, tt.args.noRevisionCache) + if !tt.wantErr(t, err, fmt.Sprintf("GetFiles(%v, %v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern, tt.args.noRevisionCache)) { return } - assert.Equalf(t, tt.want, got, "GetFiles(%v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern) + assert.Equalf(t, tt.want, got, "GetFiles(%v, %v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern, tt.args.noRevisionCache) }) } } diff --git a/reposerver/apiclient/repository.pb.go b/reposerver/apiclient/repository.pb.go index 4c05248b87e16..914a967db3dfc 100644 --- a/reposerver/apiclient/repository.pb.go +++ b/reposerver/apiclient/repository.pb.go @@ -1910,6 +1910,7 @@ type GitFilesRequest struct { Revision string `protobuf:"bytes,3,opt,name=revision,proto3" json:"revision,omitempty"` Path string `protobuf:"bytes,4,opt,name=path,proto3" json:"path,omitempty"` NewGitFileGlobbingEnabled bool `protobuf:"varint,5,opt,name=NewGitFileGlobbingEnabled,proto3" json:"NewGitFileGlobbingEnabled,omitempty"` + NoRevisionCache bool `protobuf:"varint,6,opt,name=noRevisionCache,proto3" json:"noRevisionCache,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1983,6 +1984,13 @@ func (m *GitFilesRequest) GetNewGitFileGlobbingEnabled() bool { return false } +func (m *GitFilesRequest) GetNoRevisionCache() bool { + if m != nil { + return m.NoRevisionCache + } + return false +} + type GitFilesResponse struct { // Map consisting of path of the path to its contents in bytes Map map[string][]byte `protobuf:"bytes,1,rep,name=map,proto3" json:"map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` @@ -2035,6 +2043,7 @@ type GitDirectoriesRequest struct { Repo *v1alpha1.Repository `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` SubmoduleEnabled bool `protobuf:"varint,2,opt,name=submoduleEnabled,proto3" json:"submoduleEnabled,omitempty"` Revision string `protobuf:"bytes,3,opt,name=revision,proto3" json:"revision,omitempty"` + NoRevisionCache bool `protobuf:"varint,4,opt,name=noRevisionCache,proto3" json:"noRevisionCache,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2094,6 +2103,13 @@ func (m *GitDirectoriesRequest) GetRevision() string { return "" } +func (m *GitDirectoriesRequest) GetNoRevisionCache() bool { + if m != nil { + return m.NoRevisionCache + } + return false +} + type GitDirectoriesResponse struct { // A set of directory paths Paths []string `protobuf:"bytes,1,rep,name=paths,proto3" json:"paths,omitempty"` @@ -2189,140 +2205,140 @@ func init() { } var fileDescriptor_dd8723cfcc820480 = []byte{ - // 2114 bytes of a gzipped FileDescriptorProto + // 2127 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x5a, 0x5b, 0x6f, 0x1b, 0xc7, - 0x15, 0xe6, 0x92, 0xba, 0x90, 0x47, 0xb2, 0x44, 0x8d, 0x75, 0x59, 0x31, 0x8e, 0xa0, 0x6c, 0x6b, - 0x43, 0xb5, 0x13, 0x12, 0x92, 0x91, 0xb8, 0x70, 0xd2, 0x14, 0x8a, 0x62, 0x4b, 0x8e, 0x2d, 0x5b, - 0x5d, 0xbb, 0x2d, 0xd2, 0xba, 0x2d, 0x86, 0xcb, 0x21, 0xb9, 0xe1, 0x5e, 0xc6, 0xbb, 0xb3, 0x0a, - 0x64, 0xa0, 0x0f, 0x45, 0x8b, 0x02, 0xfd, 0x03, 0x7d, 0xe8, 0xff, 0x28, 0xfa, 0x54, 0xf4, 0xa9, - 0x97, 0xc7, 0xa0, 0x7f, 0xa0, 0x85, 0x1f, 0xfb, 0x2b, 0x8a, 0xb9, 0xec, 0x95, 0x2b, 0xd9, 0x29, - 0x65, 0x19, 0xcd, 0x8b, 0xbd, 0x73, 0xe6, 0xcc, 0x39, 0x67, 0xce, 0x9c, 0xcb, 0x37, 0x43, 0xc1, - 0xb5, 0x80, 0x50, 0x3f, 0x24, 0xc1, 0x31, 0x09, 0x3a, 0xe2, 0xd3, 0x66, 0x7e, 0x70, 0x92, 0xf9, - 0x6c, 0xd3, 0xc0, 0x67, 0x3e, 0x82, 0x94, 0xd2, 0x7a, 0x30, 0xb0, 0xd9, 0x30, 0xea, 0xb6, 0x2d, - 0xdf, 0xed, 0xe0, 0x60, 0xe0, 0xd3, 0xc0, 0xff, 0x42, 0x7c, 0xbc, 0x67, 0xf5, 0x3a, 0xc7, 0x3b, - 0x1d, 0x3a, 0x1a, 0x74, 0x30, 0xb5, 0xc3, 0x0e, 0xa6, 0xd4, 0xb1, 0x2d, 0xcc, 0x6c, 0xdf, 0xeb, - 0x1c, 0x6f, 0x63, 0x87, 0x0e, 0xf1, 0x76, 0x67, 0x40, 0x3c, 0x12, 0x60, 0x46, 0x7a, 0x52, 0x72, - 0xeb, 0xad, 0x81, 0xef, 0x0f, 0x1c, 0xd2, 0x11, 0xa3, 0x6e, 0xd4, 0xef, 0x10, 0x97, 0x32, 0xa5, - 0xd6, 0xf8, 0xcf, 0x3c, 0x2c, 0x1e, 0x62, 0xcf, 0xee, 0x93, 0x90, 0x99, 0xe4, 0x59, 0x44, 0x42, - 0x86, 0x9e, 0xc2, 0x14, 0x37, 0x46, 0xd7, 0x36, 0xb5, 0xad, 0xb9, 0x9d, 0x83, 0x76, 0x6a, 0x4d, - 0x3b, 0xb6, 0x46, 0x7c, 0xfc, 0xc2, 0xea, 0xb5, 0x8f, 0x77, 0xda, 0x74, 0x34, 0x68, 0x73, 0x6b, - 0xda, 0x19, 0x6b, 0xda, 0xb1, 0x35, 0x6d, 0x33, 0xd9, 0x96, 0x29, 0xa4, 0xa2, 0x16, 0xd4, 0x03, - 0x72, 0x6c, 0x87, 0xb6, 0xef, 0xe9, 0xd5, 0x4d, 0x6d, 0xab, 0x61, 0x26, 0x63, 0xa4, 0xc3, 0xac, - 0xe7, 0xef, 0x61, 0x6b, 0x48, 0xf4, 0xda, 0xa6, 0xb6, 0x55, 0x37, 0xe3, 0x21, 0xda, 0x84, 0x39, - 0x4c, 0xe9, 0x03, 0xdc, 0x25, 0xce, 0x7d, 0x72, 0xa2, 0x4f, 0x89, 0x85, 0x59, 0x12, 0x5f, 0x8b, - 0x29, 0x7d, 0x88, 0x5d, 0xa2, 0x4f, 0x8b, 0xd9, 0x78, 0x88, 0xae, 0x40, 0xc3, 0xc3, 0x2e, 0x09, - 0x29, 0xb6, 0x88, 0x5e, 0x17, 0x73, 0x29, 0x01, 0xfd, 0x12, 0x96, 0x32, 0x86, 0x3f, 0xf6, 0xa3, - 0xc0, 0x22, 0x3a, 0x88, 0xad, 0x3f, 0x9a, 0x6c, 0xeb, 0xbb, 0x45, 0xb1, 0xe6, 0xb8, 0x26, 0xf4, - 0x73, 0x98, 0x16, 0x27, 0xaf, 0xcf, 0x6d, 0xd6, 0xce, 0xd5, 0xdb, 0x52, 0x2c, 0xf2, 0x60, 0x96, - 0x3a, 0xd1, 0xc0, 0xf6, 0x42, 0x7d, 0x5e, 0x68, 0x78, 0x32, 0x99, 0x86, 0x3d, 0xdf, 0xeb, 0xdb, - 0x83, 0x43, 0xec, 0xe1, 0x01, 0x71, 0x89, 0xc7, 0x8e, 0x84, 0x70, 0x33, 0x56, 0x82, 0x9e, 0x43, - 0x73, 0x14, 0x85, 0xcc, 0x77, 0xed, 0xe7, 0xe4, 0x11, 0xe5, 0x6b, 0x43, 0xfd, 0x92, 0xf0, 0xe6, - 0xc3, 0xc9, 0x14, 0xdf, 0x2f, 0x48, 0x35, 0xc7, 0xf4, 0xf0, 0x20, 0x19, 0x45, 0x5d, 0xf2, 0x23, - 0x12, 0x88, 0xe8, 0x5a, 0x90, 0x41, 0x92, 0x21, 0xc9, 0x30, 0xb2, 0xd5, 0x28, 0xd4, 0x17, 0x37, - 0x6b, 0x32, 0x8c, 0x12, 0x12, 0xda, 0x82, 0xc5, 0x63, 0x12, 0xd8, 0xfd, 0x93, 0xc7, 0xf6, 0xc0, - 0xc3, 0x2c, 0x0a, 0x88, 0xde, 0x14, 0xa1, 0x58, 0x24, 0x23, 0x17, 0x2e, 0x0d, 0x89, 0xe3, 0x72, - 0x97, 0xef, 0x05, 0xa4, 0x17, 0xea, 0x4b, 0xc2, 0xbf, 0xfb, 0x93, 0x9f, 0xa0, 0x10, 0x67, 0xe6, - 0xa5, 0x73, 0xc3, 0x3c, 0xdf, 0x54, 0x99, 0x22, 0x73, 0x04, 0x49, 0xc3, 0x0a, 0x64, 0x74, 0x0d, - 0x16, 0x58, 0x80, 0xad, 0x91, 0xed, 0x0d, 0x0e, 0x09, 0x1b, 0xfa, 0x3d, 0xfd, 0xb2, 0xf0, 0x44, - 0x81, 0x8a, 0x2c, 0x40, 0xc4, 0xc3, 0x5d, 0x87, 0xf4, 0x64, 0x2c, 0x3e, 0x39, 0xa1, 0x24, 0xd4, - 0x97, 0xc5, 0x2e, 0x6e, 0xb6, 0x33, 0x15, 0xaa, 0x50, 0x20, 0xda, 0x77, 0xc6, 0x56, 0xdd, 0xf1, - 0x58, 0x70, 0x62, 0x96, 0x88, 0x43, 0x23, 0x98, 0xe3, 0xfb, 0x88, 0x43, 0x61, 0x45, 0x84, 0xc2, - 0xbd, 0xc9, 0x7c, 0x74, 0x90, 0x0a, 0x34, 0xb3, 0xd2, 0x51, 0x1b, 0xd0, 0x10, 0x87, 0x87, 0x91, - 0xc3, 0x6c, 0xea, 0x10, 0x69, 0x46, 0xa8, 0xaf, 0x0a, 0x37, 0x95, 0xcc, 0xa0, 0xfb, 0x00, 0x01, - 0xe9, 0xc7, 0x7c, 0x6b, 0x62, 0xe7, 0x37, 0xce, 0xda, 0xb9, 0x99, 0x70, 0xcb, 0x1d, 0x67, 0x96, - 0x73, 0xe5, 0x7c, 0x1b, 0xc4, 0x62, 0x2a, 0xdb, 0x45, 0x5a, 0xeb, 0x22, 0xc4, 0x4a, 0x66, 0x78, - 0x2c, 0x2a, 0xaa, 0x28, 0x5a, 0xeb, 0x32, 0x5a, 0x33, 0xa4, 0xd6, 0x1d, 0x58, 0x3b, 0xc5, 0xd5, - 0xa8, 0x09, 0xb5, 0x11, 0x39, 0x11, 0x25, 0xba, 0x61, 0xf2, 0x4f, 0xb4, 0x0c, 0xd3, 0xc7, 0xd8, - 0x89, 0x88, 0x28, 0xaa, 0x75, 0x53, 0x0e, 0x6e, 0x57, 0xbf, 0xab, 0xb5, 0x7e, 0xab, 0xc1, 0x62, - 0xc1, 0xf0, 0x92, 0xf5, 0x3f, 0xcb, 0xae, 0x3f, 0x87, 0x30, 0xee, 0x3f, 0xc1, 0xc1, 0x80, 0xb0, - 0x8c, 0x21, 0xc6, 0x3f, 0x35, 0xd0, 0x0b, 0x1e, 0xfd, 0xb1, 0xcd, 0x86, 0x77, 0x6d, 0x87, 0x84, - 0xe8, 0x16, 0xcc, 0x06, 0x92, 0xa6, 0x1a, 0xcf, 0x5b, 0x67, 0x1c, 0xc4, 0x41, 0xc5, 0x8c, 0xb9, - 0xd1, 0xc7, 0x50, 0x77, 0x09, 0xc3, 0x3d, 0xcc, 0xb0, 0xb2, 0x7d, 0xb3, 0x6c, 0x25, 0xd7, 0x72, - 0xa8, 0xf8, 0x0e, 0x2a, 0x66, 0xb2, 0x06, 0xbd, 0x0f, 0xd3, 0xd6, 0x30, 0xf2, 0x46, 0xa2, 0xe5, - 0xcc, 0xed, 0xbc, 0x7d, 0xda, 0xe2, 0x3d, 0xce, 0x74, 0x50, 0x31, 0x25, 0xf7, 0x27, 0x33, 0x30, - 0x45, 0x71, 0xc0, 0x8c, 0xbb, 0xb0, 0x5c, 0xa6, 0x82, 0xf7, 0x39, 0x6b, 0x48, 0xac, 0x51, 0x18, - 0xb9, 0xca, 0xcd, 0xc9, 0x18, 0x21, 0x98, 0x0a, 0xed, 0xe7, 0xd2, 0xd5, 0x35, 0x53, 0x7c, 0x1b, - 0xdf, 0x81, 0xa5, 0x31, 0x6d, 0xfc, 0x50, 0xa5, 0x6d, 0x5c, 0xc2, 0xbc, 0x52, 0x6d, 0x44, 0xb0, - 0xf2, 0x44, 0xf8, 0x22, 0x29, 0xf6, 0x17, 0xd1, 0xb9, 0x8d, 0x03, 0x58, 0x2d, 0xaa, 0x0d, 0xa9, - 0xef, 0x85, 0x84, 0x87, 0xbe, 0xa8, 0x8e, 0x36, 0xe9, 0xa5, 0xb3, 0xc2, 0x8a, 0xba, 0x59, 0x32, - 0x63, 0xfc, 0xaa, 0x0a, 0xab, 0x26, 0x09, 0x7d, 0xe7, 0x98, 0xc4, 0xa5, 0xeb, 0x62, 0xc0, 0xc7, - 0x4f, 0xa1, 0x86, 0x29, 0x55, 0x61, 0x72, 0xef, 0xdc, 0xda, 0xbb, 0xc9, 0xa5, 0xa2, 0x77, 0x61, - 0x09, 0xbb, 0x5d, 0x7b, 0x10, 0xf9, 0x51, 0x18, 0x6f, 0x4b, 0x04, 0x55, 0xc3, 0x1c, 0x9f, 0x30, - 0x2c, 0x58, 0x1b, 0x73, 0x81, 0x72, 0x67, 0x16, 0x22, 0x69, 0x05, 0x88, 0x54, 0xaa, 0xa4, 0x7a, - 0x9a, 0x92, 0xbf, 0x69, 0xd0, 0x4c, 0x53, 0x47, 0x89, 0xbf, 0x02, 0x0d, 0x57, 0xd1, 0x42, 0x5d, - 0x13, 0xf5, 0x29, 0x25, 0xe4, 0xd1, 0x52, 0xb5, 0x88, 0x96, 0x56, 0x61, 0x46, 0x82, 0x59, 0xb5, - 0x31, 0x35, 0xca, 0x99, 0x3c, 0x55, 0x30, 0x79, 0x03, 0x20, 0x4c, 0xea, 0x97, 0x3e, 0x23, 0x66, - 0x33, 0x14, 0x64, 0xc0, 0xbc, 0xec, 0xad, 0x26, 0x09, 0x23, 0x87, 0xe9, 0xb3, 0x82, 0x23, 0x47, - 0x33, 0x7c, 0x58, 0x7c, 0x60, 0xf3, 0x3d, 0xf4, 0xc3, 0x8b, 0x09, 0xf6, 0x0f, 0x60, 0x8a, 0x2b, - 0xe3, 0x1b, 0xeb, 0x06, 0xd8, 0xb3, 0x86, 0x24, 0xf6, 0x55, 0x32, 0xe6, 0x69, 0xcc, 0xf0, 0x20, - 0xd4, 0xab, 0x82, 0x2e, 0xbe, 0x8d, 0x3f, 0x55, 0xa5, 0xa5, 0xbb, 0x94, 0x86, 0x6f, 0x1e, 0x50, - 0x97, 0xb7, 0xf8, 0xda, 0x78, 0x8b, 0x2f, 0x98, 0xfc, 0x75, 0x5a, 0xfc, 0x39, 0xb5, 0x29, 0x23, - 0x82, 0xd9, 0x5d, 0x4a, 0xb9, 0x21, 0x68, 0x1b, 0xa6, 0x30, 0xa5, 0xd2, 0xe1, 0x85, 0x8a, 0xac, - 0x58, 0xf8, 0xff, 0xca, 0x24, 0xc1, 0xda, 0xba, 0x05, 0x8d, 0x84, 0xf4, 0x32, 0xb5, 0x8d, 0xac, - 0xda, 0x4d, 0x00, 0x89, 0x61, 0xef, 0x79, 0x7d, 0x9f, 0x1f, 0x29, 0x0f, 0x76, 0xb5, 0x54, 0x7c, - 0x1b, 0xb7, 0x63, 0x0e, 0x61, 0xdb, 0xbb, 0x30, 0x6d, 0x33, 0xe2, 0xc6, 0xc6, 0xad, 0x66, 0x8d, - 0x4b, 0x05, 0x99, 0x92, 0xc9, 0xf8, 0x7b, 0x1d, 0xd6, 0xf9, 0x89, 0x3d, 0x16, 0x69, 0xb2, 0x4b, - 0xe9, 0xa7, 0x84, 0x61, 0xdb, 0x09, 0x7f, 0x10, 0x91, 0xe0, 0xe4, 0x35, 0x07, 0xc6, 0x00, 0x66, - 0x64, 0x96, 0xa9, 0x7a, 0x77, 0xee, 0xd7, 0x19, 0x25, 0x3e, 0xbd, 0xc3, 0xd4, 0x5e, 0xcf, 0x1d, - 0xa6, 0xec, 0x4e, 0x31, 0x75, 0x41, 0x77, 0x8a, 0xd3, 0xaf, 0x95, 0x99, 0xcb, 0xea, 0x4c, 0xfe, - 0xb2, 0x5a, 0x02, 0xd5, 0x67, 0x5f, 0x15, 0xaa, 0xd7, 0x4b, 0xa1, 0xba, 0x5b, 0x9a, 0xc7, 0x0d, - 0xe1, 0xee, 0xef, 0x65, 0x23, 0xf0, 0xd4, 0x58, 0x9b, 0x04, 0xb4, 0xc3, 0x6b, 0x05, 0xed, 0x3f, - 0xcc, 0x81, 0x70, 0x79, 0x0d, 0x7e, 0xff, 0xd5, 0xf6, 0x74, 0x06, 0x1c, 0xff, 0xc6, 0x81, 0xe7, - 0xdf, 0x08, 0xcc, 0x44, 0xfd, 0xd4, 0x07, 0x49, 0x43, 0xe7, 0x7d, 0x88, 0xb7, 0x56, 0x55, 0xb4, - 0xf8, 0x37, 0xba, 0x01, 0x53, 0xdc, 0xc9, 0x0a, 0xd4, 0xae, 0x65, 0xfd, 0xc9, 0x4f, 0x62, 0x97, - 0xd2, 0xc7, 0x94, 0x58, 0xa6, 0x60, 0x42, 0xb7, 0xa1, 0x91, 0x04, 0xbe, 0xca, 0xac, 0x2b, 0xd9, - 0x15, 0x49, 0x9e, 0xc4, 0xcb, 0x52, 0x76, 0xbe, 0xb6, 0x67, 0x07, 0xc4, 0x12, 0x90, 0x6f, 0x7a, - 0x7c, 0xed, 0xa7, 0xf1, 0x64, 0xb2, 0x36, 0x61, 0x47, 0xdb, 0x30, 0x23, 0xdf, 0x0d, 0x44, 0x06, - 0xcd, 0xed, 0xac, 0x8f, 0x17, 0xd3, 0x78, 0x95, 0x62, 0x34, 0xfe, 0xaa, 0xc1, 0x3b, 0x69, 0x40, - 0xc4, 0xd9, 0x14, 0xa3, 0xee, 0x37, 0xdf, 0x71, 0xaf, 0xc1, 0x82, 0x80, 0xf9, 0xe9, 0xf3, 0x81, - 0x7c, 0xc9, 0x2a, 0x50, 0x8d, 0x3f, 0x6a, 0x70, 0x75, 0x7c, 0x1f, 0x7b, 0x43, 0x1c, 0xb0, 0xe4, - 0x78, 0x2f, 0x62, 0x2f, 0x71, 0xc3, 0xab, 0xa6, 0x0d, 0x2f, 0xb7, 0xbf, 0x5a, 0x7e, 0x7f, 0xc6, - 0x5f, 0xaa, 0x30, 0x97, 0x09, 0xa0, 0xb2, 0x86, 0xc9, 0x01, 0x9f, 0x88, 0x5b, 0x71, 0xb1, 0x13, - 0x4d, 0xa1, 0x61, 0x66, 0x28, 0x68, 0x04, 0x40, 0x71, 0x80, 0x5d, 0xc2, 0x48, 0xc0, 0x2b, 0x39, - 0xcf, 0xf8, 0xfb, 0x93, 0x57, 0x97, 0xa3, 0x58, 0xa6, 0x99, 0x11, 0xcf, 0x11, 0xab, 0x50, 0x1d, - 0xaa, 0xfa, 0xad, 0x46, 0xe8, 0x4b, 0x58, 0xe8, 0xdb, 0x0e, 0x39, 0x4a, 0x0d, 0x99, 0x11, 0x86, - 0x3c, 0x9a, 0xdc, 0x90, 0xbb, 0x59, 0xb9, 0x66, 0x41, 0x8d, 0x71, 0x1d, 0x9a, 0xc5, 0x7c, 0xe2, - 0x46, 0xda, 0x2e, 0x1e, 0x24, 0xde, 0x52, 0x23, 0x03, 0x41, 0xb3, 0x98, 0x3f, 0xc6, 0xbf, 0xaa, - 0xb0, 0x92, 0x88, 0xdb, 0xf5, 0x3c, 0x3f, 0xf2, 0x2c, 0xf1, 0x14, 0x57, 0x7a, 0x16, 0xcb, 0x30, - 0xcd, 0x6c, 0xe6, 0x24, 0xc0, 0x47, 0x0c, 0x78, 0xef, 0x62, 0xbe, 0xef, 0x30, 0x9b, 0xaa, 0x03, - 0x8e, 0x87, 0xf2, 0xec, 0x9f, 0x45, 0x76, 0x40, 0x7a, 0xa2, 0x12, 0xd4, 0xcd, 0x64, 0xcc, 0xe7, - 0x38, 0xaa, 0x11, 0x30, 0x5e, 0x3a, 0x33, 0x19, 0x8b, 0xb8, 0xf7, 0x1d, 0x87, 0x58, 0xdc, 0x1d, - 0x19, 0xa0, 0x5f, 0xa0, 0x8a, 0x0b, 0x04, 0x0b, 0x6c, 0x6f, 0xa0, 0x60, 0xbe, 0x1a, 0x71, 0x3b, - 0x71, 0x10, 0xe0, 0x13, 0xbd, 0x2e, 0x1c, 0x20, 0x07, 0xe8, 0x23, 0xa8, 0xb9, 0x98, 0xaa, 0x46, - 0x77, 0x3d, 0x57, 0x1d, 0xca, 0x3c, 0xd0, 0x3e, 0xc4, 0x54, 0x76, 0x02, 0xbe, 0xac, 0xf5, 0x01, - 0xd4, 0x63, 0xc2, 0xd7, 0x82, 0x84, 0x5f, 0xc0, 0xa5, 0x5c, 0xf1, 0x41, 0x9f, 0xc3, 0x6a, 0x1a, - 0x51, 0x59, 0x85, 0x0a, 0x04, 0xbe, 0xf3, 0x52, 0xcb, 0xcc, 0x53, 0x04, 0x18, 0xcf, 0x60, 0x89, - 0x87, 0x8c, 0x48, 0xfc, 0x0b, 0xba, 0xda, 0x7c, 0x08, 0x8d, 0x44, 0x65, 0x69, 0xcc, 0xb4, 0xa0, - 0x7e, 0x1c, 0x3f, 0x91, 0xca, 0xbb, 0x4d, 0x32, 0x36, 0x76, 0x01, 0x65, 0xed, 0x55, 0x1d, 0xe8, - 0x46, 0x1e, 0x14, 0xaf, 0x14, 0xdb, 0x8d, 0x60, 0x8f, 0x31, 0xf1, 0xef, 0xaa, 0xb0, 0xb8, 0x6f, - 0x8b, 0x57, 0x8e, 0x0b, 0x2a, 0x72, 0xd7, 0xa1, 0x19, 0x46, 0x5d, 0xd7, 0xef, 0x45, 0x0e, 0x51, - 0xa0, 0x40, 0x75, 0xfa, 0x31, 0xfa, 0x59, 0xc5, 0x8f, 0x3b, 0x8b, 0x62, 0x36, 0x54, 0x37, 0x5c, - 0xf1, 0x8d, 0x3e, 0x82, 0xf5, 0x87, 0xe4, 0x4b, 0xb5, 0x9f, 0x7d, 0xc7, 0xef, 0x76, 0x6d, 0x6f, - 0x10, 0x2b, 0x99, 0x16, 0x4a, 0x4e, 0x67, 0x30, 0x7e, 0xad, 0x41, 0x33, 0xf5, 0x85, 0xf2, 0xe6, - 0x2d, 0x19, 0xf5, 0xd2, 0x97, 0x57, 0xb3, 0xbe, 0x2c, 0xb2, 0xfe, 0xef, 0x01, 0x3f, 0x9f, 0x0d, - 0xf8, 0x3f, 0x6b, 0xb0, 0xb2, 0x6f, 0xb3, 0xb8, 0xd4, 0xd8, 0xff, 0x67, 0xe7, 0x62, 0xb4, 0x61, - 0xb5, 0x68, 0xbe, 0x72, 0xe5, 0x32, 0x4c, 0xf3, 0x53, 0x8a, 0xef, 0xee, 0x72, 0xb0, 0xf3, 0x55, - 0x03, 0x96, 0xd2, 0xe6, 0xcb, 0xff, 0xb5, 0x2d, 0x82, 0x1e, 0x41, 0x73, 0x5f, 0xfd, 0x76, 0x16, - 0xbf, 0x99, 0xa0, 0xb3, 0x1e, 0x21, 0x5b, 0x57, 0xca, 0x27, 0xa5, 0x6a, 0xa3, 0x82, 0x2c, 0x58, - 0x2f, 0x0a, 0x4c, 0xdf, 0x3b, 0xbf, 0x7d, 0x86, 0xe4, 0x84, 0xeb, 0x65, 0x2a, 0xb6, 0x34, 0xf4, - 0x39, 0x2c, 0xe4, 0x5f, 0xe5, 0x50, 0xae, 0x1a, 0x95, 0x3e, 0x14, 0xb6, 0x8c, 0xb3, 0x58, 0x12, - 0xfb, 0x9f, 0x72, 0xe8, 0x9b, 0x7b, 0xa2, 0x42, 0x46, 0x1e, 0x98, 0x97, 0x3d, 0xe1, 0xb5, 0xbe, - 0x75, 0x26, 0x4f, 0x22, 0xfd, 0x43, 0xa8, 0xc7, 0x4f, 0x3a, 0x79, 0x37, 0x17, 0x1e, 0x7a, 0x5a, - 0xcd, 0xbc, 0xbc, 0x7e, 0x68, 0x54, 0xd0, 0xc7, 0x72, 0x31, 0xbf, 0xf2, 0x8f, 0x2f, 0xce, 0x3c, - 0x64, 0xb4, 0x2e, 0x97, 0x3c, 0x1e, 0x18, 0x15, 0xf4, 0x7d, 0x98, 0xe3, 0x5f, 0x47, 0xea, 0x57, - 0xab, 0xd5, 0xb6, 0xfc, 0x91, 0xb4, 0x1d, 0xff, 0x48, 0xda, 0xbe, 0xe3, 0x52, 0x76, 0xd2, 0x2a, - 0xb9, 0xdd, 0x2b, 0x01, 0x4f, 0xe1, 0xd2, 0x3e, 0x61, 0x29, 0x18, 0x47, 0x57, 0x5f, 0xe9, 0xca, - 0xd2, 0x32, 0x8a, 0x6c, 0xe3, 0x78, 0xde, 0xa8, 0xa0, 0xdf, 0x6b, 0x70, 0x79, 0x9f, 0xb0, 0x22, - 0xbc, 0x45, 0xef, 0x95, 0x2b, 0x39, 0x05, 0x06, 0xb7, 0x1e, 0x4e, 0x9a, 0xaf, 0x79, 0xb1, 0x46, - 0x05, 0xfd, 0x41, 0x83, 0xb5, 0x8c, 0x61, 0x59, 0xbc, 0x8a, 0xb6, 0xcf, 0x36, 0xae, 0x04, 0xdb, - 0xb6, 0x3e, 0x9b, 0xf0, 0xc7, 0xc8, 0x8c, 0x48, 0xa3, 0x82, 0x8e, 0xc4, 0x99, 0xa4, 0xed, 0x09, - 0xbd, 0x5d, 0xda, 0x87, 0x12, 0xed, 0x1b, 0xa7, 0x4d, 0x27, 0xe7, 0xf0, 0x19, 0xcc, 0xed, 0x13, - 0x16, 0x57, 0xdd, 0x7c, 0xa4, 0x15, 0x5a, 0x58, 0x3e, 0x55, 0x8b, 0x85, 0x5a, 0x44, 0xcc, 0x92, - 0x94, 0x95, 0xa9, 0x53, 0xf9, 0x5c, 0x2d, 0x2d, 0xc1, 0xf9, 0x88, 0x29, 0x2f, 0x73, 0x46, 0xe5, - 0x93, 0xdd, 0x7f, 0xbc, 0xd8, 0xd0, 0xbe, 0x7a, 0xb1, 0xa1, 0xfd, 0xfb, 0xc5, 0x86, 0xf6, 0x93, - 0x9b, 0x2f, 0xf9, 0x0b, 0x82, 0xcc, 0x1f, 0x25, 0x60, 0x6a, 0x5b, 0x8e, 0x4d, 0x3c, 0xd6, 0x9d, - 0x11, 0xc1, 0x7f, 0xf3, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x86, 0xe4, 0x0d, 0xb3, 0x20, - 0x00, 0x00, + 0xf5, 0xe7, 0x92, 0x94, 0x44, 0x1e, 0xd9, 0x12, 0x35, 0xd6, 0x65, 0xc5, 0x38, 0x82, 0xb2, 0xff, + 0xbf, 0x0d, 0xd5, 0x4e, 0x48, 0x48, 0x46, 0xe2, 0xc2, 0x49, 0x53, 0x28, 0x8a, 0x2d, 0x39, 0xb6, + 0x6c, 0x75, 0xed, 0xb6, 0x48, 0xeb, 0xb6, 0x18, 0x2e, 0x87, 0xe4, 0x86, 0x7b, 0x19, 0xef, 0xce, + 0x2a, 0x90, 0x81, 0x3e, 0x14, 0x2d, 0xfa, 0x11, 0xfa, 0xd0, 0xaf, 0x51, 0x14, 0x7d, 0xec, 0x53, + 0x2f, 0x8f, 0x41, 0xbf, 0x40, 0x0b, 0xbf, 0x14, 0xe8, 0xa7, 0x28, 0xe6, 0xb2, 0x57, 0xae, 0x64, + 0xa7, 0x94, 0x15, 0xb4, 0x2f, 0xf6, 0xce, 0x99, 0x33, 0xe7, 0x9c, 0x39, 0x73, 0x2e, 0xbf, 0x19, + 0x0a, 0xae, 0x07, 0x84, 0xfa, 0x21, 0x09, 0x8e, 0x49, 0xd0, 0x15, 0x9f, 0x36, 0xf3, 0x83, 0x93, + 0xcc, 0x67, 0x87, 0x06, 0x3e, 0xf3, 0x11, 0xa4, 0x94, 0xf6, 0xc3, 0xa1, 0xcd, 0x46, 0x51, 0xaf, + 0x63, 0xf9, 0x6e, 0x17, 0x07, 0x43, 0x9f, 0x06, 0xfe, 0x17, 0xe2, 0xe3, 0x3d, 0xab, 0xdf, 0x3d, + 0xde, 0xe9, 0xd2, 0xf1, 0xb0, 0x8b, 0xa9, 0x1d, 0x76, 0x31, 0xa5, 0x8e, 0x6d, 0x61, 0x66, 0xfb, + 0x5e, 0xf7, 0x78, 0x1b, 0x3b, 0x74, 0x84, 0xb7, 0xbb, 0x43, 0xe2, 0x91, 0x00, 0x33, 0xd2, 0x97, + 0x92, 0xdb, 0x6f, 0x0d, 0x7d, 0x7f, 0xe8, 0x90, 0xae, 0x18, 0xf5, 0xa2, 0x41, 0x97, 0xb8, 0x94, + 0x29, 0xb5, 0xc6, 0xbf, 0x2e, 0xc1, 0xe2, 0x21, 0xf6, 0xec, 0x01, 0x09, 0x99, 0x49, 0x9e, 0x47, + 0x24, 0x64, 0xe8, 0x19, 0xd4, 0xb9, 0x31, 0xba, 0xb6, 0xa9, 0x6d, 0xcd, 0xef, 0x1c, 0x74, 0x52, + 0x6b, 0x3a, 0xb1, 0x35, 0xe2, 0xe3, 0x67, 0x56, 0xbf, 0x73, 0xbc, 0xd3, 0xa1, 0xe3, 0x61, 0x87, + 0x5b, 0xd3, 0xc9, 0x58, 0xd3, 0x89, 0xad, 0xe9, 0x98, 0xc9, 0xb6, 0x4c, 0x21, 0x15, 0xb5, 0xa1, + 0x11, 0x90, 0x63, 0x3b, 0xb4, 0x7d, 0x4f, 0xaf, 0x6e, 0x6a, 0x5b, 0x4d, 0x33, 0x19, 0x23, 0x1d, + 0xe6, 0x3c, 0x7f, 0x0f, 0x5b, 0x23, 0xa2, 0xd7, 0x36, 0xb5, 0xad, 0x86, 0x19, 0x0f, 0xd1, 0x26, + 0xcc, 0x63, 0x4a, 0x1f, 0xe2, 0x1e, 0x71, 0x1e, 0x90, 0x13, 0xbd, 0x2e, 0x16, 0x66, 0x49, 0x7c, + 0x2d, 0xa6, 0xf4, 0x11, 0x76, 0x89, 0x3e, 0x23, 0x66, 0xe3, 0x21, 0xba, 0x0a, 0x4d, 0x0f, 0xbb, + 0x24, 0xa4, 0xd8, 0x22, 0x7a, 0x43, 0xcc, 0xa5, 0x04, 0xf4, 0x73, 0x58, 0xca, 0x18, 0xfe, 0xc4, + 0x8f, 0x02, 0x8b, 0xe8, 0x20, 0xb6, 0xfe, 0x78, 0xba, 0xad, 0xef, 0x16, 0xc5, 0x9a, 0x93, 0x9a, + 0xd0, 0x4f, 0x61, 0x46, 0x9c, 0xbc, 0x3e, 0xbf, 0x59, 0x3b, 0x57, 0x6f, 0x4b, 0xb1, 0xc8, 0x83, + 0x39, 0xea, 0x44, 0x43, 0xdb, 0x0b, 0xf5, 0x4b, 0x42, 0xc3, 0xd3, 0xe9, 0x34, 0xec, 0xf9, 0xde, + 0xc0, 0x1e, 0x1e, 0x62, 0x0f, 0x0f, 0x89, 0x4b, 0x3c, 0x76, 0x24, 0x84, 0x9b, 0xb1, 0x12, 0xf4, + 0x02, 0x5a, 0xe3, 0x28, 0x64, 0xbe, 0x6b, 0xbf, 0x20, 0x8f, 0x29, 0x5f, 0x1b, 0xea, 0x97, 0x85, + 0x37, 0x1f, 0x4d, 0xa7, 0xf8, 0x41, 0x41, 0xaa, 0x39, 0xa1, 0x87, 0x07, 0xc9, 0x38, 0xea, 0x91, + 0x1f, 0x90, 0x40, 0x44, 0xd7, 0x82, 0x0c, 0x92, 0x0c, 0x49, 0x86, 0x91, 0xad, 0x46, 0xa1, 0xbe, + 0xb8, 0x59, 0x93, 0x61, 0x94, 0x90, 0xd0, 0x16, 0x2c, 0x1e, 0x93, 0xc0, 0x1e, 0x9c, 0x3c, 0xb1, + 0x87, 0x1e, 0x66, 0x51, 0x40, 0xf4, 0x96, 0x08, 0xc5, 0x22, 0x19, 0xb9, 0x70, 0x79, 0x44, 0x1c, + 0x97, 0xbb, 0x7c, 0x2f, 0x20, 0xfd, 0x50, 0x5f, 0x12, 0xfe, 0xdd, 0x9f, 0xfe, 0x04, 0x85, 0x38, + 0x33, 0x2f, 0x9d, 0x1b, 0xe6, 0xf9, 0xa6, 0xca, 0x14, 0x99, 0x23, 0x48, 0x1a, 0x56, 0x20, 0xa3, + 0xeb, 0xb0, 0xc0, 0x02, 0x6c, 0x8d, 0x6d, 0x6f, 0x78, 0x48, 0xd8, 0xc8, 0xef, 0xeb, 0x57, 0x84, + 0x27, 0x0a, 0x54, 0x64, 0x01, 0x22, 0x1e, 0xee, 0x39, 0xa4, 0x2f, 0x63, 0xf1, 0xe9, 0x09, 0x25, + 0xa1, 0xbe, 0x2c, 0x76, 0x71, 0xab, 0x93, 0xa9, 0x50, 0x85, 0x02, 0xd1, 0xb9, 0x3b, 0xb1, 0xea, + 0xae, 0xc7, 0x82, 0x13, 0xb3, 0x44, 0x1c, 0x1a, 0xc3, 0x3c, 0xdf, 0x47, 0x1c, 0x0a, 0x2b, 0x22, + 0x14, 0xee, 0x4f, 0xe7, 0xa3, 0x83, 0x54, 0xa0, 0x99, 0x95, 0x8e, 0x3a, 0x80, 0x46, 0x38, 0x3c, + 0x8c, 0x1c, 0x66, 0x53, 0x87, 0x48, 0x33, 0x42, 0x7d, 0x55, 0xb8, 0xa9, 0x64, 0x06, 0x3d, 0x00, + 0x08, 0xc8, 0x20, 0xe6, 0x5b, 0x13, 0x3b, 0xbf, 0x79, 0xd6, 0xce, 0xcd, 0x84, 0x5b, 0xee, 0x38, + 0xb3, 0x9c, 0x2b, 0xe7, 0xdb, 0x20, 0x16, 0x53, 0xd9, 0x2e, 0xd2, 0x5a, 0x17, 0x21, 0x56, 0x32, + 0xc3, 0x63, 0x51, 0x51, 0x45, 0xd1, 0x5a, 0x97, 0xd1, 0x9a, 0x21, 0xb5, 0xef, 0xc2, 0xda, 0x29, + 0xae, 0x46, 0x2d, 0xa8, 0x8d, 0xc9, 0x89, 0x28, 0xd1, 0x4d, 0x93, 0x7f, 0xa2, 0x65, 0x98, 0x39, + 0xc6, 0x4e, 0x44, 0x44, 0x51, 0x6d, 0x98, 0x72, 0x70, 0xa7, 0xfa, 0x6d, 0xad, 0xfd, 0x6b, 0x0d, + 0x16, 0x0b, 0x86, 0x97, 0xac, 0xff, 0x49, 0x76, 0xfd, 0x39, 0x84, 0xf1, 0xe0, 0x29, 0x0e, 0x86, + 0x84, 0x65, 0x0c, 0x31, 0xfe, 0xa6, 0x81, 0x5e, 0xf0, 0xe8, 0x0f, 0x6d, 0x36, 0xba, 0x67, 0x3b, + 0x24, 0x44, 0xb7, 0x61, 0x2e, 0x90, 0x34, 0xd5, 0x78, 0xde, 0x3a, 0xe3, 0x20, 0x0e, 0x2a, 0x66, + 0xcc, 0x8d, 0x3e, 0x86, 0x86, 0x4b, 0x18, 0xee, 0x63, 0x86, 0x95, 0xed, 0x9b, 0x65, 0x2b, 0xb9, + 0x96, 0x43, 0xc5, 0x77, 0x50, 0x31, 0x93, 0x35, 0xe8, 0x7d, 0x98, 0xb1, 0x46, 0x91, 0x37, 0x16, + 0x2d, 0x67, 0x7e, 0xe7, 0xed, 0xd3, 0x16, 0xef, 0x71, 0xa6, 0x83, 0x8a, 0x29, 0xb9, 0x3f, 0x99, + 0x85, 0x3a, 0xc5, 0x01, 0x33, 0xee, 0xc1, 0x72, 0x99, 0x0a, 0xde, 0xe7, 0xac, 0x11, 0xb1, 0xc6, + 0x61, 0xe4, 0x2a, 0x37, 0x27, 0x63, 0x84, 0xa0, 0x1e, 0xda, 0x2f, 0xa4, 0xab, 0x6b, 0xa6, 0xf8, + 0x36, 0xbe, 0x05, 0x4b, 0x13, 0xda, 0xf8, 0xa1, 0x4a, 0xdb, 0xb8, 0x84, 0x4b, 0x4a, 0xb5, 0x11, + 0xc1, 0xca, 0x53, 0xe1, 0x8b, 0xa4, 0xd8, 0x5f, 0x44, 0xe7, 0x36, 0x0e, 0x60, 0xb5, 0xa8, 0x36, + 0xa4, 0xbe, 0x17, 0x12, 0x1e, 0xfa, 0xa2, 0x3a, 0xda, 0xa4, 0x9f, 0xce, 0x0a, 0x2b, 0x1a, 0x66, + 0xc9, 0x8c, 0xf1, 0x8b, 0x2a, 0xac, 0x9a, 0x24, 0xf4, 0x9d, 0x63, 0x12, 0x97, 0xae, 0x8b, 0x01, + 0x1f, 0x3f, 0x86, 0x1a, 0xa6, 0x54, 0x85, 0xc9, 0xfd, 0x73, 0x6b, 0xef, 0x26, 0x97, 0x8a, 0xde, + 0x85, 0x25, 0xec, 0xf6, 0xec, 0x61, 0xe4, 0x47, 0x61, 0xbc, 0x2d, 0x11, 0x54, 0x4d, 0x73, 0x72, + 0xc2, 0xb0, 0x60, 0x6d, 0xc2, 0x05, 0xca, 0x9d, 0x59, 0x88, 0xa4, 0x15, 0x20, 0x52, 0xa9, 0x92, + 0xea, 0x69, 0x4a, 0xfe, 0xac, 0x41, 0x2b, 0x4d, 0x1d, 0x25, 0xfe, 0x2a, 0x34, 0x5d, 0x45, 0x0b, + 0x75, 0x4d, 0xd4, 0xa7, 0x94, 0x90, 0x47, 0x4b, 0xd5, 0x22, 0x5a, 0x5a, 0x85, 0x59, 0x09, 0x66, + 0xd5, 0xc6, 0xd4, 0x28, 0x67, 0x72, 0xbd, 0x60, 0xf2, 0x06, 0x40, 0x98, 0xd4, 0x2f, 0x7d, 0x56, + 0xcc, 0x66, 0x28, 0xc8, 0x80, 0x4b, 0xb2, 0xb7, 0x9a, 0x24, 0x8c, 0x1c, 0xa6, 0xcf, 0x09, 0x8e, + 0x1c, 0xcd, 0xf0, 0x61, 0xf1, 0xa1, 0xcd, 0xf7, 0x30, 0x08, 0x2f, 0x26, 0xd8, 0x3f, 0x80, 0x3a, + 0x57, 0xc6, 0x37, 0xd6, 0x0b, 0xb0, 0x67, 0x8d, 0x48, 0xec, 0xab, 0x64, 0xcc, 0xd3, 0x98, 0xe1, + 0x61, 0xa8, 0x57, 0x05, 0x5d, 0x7c, 0x1b, 0x7f, 0xa8, 0x4a, 0x4b, 0x77, 0x29, 0x0d, 0xbf, 0x79, + 0x40, 0x5d, 0xde, 0xe2, 0x6b, 0x93, 0x2d, 0xbe, 0x60, 0xf2, 0xd7, 0x69, 0xf1, 0xe7, 0xd4, 0xa6, + 0x8c, 0x08, 0xe6, 0x76, 0x29, 0xe5, 0x86, 0xa0, 0x6d, 0xa8, 0x63, 0x4a, 0xa5, 0xc3, 0x0b, 0x15, + 0x59, 0xb1, 0xf0, 0xff, 0x95, 0x49, 0x82, 0xb5, 0x7d, 0x1b, 0x9a, 0x09, 0xe9, 0x55, 0x6a, 0x9b, + 0x59, 0xb5, 0x9b, 0x00, 0x12, 0xc3, 0xde, 0xf7, 0x06, 0x3e, 0x3f, 0x52, 0x1e, 0xec, 0x6a, 0xa9, + 0xf8, 0x36, 0xee, 0xc4, 0x1c, 0xc2, 0xb6, 0x77, 0x61, 0xc6, 0x66, 0xc4, 0x8d, 0x8d, 0x5b, 0xcd, + 0x1a, 0x97, 0x0a, 0x32, 0x25, 0x93, 0xf1, 0x97, 0x06, 0xac, 0xf3, 0x13, 0x7b, 0x22, 0xd2, 0x64, + 0x97, 0xd2, 0x4f, 0x09, 0xc3, 0xb6, 0x13, 0x7e, 0x2f, 0x22, 0xc1, 0xc9, 0x1b, 0x0e, 0x8c, 0x21, + 0xcc, 0xca, 0x2c, 0x53, 0xf5, 0xee, 0xdc, 0xaf, 0x33, 0x4a, 0x7c, 0x7a, 0x87, 0xa9, 0xbd, 0x99, + 0x3b, 0x4c, 0xd9, 0x9d, 0xa2, 0x7e, 0x41, 0x77, 0x8a, 0xd3, 0xaf, 0x95, 0x99, 0xcb, 0xea, 0x6c, + 0xfe, 0xb2, 0x5a, 0x02, 0xd5, 0xe7, 0x5e, 0x17, 0xaa, 0x37, 0x4a, 0xa1, 0xba, 0x5b, 0x9a, 0xc7, + 0x4d, 0xe1, 0xee, 0xef, 0x64, 0x23, 0xf0, 0xd4, 0x58, 0x9b, 0x06, 0xb4, 0xc3, 0x1b, 0x05, 0xed, + 0xdf, 0xcf, 0x81, 0x70, 0x79, 0x0d, 0x7e, 0xff, 0xf5, 0xf6, 0x74, 0x06, 0x1c, 0xff, 0x9f, 0x03, + 0xcf, 0xbf, 0x12, 0x98, 0x89, 0xfa, 0xa9, 0x0f, 0x92, 0x86, 0xce, 0xfb, 0x10, 0x6f, 0xad, 0xaa, + 0x68, 0xf1, 0x6f, 0x74, 0x13, 0xea, 0xdc, 0xc9, 0x0a, 0xd4, 0xae, 0x65, 0xfd, 0xc9, 0x4f, 0x62, + 0x97, 0xd2, 0x27, 0x94, 0x58, 0xa6, 0x60, 0x42, 0x77, 0xa0, 0x99, 0x04, 0xbe, 0xca, 0xac, 0xab, + 0xd9, 0x15, 0x49, 0x9e, 0xc4, 0xcb, 0x52, 0x76, 0xbe, 0xb6, 0x6f, 0x07, 0xc4, 0x12, 0x90, 0x6f, + 0x66, 0x72, 0xed, 0xa7, 0xf1, 0x64, 0xb2, 0x36, 0x61, 0x47, 0xdb, 0x30, 0x2b, 0xdf, 0x0d, 0x44, + 0x06, 0xcd, 0xef, 0xac, 0x4f, 0x16, 0xd3, 0x78, 0x95, 0x62, 0x34, 0xfe, 0xa4, 0xc1, 0x3b, 0x69, + 0x40, 0xc4, 0xd9, 0x14, 0xa3, 0xee, 0x6f, 0xbe, 0xe3, 0x5e, 0x87, 0x05, 0x01, 0xf3, 0xd3, 0xe7, + 0x03, 0xf9, 0x92, 0x55, 0xa0, 0x1a, 0xbf, 0xd7, 0xe0, 0xda, 0xe4, 0x3e, 0xf6, 0x46, 0x38, 0x60, + 0xc9, 0xf1, 0x5e, 0xc4, 0x5e, 0xe2, 0x86, 0x57, 0x4d, 0x1b, 0x5e, 0x6e, 0x7f, 0xb5, 0xfc, 0xfe, + 0x8c, 0x3f, 0x56, 0x61, 0x3e, 0x13, 0x40, 0x65, 0x0d, 0x93, 0x03, 0x3e, 0x11, 0xb7, 0xe2, 0x62, + 0x27, 0x9a, 0x42, 0xd3, 0xcc, 0x50, 0xd0, 0x18, 0x80, 0xe2, 0x00, 0xbb, 0x84, 0x91, 0x80, 0x57, + 0x72, 0x9e, 0xf1, 0x0f, 0xa6, 0xaf, 0x2e, 0x47, 0xb1, 0x4c, 0x33, 0x23, 0x9e, 0x23, 0x56, 0xa1, + 0x3a, 0x54, 0xf5, 0x5b, 0x8d, 0xd0, 0x97, 0xb0, 0x30, 0xb0, 0x1d, 0x72, 0x94, 0x1a, 0x32, 0x2b, + 0x0c, 0x79, 0x3c, 0xbd, 0x21, 0xf7, 0xb2, 0x72, 0xcd, 0x82, 0x1a, 0xe3, 0x06, 0xb4, 0x8a, 0xf9, + 0xc4, 0x8d, 0xb4, 0x5d, 0x3c, 0x4c, 0xbc, 0xa5, 0x46, 0x06, 0x82, 0x56, 0x31, 0x7f, 0x8c, 0xbf, + 0x57, 0x61, 0x25, 0x11, 0xb7, 0xeb, 0x79, 0x7e, 0xe4, 0x59, 0xe2, 0x29, 0xae, 0xf4, 0x2c, 0x96, + 0x61, 0x86, 0xd9, 0xcc, 0x49, 0x80, 0x8f, 0x18, 0xf0, 0xde, 0xc5, 0x7c, 0xdf, 0x61, 0x36, 0x55, + 0x07, 0x1c, 0x0f, 0xe5, 0xd9, 0x3f, 0x8f, 0xec, 0x80, 0xf4, 0x45, 0x25, 0x68, 0x98, 0xc9, 0x98, + 0xcf, 0x71, 0x54, 0x23, 0x60, 0xbc, 0x74, 0x66, 0x32, 0x16, 0x71, 0xef, 0x3b, 0x0e, 0xb1, 0xb8, + 0x3b, 0x32, 0x40, 0xbf, 0x40, 0x15, 0x17, 0x08, 0x16, 0xd8, 0xde, 0x50, 0xc1, 0x7c, 0x35, 0xe2, + 0x76, 0xe2, 0x20, 0xc0, 0x27, 0x7a, 0x43, 0x38, 0x40, 0x0e, 0xd0, 0x47, 0x50, 0x73, 0x31, 0x55, + 0x8d, 0xee, 0x46, 0xae, 0x3a, 0x94, 0x79, 0xa0, 0x73, 0x88, 0xa9, 0xec, 0x04, 0x7c, 0x59, 0xfb, + 0x03, 0x68, 0xc4, 0x84, 0xaf, 0x05, 0x09, 0xbf, 0x80, 0xcb, 0xb9, 0xe2, 0x83, 0x3e, 0x87, 0xd5, + 0x34, 0xa2, 0xb2, 0x0a, 0x15, 0x08, 0x7c, 0xe7, 0x95, 0x96, 0x99, 0xa7, 0x08, 0x30, 0x9e, 0xc3, + 0x12, 0x0f, 0x19, 0x91, 0xf8, 0x17, 0x74, 0xb5, 0xf9, 0x10, 0x9a, 0x89, 0xca, 0xd2, 0x98, 0x69, + 0x43, 0xe3, 0x38, 0x7e, 0x22, 0x95, 0x77, 0x9b, 0x64, 0x6c, 0xec, 0x02, 0xca, 0xda, 0xab, 0x3a, + 0xd0, 0xcd, 0x3c, 0x28, 0x5e, 0x29, 0xb6, 0x1b, 0xc1, 0x1e, 0x63, 0xe2, 0xdf, 0x55, 0x61, 0x71, + 0xdf, 0x16, 0xaf, 0x1c, 0x17, 0x54, 0xe4, 0x6e, 0x40, 0x2b, 0x8c, 0x7a, 0xae, 0xdf, 0x8f, 0x1c, + 0xa2, 0x40, 0x81, 0xea, 0xf4, 0x13, 0xf4, 0xb3, 0x8a, 0x1f, 0x77, 0x16, 0xc5, 0x6c, 0xa4, 0x6e, + 0xb8, 0xe2, 0x1b, 0x7d, 0x04, 0xeb, 0x8f, 0xc8, 0x97, 0x6a, 0x3f, 0xfb, 0x8e, 0xdf, 0xeb, 0xd9, + 0xde, 0x30, 0x56, 0x32, 0x23, 0x94, 0x9c, 0xce, 0x50, 0x06, 0x15, 0x67, 0x4b, 0xa1, 0xa2, 0xf1, + 0x4b, 0x0d, 0x5a, 0xa9, 0xd7, 0x94, 0xdf, 0x6f, 0xcb, 0xfc, 0x90, 0x5e, 0xbf, 0x96, 0xf5, 0x7a, + 0x91, 0xf5, 0x3f, 0x4f, 0x8d, 0x4b, 0xd9, 0xd4, 0xf8, 0xa7, 0x06, 0x2b, 0xfb, 0x36, 0x8b, 0x8b, + 0x92, 0xfd, 0xdf, 0x76, 0x82, 0x25, 0xfe, 0xae, 0x97, 0xfb, 0xbb, 0x03, 0xab, 0xc5, 0x8d, 0x2a, + 0xa7, 0x2f, 0xc3, 0x0c, 0x3f, 0xf9, 0xf8, 0x3d, 0x40, 0x0e, 0x76, 0xbe, 0x6a, 0xc2, 0x52, 0xda, + 0xd0, 0xf9, 0xbf, 0xb6, 0x45, 0xd0, 0x63, 0x68, 0xed, 0xab, 0xdf, 0xe3, 0xe2, 0x77, 0x18, 0x74, + 0xd6, 0xc3, 0x66, 0xfb, 0x6a, 0xf9, 0xa4, 0x54, 0x6d, 0x54, 0x90, 0x05, 0xeb, 0x45, 0x81, 0xe9, + 0x1b, 0xea, 0xff, 0x9f, 0x21, 0x39, 0xe1, 0x7a, 0x95, 0x8a, 0x2d, 0x0d, 0x7d, 0x0e, 0x0b, 0xf9, + 0x97, 0x3e, 0x94, 0xab, 0x70, 0xa5, 0x8f, 0x8f, 0x6d, 0xe3, 0x2c, 0x96, 0xc4, 0xfe, 0x67, 0x1c, + 0x4e, 0xe7, 0x9e, 0xbd, 0x90, 0x91, 0x07, 0xfb, 0x65, 0xcf, 0x82, 0xed, 0xff, 0x3b, 0x93, 0x27, + 0x91, 0xfe, 0x21, 0x34, 0xe2, 0x67, 0xa2, 0xbc, 0x9b, 0x0b, 0x8f, 0x47, 0xed, 0x56, 0x5e, 0xde, + 0x20, 0x34, 0x2a, 0xe8, 0x63, 0xb9, 0x78, 0x97, 0xd2, 0x92, 0xc5, 0x99, 0xc7, 0x91, 0xf6, 0x95, + 0x92, 0x07, 0x09, 0xa3, 0x82, 0xbe, 0x0b, 0xf3, 0xfc, 0xeb, 0x48, 0xfd, 0x12, 0xb6, 0xda, 0x91, + 0x3f, 0xbc, 0x76, 0xe2, 0x1f, 0x5e, 0x3b, 0x77, 0x5d, 0xca, 0x4e, 0xda, 0x25, 0x2f, 0x06, 0x4a, + 0xc0, 0x33, 0xb8, 0xbc, 0x4f, 0x58, 0x0a, 0xf0, 0xd1, 0xb5, 0xd7, 0xba, 0x06, 0xb5, 0x8d, 0x22, + 0xdb, 0xe4, 0x1d, 0xc1, 0xa8, 0xa0, 0xdf, 0x68, 0x70, 0x65, 0x9f, 0xb0, 0x22, 0x64, 0x46, 0xef, + 0x95, 0x2b, 0x39, 0x05, 0x5a, 0xb7, 0x1f, 0x4d, 0x9b, 0xd9, 0x79, 0xb1, 0x46, 0x05, 0xfd, 0x56, + 0x83, 0xb5, 0x8c, 0x61, 0x59, 0x0c, 0x8c, 0xb6, 0xcf, 0x36, 0xae, 0x04, 0x2f, 0xb7, 0x3f, 0x9b, + 0xf2, 0x07, 0xce, 0x8c, 0x48, 0xa3, 0x82, 0x8e, 0xc4, 0x99, 0xa4, 0x2d, 0x0f, 0xbd, 0x5d, 0xda, + 0xdb, 0x12, 0xed, 0x1b, 0xa7, 0x4d, 0x27, 0xe7, 0xf0, 0x19, 0xcc, 0xef, 0x13, 0x16, 0xd7, 0xe7, + 0x7c, 0xa4, 0x15, 0xda, 0x62, 0x3e, 0x55, 0x8b, 0x25, 0x5d, 0x44, 0xcc, 0x92, 0x94, 0x95, 0xa9, + 0x53, 0xf9, 0x5c, 0x2d, 0x2d, 0xd6, 0xf9, 0x88, 0x29, 0x2f, 0x73, 0x46, 0xe5, 0x93, 0xdd, 0xbf, + 0xbe, 0xdc, 0xd0, 0xbe, 0x7a, 0xb9, 0xa1, 0xfd, 0xe3, 0xe5, 0x86, 0xf6, 0xa3, 0x5b, 0xaf, 0xf8, + 0xab, 0x84, 0xcc, 0x1f, 0x3a, 0x60, 0x6a, 0x5b, 0x8e, 0x4d, 0x3c, 0xd6, 0x9b, 0x15, 0xc1, 0x7f, + 0xeb, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf2, 0x91, 0xe2, 0xd9, 0x07, 0x21, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -4679,6 +4695,16 @@ func (m *GitFilesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.NoRevisionCache { + i-- + if m.NoRevisionCache { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } if m.NewGitFileGlobbingEnabled { i-- if m.NewGitFileGlobbingEnabled { @@ -4800,6 +4826,16 @@ func (m *GitDirectoriesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.NoRevisionCache { + i-- + if m.NoRevisionCache { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } if len(m.Revision) > 0 { i -= len(m.Revision) copy(dAtA[i:], m.Revision) @@ -5686,6 +5722,9 @@ func (m *GitFilesRequest) Size() (n int) { if m.NewGitFileGlobbingEnabled { n += 2 } + if m.NoRevisionCache { + n += 2 + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5733,6 +5772,9 @@ func (m *GitDirectoriesRequest) Size() (n int) { if l > 0 { n += 1 + l + sovRepository(uint64(l)) } + if m.NoRevisionCache { + n += 2 + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -10874,6 +10916,26 @@ func (m *GitFilesRequest) Unmarshal(dAtA []byte) error { } } m.NewGitFileGlobbingEnabled = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NoRevisionCache", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.NoRevisionCache = bool(v != 0) default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) @@ -11192,6 +11254,26 @@ func (m *GitDirectoriesRequest) Unmarshal(dAtA []byte) error { } m.Revision = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NoRevisionCache", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRepository + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.NoRevisionCache = bool(v != 0) default: iNdEx = preIndex skippy, err := skipRepository(dAtA[iNdEx:]) diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go index f5a934ee12c5c..67c1d24e072d4 100644 --- a/reposerver/repository/repository.go +++ b/reposerver/repository/repository.go @@ -2506,6 +2506,7 @@ func (s *Service) GetGitFiles(_ context.Context, request *apiclient.GitFilesRequ repo := request.GetRepo() revision := request.GetRevision() gitPath := request.GetPath() + noRevisionCache := request.GetNoRevisionCache() enableNewGitFileGlobbing := request.GetNewGitFileGlobbingEnabled() if gitPath == "" { gitPath = "." @@ -2515,7 +2516,7 @@ func (s *Service) GetGitFiles(_ context.Context, request *apiclient.GitFilesRequ return nil, status.Error(codes.InvalidArgument, "must pass a valid repo") } - gitClient, revision, err := s.newClientResolveRevision(repo, revision, git.WithCache(s.cache, true)) + gitClient, revision, err := s.newClientResolveRevision(repo, revision, git.WithCache(s.cache, !noRevisionCache)) if err != nil { return nil, status.Errorf(codes.Internal, "unable to resolve git revision %s: %v", revision, err) } @@ -2568,12 +2569,12 @@ func (s *Service) GetGitFiles(_ context.Context, request *apiclient.GitFilesRequ func (s *Service) GetGitDirectories(_ context.Context, request *apiclient.GitDirectoriesRequest) (*apiclient.GitDirectoriesResponse, error) { repo := request.GetRepo() revision := request.GetRevision() - + noRevisionCache := request.GetNoRevisionCache() if repo == nil { return nil, status.Error(codes.InvalidArgument, "must pass a valid repo") } - gitClient, revision, err := s.newClientResolveRevision(repo, revision, git.WithCache(s.cache, true)) + gitClient, revision, err := s.newClientResolveRevision(repo, revision, git.WithCache(s.cache, !noRevisionCache)) if err != nil { return nil, status.Errorf(codes.Internal, "unable to resolve git revision %s: %v", revision, err) } diff --git a/reposerver/repository/repository.proto b/reposerver/repository/repository.proto index 8e4b69000f7e1..de061122e2586 100644 --- a/reposerver/repository/repository.proto +++ b/reposerver/repository/repository.proto @@ -236,6 +236,7 @@ message GitFilesRequest { string revision = 3; string path = 4; bool NewGitFileGlobbingEnabled = 5; + bool noRevisionCache = 6; } message GitFilesResponse { @@ -247,6 +248,7 @@ message GitDirectoriesRequest { github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Repository repo = 1; bool submoduleEnabled = 2; string revision = 3; + bool noRevisionCache = 4; } message GitDirectoriesResponse { From 30b92b246da8649a6c230a253ed4d751536faaf2 Mon Sep 17 00:00:00 2001 From: Geoffrey MUSELLI Date: Mon, 4 Dec 2023 10:59:18 -0500 Subject: [PATCH 20/66] fix(doc): Fix documentation templatePatch (#16522) Signed-off-by: gmuselli --- docs/operator-manual/applicationset/Template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/operator-manual/applicationset/Template.md b/docs/operator-manual/applicationset/Template.md index 76ff9132802d5..573e297bff2e2 100644 --- a/docs/operator-manual/applicationset/Template.md +++ b/docs/operator-manual/applicationset/Template.md @@ -157,12 +157,12 @@ spec: helm: valueFiles: {{- range $valueFile := .valueFiles }} - - {{ $valueFile | toJson }} + - {{ $valueFile }} {{- end }} {{- if .autoSync }} syncPolicy: automated: - prune: {{ .prune | toJson }} + prune: {{ .prune }} {{- end }} ``` From 888687452fded683dfbef2f86ff818b4c5aff95a Mon Sep 17 00:00:00 2001 From: Chris Murray Date: Mon, 4 Dec 2023 19:42:33 +0000 Subject: [PATCH 21/66] fix: cert-manager.io/certificate health.lua for consistent issuing (Issue #16523) (#16520) * Update cert-manager.opcertificate health.lua Signed-off-by: Chris Murray * adding test case for cert issuing Signed-off-by: Chris Murray * fixing typo Signed-off-by: Chris Murray --------- Signed-off-by: Chris Murray --- .../cert-manager.io/Certificate/health.lua | 5 +++ .../Certificate/health_test.yaml | 4 +++ .../testdata/progressing_issuing_last.yaml | 36 +++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 resource_customizations/cert-manager.io/Certificate/testdata/progressing_issuing_last.yaml diff --git a/resource_customizations/cert-manager.io/Certificate/health.lua b/resource_customizations/cert-manager.io/Certificate/health.lua index ddecb2631b39a..fce5bcbe3d1d0 100644 --- a/resource_customizations/cert-manager.io/Certificate/health.lua +++ b/resource_customizations/cert-manager.io/Certificate/health.lua @@ -1,12 +1,17 @@ local hs = {} if obj.status ~= nil then if obj.status.conditions ~= nil then + + -- Always Handle Issuing First to ensure consistent behaviour for i, condition in ipairs(obj.status.conditions) do if condition.type == "Issuing" and condition.status == "True" then hs.status = "Progressing" hs.message = condition.message return hs end + end + + for i, condition in ipairs(obj.status.conditions) do if condition.type == "Ready" and condition.status == "False" then hs.status = "Degraded" hs.message = condition.message diff --git a/resource_customizations/cert-manager.io/Certificate/health_test.yaml b/resource_customizations/cert-manager.io/Certificate/health_test.yaml index ebf8e75e89064..1af7b1a759a60 100644 --- a/resource_customizations/cert-manager.io/Certificate/health_test.yaml +++ b/resource_customizations/cert-manager.io/Certificate/health_test.yaml @@ -7,6 +7,10 @@ tests: status: Progressing message: Issuing certificate as Secret does not exist inputPath: testdata/progressing_issuing.yaml +- healthStatus: + status: Progressing + message: Issuing certificate as Secret does not exist + inputPath: testdata/progressing_issuing_last.yaml - healthStatus: status: Degraded message: 'Resource validation failed: spec.acme.config: Required value: no ACME diff --git a/resource_customizations/cert-manager.io/Certificate/testdata/progressing_issuing_last.yaml b/resource_customizations/cert-manager.io/Certificate/testdata/progressing_issuing_last.yaml new file mode 100644 index 0000000000000..4d21a9b3610f1 --- /dev/null +++ b/resource_customizations/cert-manager.io/Certificate/testdata/progressing_issuing_last.yaml @@ -0,0 +1,36 @@ +apiVersion: cert-manager.io/v1alpha2 +kind: Certificate +metadata: + creationTimestamp: '2018-11-07T00:06:12Z' + generation: 1 + name: test-cert + namespace: argocd + resourceVersion: '64763033' + selfLink: /apis/cert-manager.io/v1alpha2/namespaces/argocd/certificates/test-cert + uid: e6cfba50-314d-11e9-be3f-42010a800011 +spec: + acme: + config: + - domains: + - cd.apps.argoproj.io + http01: + ingress: http01 + commonName: cd.apps.argoproj.io + dnsNames: + - cd.apps.argoproj.io + issuerRef: + kind: Issuer + name: argo-cd-issuer + secretName: test-secret +status: + conditions: + - lastTransitionTime: '2021-09-15T02:10:00Z' + message: Issuing certificate as Secret does not exist + reason: DoesNotExist + status: 'False' + type: Ready + - lastTransitionTime: '2021-09-15T02:10:00Z' + message: Issuing certificate as Secret does not exist + reason: DoesNotExist + status: 'True' + type: Issuing From 99c2859560817b4ca514613d3417cc1a93565844 Mon Sep 17 00:00:00 2001 From: Ondrej Sika Date: Mon, 4 Dec 2023 23:14:32 +0100 Subject: [PATCH 22/66] fix: Use math.MaxInt (instead of math.MaxInt64) to fix builds on 32bit platforms (#16065) Signed-off-by: Ondrej Sika From 7484f1df6549d188d4edcc234511d33947af4c45 Mon Sep 17 00:00:00 2001 From: ffppmm Date: Tue, 5 Dec 2023 05:26:58 +0100 Subject: [PATCH 23/66] Added missing 'alias:' prefix for repository name as described here: (#15902) --- reposerver/repository/repository.go | 4 ++++ reposerver/repository/repository_test.go | 17 +++++++++++++++++ .../helm-with-dependencies-alias/Chart.yaml | 7 +++++++ 3 files changed, 28 insertions(+) create mode 100644 reposerver/repository/testdata/helm-with-dependencies-alias/Chart.yaml diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go index 67c1d24e072d4..155ff8c6f8653 100644 --- a/reposerver/repository/repository.go +++ b/reposerver/repository/repository.go @@ -1022,6 +1022,10 @@ func getHelmDependencyRepos(appPath string) ([]*v1alpha1.Repository, error) { repos = append(repos, &v1alpha1.Repository{ Name: r.Repository[1:], }) + } else if strings.HasPrefix(r.Repository, "alias:") { + repos = append(repos, &v1alpha1.Repository{ + Name: strings.TrimPrefix(r.Repository, "alias:"), + }) } else if u, err := url.Parse(r.Repository); err == nil && (u.Scheme == "https" || u.Scheme == "oci") { repo := &v1alpha1.Repository{ // trimming oci:// prefix since it is currently not supported by Argo CD (OCI repos just have no scheme) diff --git a/reposerver/repository/repository_test.go b/reposerver/repository/repository_test.go index 74d09dd6f86b8..ab2225b6e778d 100644 --- a/reposerver/repository/repository_test.go +++ b/reposerver/repository/repository_test.go @@ -1440,6 +1440,7 @@ func TestListApps(t *testing.T) { "out-of-bounds-values-file-link": "Helm", "values-files": "Helm", "helm-with-dependencies": "Helm", + "helm-with-dependencies-alias": "Helm", } assert.Equal(t, expectedApps, res.Apps) } @@ -2949,6 +2950,22 @@ func TestGetHelmRepo_NamedRepos(t *testing.T) { assert.Equal(t, helmRepos[0].Repo, "https://example.com") } +func TestGetHelmRepo_NamedReposAlias(t *testing.T) { + src := argoappv1.ApplicationSource{Path: "."} + q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src, Repos: []*argoappv1.Repository{{ + Name: "custom-repo-alias", + Repo: "https://example.com", + Username: "test-alias", + }}} + + helmRepos, err := getHelmRepos("./testdata/helm-with-dependencies-alias", q.Repos, q.HelmRepoCreds) + assert.Nil(t, err) + + assert.Equal(t, len(helmRepos), 1) + assert.Equal(t, helmRepos[0].Username, "test-alias") + assert.Equal(t, helmRepos[0].Repo, "https://example.com") +} + func Test_getResolvedValueFiles(t *testing.T) { tempDir := t.TempDir() paths := io.NewRandomizedTempPaths(tempDir) diff --git a/reposerver/repository/testdata/helm-with-dependencies-alias/Chart.yaml b/reposerver/repository/testdata/helm-with-dependencies-alias/Chart.yaml new file mode 100644 index 0000000000000..8a38d551070c7 --- /dev/null +++ b/reposerver/repository/testdata/helm-with-dependencies-alias/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: helm-with-dependencies-alias +version: v1.0.0 +dependencies: + - name: helm + repository: "alias:custom-repo-alias" + version: v1.0.0 From 5c51dcb6a1c76cb346990d14fb650dc56b054c88 Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Tue, 5 Dec 2023 14:00:39 -0500 Subject: [PATCH 24/66] fix(ui): use background delete to match k8s terminology (#15579) Signed-off-by: Josh Soref Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --- ui/src/app/applications/components/utils.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/app/applications/components/utils.tsx b/ui/src/app/applications/components/utils.tsx index f11ca2a916307..cd39470bfb25b 100644 --- a/ui/src/app/applications/components/utils.tsx +++ b/ui/src/app/applications/components/utils.tsx @@ -361,7 +361,7 @@ export const deletePopup = async (ctx: ContextApis, resource: ResourceTreeNode, handleStateChange('force')} style={{marginRight: '5px'}} id='force-delete-radio' /> handleStateChange('orphan')} style={{marginRight: '5px'}} id='cascade-delete-radio' /> From 07a2e643239da9f80fbf56a04443741ec4611c3e Mon Sep 17 00:00:00 2001 From: Elouan Keryell-Even Date: Tue, 5 Dec 2023 22:04:55 +0100 Subject: [PATCH 25/66] docs: Fix format issue in rbac.md (#16521) Wrongly placed horizontal line (`----`) was formatting code-block as a header. Fixed it with a necessary line break Signed-off-by: Elouan Keryell-Even --- docs/operator-manual/rbac.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/operator-manual/rbac.md b/docs/operator-manual/rbac.md index 0f15a18be1973..b1d386fb5eb8e 100644 --- a/docs/operator-manual/rbac.md +++ b/docs/operator-manual/rbac.md @@ -159,6 +159,7 @@ data: g, your-github-org:your-team, role:org-admin ``` + ---- Another `policy.csv` example might look as follows: From 86f79ecd741d1bccdec31fe5819de48023479e25 Mon Sep 17 00:00:00 2001 From: Phil Nichol <35630607+philnichol@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:37:14 +0800 Subject: [PATCH 26/66] docs: Fix minor typo in Declarative Setup (#16550) Signed-off-by: Phil Nichol <35630607+philnichol@users.noreply.github.com> --- docs/operator-manual/declarative-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/operator-manual/declarative-setup.md b/docs/operator-manual/declarative-setup.md index a156f27589dbd..ee216a7118f7f 100644 --- a/docs/operator-manual/declarative-setup.md +++ b/docs/operator-manual/declarative-setup.md @@ -490,7 +490,7 @@ stringData: ### Legacy behaviour -In Argo CD version 2.0 and earlier, repositories where stored as part of the `argocd-cm` config map. For +In Argo CD version 2.0 and earlier, repositories were stored as part of the `argocd-cm` config map. For backward-compatibility, Argo CD will still honor repositories in the config map, but this style of repository configuration is deprecated and support for it will be removed in a future version. From 710777e261f359fe8e4f69b0a97bfb6d070938b6 Mon Sep 17 00:00:00 2001 From: Jesse Suen Date: Wed, 6 Dec 2023 11:49:40 -0800 Subject: [PATCH 27/66] chore: update PR template to suggest cherry-pick releases (#16560) Signed-off-by: Jesse Suen --- .github/pull_request_template.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 406306bbeca2e..c1a3f42508aaa 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -13,11 +13,12 @@ Checklist: * [ ] I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them. * [ ] Does this PR require documentation updates? * [ ] I've updated documentation as required by this PR. -* [ ] Optional. My organization is added to USERS.md. * [ ] I have signed off all my commits as required by [DCO](https://github.com/argoproj/argoproj/blob/master/community/CONTRIBUTING.md#legal) * [ ] I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged. * [ ] My build is green ([troubleshooting builds](https://argo-cd.readthedocs.io/en/latest/developer-guide/ci/)). * [ ] My new feature complies with the [feature status](https://github.com/argoproj/argoproj/blob/master/community/feature-status.md) guidelines. * [ ] I have added a brief description of why this PR is necessary and/or what this PR solves. +* [ ] Optional. My organization is added to USERS.md. +* [ ] Optional. For bug fixes, I've indicated what older releases this fix should be cherry-picked into (this may or may not happen depending on risk/complexity). From 35f1ee78448aa36f0560686df880da1a14a33ee3 Mon Sep 17 00:00:00 2001 From: Rotem Tamir Date: Thu, 7 Dec 2023 13:11:07 +0200 Subject: [PATCH 28/66] resource_customizations/db.atlasgo.io: atlas operator resources (#16364) Signed-off-by: Rotem Tamir --- .../db.atlasgo.io/AtlasMigration/health.lua | 37 ++++++++++++++++++ .../AtlasMigration/health_test.yaml | 13 +++++++ .../AtlasMigration/testdata/degraded.yaml | 29 ++++++++++++++ .../AtlasMigration/testdata/healthy.yaml | 30 ++++++++++++++ .../AtlasMigration/testdata/progressing.yaml | 30 ++++++++++++++ .../db.atlasgo.io/AtlasSchema/health.lua | 37 ++++++++++++++++++ .../AtlasSchema/health_test.yaml | 13 +++++++ .../AtlasSchema/testdata/degraded.yaml | 38 ++++++++++++++++++ .../AtlasSchema/testdata/healthy.yaml | 39 +++++++++++++++++++ .../AtlasSchema/testdata/progressing.yaml | 35 +++++++++++++++++ 10 files changed, 301 insertions(+) create mode 100644 resource_customizations/db.atlasgo.io/AtlasMigration/health.lua create mode 100644 resource_customizations/db.atlasgo.io/AtlasMigration/health_test.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasMigration/testdata/degraded.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasMigration/testdata/healthy.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasMigration/testdata/progressing.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasSchema/health.lua create mode 100644 resource_customizations/db.atlasgo.io/AtlasSchema/health_test.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasSchema/testdata/degraded.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasSchema/testdata/healthy.yaml create mode 100644 resource_customizations/db.atlasgo.io/AtlasSchema/testdata/progressing.yaml diff --git a/resource_customizations/db.atlasgo.io/AtlasMigration/health.lua b/resource_customizations/db.atlasgo.io/AtlasMigration/health.lua new file mode 100644 index 0000000000000..332b43ec21314 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasMigration/health.lua @@ -0,0 +1,37 @@ +hs = {} + +local function readyCond(obj) + if obj.status ~= nil and obj.status.conditions ~= nil then + for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" then + return condition + end + end + end + return nil +end + +local ready = readyCond(obj) + +if ready == nil then + hs.status = "Progressing" + hs.message = "Waiting for Atlas Operator" + return hs +end + +if ready.status == "True" then + hs.status = "Healthy" + hs.message = ready.reason + return hs +end + +if ready.reason == "Reconciling" then + hs.status = "Progressing" +else + hs.status = "Degraded" +end + +hs.message = ready.reason + +return hs + diff --git a/resource_customizations/db.atlasgo.io/AtlasMigration/health_test.yaml b/resource_customizations/db.atlasgo.io/AtlasMigration/health_test.yaml new file mode 100644 index 0000000000000..b827f89c0bdf2 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasMigration/health_test.yaml @@ -0,0 +1,13 @@ +tests: +- healthStatus: + status: Progressing + message: "Reconciling" + inputPath: testdata/progressing.yaml +- healthStatus: + status: Degraded + message: "Migrating" + inputPath: testdata/degraded.yaml +- healthStatus: + status: Healthy + message: "Applied" + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/degraded.yaml b/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/degraded.yaml new file mode 100644 index 0000000000000..ee51f15e48241 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/degraded.yaml @@ -0,0 +1,29 @@ +apiVersion: db.atlasgo.io/v1alpha1 +kind: AtlasMigration +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"db.atlasgo.io/v1alpha1","kind":"AtlasMigration","metadata":{"annotations":{},"name":"atlasmigration-sample","namespace":"default"},"spec":{"dir":{"configMapRef":{"name":"migration-dir"}},"urlFrom":{"secretKeyRef":{"key":"url","name":"mysql-credentials"}}}} + creationTimestamp: "2023-11-16T08:37:23Z" + generation: 1 + name: atlasmigration-sample + namespace: default + resourceVersion: "49923" + uid: 0d5bc3d6-750e-4f5a-82a3-8b9173106ef4 +spec: + dir: + configMapRef: + name: migration-dir + urlFrom: + secretKeyRef: + key: url + name: mysql-credentials +status: + conditions: + - lastTransitionTime: "2023-11-16T08:37:23Z" + message: 'Error: checksum mismatch' + reason: Migrating + status: "False" + type: Ready + lastApplied: 0 + observed_hash: "" diff --git a/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/healthy.yaml b/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/healthy.yaml new file mode 100644 index 0000000000000..4a7a91324d196 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/healthy.yaml @@ -0,0 +1,30 @@ +apiVersion: db.atlasgo.io/v1alpha1 +kind: AtlasMigration +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"db.atlasgo.io/v1alpha1","kind":"AtlasMigration","metadata":{"annotations":{},"name":"atlasmigration-sample","namespace":"default"},"spec":{"dir":{"configMapRef":{"name":"migration-dir"}},"urlFrom":{"secretKeyRef":{"key":"url","name":"mysql-credentials"}}}} + creationTimestamp: "2023-11-16T08:37:23Z" + generation: 1 + name: atlasmigration-sample + namespace: default + resourceVersion: "50387" + uid: 0d5bc3d6-750e-4f5a-82a3-8b9173106ef4 +spec: + dir: + configMapRef: + name: migration-dir + urlFrom: + secretKeyRef: + key: url + name: mysql-credentials +status: + conditions: + - lastTransitionTime: "2023-11-16T08:46:27Z" + message: "" + reason: Applied + status: "True" + type: Ready + lastApplied: 1700124387 + lastAppliedVersion: "20230316085611" + observed_hash: 4969b3c84c097ff61a9f9722b595a66c1a4473bd85fdd282107b98a92db8a43b diff --git a/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/progressing.yaml b/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/progressing.yaml new file mode 100644 index 0000000000000..024f9f7558d78 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasMigration/testdata/progressing.yaml @@ -0,0 +1,30 @@ +apiVersion: db.atlasgo.io/v1alpha1 +kind: AtlasMigration +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"db.atlasgo.io/v1alpha1","kind":"AtlasMigration","metadata":{"annotations":{},"name":"atlasmigration-sample","namespace":"default"},"spec":{"dir":{"configMapRef":{"name":"migration-dir"}},"urlFrom":{"secretKeyRef":{"key":"url","name":"mysql-credentials"}}}} + creationTimestamp: "2023-11-16T08:37:23Z" + generation: 1 + name: atlasmigration-sample + namespace: default + resourceVersion: "50387" + uid: 0d5bc3d6-750e-4f5a-82a3-8b9173106ef4 +spec: + dir: + configMapRef: + name: migration-dir + urlFrom: + secretKeyRef: + key: url + name: mysql-credentials +status: + conditions: + - lastTransitionTime: "2023-11-16T08:46:27Z" + message: "Current migration data has changed" + reason: "Reconciling" + status: "False" + type: Ready + lastApplied: 1700124387 + lastAppliedVersion: "20230316085611" + observed_hash: 4969b3c84c097ff61a9f9722b595a66c1a4473bd85fdd282107b98a92db8a43b diff --git a/resource_customizations/db.atlasgo.io/AtlasSchema/health.lua b/resource_customizations/db.atlasgo.io/AtlasSchema/health.lua new file mode 100644 index 0000000000000..c66d66d15b5a8 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasSchema/health.lua @@ -0,0 +1,37 @@ +hs = {} + +local function readyCond(obj) + if obj.status ~= nil and obj.status.conditions ~= nil then + for _, condition in ipairs(obj.status.conditions) do + if condition.type == "Ready" then + return condition + end + end + end + return nil +end + +local ready = readyCond(obj) + +if ready == nil then + hs.status = "Progressing" + hs.message = "Waiting for Atlas Operator" + return hs +end + +if ready.status == "True" then + hs.status = "Healthy" + hs.message = ready.reason + return hs +end + +if ready.message == "Reconciling" or ready.message == "GettingDevDB" then + hs.status = "Progressing" +else + hs.status = "Degraded" +end + +hs.message = ready.reason + +return hs + diff --git a/resource_customizations/db.atlasgo.io/AtlasSchema/health_test.yaml b/resource_customizations/db.atlasgo.io/AtlasSchema/health_test.yaml new file mode 100644 index 0000000000000..0fe102f299138 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasSchema/health_test.yaml @@ -0,0 +1,13 @@ +tests: +- healthStatus: + status: Progressing + message: "Reconciling" + inputPath: testdata/progressing.yaml +- healthStatus: + status: Degraded + message: "ApplyingSchema" + inputPath: testdata/degraded.yaml +- healthStatus: + status: Healthy + message: "Applied" + inputPath: testdata/healthy.yaml diff --git a/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/degraded.yaml b/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/degraded.yaml new file mode 100644 index 0000000000000..08383988e996a --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/degraded.yaml @@ -0,0 +1,38 @@ +apiVersion: db.atlasgo.io/v1alpha1 +kind: AtlasSchema +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"db.atlasgo.io/v1alpha1","kind":"AtlasSchema","metadata":{"annotations":{},"name":"atlasschema-mysql","namespace":"default"},"spec":{"schema":{"sql":"create table users (\n id int not null auto_increment,\n name varchar(255) not null,\n email varchar(255) unique not null,\n short_bio varchar(255) not null,\n primary key (id)\n);\n"},"urlFrom":{"secretKeyRef":{"key":"url","name":"mysql-credentials"}}}} + creationTimestamp: "2023-11-15T14:33:18Z" + generation: 2 + name: atlasschema-mysql + namespace: default + resourceVersion: "46659" + uid: 54a4cdfc-e4f9-4c3d-934c-e08b6122e38a +spec: + schema: + sql: | + xcreate table users ( + id int not null auto_increment, + name varchar(255) not null, + email varchar(255) unique not null, + short_bio varchar(255) not null, + primary key (id) + ); + urlFrom: + secretKeyRef: + key: url + name: mysql-credentials +status: + conditions: + - lastTransitionTime: "2023-11-15T14:38:41Z" + message: |- + Error: sql/migrate: read migration directory state: sql/migrate: execute: executing statement "xcreate table users (\n id int not null auto_increment,\n name varchar(255) not null,\n email varchar(255) unique not null,\n short_bio varchar(255) not null,\n primary key (id)\n);" from version "schema": Error 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xcreate table users ( + id int not null auto_increment, + name varchar(255) not ' at line 1 + reason: ApplyingSchema + status: "False" + type: Ready + last_applied: 1700058814 + observed_hash: ddfe666707ddf5c2cc7625c2a0de89da51e54fc7caa6403db307146430d20d85 diff --git a/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/healthy.yaml b/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/healthy.yaml new file mode 100644 index 0000000000000..eca8ec497f09a --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/healthy.yaml @@ -0,0 +1,39 @@ +apiVersion: db.atlasgo.io/v1alpha1 +kind: AtlasSchema +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"db.atlasgo.io/v1alpha1","kind":"AtlasSchema","metadata":{"annotations":{},"name":"atlasschema-mysql","namespace":"default"},"spec":{"schema":{"sql":"create table users (\n id int not null auto_increment,\n name varchar(255) not null,\n email varchar(255) unique not null,\n short_bio varchar(255) not null,\n primary key (id)\n);\n"},"urlFrom":{"secretKeyRef":{"key":"url","name":"mysql-credentials"}}}} + creationTimestamp: "2023-11-15T14:33:18Z" + generation: 1 + name: atlasschema-mysql + namespace: default + resourceVersion: "46390" + uid: 54a4cdfc-e4f9-4c3d-934c-e08b6122e38a +spec: + schema: + sql: | + create table users ( + id int not null auto_increment, + name varchar(255) not null, + email varchar(255) unique not null, + short_bio varchar(255) not null, + primary key (id) + ); + urlFrom: + secretKeyRef: + key: url + name: mysql-credentials +status: + conditions: + - lastTransitionTime: "2023-11-15T14:33:34Z" + message: 'The schema has been applied successfully. Apply response: {"Driver":"mysql","URL":{"Scheme":"mysql","Opaque":"","User":{},"Host":"mysql.default:3306","Path":"/myapp","RawPath":"","OmitHost":false,"ForceQuery":false,"RawQuery":"parseTime=true","Fragment":"","RawFragment":"","Schema":"myapp"},"Changes":{"Applied":["CREATE + TABLE `users` (\n `id` int NOT NULL AUTO_INCREMENT,\n `name` varchar(255) + NOT NULL,\n `email` varchar(255) NOT NULL,\n `short_bio` varchar(255) NOT + NULL,\n PRIMARY KEY (`id`),\n UNIQUE INDEX `email` (`email`)\n) CHARSET utf8mb4 + COLLATE utf8mb4_0900_ai_ci"]}}' + reason: Applied + status: "True" + type: Ready + last_applied: 1700058814 + observed_hash: ddfe666707ddf5c2cc7625c2a0de89da51e54fc7caa6403db307146430d20d85 diff --git a/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/progressing.yaml b/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/progressing.yaml new file mode 100644 index 0000000000000..79d59ca768141 --- /dev/null +++ b/resource_customizations/db.atlasgo.io/AtlasSchema/testdata/progressing.yaml @@ -0,0 +1,35 @@ +apiVersion: db.atlasgo.io/v1alpha1 +kind: AtlasSchema +metadata: + annotations: + kubectl.kubernetes.io/last-applied-configuration: | + {"apiVersion":"db.atlasgo.io/v1alpha1","kind":"AtlasSchema","metadata":{"annotations":{},"name":"atlasschema-mysql","namespace":"default"},"spec":{"schema":{"sql":"create table users (\n id int not null auto_increment,\n name varchar(255) not null,\n email varchar(255) unique not null,\n short_bio varchar(255) not null,\n primary key (id)\n);\n"},"urlFrom":{"secretKeyRef":{"key":"url","name":"mysql-credentials"}}}} + creationTimestamp: "2023-11-15T14:33:18Z" + generation: 1 + name: atlasschema-mysql + namespace: default + resourceVersion: "46390" + uid: 54a4cdfc-e4f9-4c3d-934c-e08b6122e38a +spec: + schema: + sql: | + create table users ( + id int not null auto_increment, + name varchar(255) not null, + email varchar(255) unique not null, + short_bio varchar(255) not null, + primary key (id) + ); + urlFrom: + secretKeyRef: + key: url + name: mysql-credentials +status: + conditions: + - lastTransitionTime: "2023-11-15T14:33:34Z" + message: 'Reconciling' + reason: Reconciling + status: "False" + type: Ready + last_applied: 1700058814 + observed_hash: ddfe666707ddf5c2cc7625c2a0de89da51e54fc7caa6403db307146430d20d85 From 9179835ec1995d089a0c6938f459b8c637906eef Mon Sep 17 00:00:00 2001 From: Takumi Sue <23391543+mikutas@users.noreply.github.com> Date: Fri, 8 Dec 2023 23:20:01 +0900 Subject: [PATCH 29/66] fix(appset): Always remove ownerReferences when appset policy doesn't allow app's deletion (#12172) (#16506) * fix(appset): remove unnecessary condition Signed-off-by: mikutas <23391543+mikutas@users.noreply.github.com> * docs: update explanation about policy Signed-off-by: mikutas <23391543+mikutas@users.noreply.github.com> --------- Signed-off-by: mikutas <23391543+mikutas@users.noreply.github.com> --- .../controllers/applicationset_controller.go | 18 ++++++++---------- .../Controlling-Resource-Modification.md | 6 ++---- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/applicationset/controllers/applicationset_controller.go b/applicationset/controllers/applicationset_controller.go index 527a4ae1e3883..4f5ac66fc016d 100644 --- a/applicationset/controllers/applicationset_controller.go +++ b/applicationset/controllers/applicationset_controller.go @@ -108,16 +108,14 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque // Do not attempt to further reconcile the ApplicationSet if it is being deleted. if applicationSetInfo.ObjectMeta.DeletionTimestamp != nil { - if controllerutil.ContainsFinalizer(&applicationSetInfo, argov1alpha1.ResourcesFinalizerName) { - deleteAllowed := utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowDelete() - if !deleteAllowed { - if err := r.removeOwnerReferencesOnDeleteAppSet(ctx, applicationSetInfo); err != nil { - return ctrl.Result{}, err - } - controllerutil.RemoveFinalizer(&applicationSetInfo, argov1alpha1.ResourcesFinalizerName) - if err := r.Update(ctx, &applicationSetInfo); err != nil { - return ctrl.Result{}, err - } + deleteAllowed := utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowDelete() + if !deleteAllowed { + if err := r.removeOwnerReferencesOnDeleteAppSet(ctx, applicationSetInfo); err != nil { + return ctrl.Result{}, err + } + controllerutil.RemoveFinalizer(&applicationSetInfo, argov1alpha1.ResourcesFinalizerName) + if err := r.Update(ctx, &applicationSetInfo); err != nil { + return ctrl.Result{}, err } } return ctrl.Result{}, nil diff --git a/docs/operator-manual/applicationset/Controlling-Resource-Modification.md b/docs/operator-manual/applicationset/Controlling-Resource-Modification.md index 44b6797b24d11..d72cee60ad401 100644 --- a/docs/operator-manual/applicationset/Controlling-Resource-Modification.md +++ b/docs/operator-manual/applicationset/Controlling-Resource-Modification.md @@ -32,15 +32,13 @@ spec: ``` -- Policy `create-only`: Prevents ApplicationSet controller from modifying or deleting Applications. -- Policy `create-update`: Prevents ApplicationSet controller from deleting Applications. Update is allowed. +- Policy `create-only`: Prevents ApplicationSet controller from modifying or deleting Applications. Prevents Application controller from deleting Applications according to [ownerReferences](https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/). +- Policy `create-update`: Prevents ApplicationSet controller from deleting Applications. Update is allowed. Prevents Application controller from deleting Applications according to [ownerReferences](https://kubernetes.io/docs/concepts/overview/working-with-objects/owners-dependents/). - Policy `create-delete`: Prevents ApplicationSet controller from modifying Applications. Delete is allowed. - Policy `sync`: Update and Delete are allowed. If the controller parameter `--policy` is set, it takes precedence on the field `applicationsSync`. It is possible to allow per ApplicationSet sync policy by setting variable `ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_POLICY_OVERRIDE` to argocd-cmd-params-cm `applicationsetcontroller.enable.policy.override` or directly with controller parameter `--enable-policy-override` (default to `false`). -This does not prevent deletion of Applications if the ApplicationSet is deleted - ### Controller parameter To allow the ApplicationSet controller to *create* `Application` resources, but prevent any further modification, such as deletion, or modification of Application fields, add this parameter in the ApplicationSet controller: From f67dcac945ab2364071c4a4e9966807be480c672 Mon Sep 17 00:00:00 2001 From: Jay Shah Date: Fri, 8 Dec 2023 12:40:34 -0500 Subject: [PATCH 30/66] docs: Add Kong Inc. as a user (#16582) Signed-off-by: Jay Shah --- USERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/USERS.md b/USERS.md index 431a5ef3944f4..cc1c22e3d9d7b 100644 --- a/USERS.md +++ b/USERS.md @@ -148,6 +148,7 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Kinguin](https://www.kinguin.net/) 1. [KintoHub](https://www.kintohub.com/) 1. [KompiTech GmbH](https://www.kompitech.com/) +1. [Kong Inc.](https://konghq.com/) 1. [KPMG](https://kpmg.com/uk) 1. [KubeSphere](https://github.com/kubesphere) 1. [Kurly](https://www.kurly.com/) From a761a495f16d76c0a8e50359eda50f605e329aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Reegn?= Date: Fri, 8 Dec 2023 21:01:05 +0100 Subject: [PATCH 31/66] chore: upgrade kubernetes dependencies from 0.26.4 to 0.26.11 (#16581) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: upgrade kubernetes dependencies from 0.26.4 to 0.26.11 Fixes some vulnerabilities trivy is reporting on (not necessarily vulnerabe, trivy tends to have a lot of false positives when it comes to golang projects): * CVE-2023-3676 * CVE-2023-3955 * CVE-2023-5528 * CVE-2023-2431 * CVE-2023-2727 * CVE-2023-2728 Signed-off-by: Zoltán Reegn * go mod tidy Signed-off-by: Zoltán Reegn * Add go mod tidy to kubernetes updater script Signed-off-by: Zoltán Reegn --------- Signed-off-by: Zoltán Reegn --- go.mod | 80 +++++++++++++++---------------- go.sum | 60 ++++++++++++----------- hack/update-kubernetes-version.sh | 22 +++++++++ 3 files changed, 93 insertions(+), 69 deletions(-) create mode 100755 hack/update-kubernetes-version.sh diff --git a/go.mod b/go.mod index 8b91fe20eef1b..03283f5c61f09 100644 --- a/go.mod +++ b/go.mod @@ -91,12 +91,12 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.26.4 + k8s.io/api v0.26.11 k8s.io/apiextensions-apiserver v0.26.4 - k8s.io/apimachinery v0.26.4 - k8s.io/apiserver v0.26.4 - k8s.io/client-go v0.26.4 - k8s.io/code-generator v0.26.4 + k8s.io/apimachinery v0.26.11 + k8s.io/apiserver v0.26.11 + k8s.io/client-go v0.26.11 + k8s.io/code-generator v0.26.11 k8s.io/klog/v2 v2.100.1 k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f k8s.io/kubectl v0.26.4 @@ -267,12 +267,12 @@ require ( go.opentelemetry.io/otel/trace v1.21.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect - golang.org/x/mod v0.9.0 // indirect + golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.17.0 golang.org/x/sys v0.14.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.12.0 // indirect gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect gomodules.xyz/notify v0.1.1 // indirect @@ -281,12 +281,12 @@ require ( gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/cli-runtime v0.26.4 // indirect - k8s.io/component-base v0.26.4 // indirect - k8s.io/component-helpers v0.26.4 // indirect + k8s.io/cli-runtime v0.26.11 // indirect + k8s.io/component-base v0.26.11 // indirect + k8s.io/component-helpers v0.26.11 // indirect k8s.io/gengo v0.0.0-20220902162205-c0856e24416d // indirect k8s.io/kube-aggregator v0.26.4 // indirect - k8s.io/kubernetes v1.26.4 // indirect + k8s.io/kubernetes v1.26.11 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.12.1 // indirect sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect @@ -305,34 +305,34 @@ replace ( // Avoid CVE-2022-28948 gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1 - k8s.io/api => k8s.io/api v0.26.4 - k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.26.4 - k8s.io/apimachinery => k8s.io/apimachinery v0.26.4 - k8s.io/apiserver => k8s.io/apiserver v0.26.4 - k8s.io/cli-runtime => k8s.io/cli-runtime v0.26.4 - k8s.io/client-go => k8s.io/client-go v0.26.4 - k8s.io/cloud-provider => k8s.io/cloud-provider v0.26.4 - k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.26.4 - k8s.io/code-generator => k8s.io/code-generator v0.26.4 - k8s.io/component-base => k8s.io/component-base v0.26.4 - k8s.io/component-helpers => k8s.io/component-helpers v0.26.4 - k8s.io/controller-manager => k8s.io/controller-manager v0.26.4 - k8s.io/cri-api => k8s.io/cri-api v0.26.4 - k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.26.4 - k8s.io/dynamic-resource-allocation => k8s.io/dynamic-resource-allocation v0.26.4 - k8s.io/kms => k8s.io/kms v0.26.4 - k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.26.4 - k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.26.4 - k8s.io/kube-proxy => k8s.io/kube-proxy v0.26.4 - k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.26.4 - k8s.io/kubectl => k8s.io/kubectl v0.26.4 - k8s.io/kubelet => k8s.io/kubelet v0.26.4 - k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.26.4 - k8s.io/metrics => k8s.io/metrics v0.26.4 - k8s.io/mount-utils => k8s.io/mount-utils v0.26.4 - k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.26.4 - k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.26.4 - k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.26.4 - k8s.io/sample-controller => k8s.io/sample-controller v0.26.4 + k8s.io/api => k8s.io/api v0.26.11 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.26.11 + k8s.io/apimachinery => k8s.io/apimachinery v0.26.11 + k8s.io/apiserver => k8s.io/apiserver v0.26.11 + k8s.io/cli-runtime => k8s.io/cli-runtime v0.26.11 + k8s.io/client-go => k8s.io/client-go v0.26.11 + k8s.io/cloud-provider => k8s.io/cloud-provider v0.26.11 + k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.26.11 + k8s.io/code-generator => k8s.io/code-generator v0.26.11 + k8s.io/component-base => k8s.io/component-base v0.26.11 + k8s.io/component-helpers => k8s.io/component-helpers v0.26.11 + k8s.io/controller-manager => k8s.io/controller-manager v0.26.11 + k8s.io/cri-api => k8s.io/cri-api v0.26.11 + k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.26.11 + k8s.io/dynamic-resource-allocation => k8s.io/dynamic-resource-allocation v0.26.11 + k8s.io/kms => k8s.io/kms v0.26.11 + k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.26.11 + k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.26.11 + k8s.io/kube-proxy => k8s.io/kube-proxy v0.26.11 + k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.26.11 + k8s.io/kubectl => k8s.io/kubectl v0.26.11 + k8s.io/kubelet => k8s.io/kubelet v0.26.11 + k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.26.11 + k8s.io/metrics => k8s.io/metrics v0.26.11 + k8s.io/mount-utils => k8s.io/mount-utils v0.26.11 + k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.26.11 + k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.26.11 + k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.26.11 + k8s.io/sample-controller => k8s.io/sample-controller v0.26.11 ) diff --git a/go.sum b/go.sum index dbe7107f59cba..9f0d3f9a0976e 100644 --- a/go.sum +++ b/go.sum @@ -1596,8 +1596,8 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= @@ -1878,8 +1878,9 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2132,6 +2133,7 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -2173,7 +2175,6 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= @@ -2258,8 +2259,9 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= +golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2616,24 +2618,24 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -k8s.io/api v0.26.4 h1:qSG2PmtcD23BkYiWfoYAcak870eF/hE7NNYBYavTT94= -k8s.io/api v0.26.4/go.mod h1:WwKEXU3R1rgCZ77AYa7DFksd9/BAIKyOmRlbVxgvjCk= -k8s.io/apiextensions-apiserver v0.26.4 h1:9D2RTxYGxrG5uYg6D7QZRcykXvavBvcA59j5kTaedQI= -k8s.io/apiextensions-apiserver v0.26.4/go.mod h1:cd4uGFGIgzEqUghWpRsr9KE8j2KNTjY8Ji8pnMMazyw= -k8s.io/apimachinery v0.26.4 h1:rZccKdBLg9vP6J09JD+z8Yr99Ce8gk3Lbi9TCx05Jzs= -k8s.io/apimachinery v0.26.4/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= -k8s.io/apiserver v0.26.4 h1:3Oq4mnJv0mzVX7BR/Nod+8KjlELf/3Ljvu9ZWDyLUoA= -k8s.io/apiserver v0.26.4/go.mod h1:yAY3O1vBM4/0OIGAGeWcdfzQvgdwJ188VirLcuSAVnw= -k8s.io/cli-runtime v0.26.4 h1:MgSU871KDzBDX7V9GtuqS6Ai9lhQCHgRzkurnXOWtZ0= -k8s.io/cli-runtime v0.26.4/go.mod h1:MjJ2DXMChw2zcG0/agzm17xwKpfVxOfuoCdfY9iOCOE= -k8s.io/client-go v0.26.4 h1:/7P/IbGBuT73A+G97trf44NTPSNqvuBREpOfdLbHvD4= -k8s.io/client-go v0.26.4/go.mod h1:6qOItWm3EwxJdl/8p5t7FWtWUOwyMdA8N9ekbW4idpI= -k8s.io/code-generator v0.26.4 h1:zgDD0qX13p/jtrAoYRRiYeQ5ibnriwmo2cMkMZAtJxc= -k8s.io/code-generator v0.26.4/go.mod h1:ryaiIKwfxEJEaywEzx3dhWOydpVctKYbqLajJf0O8dI= -k8s.io/component-base v0.26.4 h1:Bg2xzyXNKL3eAuiTEu3XE198d6z22ENgFgGQv2GGOUk= -k8s.io/component-base v0.26.4/go.mod h1:lTuWL1Xz/a4e80gmIC3YZG2JCO4xNwtKWHJWeJmsq20= -k8s.io/component-helpers v0.26.4 h1:qbZrh8QmfL+Yn7lWEI/BPrvITGgkBy33djP5Tzsu2hA= -k8s.io/component-helpers v0.26.4/go.mod h1:2Siz5eWmaKu0khASXMTCfJuASZAbCPX9mtjlCe5IWRs= +k8s.io/api v0.26.11 h1:hLhTZRdYc3vBBOY4wbEyTLWgMyieOAk2Ws9NG57QqO4= +k8s.io/api v0.26.11/go.mod h1:bSr/A0TKRt5W2OMDdexkM/ER1NxOxiQqNNFXW2nMZrM= +k8s.io/apiextensions-apiserver v0.26.11 h1:6/T0Jm9c+Aw1AYUflPOz2sAsty304/DDSkciTr8+HuE= +k8s.io/apiextensions-apiserver v0.26.11/go.mod h1:xMqWxAB+AvSTdmFRVWlpavY9bJl/3g6yWiPn/fwZbT0= +k8s.io/apimachinery v0.26.11 h1:w//840HHdwSRKqD15j9YX9HLlU6RPlfrvW0xEhLk2+0= +k8s.io/apimachinery v0.26.11/go.mod h1:2/HZp0l6coXtS26du1Bk36fCuAEr/lVs9Q9NbpBtd1Y= +k8s.io/apiserver v0.26.11 h1:JcrlATLu5xQVLV7/rfRFFl9ivvNLmZH0dM3DFFdFp+w= +k8s.io/apiserver v0.26.11/go.mod h1:htEG/Q3sI3+6Is3Z26QzBjaCGICsz/kFj+IhIP4oJuE= +k8s.io/cli-runtime v0.26.11 h1:HO3Sgf06XkT8/8wWnhskfz4+LMKrChRz+A13vDJSQrE= +k8s.io/cli-runtime v0.26.11/go.mod h1:D98GjQtDmqn7WDuKBgWivd6R8qEs3yzT19EmCM5pqBs= +k8s.io/client-go v0.26.11 h1:RjfZr5+vQjjTRmk4oCqHyC0cgrZXPjw+X+ge35sk4GI= +k8s.io/client-go v0.26.11/go.mod h1:+emNszw9va/uRJIM5ALTBtFnlZMTjwBrNjRfEh0iuw8= +k8s.io/code-generator v0.26.11 h1:S0PJxapUhG6LWYezYB/FVE5Gf4BxGY0fCwnLrwfQ/70= +k8s.io/code-generator v0.26.11/go.mod h1:Hjxj7hpvSxcNnYIWzCSuEdwN0/9aHlezQRKJXr0Kv8U= +k8s.io/component-base v0.26.11 h1:1/JmB6fexefGByfFyIK6aHksZZVtaDskttzXOzmZ6zA= +k8s.io/component-base v0.26.11/go.mod h1:jYNisnoM6iWFRUg51pxaQabzL5fBYTr5CMpsLjUYGp0= +k8s.io/component-helpers v0.26.11 h1:XD2/2lik/5n1WFepDvgHzIGL0tix/EU3GaxGJHdsgkA= +k8s.io/component-helpers v0.26.11/go.mod h1:lw3bchkI0NHMPmb+CE73GznPW0Mvqd/Y9UVMEqBkysE= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20220902162205-c0856e24416d h1:U9tB195lKdzwqicbJvyJeOXV7Klv+wNAWENRnXEGi08= k8s.io/gengo v0.0.0-20220902162205-c0856e24416d/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= @@ -2645,15 +2647,15 @@ k8s.io/klog/v2 v2.5.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-aggregator v0.26.4 h1:iGljhq5exQkbuc3bnkwUx95RPCBDExg7DkX9XaYhg6w= -k8s.io/kube-aggregator v0.26.4/go.mod h1:eWfg4tU0+l57ebWiS5THOANIJUrKRxudSVDJ+63bqvQ= +k8s.io/kube-aggregator v0.26.11 h1:P46aQPWOE+8bTbK2cqxUFP1XwH4ShZEHnlk1T5QFT8U= +k8s.io/kube-aggregator v0.26.11/go.mod h1:XNGLFzn4Ex7qFVqpCnvLUr354EM4QhMFuFSoB6JHmL4= k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= -k8s.io/kubectl v0.26.4 h1:A0Oa0u/po4KxXnXsNCOwLojAe9cQR3TJNJabEIf7U1w= -k8s.io/kubectl v0.26.4/go.mod h1:cWtp/+I4p+h5En3s2zO1zCry9v3/6h37EQ2tF3jNRnM= -k8s.io/kubernetes v1.26.4 h1:/c00/JjOltBwhNOBs+hO433Q3fRyeWWtyYF6z2xtmag= -k8s.io/kubernetes v1.26.4/go.mod h1:NxzR7U7mS+OGa3J/qweI86Pek//mlfHqDgt6NNGdz8g= +k8s.io/kubectl v0.26.11 h1:cVPzYA4HKefU3tPiVK7hZpJ+5Lm04XoyvCCY5ODznpQ= +k8s.io/kubectl v0.26.11/go.mod h1:xjEX/AHtEQrGj2AGqVopyHr/JU1hLy1k7Yn48JuK9LQ= +k8s.io/kubernetes v1.26.11 h1:g3r1IAUqsaHnOG2jdpoagJ5W9UCXkR2ljQ/7BmCzPNg= +k8s.io/kubernetes v1.26.11/go.mod h1:z1URAaBJ+XnOTr3Q/l4umxRUxn/OyD2fbkUgS0Bl7u4= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk= diff --git a/hack/update-kubernetes-version.sh b/hack/update-kubernetes-version.sh new file mode 100755 index 0000000000000..8d52033a601fc --- /dev/null +++ b/hack/update-kubernetes-version.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ -z "${1:-}" ]; then + echo "Example usage: ./hack/update-kubernetes-version.sh v1.26.11" + exit 1 +fi +VERSION=${1#"v"} +MODS=($( + curl -sS https://raw.githubusercontent.com/kubernetes/kubernetes/v${VERSION}/go.mod | + sed -n 's|.*k8s.io/\(.*\) => ./staging/src/k8s.io/.*|k8s.io/\1|p' +)) +for MOD in "${MODS[@]}"; do + echo "Updating $MOD..." >&2 + V=$( + go mod download -json "${MOD}@kubernetes-${VERSION}" | + sed -n 's|.*"Version": "\(.*\)".*|\1|p' + ) + go mod edit "-replace=${MOD}=${MOD}@${V}" +done +go get "k8s.io/kubernetes@v${VERSION}" +go mod tidy From 10bb8b0f6865309c4ae578a6d0d31d9fcee30d94 Mon Sep 17 00:00:00 2001 From: Elouan Keryell-Even Date: Mon, 11 Dec 2023 13:03:13 +0100 Subject: [PATCH 32/66] docs: fix broken link in secret-management.md (#16588) Signed-off-by: Elouan Keryell-Even --- docs/operator-manual/secret-management.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/operator-manual/secret-management.md b/docs/operator-manual/secret-management.md index ab06a46014b20..aa224e20ff742 100644 --- a/docs/operator-manual/secret-management.md +++ b/docs/operator-manual/secret-management.md @@ -10,7 +10,7 @@ Here are some ways people are doing GitOps secrets: * [Bitnami Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets) * [External Secrets Operator](https://github.com/external-secrets/external-secrets) * [Hashicorp Vault](https://www.vaultproject.io) -* [Bank-Vaults]((https://bank-vaults.dev/)) +* [Bank-Vaults](https://bank-vaults.dev/) * [Helm Secrets](https://github.com/jkroepke/helm-secrets) * [Kustomize secret generator plugins](https://github.com/kubernetes-sigs/kustomize/blob/fd7a353df6cece4629b8e8ad56b71e30636f38fc/examples/kvSourceGoPlugin.md#secret-values-from-anywhere) * [aws-secret-operator](https://github.com/mumoshu/aws-secret-operator) From d70f9a49b139872e5540e9bdf60e78867112d78c Mon Sep 17 00:00:00 2001 From: John Date: Wed, 13 Dec 2023 08:48:20 +0100 Subject: [PATCH 33/66] docs(monitoring): add new ServiceMonitors for redis, dex, notifications (#16534) Signed-off-by: dmpe --- docs/operator-manual/metrics.md | 48 ++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/docs/operator-manual/metrics.md b/docs/operator-manual/metrics.md index 2bde146a786a2..cfd2a8a8093ac 100644 --- a/docs/operator-manual/metrics.md +++ b/docs/operator-manual/metrics.md @@ -86,7 +86,7 @@ Scraped at the `argocd-repo-server:8084/metrics` endpoint. ## Prometheus Operator If using Prometheus Operator, the following ServiceMonitor example manifests can be used. -Change `metadata.labels.release` to the name of label selected by your Prometheus. +Add a namespace where Argo CD is installed and change `metadata.labels.release` to the name of label selected by your Prometheus. ```yaml apiVersion: monitoring.coreos.com/v1 @@ -148,6 +148,52 @@ spec: - port: metrics ``` +```yaml +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: argocd-dex-server + labels: + release: prometheus-operator +spec: + selector: + matchLabels: + app.kubernetes.io/name: argocd-dex-server + endpoints: + - port: metrics +``` + +```yaml +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: argocd-redis-haproxy-metrics +spec: + selector: + matchLabels: + app.kubernetes.io/name: argocd-redis-ha-haproxy + endpoints: + - port: http-exporter-port +``` + +For notifications controller, you need to additionally add following: + +```yaml +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: argocd-notifications-controller + labels: + release: prometheus-operator +spec: + selector: + matchLabels: + app.kubernetes.io/name: argocd-notifications-controller-metrics + endpoints: + - port: metrics +``` + + ## Dashboards You can find an example Grafana dashboard [here](https://github.com/argoproj/argo-cd/blob/master/examples/dashboard.json) or check demo instance From cde68e0691dd23d184ead6b90b71ce10b94eb068 Mon Sep 17 00:00:00 2001 From: Jason Wang <7304774+jasonwzm@users.noreply.github.com> Date: Thu, 14 Dec 2023 00:36:12 -0800 Subject: [PATCH 34/66] Add Statsig as a user (#16604) Signed-off-by: Jason Wang <7304774+jasonwzm@users.noreply.github.com> --- USERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/USERS.md b/USERS.md index cc1c22e3d9d7b..ea0c44ae1ff4b 100644 --- a/USERS.md +++ b/USERS.md @@ -266,6 +266,7 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Spendesk](https://spendesk.com/) 1. [Splunk](https://splunk.com/) 1. [Spores Labs](https://spores.app) +1. [Statsig](https://statsig.com) 1. [StreamNative](https://streamnative.io) 1. [Stuart](https://stuart.com/) 1. [Sumo Logic](https://sumologic.com/) From ecef174301214c9d2ebbb487aaa33f14783e287d Mon Sep 17 00:00:00 2001 From: yedayak <43016107+yedayak@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:18:30 +0200 Subject: [PATCH 35/66] docs: fix indentation for preserve file mode (#16598) The preserveFileMode isn't under parameters, it should be right under spec. It's correct in the other example here. Signed-off-by: yedayak --- docs/operator-manual/config-management-plugins.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/operator-manual/config-management-plugins.md b/docs/operator-manual/config-management-plugins.md index 5a831dc5e7c47..7c86075ff2f7f 100644 --- a/docs/operator-manual/config-management-plugins.md +++ b/docs/operator-manual/config-management-plugins.md @@ -110,9 +110,9 @@ spec: # static parameter announcements list. command: [echo, '[{"name": "example-param", "string": "default-string-value"}]'] - # If set to `true` then the plugin receives repository files with original file mode. Dangerous since the repository - # might have executable files. Set to true only if you trust the CMP plugin authors. - preserveFileMode: false + # If set to `true` then the plugin receives repository files with original file mode. Dangerous since the repository + # might have executable files. Set to true only if you trust the CMP plugin authors. + preserveFileMode: false ``` !!! note From 2ad419bf63cdb32166422591d3b1e005d0df1946 Mon Sep 17 00:00:00 2001 From: Nathan Romriell Date: Thu, 14 Dec 2023 03:35:55 -0800 Subject: [PATCH 36/66] fix(repo-server): excess git requests, short-circuit GenerateManifests ref only (#16501) * fix(repo-server): excess git requests part 2, short-circuit GenerateManifests ref only Signed-off-by: nromriell * chore(logging): pr feedback, add debug log to short circuit Signed-off-by: nromriell * chore: pr feedback, add ref to debug statement Signed-off-by: nromriell --------- Signed-off-by: nromriell --- reposerver/repository/repository.go | 11 +++++ reposerver/repository/repository_test.go | 58 +++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go index 155ff8c6f8653..41f26b1f434b8 100644 --- a/reposerver/repository/repository.go +++ b/reposerver/repository/repository.go @@ -507,6 +507,17 @@ func resolveReferencedSources(hasMultipleSources bool, source *v1alpha1.Applicat func (s *Service) GenerateManifest(ctx context.Context, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) { var res *apiclient.ManifestResponse var err error + + // Skip this path for ref only sources + if q.HasMultipleSources && q.ApplicationSource.Path == "" && q.ApplicationSource.Chart == "" && q.ApplicationSource.Ref != "" { + log.Debugf("Skipping manifest generation for ref only source for application: %s and ref %s", q.AppName, q.ApplicationSource.Ref) + _, revision, err := s.newClientResolveRevision(q.Repo, q.Revision, git.WithCache(s.cache, !q.NoRevisionCache && !q.NoCache)) + res = &apiclient.ManifestResponse{ + Revision: revision, + } + return res, err + } + cacheFn := func(cacheKey string, refSourceCommitSHAs cache.ResolvedRevisions, firstInvocation bool) (bool, error) { ok, resp, err := s.getManifestCacheEntry(cacheKey, q, refSourceCommitSHAs, firstInvocation) res = resp diff --git a/reposerver/repository/repository_test.go b/reposerver/repository/repository_test.go index ab2225b6e778d..3f2f74c4e5ae0 100644 --- a/reposerver/repository/repository_test.go +++ b/reposerver/repository/repository_test.go @@ -337,6 +337,62 @@ func TestGenerateManifests_EmptyCache(t *testing.T) { gitMocks.AssertCalled(t, "Fetch", mock.Anything) } +// Test that when Generate manifest is called with a source that is ref only it does not try to generate manifests or hit the manifest cache +// but it does resolve and cache the revision +func TestGenerateManifest_RefOnlyShortCircuit(t *testing.T) { + lsremoteCalled := false + dir := t.TempDir() + repopath := fmt.Sprintf("%s/tmprepo", dir) + repoRemote := fmt.Sprintf("file://%s", repopath) + cacheMocks := newCacheMocks() + t.Cleanup(cacheMocks.mockCache.StopRedisCallback) + service := NewService(metrics.NewMetricsServer(), cacheMocks.cache, RepoServerInitConstants{ParallelismLimit: 1}, argo.NewResourceTracking(), &git.NoopCredsStore{}, repopath) + service.newGitClient = func(rawRepoURL string, root string, creds git.Creds, insecure bool, enableLfs bool, proxy string, opts ...git.ClientOpts) (client git.Client, e error) { + opts = append(opts, git.WithEventHandlers(git.EventHandlers{ + // Primary check, we want to make sure ls-remote is not called when the item is in cache + OnLsRemote: func(repo string) func() { + return func() { + lsremoteCalled = true + } + }, + OnFetch: func(repo string) func() { + return func() { + assert.Fail(t, "Fetch should not be called from GenerateManifest when the source is ref only") + } + }, + })) + gitClient, err := git.NewClientExt(rawRepoURL, root, creds, insecure, enableLfs, proxy, opts...) + return gitClient, err + } + revision := initGitRepo(t, newGitRepoOptions{ + path: repopath, + createPath: true, + remote: repoRemote, + addEmptyCommit: true, + }) + src := argoappv1.ApplicationSource{RepoURL: repoRemote, TargetRevision: "HEAD", Ref: "test-ref"} + repo := &argoappv1.Repository{ + Repo: repoRemote, + } + q := apiclient.ManifestRequest{ + Repo: repo, + Revision: "HEAD", + HasMultipleSources: true, + ApplicationSource: &src, + ProjectName: "default", + ProjectSourceRepos: []string{"*"}, + } + _, err := service.GenerateManifest(context.Background(), &q) + assert.NoError(t, err) + cacheMocks.mockCache.AssertCacheCalledTimes(t, &repositorymocks.CacheCallCounts{ + ExternalSets: 1, + ExternalGets: 1}) + assert.True(t, lsremoteCalled, "ls-remote should be called when the source is ref only") + var revisions [][2]string + assert.NoError(t, cacheMocks.cacheutilCache.GetItem(fmt.Sprintf("git-refs|%s", repoRemote), &revisions)) + assert.ElementsMatch(t, [][2]string{{"refs/heads/main", revision}, {"HEAD", "ref: refs/heads/main"}}, revisions) +} + // Test that calling manifest generation on source helm reference helm files that when the revision is cached it does not call ls-remote func TestGenerateManifestsHelmWithRefs_CachedNoLsRemote(t *testing.T) { dir := t.TempDir() @@ -2758,7 +2814,7 @@ func initGitRepo(t *testing.T, options newGitRepoOptions) (revision string) { assert.NoError(t, os.Mkdir(options.path, 0755)) } - cmd := exec.Command("git", "init", options.path) + cmd := exec.Command("git", "init", "-b", "main", options.path) cmd.Dir = options.path assert.NoError(t, cmd.Run()) From 23959ca1f739cb53e3841d119a9f7678aa799f4d Mon Sep 17 00:00:00 2001 From: flux-ricky <42919858+flux-ricky@users.noreply.github.com> Date: Fri, 15 Dec 2023 00:57:21 +1300 Subject: [PATCH 37/66] fix(cli): `argocd admin settings resource-overrides health` to not ignore wildard customizations (#16461) Signed-off-by: Ricky McMillen --- cmd/argocd/commands/admin/settings.go | 22 +++++------- cmd/argocd/commands/admin/settings_test.go | 39 ++++++++++++++++++---- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/cmd/argocd/commands/admin/settings.go b/cmd/argocd/commands/admin/settings.go index 281d9875691c4..0274b4a422f09 100644 --- a/cmd/argocd/commands/admin/settings.go +++ b/cmd/argocd/commands/admin/settings.go @@ -373,11 +373,7 @@ func executeResourceOverrideCommand(ctx context.Context, cmdCtx commandContext, if gvk.Group != "" { key = fmt.Sprintf("%s/%s", gvk.Group, gvk.Kind) } - override, hasOverride := overrides[key] - if !hasOverride { - _, _ = fmt.Printf("No overrides configured for '%s/%s'\n", gvk.Group, gvk.Kind) - return - } + override := overrides[key] callback(res, override, overrides) } @@ -519,16 +515,16 @@ argocd admin settings resource-overrides health ./deploy.yaml --argocd-cm-path . executeResourceOverrideCommand(ctx, cmdCtx, args, func(res unstructured.Unstructured, override v1alpha1.ResourceOverride, overrides map[string]v1alpha1.ResourceOverride) { gvk := res.GroupVersionKind() - if override.HealthLua == "" { - _, _ = fmt.Printf("Health script is not configured for '%s/%s'\n", gvk.Group, gvk.Kind) - return - } - resHealth, err := healthutil.GetResourceHealth(&res, lua.ResourceHealthOverrides(overrides)) - errors.CheckError(err) - _, _ = fmt.Printf("STATUS: %s\n", resHealth.Status) - _, _ = fmt.Printf("MESSAGE: %s\n", resHealth.Message) + if err != nil { + errors.CheckError(err) + } else if resHealth == nil { + fmt.Printf("Health script is not configured for '%s/%s'\n", gvk.Group, gvk.Kind) + } else { + _, _ = fmt.Printf("STATUS: %s\n", resHealth.Status) + _, _ = fmt.Printf("MESSAGE: %s\n", resHealth.Message) + } }) }, } diff --git a/cmd/argocd/commands/admin/settings_test.go b/cmd/argocd/commands/admin/settings_test.go index adb18c80ee84e..ff817017f4be5 100644 --- a/cmd/argocd/commands/admin/settings_test.go +++ b/cmd/argocd/commands/admin/settings_test.go @@ -226,6 +226,18 @@ spec: replicas: 0` ) +const ( + testCustomResourceYAML = `apiVersion: v1 +apiVersion: example.com/v1alpha1 +kind: ExampleResource +metadata: + name: example-resource + labels: + app: example +spec: + replicas: 0` +) + const ( testCronJobYAML = `apiVersion: batch/v1 kind: CronJob @@ -285,7 +297,7 @@ func TestResourceOverrideIgnoreDifferences(t *testing.T) { assert.NoError(t, err) }) assert.NoError(t, err) - assert.Contains(t, out, "No overrides configured") + assert.Contains(t, out, "Ignore differences are not configured for 'apps/Deployment'\n") }) t.Run("DataIgnored", func(t *testing.T) { @@ -305,7 +317,7 @@ func TestResourceOverrideIgnoreDifferences(t *testing.T) { } func TestResourceOverrideHealth(t *testing.T) { - f, closer, err := tempFile(testDeploymentYAML) + f, closer, err := tempFile(testCustomResourceYAML) if !assert.NoError(t, err) { return } @@ -313,19 +325,34 @@ func TestResourceOverrideHealth(t *testing.T) { t.Run("NoHealthAssessment", func(t *testing.T) { cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ - "resource.customizations": `apps/Deployment: {}`})) + "resource.customizations": `example.com/ExampleResource: {}`})) out, err := captureStdout(func() { cmd.SetArgs([]string{"health", f}) err := cmd.Execute() assert.NoError(t, err) }) assert.NoError(t, err) - assert.Contains(t, out, "Health script is not configured") + assert.Contains(t, out, "Health script is not configured for 'example.com/ExampleResource'\n") }) t.Run("HealthAssessmentConfigured", func(t *testing.T) { cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ - "resource.customizations": `apps/Deployment: + "resource.customizations": `example.com/ExampleResource: + health.lua: | + return { status = "Progressing" } +`})) + out, err := captureStdout(func() { + cmd.SetArgs([]string{"health", f}) + err := cmd.Execute() + assert.NoError(t, err) + }) + assert.NoError(t, err) + assert.Contains(t, out, "Progressing") + }) + + t.Run("HealthAssessmentConfiguredWildcard", func(t *testing.T) { + cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{ + "resource.customizations": `example.com/*: health.lua: | return { status = "Progressing" } `})) @@ -412,7 +439,7 @@ resume false action.lua: | job1 = {} job1.apiVersion = "batch/v1" - job1.kind = "Job" + job1.kind = "Job" job1.metadata = {} job1.metadata.name = "hello-1" job1.metadata.namespace = "obj.metadata.namespace" From a7d0da941cc9d92f3aef19acc0c898a43fee3b16 Mon Sep 17 00:00:00 2001 From: Matthew Hughes <34972397+matthewhughes934@users.noreply.github.com> Date: Thu, 14 Dec 2023 12:03:33 +0000 Subject: [PATCH 38/66] Add meta.Duration as known type for diffing (#16587) So that this can be used for custom resources. The motivation for this is issue #6008: with this change one should be able to use the known type to ensure equal durations don't present a diff, e,g. via adding to `argocd-cm`: data: resource.customizations.knownTypeFields.cert-manager.io_Certificate: | - field: spec.duration type: meta/v1/Duration This change is based on 5b464c996bd5ebc1d39bb013af3bed34415d77d2, though I've included the API version in the type path to be consistent with the generated type (i.e. `meta/v1/Duration` and not `meta/Duration`). For documentation I've just manually listed the extra types that aren't auto-generated, though this requires having to keep this list and the code in-sync but this is probably not a big issue since the number of extra types is not frequently changed. In the tests I've used `require.*` methods since I find this simpler than `if !assert.Blah(...) { return }` which the other tests in this file are using. Signed-off-by: Matthew Hughes --- docs/user-guide/diffing.md | 5 +++- .../argo/normalizers/knowntypes_normalizer.go | 4 +++ .../normalizers/knowntypes_normalizer_test.go | 28 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/user-guide/diffing.md b/docs/user-guide/diffing.md index 5a056b9c3769b..61f799e514d6a 100644 --- a/docs/user-guide/diffing.md +++ b/docs/user-guide/diffing.md @@ -181,4 +181,7 @@ data: type: core/v1/PodSpec ``` -The list of supported Kubernetes types is available in [diffing_known_types.txt](https://raw.githubusercontent.com/argoproj/argo-cd/master/util/argo/normalizers/diffing_known_types.txt) +The list of supported Kubernetes types is available in [diffing_known_types.txt](https://raw.githubusercontent.com/argoproj/argo-cd/master/util/argo/normalizers/diffing_known_types.txt) and additionally: + +* `core/Quantity` +* `meta/v1/duration` diff --git a/util/argo/normalizers/knowntypes_normalizer.go b/util/argo/normalizers/knowntypes_normalizer.go index 403065c58a6df..f96a366a75d6a 100644 --- a/util/argo/normalizers/knowntypes_normalizer.go +++ b/util/argo/normalizers/knowntypes_normalizer.go @@ -8,6 +8,7 @@ import ( log "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -32,6 +33,9 @@ func init() { knownTypes["core/Quantity"] = func() interface{} { return &resource.Quantity{} } + knownTypes["meta/v1/Duration"] = func() interface{} { + return &metav1.Duration{} + } } // NewKnownTypesNormalizer create a normalizer that re-format custom resource fields using built-in Kubernetes types. diff --git a/util/argo/normalizers/knowntypes_normalizer_test.go b/util/argo/normalizers/knowntypes_normalizer_test.go index 57e436195f890..37c34d37509b3 100644 --- a/util/argo/normalizers/knowntypes_normalizer_test.go +++ b/util/argo/normalizers/knowntypes_normalizer_test.go @@ -11,6 +11,7 @@ import ( "github.com/argoproj/pkg/errors" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/yaml" @@ -228,6 +229,33 @@ spec: assert.Equal(t, "1250M", ram) } +func TestNormalize_Duration(t *testing.T) { + cert := mustUnmarshalYAML(` +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: my-cert +spec: + duration: 8760h +`) + normalizer, err := NewKnownTypesNormalizer(map[string]v1alpha1.ResourceOverride{ + "cert-manager.io/Certificate": { + KnownTypeFields: []v1alpha1.KnownTypeField{{ + Type: "meta/v1/Duration", + Field: "spec.duration", + }}, + }, + }) + require.NoError(t, err) + + require.NoError(t, normalizer.Normalize(cert)) + + duration, ok, err := unstructured.NestedFieldNoCopy(cert.Object, "spec", "duration") + require.NoError(t, err) + require.True(t, ok) + require.Equal(t, "8760h0m0s", duration) +} + func TestFieldDoesNotExist(t *testing.T) { rollout := mustUnmarshalYAML(someCRDYaml) normalizer, err := NewKnownTypesNormalizer(map[string]v1alpha1.ResourceOverride{ From a6d8a01c0725dad431a69d28671d8f7ce41d4404 Mon Sep 17 00:00:00 2001 From: afrancis101 <64639952+afrancis101@users.noreply.github.com> Date: Thu, 14 Dec 2023 16:49:18 +0000 Subject: [PATCH 39/66] fix(action): Add missing owner refs and annotation to create-job action (#16607) Signed-off-by: Arron Francis --- .../batch/CronJob/actions/create-job/action.lua | 6 ++++++ .../batch/CronJob/actions/testdata/job.yaml | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/resource_customizations/batch/CronJob/actions/create-job/action.lua b/resource_customizations/batch/CronJob/actions/create-job/action.lua index 8753144d404e7..a6f3253a5b757 100644 --- a/resource_customizations/batch/CronJob/actions/create-job/action.lua +++ b/resource_customizations/batch/CronJob/actions/create-job/action.lua @@ -38,12 +38,18 @@ if job.metadata == nil then end job.metadata.name = obj.metadata.name .. "-" ..os.date("!%Y%m%d%H%M") job.metadata.namespace = obj.metadata.namespace +if job.metadata.annotations == nil then + job.metadata.annotations = {} +end +job.metadata.annotations['cronjob.kubernetes.io/instantiate'] = "manual" local ownerRef = {} ownerRef.apiVersion = obj.apiVersion ownerRef.kind = obj.kind ownerRef.name = obj.metadata.name ownerRef.uid = obj.metadata.uid +ownerRef.blockOwnerDeletion = true +ownerRef.controller = true job.metadata.ownerReferences = {} job.metadata.ownerReferences[1] = ownerRef diff --git a/resource_customizations/batch/CronJob/actions/testdata/job.yaml b/resource_customizations/batch/CronJob/actions/testdata/job.yaml index 1ef281afdcdb4..322ab0480beb5 100644 --- a/resource_customizations/batch/CronJob/actions/testdata/job.yaml +++ b/resource_customizations/batch/CronJob/actions/testdata/job.yaml @@ -8,6 +8,7 @@ labels: my: label annotations: + cronjob.kubernetes.io/instantiate: manual my: annotation spec: ttlSecondsAfterFinished: 100 @@ -26,4 +27,4 @@ - /bin/sh - -c - date; echo Hello from the Kubernetes cluster - restartPolicy: OnFailure \ No newline at end of file + restartPolicy: OnFailure From 2f22a690e706c7c7746d6569d22aa345f7005362 Mon Sep 17 00:00:00 2001 From: Dennis Pan Date: Thu, 14 Dec 2023 23:48:26 -0800 Subject: [PATCH 40/66] docs: callout the need to restart pods to pickup service accounts (#16610) Signed-off-by: Dennis Pan --- docs/operator-manual/declarative-setup.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/operator-manual/declarative-setup.md b/docs/operator-manual/declarative-setup.md index ee216a7118f7f..c1f5ba2b2d3bd 100644 --- a/docs/operator-manual/declarative-setup.md +++ b/docs/operator-manual/declarative-setup.md @@ -676,8 +676,10 @@ extended to allow assumption of multiple roles, either as an explicit array of r } ``` -Example service account configs for `argocd-application-controller` and `argocd-server`. Note that once the annotations -have been set on the service accounts, both the application controller and server pods need to be restarted. +Example service account configs for `argocd-application-controller` and `argocd-server`. + +!!! warning + Once the annotations have been set on the service accounts, both the application controller and server pods need to be restarted. ```yaml apiVersion: v1 From bdf2c6a18c6572c0aa024e36d592b925f9836467 Mon Sep 17 00:00:00 2001 From: Robin Lieb Date: Fri, 15 Dec 2023 18:44:14 +0100 Subject: [PATCH 41/66] docs: update telepresence quickstart link (#16616) Signed-off-by: Robin Lieb --- docs/developer-guide/debugging-remote-environment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/debugging-remote-environment.md b/docs/developer-guide/debugging-remote-environment.md index 7f8102a75c502..5548d3444af8c 100644 --- a/docs/developer-guide/debugging-remote-environment.md +++ b/docs/developer-guide/debugging-remote-environment.md @@ -45,7 +45,7 @@ And uninstall telepresence from your cluster: telepresence helm uninstall ``` -See [this quickstart](https://www.telepresence.io/docs/latest/howtos/intercepts/) for more information on how to intercept services using Telepresence. +See [this quickstart](https://www.telepresence.io/docs/latest/quick-start/) for more information on how to intercept services using Telepresence. ### Connect (telepresence v1) Use the following command instead: From 0b35e2f1fe27f395e6106a7466d58911c4f7ec9c Mon Sep 17 00:00:00 2001 From: morre Date: Fri, 15 Dec 2023 18:51:42 +0100 Subject: [PATCH 42/66] docs: add documentation for the argocd cluster commands (#16555) * docs: add documentation for the argocd cluster commands This adds initial non-reference documentation for the `argocd cluster` commands as part of #14531. The main goal here is to bootstrap that page and point users wanting to remove the `in-cluster` cluster to the right setting. Signed-off-by: Maurice Meyer * fixup! docs: add documentation for the argocd cluster commands Signed-off-by: Morre * fixup! docs: add documentation for the argocd cluster commands Signed-off-by: Maurice Meyer --------- Signed-off-by: Maurice Meyer Signed-off-by: Morre --- docs/operator-manual/cluster-management.md | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 docs/operator-manual/cluster-management.md diff --git a/docs/operator-manual/cluster-management.md b/docs/operator-manual/cluster-management.md new file mode 100644 index 0000000000000..bd0d28e08dba7 --- /dev/null +++ b/docs/operator-manual/cluster-management.md @@ -0,0 +1,23 @@ +# Cluster Management + +This guide is for operators looking to manage clusters on the CLI. If you want to use Kubernetes resources for this, check out [Declarative Setup](./declarative-setup.md#clusters). + +Not all commands are described here, see the [argocd cluster Command Reference](../user-guide/commands/argocd_cluster.md) for all available commands. + +## Adding a cluster + +Run `argocd cluster add context-name`. + +If you're unsure about the context names, run `kubectl config get-contexts` to get them all listed. + +This will connect to the cluster and install the necessary resources for ArgoCD to connect to it. +Note that you will need privileged access to the cluster. + +## Removing a cluster + +Run `argocd cluster rm context-name`. + +This removes the cluster with the specified name. + +!!!note "in-cluster cannot be removed" + The `in-cluster` cluster cannot be removed with this. If you want to disable the `in-cluster` configuration, you need to update your `argocd-cm` ConfigMap. Set [`cluster.inClusterEnabled`](./argocd-cm-yaml.md) to `"false"` From c5d5cdb31a4b5df312dd4e21aa20fdf26ccf2168 Mon Sep 17 00:00:00 2001 From: Sam Greening <2552620+SG60@users.noreply.github.com> Date: Sun, 17 Dec 2023 18:07:25 +0000 Subject: [PATCH 43/66] fix(appset): Use case insensitive comparison of repo details in appset webhook handler (#16503) (#16504) * fix: case insensitive comparison of repo details in appset webhook handler Signed-off-by: Sam Greening <2552620+SG60@users.noreply.github.com> * chore(appset test): test case-insensitivity of appset github webhooks Signed-off-by: Sam Greening <2552620+SG60@users.noreply.github.com> * chore(appset test): test case for PR labeled event And fix a couple of spelling errors Signed-off-by: Sam Greening <2552620+SG60@users.noreply.github.com> --------- Signed-off-by: Sam Greening <2552620+SG60@users.noreply.github.com> Co-authored-by: Soumya Ghosh Dastidar <44349253+gdsoumya@users.noreply.github.com> --- .../github-pull-request-labeled-event.json | 473 ++++++++++++++++++ applicationset/webhook/webhook.go | 6 +- applicationset/webhook/webhook_test.go | 17 +- 3 files changed, 490 insertions(+), 6 deletions(-) create mode 100644 applicationset/webhook/testdata/github-pull-request-labeled-event.json diff --git a/applicationset/webhook/testdata/github-pull-request-labeled-event.json b/applicationset/webhook/testdata/github-pull-request-labeled-event.json new file mode 100644 index 0000000000000..f912a2fdb4a97 --- /dev/null +++ b/applicationset/webhook/testdata/github-pull-request-labeled-event.json @@ -0,0 +1,473 @@ +{ + "action": "labeled", + "number": 2, + "label": { + "id": 6129306173, + "node_id": "LA_kwDOIqudU88AAAABbVXKPQ", + "url": "https://api.github.com/repos/SG60/backstage/labels/deploy-preview", + "name": "deploy-preview", + "color": "bfd4f2", + "default": false, + "description": "" + }, + "pull_request": { + "url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2", + "id": 279147437, + "node_id": "MDExOlB1bGxSZXF1ZXN0Mjc5MTQ3NDM3", + "html_url": "https://github.com/Codertocat/Hello-World/pull/2", + "diff_url": "https://github.com/Codertocat/Hello-World/pull/2.diff", + "patch_url": "https://github.com/Codertocat/Hello-World/pull/2.patch", + "issue_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/2", + "number": 2, + "state": "open", + "locked": false, + "title": "Update the README with new information.", + "user": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "body": "This is a pretty simple change that we need to pull into master.", + "created_at": "2019-05-15T15:20:33Z", + "updated_at": "2019-05-15T15:20:33Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": null, + "assignee": null, + "assignees": [], + "requested_reviewers": [], + "requested_teams": [], + "labels": [ + { + "id": 6129306173, + "node_id": "LA_kwDOIqudU88AAAABbVXKPQ", + "url": "https://api.github.com/repos/Codertocat/Hello-World/labels/deploy-preview", + "name": "deploy-preview", + "color": "bfd4f2", + "default": false, + "description": "" + } + ], + "milestone": null, + "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2/commits", + "review_comments_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2/comments", + "review_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/2/comments", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/ec26c3e57ca3a959ca5aad62de7213c562f8c821", + "head": { + "label": "Codertocat:changes", + "ref": "changes", + "sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", + "user": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 186853002, + "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": false, + "owner": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/Codertocat/Hello-World", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks", + "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams", + "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks", + "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}", + "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events", + "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}", + "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}", + "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags", + "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}", + "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages", + "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers", + "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors", + "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription", + "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}", + "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges", + "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads", + "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}", + "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}", + "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}", + "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}", + "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments", + "created_at": "2019-05-15T15:19:25Z", + "updated_at": "2019-05-15T15:19:27Z", + "pushed_at": "2019-05-15T15:20:32Z", + "git_url": "git://github.com/Codertocat/Hello-World.git", + "ssh_url": "git@github.com:Codertocat/Hello-World.git", + "clone_url": "https://github.com/Codertocat/Hello-World.git", + "svn_url": "https://github.com/Codertocat/Hello-World", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 2, + "license": null, + "forks": 0, + "open_issues": 2, + "watchers": 0, + "default_branch": "master", + "allow_squash_merge": true, + "allow_merge_commit": true, + "allow_rebase_merge": true, + "delete_branch_on_merge": false + } + }, + "base": { + "label": "Codertocat:master", + "ref": "master", + "sha": "f95f852bd8fca8fcc58a9a2d6c842781e32a215e", + "user": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 186853002, + "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": false, + "owner": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/Codertocat/Hello-World", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks", + "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams", + "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks", + "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}", + "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events", + "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}", + "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}", + "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags", + "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}", + "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages", + "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers", + "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors", + "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription", + "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}", + "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges", + "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads", + "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}", + "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}", + "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}", + "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}", + "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments", + "created_at": "2019-05-15T15:19:25Z", + "updated_at": "2019-05-15T15:19:27Z", + "pushed_at": "2019-05-15T15:20:32Z", + "git_url": "git://github.com/Codertocat/Hello-World.git", + "ssh_url": "git@github.com:Codertocat/Hello-World.git", + "clone_url": "https://github.com/Codertocat/Hello-World.git", + "svn_url": "https://github.com/Codertocat/Hello-World", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 2, + "license": null, + "forks": 0, + "open_issues": 2, + "watchers": 0, + "default_branch": "master", + "allow_squash_merge": true, + "allow_merge_commit": true, + "allow_rebase_merge": true, + "delete_branch_on_merge": false + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2" + }, + "html": { + "href": "https://github.com/Codertocat/Hello-World/pull/2" + }, + "issue": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/issues/2" + }, + "comments": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/issues/2/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/pulls/2/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/Codertocat/Hello-World/statuses/ec26c3e57ca3a959ca5aad62de7213c562f8c821" + } + }, + "author_association": "OWNER", + "draft": false, + "merged": false, + "mergeable": null, + "rebaseable": null, + "mergeable_state": "unknown", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "maintainer_can_modify": false, + "commits": 1, + "additions": 1, + "deletions": 1, + "changed_files": 1 + }, + "repository": { + "id": 186853002, + "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", + "name": "Hello-World", + "full_name": "Codertocat/Hello-World", + "private": false, + "owner": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/Codertocat/Hello-World", + "description": null, + "fork": false, + "url": "https://api.github.com/repos/Codertocat/Hello-World", + "forks_url": "https://api.github.com/repos/Codertocat/Hello-World/forks", + "keys_url": "https://api.github.com/repos/Codertocat/Hello-World/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/Codertocat/Hello-World/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/Codertocat/Hello-World/teams", + "hooks_url": "https://api.github.com/repos/Codertocat/Hello-World/hooks", + "issue_events_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/events{/number}", + "events_url": "https://api.github.com/repos/Codertocat/Hello-World/events", + "assignees_url": "https://api.github.com/repos/Codertocat/Hello-World/assignees{/user}", + "branches_url": "https://api.github.com/repos/Codertocat/Hello-World/branches{/branch}", + "tags_url": "https://api.github.com/repos/Codertocat/Hello-World/tags", + "blobs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/Codertocat/Hello-World/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/Codertocat/Hello-World/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/Codertocat/Hello-World/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/Codertocat/Hello-World/statuses/{sha}", + "languages_url": "https://api.github.com/repos/Codertocat/Hello-World/languages", + "stargazers_url": "https://api.github.com/repos/Codertocat/Hello-World/stargazers", + "contributors_url": "https://api.github.com/repos/Codertocat/Hello-World/contributors", + "subscribers_url": "https://api.github.com/repos/Codertocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/Codertocat/Hello-World/subscription", + "commits_url": "https://api.github.com/repos/Codertocat/Hello-World/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/Codertocat/Hello-World/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/Codertocat/Hello-World/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/Codertocat/Hello-World/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/Codertocat/Hello-World/contents/{+path}", + "compare_url": "https://api.github.com/repos/Codertocat/Hello-World/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/Codertocat/Hello-World/merges", + "archive_url": "https://api.github.com/repos/Codertocat/Hello-World/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/Codertocat/Hello-World/downloads", + "issues_url": "https://api.github.com/repos/Codertocat/Hello-World/issues{/number}", + "pulls_url": "https://api.github.com/repos/Codertocat/Hello-World/pulls{/number}", + "milestones_url": "https://api.github.com/repos/Codertocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/Codertocat/Hello-World/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/Codertocat/Hello-World/labels{/name}", + "releases_url": "https://api.github.com/repos/Codertocat/Hello-World/releases{/id}", + "deployments_url": "https://api.github.com/repos/Codertocat/Hello-World/deployments", + "created_at": "2019-05-15T15:19:25Z", + "updated_at": "2019-05-15T15:19:27Z", + "pushed_at": "2019-05-15T15:20:32Z", + "git_url": "git://github.com/Codertocat/Hello-World.git", + "ssh_url": "git@github.com:Codertocat/Hello-World.git", + "clone_url": "https://github.com/Codertocat/Hello-World.git", + "svn_url": "https://github.com/Codertocat/Hello-World", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 2, + "license": null, + "forks": 0, + "open_issues": 2, + "watchers": 0, + "default_branch": "master" + }, + "sender": { + "login": "Codertocat", + "id": 21031067, + "node_id": "MDQ6VXNlcjIxMDMxMDY3", + "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/Codertocat", + "html_url": "https://github.com/Codertocat", + "followers_url": "https://api.github.com/users/Codertocat/followers", + "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", + "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", + "organizations_url": "https://api.github.com/users/Codertocat/orgs", + "repos_url": "https://api.github.com/users/Codertocat/repos", + "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/Codertocat/received_events", + "type": "User", + "site_admin": false + } +} diff --git a/applicationset/webhook/webhook.go b/applicationset/webhook/webhook.go index ce099df35ea35..d55e63e064f5a 100644 --- a/applicationset/webhook/webhook.go +++ b/applicationset/webhook/webhook.go @@ -412,10 +412,12 @@ func shouldRefreshPRGenerator(gen *v1alpha1.PullRequestGenerator, info *prGenera } if gen.Github != nil && info.Github != nil { - if gen.Github.Owner != info.Github.Owner { + // repository owner and name are case-insensitive + // See https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests + if !strings.EqualFold(gen.Github.Owner, info.Github.Owner) { return false } - if gen.Github.Repo != info.Github.Repo { + if !strings.EqualFold(gen.Github.Repo, info.Github.Repo) { return false } api := gen.Github.API diff --git a/applicationset/webhook/webhook_test.go b/applicationset/webhook/webhook_test.go index 349d275948aee..d22b1a07ca6f2 100644 --- a/applicationset/webhook/webhook_test.go +++ b/applicationset/webhook/webhook_test.go @@ -111,7 +111,7 @@ func TestWebhookHandler(t *testing.T) { expectedRefresh: false, }, { - desc: "WebHook from a GitHub repository via pull_reqeuest opened event", + desc: "WebHook from a GitHub repository via pull_request opened event", headerKey: "X-GitHub-Event", headerValue: "pull_request", payloadFile: "github-pull-request-opened-event.json", @@ -120,7 +120,7 @@ func TestWebhookHandler(t *testing.T) { expectedRefresh: true, }, { - desc: "WebHook from a GitHub repository via pull_reqeuest assigned event", + desc: "WebHook from a GitHub repository via pull_request assigned event", headerKey: "X-GitHub-Event", headerValue: "pull_request", payloadFile: "github-pull-request-assigned-event.json", @@ -128,6 +128,15 @@ func TestWebhookHandler(t *testing.T) { expectedStatusCode: http.StatusOK, expectedRefresh: false, }, + { + desc: "WebHook from a GitHub repository via pull_request labeled event", + headerKey: "X-GitHub-Event", + headerValue: "pull_request", + payloadFile: "github-pull-request-labeled-event.json", + effectedAppSets: []string{"pull-request-github", "matrix-pull-request-github", "matrix-scm-pull-request-github", "merge-pull-request-github", "plugin", "matrix-pull-request-github-plugin"}, + expectedStatusCode: http.StatusOK, + expectedRefresh: true, + }, { desc: "WebHook from a GitLab repository via open merge request event", headerKey: "X-Gitlab-Event", @@ -180,7 +189,7 @@ func TestWebhookHandler(t *testing.T) { fakeAppWithGitGenerator("git-github", namespace, "https://github.com/org/repo"), fakeAppWithGitGenerator("git-gitlab", namespace, "https://gitlab/group/name"), fakeAppWithGitGenerator("git-azure-devops", namespace, "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git"), - fakeAppWithGithubPullRequestGenerator("pull-request-github", namespace, "Codertocat", "Hello-World"), + fakeAppWithGithubPullRequestGenerator("pull-request-github", namespace, "CodErTOcat", "Hello-World"), fakeAppWithGitlabPullRequestGenerator("pull-request-gitlab", namespace, "100500"), fakeAppWithAzureDevOpsPullRequestGenerator("pull-request-azure-devops", namespace, "DefaultCollection", "Fabrikam"), fakeAppWithPluginGenerator("plugin", namespace), @@ -189,7 +198,7 @@ func TestWebhookHandler(t *testing.T) { fakeAppWithMatrixAndScmWithGitGenerator("matrix-scm-git-github", namespace, "org"), fakeAppWithMatrixAndScmWithPullRequestGenerator("matrix-scm-pull-request-github", namespace, "Codertocat"), fakeAppWithMatrixAndNestedGitGenerator("matrix-nested-git-github", namespace, "https://github.com/org/repo"), - fakeAppWithMatrixAndPullRequestGeneratorWithPluginGenerator("matrix-pull-request-github-plugin", namespace, "Codertocat", "Hello-World", "plugin-cm"), + fakeAppWithMatrixAndPullRequestGeneratorWithPluginGenerator("matrix-pull-request-github-plugin", namespace, "coDErtoCat", "HeLLO-WorLD", "plugin-cm"), fakeAppWithMergeAndGitGenerator("merge-git-github", namespace, "https://github.com/org/repo"), fakeAppWithMergeAndPullRequestGenerator("merge-pull-request-github", namespace, "Codertocat", "Hello-World"), fakeAppWithMergeAndNestedGitGenerator("merge-nested-git-github", namespace, "https://github.com/org/repo"), From 23e0d527e17f21b78c50765bed5e37f7e4c54597 Mon Sep 17 00:00:00 2001 From: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:41:32 -0500 Subject: [PATCH 44/66] feat(security): log user when access is blocked (#16558) Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --- server/application/application.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/application/application.go b/server/application/application.go index 23d4d79a767cf..8ee16b93494c8 100644 --- a/server/application/application.go +++ b/server/application/application.go @@ -152,7 +152,12 @@ func NewServer( // If the user does provide a "project," we can respond more specifically. If the user does not have access to the given // app name in the given project, we return "permission denied." If the app exists, but the project is different from func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespace, name string, getApp func() (*appv1.Application, error)) (*appv1.Application, error) { + user := session.Username(ctx) + if user == "" { + user = "Unknown user" + } logCtx := log.WithFields(map[string]interface{}{ + "user": user, "application": name, "namespace": namespace, }) From dcc17f70bf7db973fe480b8b613189b2c5ac4b4b Mon Sep 17 00:00:00 2001 From: Alexander Matyushentsev Date: Mon, 18 Dec 2023 08:40:23 -0800 Subject: [PATCH 45/66] feat: PostDelete hook support (#16595) * feat: PostDelete hooks support Signed-off-by: Alexander Matyushentsev --------- Signed-off-by: Alexander Matyushentsev --- controller/appcontroller.go | 150 +++++++++----- controller/appcontroller_test.go | 183 +++++++++++++++++- controller/hook.go | 158 +++++++++++++++ controller/state.go | 23 ++- controller/sync.go | 1 + docs/user-guide/helm.md | 4 +- docs/user-guide/resource_hooks.md | 2 + .../v1alpha1/application_defaults.go | 3 + pkg/apis/application/v1alpha1/types.go | 12 ++ test/e2e/hook_test.go | 20 ++ test/e2e/testdata/post-delete-hook/hook.yaml | 14 ++ 11 files changed, 501 insertions(+), 69 deletions(-) create mode 100644 controller/hook.go create mode 100644 test/e2e/testdata/post-delete-hook/hook.yaml diff --git a/controller/appcontroller.go b/controller/appcontroller.go index 964bcc1aa0dec..b98334b8fa567 100644 --- a/controller/appcontroller.go +++ b/controller/appcontroller.go @@ -15,7 +15,6 @@ import ( "sync" "time" - "github.com/argoproj/argo-cd/v2/pkg/ratelimiter" clustercache "github.com/argoproj/gitops-engine/pkg/cache" "github.com/argoproj/gitops-engine/pkg/diff" "github.com/argoproj/gitops-engine/pkg/health" @@ -59,6 +58,7 @@ import ( kubeerrors "k8s.io/apimachinery/pkg/api/errors" + "github.com/argoproj/argo-cd/v2/pkg/ratelimiter" appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate" "github.com/argoproj/argo-cd/v2/util/db" "github.com/argoproj/argo-cd/v2/util/errors" @@ -884,11 +884,10 @@ func (ctrl *ApplicationController) processAppOperationQueueItem() (processNext b if app.Operation != nil { ctrl.processRequestedAppOperation(app) - } else if app.DeletionTimestamp != nil && app.CascadedDeletion() { - _, err = ctrl.finalizeApplicationDeletion(app, func(project string) ([]*appv1.Cluster, error) { + } else if app.DeletionTimestamp != nil { + if err = ctrl.finalizeApplicationDeletion(app, func(project string) ([]*appv1.Cluster, error) { return ctrl.db.GetProjectClusters(context.Background(), project) - }) - if err != nil { + }); err != nil { ctrl.setAppCondition(app, appv1.ApplicationCondition{ Type: appv1.ApplicationConditionDeletionError, Message: err.Error(), @@ -1023,57 +1022,63 @@ func (ctrl *ApplicationController) getPermittedAppLiveObjects(app *appv1.Applica return objsMap, nil } -func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Application, projectClusters func(project string) ([]*appv1.Cluster, error)) ([]*unstructured.Unstructured, error) { +func (ctrl *ApplicationController) isValidDestination(app *appv1.Application) (bool, *argov1alpha.Cluster) { + // Validate the cluster using the Application destination's `name` field, if applicable, + // and set the Server field, if needed. + if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil { + log.Warnf("Unable to validate destination of the Application being deleted: %v", err) + return false, nil + } + + cluster, err := ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server) + if err != nil { + log.Warnf("Unable to locate cluster URL for Application being deleted: %v", err) + return false, nil + } + return true, cluster +} + +func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Application, projectClusters func(project string) ([]*appv1.Cluster, error)) error { logCtx := log.WithField("application", app.QualifiedName()) - logCtx.Infof("Deleting resources") // Get refreshed application info, since informer app copy might be stale app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Get(context.Background(), app.Name, metav1.GetOptions{}) if err != nil { if !apierr.IsNotFound(err) { logCtx.Errorf("Unable to get refreshed application info prior deleting resources: %v", err) } - return nil, nil + return nil } proj, err := ctrl.getAppProj(app) if err != nil { - return nil, err + return err } - // validDestination is true if the Application destination points to a cluster that is managed by Argo CD - // (and thus either a cluster secret exists for it, or it's local); validDestination is false otherwise. - validDestination := true - - // Validate the cluster using the Application destination's `name` field, if applicable, - // and set the Server field, if needed. - if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil { - log.Warnf("Unable to validate destination of the Application being deleted: %v", err) - validDestination = false - } - - objs := make([]*unstructured.Unstructured, 0) - var cluster *appv1.Cluster - - // Attempt to validate the destination via its URL - if validDestination { - if cluster, err = ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server); err != nil { - log.Warnf("Unable to locate cluster URL for Application being deleted: %v", err) - validDestination = false + isValid, cluster := ctrl.isValidDestination(app) + if !isValid { + app.UnSetCascadedDeletion() + app.UnSetPostDeleteFinalizer() + if err := ctrl.updateFinalizers(app); err != nil { + return err } + logCtx.Infof("Resource entries removed from undefined cluster") + return nil } + config := metrics.AddMetricsTransportWrapper(ctrl.metricsServer, app, cluster.RESTConfig()) - if validDestination { + if app.CascadedDeletion() { + logCtx.Infof("Deleting resources") // ApplicationDestination points to a valid cluster, so we may clean up the live objects - + objs := make([]*unstructured.Unstructured, 0) objsMap, err := ctrl.getPermittedAppLiveObjects(app, proj, projectClusters) if err != nil { - return nil, err + return err } for k := range objsMap { // Wait for objects pending deletion to complete before proceeding with next sync wave if objsMap[k].GetDeletionTimestamp() != nil { logCtx.Infof("%d objects remaining for deletion", len(objsMap)) - return objs, nil + return nil } if ctrl.shouldBeDeleted(app, objsMap[k]) { @@ -1081,8 +1086,6 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic } } - config := metrics.AddMetricsTransportWrapper(ctrl.metricsServer, app, cluster.RESTConfig()) - filteredObjs := FilterObjectsForDeletion(objs) propagationPolicy := metav1.DeletePropagationForeground @@ -1096,12 +1099,12 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic return ctrl.kubectl.DeleteResource(context.Background(), config, obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), metav1.DeleteOptions{PropagationPolicy: &propagationPolicy}) }) if err != nil { - return objs, err + return err } objsMap, err = ctrl.getPermittedAppLiveObjects(app, proj, projectClusters) if err != nil { - return nil, err + return err } for k, obj := range objsMap { @@ -1111,38 +1114,67 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic } if len(objsMap) > 0 { logCtx.Infof("%d objects remaining for deletion", len(objsMap)) - return objs, nil + return nil } + logCtx.Infof("Successfully deleted %d resources", len(objs)) + app.UnSetCascadedDeletion() + return ctrl.updateFinalizers(app) } - if err := ctrl.cache.SetAppManagedResources(app.Name, nil); err != nil { - return objs, err - } + if app.HasPostDeleteFinalizer() { + objsMap, err := ctrl.getPermittedAppLiveObjects(app, proj, projectClusters) + if err != nil { + return err + } - if err := ctrl.cache.SetAppResourcesTree(app.Name, nil); err != nil { - return objs, err + done, err := ctrl.executePostDeleteHooks(app, proj, objsMap, config, logCtx) + if err != nil { + return err + } + if !done { + return nil + } + app.UnSetPostDeleteFinalizer() + return ctrl.updateFinalizers(app) } - if err := ctrl.removeCascadeFinalizer(app); err != nil { - return objs, err + if app.HasPostDeleteFinalizer("cleanup") { + objsMap, err := ctrl.getPermittedAppLiveObjects(app, proj, projectClusters) + if err != nil { + return err + } + + done, err := ctrl.cleanupPostDeleteHooks(objsMap, config, logCtx) + if err != nil { + return err + } + if !done { + return nil + } + app.UnSetPostDeleteFinalizer("cleanup") + return ctrl.updateFinalizers(app) } - if validDestination { - logCtx.Infof("Successfully deleted %d resources", len(objs)) - } else { - logCtx.Infof("Resource entries removed from undefined cluster") + if !app.CascadedDeletion() && !app.HasPostDeleteFinalizer() { + if err := ctrl.cache.SetAppManagedResources(app.Name, nil); err != nil { + return err + } + + if err := ctrl.cache.SetAppResourcesTree(app.Name, nil); err != nil { + return err + } + ctrl.projectRefreshQueue.Add(fmt.Sprintf("%s/%s", ctrl.namespace, app.Spec.GetProject())) } - ctrl.projectRefreshQueue.Add(fmt.Sprintf("%s/%s", ctrl.namespace, app.Spec.GetProject())) - return objs, nil + return nil } -func (ctrl *ApplicationController) removeCascadeFinalizer(app *appv1.Application) error { +func (ctrl *ApplicationController) updateFinalizers(app *appv1.Application) error { _, err := ctrl.getAppProj(app) if err != nil { return fmt.Errorf("error getting project: %w", err) } - app.UnSetCascadedDeletion() + var patch []byte patch, _ = json.Marshal(map[string]interface{}{ "metadata": map[string]interface{}{ @@ -1566,6 +1598,20 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo app.Status.SourceTypes = compareResult.appSourceTypes app.Status.ControllerNamespace = ctrl.namespace patchMs = ctrl.persistAppStatus(origApp, &app.Status) + if (compareResult.hasPostDeleteHooks != app.HasPostDeleteFinalizer() || compareResult.hasPostDeleteHooks != app.HasPostDeleteFinalizer("cleanup")) && + app.GetDeletionTimestamp() == nil { + if compareResult.hasPostDeleteHooks { + app.SetPostDeleteFinalizer() + app.SetPostDeleteFinalizer("cleanup") + } else { + app.UnSetPostDeleteFinalizer() + app.UnSetPostDeleteFinalizer("cleanup") + } + + if err := ctrl.updateFinalizers(app); err != nil { + logCtx.Errorf("Failed to update finalizers: %v", err) + } + } return } diff --git a/controller/appcontroller_test.go b/controller/appcontroller_test.go index 9c2933feeb6df..2efc7ac723bc7 100644 --- a/controller/appcontroller_test.go +++ b/controller/appcontroller_test.go @@ -7,8 +7,11 @@ import ( "testing" "time" + "github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest" "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/client-go/rest" clustercache "github.com/argoproj/gitops-engine/pkg/cache" @@ -18,7 +21,6 @@ import ( "github.com/argoproj/gitops-engine/pkg/cache/mocks" synccommon "github.com/argoproj/gitops-engine/pkg/sync/common" "github.com/argoproj/gitops-engine/pkg/utils/kube" - "github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" corev1 "k8s.io/api/core/v1" @@ -59,6 +61,23 @@ type fakeData struct { applicationNamespaces []string } +type MockKubectl struct { + kube.Kubectl + + DeletedResources []kube.ResourceKey + CreatedResources []*unstructured.Unstructured +} + +func (m *MockKubectl) CreateResource(ctx context.Context, config *rest.Config, gvk schema.GroupVersionKind, name string, namespace string, obj *unstructured.Unstructured, createOptions metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) { + m.CreatedResources = append(m.CreatedResources, obj) + return m.Kubectl.CreateResource(ctx, config, gvk, name, namespace, obj, createOptions, subresources...) +} + +func (m *MockKubectl) DeleteResource(ctx context.Context, config *rest.Config, gvk schema.GroupVersionKind, name string, namespace string, deleteOptions metav1.DeleteOptions) error { + m.DeletedResources = append(m.DeletedResources, kube.NewResourceKey(gvk.Group, gvk.Kind, namespace, name)) + return m.Kubectl.DeleteResource(ctx, config, gvk, name, namespace, deleteOptions) +} + func newFakeController(data *fakeData, repoErr error) *ApplicationController { var clust corev1.Secret err := yaml.Unmarshal([]byte(fakeCluster), &clust) @@ -109,7 +128,7 @@ func newFakeController(data *fakeData, repoErr error) *ApplicationController { } kubeClient := fake.NewSimpleClientset(&clust, &cm, &secret) settingsMgr := settings.NewSettingsManager(context.Background(), kubeClient, test.FakeArgoCDNamespace) - kubectl := &kubetest.MockKubectlCmd{} + kubectl := &MockKubectl{Kubectl: &kubetest.MockKubectlCmd{}} ctrl, err := NewApplicationController( test.FakeArgoCDNamespace, settingsMgr, @@ -337,6 +356,38 @@ metadata: data: ` +var fakePostDeleteHook = ` +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "name": "post-delete-hook", + "namespace": "default", + "labels": { + "app.kubernetes.io/instance": "my-app" + }, + "annotations": { + "argocd.argoproj.io/hook": "PostDelete", + "argocd.argoproj.io/hook-delete-policy": "HookSucceeded" + } + }, + "spec": { + "containers": [ + { + "name": "post-delete-hook", + "image": "busybox", + "restartPolicy": "Never", + "command": [ + "/bin/sh", + "-c", + "sleep 5 && echo hello from the post-delete-hook pod" + ] + } + ] + } +} +` + func newFakeApp() *v1alpha1.Application { return createFakeApp(fakeApp) } @@ -371,6 +422,15 @@ func newFakeCM() map[string]interface{} { return cm } +func newFakePostDeleteHook() map[string]interface{} { + var cm map[string]interface{} + err := yaml.Unmarshal([]byte(fakePostDeleteHook), &cm) + if err != nil { + panic(err) + } + return cm +} + func TestAutoSync(t *testing.T) { app := newFakeApp() ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}, nil) @@ -619,6 +679,7 @@ func TestFinalizeAppDeletion(t *testing.T) { // Ensure app can be deleted cascading t.Run("CascadingDelete", func(t *testing.T) { app := newFakeApp() + app.SetCascadedDeletion(v1alpha1.ResourcesFinalizerName) app.Spec.Destination.Namespace = test.FakeArgoCDNamespace appObj := kube.MustToUnstructured(&app) ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}, managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ @@ -636,7 +697,7 @@ func TestFinalizeAppDeletion(t *testing.T) { patched = true return true, &v1alpha1.Application{}, nil }) - _, err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { return []*v1alpha1.Cluster{}, nil }) assert.NoError(t, err) @@ -662,6 +723,7 @@ func TestFinalizeAppDeletion(t *testing.T) { }, } app := newFakeApp() + app.SetCascadedDeletion(v1alpha1.ResourcesFinalizerName) app.Spec.Destination.Namespace = test.FakeArgoCDNamespace app.Spec.Project = "restricted" appObj := kube.MustToUnstructured(&app) @@ -686,7 +748,7 @@ func TestFinalizeAppDeletion(t *testing.T) { patched = true return true, &v1alpha1.Application{}, nil }) - objs, err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { return []*v1alpha1.Cluster{}, nil }) assert.NoError(t, err) @@ -697,14 +759,16 @@ func TestFinalizeAppDeletion(t *testing.T) { } // Managed objects must be empty assert.Empty(t, objsMap) + // Loop through all deleted objects, ensure that test-cm is none of them - for _, o := range objs { - assert.NotEqual(t, "test-cm", o.GetName()) + for _, o := range ctrl.kubectl.(*MockKubectl).DeletedResources { + assert.NotEqual(t, "test-cm", o.Name) } }) t.Run("DeleteWithDestinationClusterName", func(t *testing.T) { app := newFakeAppWithDestName() + app.SetCascadedDeletion(v1alpha1.ResourcesFinalizerName) appObj := kube.MustToUnstructured(&app) ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}, managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ kube.GetResourceKey(appObj): appObj, @@ -720,7 +784,7 @@ func TestFinalizeAppDeletion(t *testing.T) { patched = true return true, &v1alpha1.Application{}, nil }) - _, err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { return []*v1alpha1.Cluster{}, nil }) assert.NoError(t, err) @@ -745,7 +809,7 @@ func TestFinalizeAppDeletion(t *testing.T) { fakeAppCs.AddReactor("get", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { return defaultReactor.React(action) }) - _, err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { return []*v1alpha1.Cluster{}, nil }) assert.NoError(t, err) @@ -766,6 +830,109 @@ func TestFinalizeAppDeletion(t *testing.T) { }) + t.Run("PostDelete_HookIsCreated", func(t *testing.T) { + app := newFakeApp() + app.SetPostDeleteFinalizer() + app.Spec.Destination.Namespace = test.FakeArgoCDNamespace + ctrl := newFakeController(&fakeData{ + manifestResponses: []*apiclient.ManifestResponse{{ + Manifests: []string{fakePostDeleteHook}, + }}, + apps: []runtime.Object{app, &defaultProj}, + managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{}}, nil) + + patched := false + fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) + defaultReactor := fakeAppCs.ReactionChain[0] + fakeAppCs.ReactionChain = nil + fakeAppCs.AddReactor("get", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { + return defaultReactor.React(action) + }) + fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { + patched = true + return true, &v1alpha1.Application{}, nil + }) + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + return []*v1alpha1.Cluster{}, nil + }) + assert.NoError(t, err) + // finalizer is not deleted + assert.False(t, patched) + // post-delete hook is created + require.Len(t, ctrl.kubectl.(*MockKubectl).CreatedResources, 1) + require.Equal(t, "post-delete-hook", ctrl.kubectl.(*MockKubectl).CreatedResources[0].GetName()) + }) + + t.Run("PostDelete_HookIsExecuted", func(t *testing.T) { + app := newFakeApp() + app.SetPostDeleteFinalizer() + app.Spec.Destination.Namespace = test.FakeArgoCDNamespace + liveHook := &unstructured.Unstructured{Object: newFakePostDeleteHook()} + require.NoError(t, unstructured.SetNestedField(liveHook.Object, "Succeeded", "status", "phase")) + ctrl := newFakeController(&fakeData{ + manifestResponses: []*apiclient.ManifestResponse{{ + Manifests: []string{fakePostDeleteHook}, + }}, + apps: []runtime.Object{app, &defaultProj}, + managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ + kube.GetResourceKey(liveHook): liveHook, + }}, nil) + + patched := false + fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) + defaultReactor := fakeAppCs.ReactionChain[0] + fakeAppCs.ReactionChain = nil + fakeAppCs.AddReactor("get", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { + return defaultReactor.React(action) + }) + fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { + patched = true + return true, &v1alpha1.Application{}, nil + }) + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + return []*v1alpha1.Cluster{}, nil + }) + assert.NoError(t, err) + // finalizer is removed + assert.True(t, patched) + }) + + t.Run("PostDelete_HookIsDeleted", func(t *testing.T) { + app := newFakeApp() + app.SetPostDeleteFinalizer("cleanup") + app.Spec.Destination.Namespace = test.FakeArgoCDNamespace + liveHook := &unstructured.Unstructured{Object: newFakePostDeleteHook()} + require.NoError(t, unstructured.SetNestedField(liveHook.Object, "Succeeded", "status", "phase")) + ctrl := newFakeController(&fakeData{ + manifestResponses: []*apiclient.ManifestResponse{{ + Manifests: []string{fakePostDeleteHook}, + }}, + apps: []runtime.Object{app, &defaultProj}, + managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{ + kube.GetResourceKey(liveHook): liveHook, + }}, nil) + + patched := false + fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) + defaultReactor := fakeAppCs.ReactionChain[0] + fakeAppCs.ReactionChain = nil + fakeAppCs.AddReactor("get", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { + return defaultReactor.React(action) + }) + fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) { + patched = true + return true, &v1alpha1.Application{}, nil + }) + err := ctrl.finalizeApplicationDeletion(app, func(project string) ([]*v1alpha1.Cluster, error) { + return []*v1alpha1.Cluster{}, nil + }) + assert.NoError(t, err) + // post-delete hook is deleted + require.Len(t, ctrl.kubectl.(*MockKubectl).DeletedResources, 1) + require.Equal(t, "post-delete-hook", ctrl.kubectl.(*MockKubectl).DeletedResources[0].Name) + // finalizer is not removed + assert.False(t, patched) + }) } // TestNormalizeApplication verifies we normalize an application during reconciliation diff --git a/controller/hook.go b/controller/hook.go new file mode 100644 index 0000000000000..0c019ac6a1e08 --- /dev/null +++ b/controller/hook.go @@ -0,0 +1,158 @@ +package controller + +import ( + "context" + + "github.com/argoproj/gitops-engine/pkg/health" + "github.com/argoproj/gitops-engine/pkg/sync/common" + "github.com/argoproj/gitops-engine/pkg/sync/hook" + "github.com/argoproj/gitops-engine/pkg/utils/kube" + log "github.com/sirupsen/logrus" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/client-go/rest" + + "github.com/argoproj/argo-cd/v2/util/lua" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" +) + +var ( + postDeleteHook = "PostDelete" + postDeleteHooks = map[string]string{ + "argocd.argoproj.io/hook": postDeleteHook, + "helm.sh/hook": "post-delete", + } +) + +func isHook(obj *unstructured.Unstructured) bool { + return hook.IsHook(obj) || isPostDeleteHook(obj) +} + +func isPostDeleteHook(obj *unstructured.Unstructured) bool { + if obj == nil || obj.GetAnnotations() == nil { + return false + } + for k, v := range postDeleteHooks { + if val, ok := obj.GetAnnotations()[k]; ok && val == v { + return true + } + } + return false +} + +func (ctrl *ApplicationController) executePostDeleteHooks(app *v1alpha1.Application, proj *v1alpha1.AppProject, liveObjs map[kube.ResourceKey]*unstructured.Unstructured, config *rest.Config, logCtx *log.Entry) (bool, error) { + appLabelKey, err := ctrl.settingsMgr.GetAppInstanceLabelKey() + if err != nil { + return false, err + } + var revisions []string + for _, src := range app.Spec.GetSources() { + revisions = append(revisions, src.TargetRevision) + } + + targets, _, err := ctrl.appStateManager.GetRepoObjs(app, app.Spec.GetSources(), appLabelKey, revisions, false, false, false, proj) + if err != nil { + return false, err + } + runningHooks := map[kube.ResourceKey]*unstructured.Unstructured{} + for key, obj := range liveObjs { + if isPostDeleteHook(obj) { + runningHooks[key] = obj + } + } + + expectedHook := map[kube.ResourceKey]*unstructured.Unstructured{} + for _, obj := range targets { + if obj.GetNamespace() == "" { + obj.SetNamespace(app.Spec.Destination.Namespace) + } + if !isPostDeleteHook(obj) { + continue + } + if runningHook := runningHooks[kube.GetResourceKey(obj)]; runningHook == nil { + expectedHook[kube.GetResourceKey(obj)] = obj + } + } + createdCnt := 0 + for _, obj := range expectedHook { + _, err = ctrl.kubectl.CreateResource(context.Background(), config, obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), obj, v1.CreateOptions{}) + if err != nil { + return false, err + } + createdCnt++ + } + if createdCnt > 0 { + logCtx.Infof("Created %d post-delete hooks", createdCnt) + return false, nil + } + resourceOverrides, err := ctrl.settingsMgr.GetResourceOverrides() + if err != nil { + return false, err + } + healthOverrides := lua.ResourceHealthOverrides(resourceOverrides) + + progressingHooksCnt := 0 + for _, obj := range runningHooks { + hookHealth, err := health.GetResourceHealth(obj, healthOverrides) + if err != nil { + return false, err + } + if hookHealth.Status == health.HealthStatusProgressing { + progressingHooksCnt++ + } + } + if progressingHooksCnt > 0 { + logCtx.Infof("Waiting for %d post-delete hooks to complete", progressingHooksCnt) + return false, nil + } + + return true, nil +} + +func (ctrl *ApplicationController) cleanupPostDeleteHooks(liveObjs map[kube.ResourceKey]*unstructured.Unstructured, config *rest.Config, logCtx *log.Entry) (bool, error) { + resourceOverrides, err := ctrl.settingsMgr.GetResourceOverrides() + if err != nil { + return false, err + } + healthOverrides := lua.ResourceHealthOverrides(resourceOverrides) + + pendingDeletionCount := 0 + aggregatedHealth := health.HealthStatusHealthy + var hooks []*unstructured.Unstructured + for _, obj := range liveObjs { + if !isPostDeleteHook(obj) { + continue + } + hookHealth, err := health.GetResourceHealth(obj, healthOverrides) + if err != nil { + return false, err + } + if health.IsWorse(aggregatedHealth, hookHealth.Status) { + aggregatedHealth = hookHealth.Status + } + hooks = append(hooks, obj) + } + + for _, obj := range hooks { + for _, policy := range hook.DeletePolicies(obj) { + if policy == common.HookDeletePolicyHookFailed && aggregatedHealth == health.HealthStatusDegraded || policy == common.HookDeletePolicyHookSucceeded && aggregatedHealth == health.HealthStatusHealthy { + pendingDeletionCount++ + if obj.GetDeletionTimestamp() != nil { + continue + } + logCtx.Infof("Deleting post-delete hook %s/%s", obj.GetNamespace(), obj.GetName()) + err = ctrl.kubectl.DeleteResource(context.Background(), config, obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), v1.DeleteOptions{}) + if err != nil { + return false, err + } + } + } + + } + if pendingDeletionCount > 0 { + logCtx.Infof("Waiting for %d post-delete hooks to be deleted", pendingDeletionCount) + return false, nil + } + return true, nil +} diff --git a/controller/state.go b/controller/state.go index 59e7fa31248ae..ccd17bf532643 100644 --- a/controller/state.go +++ b/controller/state.go @@ -71,6 +71,7 @@ type managedResource struct { type AppStateManager interface { CompareAppState(app *v1alpha1.Application, project *v1alpha1.AppProject, revisions []string, sources []v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localObjects []string, hasMultipleSources bool) (*comparisonResult, error) SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState) + GetRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, error) } // comparisonResult holds the state of an application after the reconciliation @@ -85,8 +86,9 @@ type comparisonResult struct { // appSourceTypes stores the SourceType for each application source under sources field appSourceTypes []v1alpha1.ApplicationSourceType // timings maps phases of comparison to the duration it took to complete (for statistical purposes) - timings map[string]time.Duration - diffResultList *diff.DiffResultList + timings map[string]time.Duration + diffResultList *diff.DiffResultList + hasPostDeleteHooks bool } func (res *comparisonResult) GetSyncStatus() *v1alpha1.SyncStatus { @@ -116,11 +118,11 @@ type appStateManager struct { repoErrorGracePeriod time.Duration } -// getRepoObjs will generate the manifests for the given application delegating the +// GetRepoObjs will generate the manifests for the given application delegating the // task to the repo-server. It returns the list of generated manifests as unstructured // objects. It also returns the full response from all calls to the repo server as the // second argument. -func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, error) { +func (m *appStateManager) GetRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, error) { ts := stats.NewTimingStats() helmRepos, err := m.db.ListHelmRepositories(context.Background()) if err != nil { @@ -236,7 +238,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alp logCtx = logCtx.WithField(k, v.Milliseconds()) } logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds()) - logCtx.Info("getRepoObjs stats") + logCtx.Info("GetRepoObjs stats") return targetObjs, manifestInfos, nil } @@ -415,7 +417,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 } } - targetObjs, manifestInfos, err = m.getRepoObjs(app, sources, appLabelKey, revisions, noCache, noRevisionCache, verifySignature, project) + targetObjs, manifestInfos, err = m.GetRepoObjs(app, sources, appLabelKey, revisions, noCache, noRevisionCache, verifySignature, project) if err != nil { targetObjs = make([]*unstructured.Unstructured, 0) msg := fmt.Sprintf("Failed to load target state: %s", err.Error()) @@ -569,6 +571,12 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 } } } + hasPostDeleteHooks := false + for _, obj := range targetObjs { + if isPostDeleteHook(obj) { + hasPostDeleteHooks = true + } + } reconciliation := sync.Reconcile(targetObjs, liveObjByKey, app.Spec.Destination.Namespace, infoProvider) ts.AddCheckpoint("live_ms") @@ -643,7 +651,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 Kind: gvk.Kind, Version: gvk.Version, Group: gvk.Group, - Hook: hookutil.IsHook(obj), + Hook: isHook(obj), RequiresPruning: targetObj == nil && liveObj != nil && isSelfReferencedObj, } if targetObj != nil { @@ -776,6 +784,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 reconciliationResult: reconciliation, diffConfig: diffConfig, diffResultList: diffResults, + hasPostDeleteHooks: hasPostDeleteHooks, } if hasMultipleSources { diff --git a/controller/sync.go b/controller/sync.go index 2b925b7782b9e..435f62e587c34 100644 --- a/controller/sync.go +++ b/controller/sync.go @@ -283,6 +283,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha sync.WithInitialState(state.Phase, state.Message, initialResourcesRes, state.StartedAt), sync.WithResourcesFilter(func(key kube.ResourceKey, target *unstructured.Unstructured, live *unstructured.Unstructured) bool { return (len(syncOp.Resources) == 0 || + isPostDeleteHook(target) || argo.ContainsSyncResource(key.Name, key.Namespace, schema.GroupVersionKind{Kind: key.Kind, Group: key.Group}, syncOp.Resources)) && m.isSelfReferencedObj(live, target, app.GetName(), appLabelKey, trackingMethod) }), diff --git a/docs/user-guide/helm.md b/docs/user-guide/helm.md index 76853480a6def..866f9c6d935aa 100644 --- a/docs/user-guide/helm.md +++ b/docs/user-guide/helm.md @@ -210,7 +210,7 @@ is any normal Kubernetes resource annotated with the `helm.sh/hook` annotation. Argo CD supports many (most?) Helm hooks by mapping the Helm annotations onto Argo CD's own hook annotations: | Helm Annotation | Notes | -| ------------------------------- | --------------------------------------------------------------------------------------------- | +| ------------------------------- |-----------------------------------------------------------------------------------------------| | `helm.sh/hook: crd-install` | Supported as equivalent to `argocd.argoproj.io/hook: PreSync`. | | `helm.sh/hook: pre-delete` | Not supported. In Helm stable there are 3 cases used to clean up CRDs and 3 to clean-up jobs. | | `helm.sh/hook: pre-rollback` | Not supported. Never used in Helm stable. | @@ -218,7 +218,7 @@ Argo CD supports many (most?) Helm hooks by mapping the Helm annotations onto Ar | `helm.sh/hook: pre-upgrade` | Supported as equivalent to `argocd.argoproj.io/hook: PreSync`. | | `helm.sh/hook: post-upgrade` | Supported as equivalent to `argocd.argoproj.io/hook: PostSync`. | | `helm.sh/hook: post-install` | Supported as equivalent to `argocd.argoproj.io/hook: PostSync`. | -| `helm.sh/hook: post-delete` | Not supported. Never used in Helm stable. | +| `helm.sh/hook: post-delete` | Supported as equivalent to `argocd.argoproj.io/hook: PostDelete`. | | `helm.sh/hook: post-rollback` | Not supported. Never used in Helm stable. | | `helm.sh/hook: test-success` | Not supported. No equivalent in Argo CD. | | `helm.sh/hook: test-failure` | Not supported. No equivalent in Argo CD. | diff --git a/docs/user-guide/resource_hooks.md b/docs/user-guide/resource_hooks.md index d705f8d21423d..74098b1810011 100644 --- a/docs/user-guide/resource_hooks.md +++ b/docs/user-guide/resource_hooks.md @@ -9,6 +9,8 @@ and after a Sync operation. Hooks can also be run if a Sync operation fails at a Kubernetes rolling update strategy. * Using a `PostSync` hook to run integration and health checks after a deployment. * Using a `SyncFail` hook to run clean-up or finalizer logic if a Sync operation fails. _`SyncFail` hooks are only available starting in v1.2_ +* Using a `PostDelete` hook to run clean-up or finalizer logic after an all Application resources are deleted. Please note that + `PostDelete` hooks are only deleted if delete policy matches to the aggregated deletion hooks status and not garbage collected after the application is deleted. ## Usage diff --git a/pkg/apis/application/v1alpha1/application_defaults.go b/pkg/apis/application/v1alpha1/application_defaults.go index 2bc9b1bd0d744..ad8112af8c88d 100644 --- a/pkg/apis/application/v1alpha1/application_defaults.go +++ b/pkg/apis/application/v1alpha1/application_defaults.go @@ -9,6 +9,9 @@ const ( // ResourcesFinalizerName is the finalizer value which we inject to finalize deletion of an application ResourcesFinalizerName string = "resources-finalizer.argocd.argoproj.io" + // PostDeleteFinalizerName is the finalizer that controls post-delete hooks execution + PostDeleteFinalizerName string = "post-delete-finalizer.argocd.argoproj.io" + // ForegroundPropagationPolicyFinalizer is the finalizer we inject to delete application with foreground propagation policy ForegroundPropagationPolicyFinalizer string = "resources-finalizer.argocd.argoproj.io/foreground" diff --git a/pkg/apis/application/v1alpha1/types.go b/pkg/apis/application/v1alpha1/types.go index dc6a73f05dee5..49be82a443bc4 100644 --- a/pkg/apis/application/v1alpha1/types.go +++ b/pkg/apis/application/v1alpha1/types.go @@ -2651,6 +2651,18 @@ func (app *Application) IsRefreshRequested() (RefreshType, bool) { return refreshType, true } +func (app *Application) HasPostDeleteFinalizer(stage ...string) bool { + return getFinalizerIndex(app.ObjectMeta, strings.Join(append([]string{PostDeleteFinalizerName}, stage...), "/")) > -1 +} + +func (app *Application) SetPostDeleteFinalizer(stage ...string) { + setFinalizer(&app.ObjectMeta, strings.Join(append([]string{PostDeleteFinalizerName}, stage...), "/"), true) +} + +func (app *Application) UnSetPostDeleteFinalizer(stage ...string) { + setFinalizer(&app.ObjectMeta, strings.Join(append([]string{PostDeleteFinalizerName}, stage...), "/"), false) +} + // SetCascadedDeletion will enable cascaded deletion by setting the propagation policy finalizer func (app *Application) SetCascadedDeletion(finalizer string) { setFinalizer(&app.ObjectMeta, finalizer, true) diff --git a/test/e2e/hook_test.go b/test/e2e/hook_test.go index f6bb1be872ac6..2db8ff87795ad 100644 --- a/test/e2e/hook_test.go +++ b/test/e2e/hook_test.go @@ -1,12 +1,14 @@ package e2e import ( + "context" "fmt" "testing" "time" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/argoproj/gitops-engine/pkg/health" . "github.com/argoproj/gitops-engine/pkg/sync/common" @@ -48,6 +50,24 @@ func testHookSuccessful(t *testing.T, hookType HookType) { Expect(ResourceResultIs(ResourceResult{Version: "v1", Kind: "Pod", Namespace: DeploymentNamespace(), Name: "hook", Message: "pod/hook created", HookType: hookType, HookPhase: OperationSucceeded, SyncPhase: SyncPhase(hookType)})) } +func TestPostDeleteHook(t *testing.T) { + Given(t). + Path("post-delete-hook"). + When(). + CreateApp(). + Refresh(RefreshTypeNormal). + Delete(true). + Then(). + Expect(DoesNotExist()). + AndAction(func() { + hooks, err := KubeClientset.CoreV1().Pods(DeploymentNamespace()).List(context.Background(), metav1.ListOptions{}) + CheckError(err) + assert.Len(t, hooks.Items, 1) + assert.Equal(t, "hook", hooks.Items[0].Name) + }) + +} + // make sure that that hooks do not appear in "argocd app diff" func TestHookDiff(t *testing.T) { Given(t). diff --git a/test/e2e/testdata/post-delete-hook/hook.yaml b/test/e2e/testdata/post-delete-hook/hook.yaml new file mode 100644 index 0000000000000..5631db681f1d0 --- /dev/null +++ b/test/e2e/testdata/post-delete-hook/hook.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + argocd.argoproj.io/hook: PostDelete + name: hook +spec: + containers: + - command: + - "true" + image: quay.io/argoprojlabs/argocd-e2e-container:0.1 + imagePullPolicy: IfNotPresent + name: main + restartPolicy: Never \ No newline at end of file From 9abc1cc8372a44b001da45e6196af17fbce35b32 Mon Sep 17 00:00:00 2001 From: AS <11219262+ashutosh16@users.noreply.github.com> Date: Mon, 18 Dec 2023 12:15:42 -0800 Subject: [PATCH 46/66] feat(ui): Show prompt when every resource requires pruning (#16603) * fix(ui): show prompt when using prune option Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> * fix(ui): show prompt when using prune option Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> * fix(ui): show prompt when using prune option Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> fix(ui): show prompt when using prune option Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> * chore: update message and simplify code Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * don't warn on partial sync Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> * fix(ui): lint Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> --------- Signed-off-by: ashutosh16 <11219262+ashutosh16@users.noreply.github.com> Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --- .../application-sync-options.tsx | 1 + .../application-sync-panel.tsx | 34 +++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/ui/src/app/applications/components/application-sync-options/application-sync-options.tsx b/ui/src/app/applications/components/application-sync-options/application-sync-options.tsx index 538d74455fea0..7cc24173cd36a 100644 --- a/ui/src/app/applications/components/application-sync-options/application-sync-options.tsx +++ b/ui/src/app/applications/components/application-sync-options/application-sync-options.tsx @@ -7,6 +7,7 @@ import './application-sync-options.scss'; export const REPLACE_WARNING = `The resources will be synced using 'kubectl replace/create' command that is a potentially destructive action and might cause resources recreation.`; export const FORCE_WARNING = `The resources will be synced using '--force' that is a potentially destructive action and will immediately remove resources from the API and bypasses graceful deletion. Immediate deletion of some resources may result in inconsistency or data loss.`; +export const PRUNE_ALL_WARNING = `The resources will be synced using '--prune', and all resources are marked to be pruned. Only continue if you want to delete all of the Application's resources.`; export interface ApplicationSyncOptionProps { options: string[]; diff --git a/ui/src/app/applications/components/application-sync-panel/application-sync-panel.tsx b/ui/src/app/applications/components/application-sync-panel/application-sync-panel.tsx index 884cdd4eb85ff..c1a4505eeeba0 100644 --- a/ui/src/app/applications/components/application-sync-panel/application-sync-panel.tsx +++ b/ui/src/app/applications/components/application-sync-panel/application-sync-panel.tsx @@ -7,7 +7,14 @@ import {Consumer} from '../../../shared/context'; import * as models from '../../../shared/models'; import {services} from '../../../shared/services'; import {ApplicationRetryOptions} from '../application-retry-options/application-retry-options'; -import {ApplicationManualSyncFlags, ApplicationSyncOptions, FORCE_WARNING, SyncFlags, REPLACE_WARNING} from '../application-sync-options/application-sync-options'; +import { + ApplicationManualSyncFlags, + ApplicationSyncOptions, + FORCE_WARNING, + SyncFlags, + REPLACE_WARNING, + PRUNE_ALL_WARNING +} from '../application-sync-options/application-sync-options'; import {ComparisonStatusIcon, getAppDefaultSource, nodeKey} from '../utils'; import './application-sync-panel.scss'; @@ -57,9 +64,25 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {app })} onSubmit={async (params: any) => { setPending(true); - let resources = appResources.filter((_, i) => params.resources[i]); - if (resources.length === appResources.length) { - resources = null; + let selectedResources = appResources.filter((_, i) => params.resources[i]); + const allResourcesAreSelected = selectedResources.length === appResources.length; + const syncFlags = {...params.syncFlags} as SyncFlags; + + const allRequirePruning = !selectedResources.some(resource => !resource?.requiresPruning); + if (syncFlags.Prune && allRequirePruning && allResourcesAreSelected) { + const confirmed = await ctx.popup.confirm('Prune all resources?', () => ( +
+ + {PRUNE_ALL_WARNING} Are you sure you want to continue? +
+ )); + if (!confirmed) { + setPending(false); + return; + } + } + if (allResourcesAreSelected) { + selectedResources = null; } const replace = params.syncOptions?.findIndex((opt: string) => opt === 'Replace=true') > -1; if (replace) { @@ -74,7 +97,6 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {app } } - const syncFlags = {...params.syncFlags} as SyncFlags; const force = syncFlags.Force || false; if (syncFlags.ApplyOnly) { @@ -102,7 +124,7 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {app syncFlags.Prune || false, syncFlags.DryRun || false, syncStrategy, - resources, + selectedResources, params.syncOptions, params.retryStrategy ); From 82ca7a7f9c5df24de5e78970716d01d13b008410 Mon Sep 17 00:00:00 2001 From: Leonardo Luz Almeida Date: Mon, 18 Dec 2023 15:37:13 -0500 Subject: [PATCH 47/66] feat: Implement Server-Side Diff (#13663) * feat: Implement Server-Side Diff Signed-off-by: Leonardo Luz Almeida * propagate the refreshtype to the diff config Signed-off-by: Leonardo Luz Almeida * Create the serverSideDiff config Signed-off-by: Leonardo Luz Almeida * chore: add featureflag utility package Signed-off-by: Leonardo Luz Almeida * remove featureflag package Signed-off-by: Leonardo Luz Almeida * add param Signed-off-by: Leonardo Luz Almeida * make ssd configurable with app annotation Signed-off-by: Leonardo Luz Almeida * add server-side-diff flags Signed-off-by: Leonardo Luz Almeida * apply the same logic regardless of the refresh type Signed-off-by: Leonardo Luz Almeida * fix gitops-engine reference Signed-off-by: Leonardo Luz Almeida * address review comments Signed-off-by: Leonardo Luz Almeida * Address review comments Signed-off-by: Leonardo Luz Almeida * docs: add docs related to server-side-diff Signed-off-by: Leonardo Luz Almeida * docs: update doc Signed-off-by: Leonardo Luz Almeida * Add config to include mutation webhooks Signed-off-by: Leonardo Luz Almeida * Address review comments Signed-off-by: Leonardo Luz Almeida * go mod update Signed-off-by: Leonardo Luz Almeida * Add sdd cache test case Signed-off-by: Leonardo Luz Almeida * fix ssd cache unit test Signed-off-by: Leonardo Luz Almeida * Update clidocs Signed-off-by: Leonardo Luz Almeida * update manifests Signed-off-by: Leonardo Luz Almeida * Fix procfile Signed-off-by: Leonardo Luz Almeida * additional doc changes Signed-off-by: Leonardo Luz Almeida * update gitops-engine version Signed-off-by: Leonardo Luz Almeida --------- Signed-off-by: Leonardo Luz Almeida --- Makefile | 1 + Procfile | 2 +- .../commands/argocd_application_controller.go | 3 + cmd/argocd/commands/admin/app.go | 7 +- cmd/argocd/commands/admin/app_test.go | 1 + common/common.go | 3 + controller/appcontroller.go | 3 +- controller/appcontroller_test.go | 1 + controller/state.go | 43 ++++- controller/state_test.go | 22 ++- controller/sync.go | 21 +++ .../operator-manual/argocd-cmd-params-cm.yaml | 4 + .../argocd-application-controller.md | 1 + .../argocd_admin_app_get-reconcile-results.md | 1 + docs/user-guide/diff-strategies.md | 125 ++++++++++++++ go.mod | 4 +- go.sum | 8 +- ...ocd-application-controller-deployment.yaml | 156 +++++++++--------- ...cd-application-controller-statefulset.yaml | 150 +++++++++-------- manifests/core-install.yaml | 6 + manifests/ha/install.yaml | 6 + manifests/ha/namespace-install.yaml | 6 + manifests/install.yaml | 6 + manifests/namespace-install.yaml | 6 + mkdocs.yml | 4 +- util/argo/diff/diff.go | 55 +++++- 26 files changed, 475 insertions(+), 170 deletions(-) create mode 100644 docs/user-guide/diff-strategies.md diff --git a/Makefile b/Makefile index d4df041fa58e1..880622e7279a9 100644 --- a/Makefile +++ b/Makefile @@ -492,6 +492,7 @@ start-local: mod-vendor-local dep-ui-local cli-local ARGOCD_ZJWT_FEATURE_FLAG=always \ ARGOCD_IN_CI=false \ ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \ + BIN_MODE=$(ARGOCD_BIN_MODE) \ ARGOCD_E2E_TEST=false \ ARGOCD_APPLICATION_NAMESPACES=$(ARGOCD_APPLICATION_NAMESPACES) \ goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START} diff --git a/Procfile b/Procfile index 92f69ecf8ffbc..3bc2de5eca5e0 100644 --- a/Procfile +++ b/Procfile @@ -1,4 +1,4 @@ -controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}" +controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --server-side-diff-enabled=${ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF:-'false'}" api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}" dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && (test -f dist/dex.yaml || { echo 'Failed to generate dex configuration'; exit 1; }) && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:$(grep "image: ghcr.io/dexidp/dex" manifests/base/dex/argocd-dex-server-deployment.yaml | cut -d':' -f3) dex serve /dex.yaml" redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" = 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} docker.io/library/redis:$(grep "image: redis" manifests/base/redis/argocd-redis-deployment.yaml | cut -d':' -f3) --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi" diff --git a/cmd/argocd-application-controller/commands/argocd_application_controller.go b/cmd/argocd-application-controller/commands/argocd_application_controller.go index 74d0af45c9d7a..796a645f03393 100644 --- a/cmd/argocd-application-controller/commands/argocd_application_controller.go +++ b/cmd/argocd-application-controller/commands/argocd_application_controller.go @@ -73,6 +73,7 @@ func NewCommand() *cobra.Command { persistResourceHealth bool shardingAlgorithm string enableDynamicClusterDistribution bool + serverSideDiff bool ) var command = cobra.Command{ Use: cliName, @@ -166,6 +167,7 @@ func NewCommand() *cobra.Command { clusterFilter, applicationNamespaces, &workqueueRateLimit, + serverSideDiff, ) errors.CheckError(err) cacheutil.CollectMetrics(redisClient, appController.GetMetricsServer()) @@ -224,6 +226,7 @@ func NewCommand() *cobra.Command { command.Flags().DurationVar(&workqueueRateLimit.MaxDelay, "wq-maxdelay-ns", time.Duration(env.ParseInt64FromEnv("WORKQUEUE_MAX_DELAY_NS", time.Second.Nanoseconds(), 1*time.Millisecond.Nanoseconds(), (24*time.Hour).Nanoseconds())), "Set Workqueue Per Item Rate Limiter Max Delay duration in nanoseconds, default 1000000000 (1s)") command.Flags().Float64Var(&workqueueRateLimit.BackoffFactor, "wq-backoff-factor", env.ParseFloat64FromEnv("WORKQUEUE_BACKOFF_FACTOR", 1.5, 0, math.MaxFloat64), "Set Workqueue Per Item Rate Limiter Backoff Factor, default is 1.5") command.Flags().BoolVar(&enableDynamicClusterDistribution, "dynamic-cluster-distribution-enabled", env.ParseBoolFromEnv(common.EnvEnableDynamicClusterDistribution, false), "Enables dynamic cluster distribution.") + command.Flags().BoolVar(&serverSideDiff, "server-side-diff-enabled", env.ParseBoolFromEnv(common.EnvServerSideDiff, false), "Feature flag to enable ServerSide diff. Default (\"false\")") cacheSource = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) { redisClient = client }) diff --git a/cmd/argocd/commands/admin/app.go b/cmd/argocd/commands/admin/app.go index 10e4effe8797a..096c92f9feb01 100644 --- a/cmd/argocd/commands/admin/app.go +++ b/cmd/argocd/commands/admin/app.go @@ -243,6 +243,7 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command repoServerAddress string outputFormat string refresh bool + serverSideDiff bool ) var command = &cobra.Command{ @@ -280,7 +281,7 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command appClientset := appclientset.NewForConfigOrDie(cfg) kubeClientset := kubernetes.NewForConfigOrDie(cfg) - result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache) + result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache, serverSideDiff) errors.CheckError(err) } else { appClientset := appclientset.NewForConfigOrDie(cfg) @@ -295,6 +296,7 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command command.Flags().StringVar(&selector, "l", "", "Label selector") command.Flags().StringVar(&outputFormat, "o", "yaml", "Output format (yaml|json)") command.Flags().BoolVar(&refresh, "refresh", false, "If set to true then recalculates apps reconciliation") + command.Flags().BoolVar(&serverSideDiff, "server-side-diff", false, "If set to \"true\" will use server-side diff while comparing resources. Default (\"false\")") return command } @@ -344,6 +346,7 @@ func reconcileApplications( repoServerClient reposerverclient.Clientset, selector string, createLiveStateCache func(argoDB db.ArgoDB, appInformer kubecache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) cache.LiveStateCache, + serverSideDiff bool, ) ([]appReconcileResult, error) { settingsMgr := settings.NewSettingsManager(ctx, kubeClientset, namespace) argoDB := db.NewDB(namespace, settingsMgr, kubeClientset) @@ -384,7 +387,7 @@ func reconcileApplications( ) appStateManager := controller.NewAppStateManager( - argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking(), false, 0) + argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking(), false, 0, serverSideDiff) appsList, err := appClientset.ArgoprojV1alpha1().Applications(namespace).List(ctx, v1.ListOptions{LabelSelector: selector}) if err != nil { diff --git a/cmd/argocd/commands/admin/app_test.go b/cmd/argocd/commands/admin/app_test.go index 0cad2485e6696..a0284fe8ffa09 100644 --- a/cmd/argocd/commands/admin/app_test.go +++ b/cmd/argocd/commands/admin/app_test.go @@ -113,6 +113,7 @@ func TestGetReconcileResults_Refresh(t *testing.T) { func(argoDB db.ArgoDB, appInformer cache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) statecache.LiveStateCache { return &liveStateCache }, + false, ) if !assert.NoError(t, err) { diff --git a/common/common.go b/common/common.go index 5faedc2e344c4..c5b9362f7f943 100644 --- a/common/common.go +++ b/common/common.go @@ -260,6 +260,9 @@ const ( EnvRedisHaProxyName = "ARGOCD_REDIS_HAPROXY_NAME" // EnvGRPCKeepAliveMin defines the GRPCKeepAliveEnforcementMinimum, used in the grpc.KeepaliveEnforcementPolicy. Expects a "Duration" format (e.g. 10s). EnvGRPCKeepAliveMin = "ARGOCD_GRPC_KEEP_ALIVE_MIN" + // EnvServerSideDiff defines the env var used to enable ServerSide Diff feature. + // If defined, value must be "true" or "false". + EnvServerSideDiff = "ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF" ) // Config Management Plugin related constants diff --git a/controller/appcontroller.go b/controller/appcontroller.go index b98334b8fa567..0ded95de65d15 100644 --- a/controller/appcontroller.go +++ b/controller/appcontroller.go @@ -152,6 +152,7 @@ func NewApplicationController( clusterFilter func(cluster *appv1.Cluster) bool, applicationNamespaces []string, rateLimiterConfig *ratelimiter.AppControllerRateLimiterConfig, + serverSideDiff bool, ) (*ApplicationController, error) { log.Infof("appResyncPeriod=%v, appHardResyncPeriod=%v", appResyncPeriod, appHardResyncPeriod) db := db.NewDB(namespace, settingsMgr, kubeClientset) @@ -260,7 +261,7 @@ func NewApplicationController( } } stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settingsMgr, kubectl, ctrl.metricsServer, ctrl.handleObjectUpdated, clusterFilter, argo.NewResourceTracking()) - appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectl, ctrl.settingsMgr, stateCache, projInformer, ctrl.metricsServer, argoCache, ctrl.statusRefreshTimeout, argo.NewResourceTracking(), persistResourceHealth, repoErrorGracePeriod) + appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectl, ctrl.settingsMgr, stateCache, projInformer, ctrl.metricsServer, argoCache, ctrl.statusRefreshTimeout, argo.NewResourceTracking(), persistResourceHealth, repoErrorGracePeriod, serverSideDiff) ctrl.appInformer = appInformer ctrl.appLister = appLister ctrl.projInformer = projInformer diff --git a/controller/appcontroller_test.go b/controller/appcontroller_test.go index 2efc7ac723bc7..bf3d8bb3a2e4c 100644 --- a/controller/appcontroller_test.go +++ b/controller/appcontroller_test.go @@ -152,6 +152,7 @@ func newFakeController(data *fakeData, repoErr error) *ApplicationController { nil, data.applicationNamespaces, nil, + false, ) if err != nil { panic(err) diff --git a/controller/state.go b/controller/state.go index ccd17bf532643..5121fa68fcac9 100644 --- a/controller/state.go +++ b/controller/state.go @@ -116,6 +116,7 @@ type appStateManager struct { persistResourceHealth bool repoErrorCache goSync.Map repoErrorGracePeriod time.Duration + serverSideDiff bool } // GetRepoObjs will generate the manifests for the given application delegating the @@ -592,7 +593,16 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 manifestRevisions = append(manifestRevisions, manifestInfo.Revision) } - useDiffCache := useDiffCache(noCache, manifestInfos, sources, app, manifestRevisions, m.statusRefreshTimeout, logCtx) + serverSideDiff := m.serverSideDiff || + resourceutil.HasAnnotationOption(app, common.AnnotationCompareOptions, "ServerSideDiff=true") + + // This allows turning SSD off for a given app if it is enabled at the + // controller level + if resourceutil.HasAnnotationOption(app, common.AnnotationCompareOptions, "ServerSideDiff=false") { + serverSideDiff = false + } + + useDiffCache := useDiffCache(noCache, manifestInfos, sources, app, manifestRevisions, m.statusRefreshTimeout, serverSideDiff, logCtx) diffConfigBuilder := argodiff.NewDiffConfigBuilder(). WithDiffSettings(app.Spec.IgnoreDifferences, resourceOverrides, compareOptions.IgnoreAggregatedRoles). @@ -604,6 +614,10 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 diffConfigBuilder.WithNoCache() } + if resourceutil.HasAnnotationOption(app, common.AnnotationCompareOptions, "IncludeMutationWebhook=true") { + diffConfigBuilder.WithIgnoreMutationWebhook(false) + } + gvkParser, err := m.getGVKParser(app.Spec.Destination.Server) if err != nil { conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionUnknownError, Message: err.Error(), LastTransitionTime: &now}) @@ -611,6 +625,18 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 diffConfigBuilder.WithGVKParser(gvkParser) diffConfigBuilder.WithManager(common.ArgoCDSSAManager) + diffConfigBuilder.WithServerSideDiff(serverSideDiff) + + if serverSideDiff { + resourceOps, cleanup, err := m.getResourceOperations(app.Spec.Destination.Server) + if err != nil { + log.Errorf("CompareAppState error getting resource operations: %s", err) + conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionUnknownError, Message: err.Error(), LastTransitionTime: &now}) + } + defer cleanup() + diffConfigBuilder.WithServerSideDryRunner(diff.NewK8sServerSideDryRunner(resourceOps)) + } + // enable structured merge diff if application syncs with server-side apply if app.Spec.SyncPolicy != nil && app.Spec.SyncPolicy.SyncOptions.HasOption("ServerSideApply=true") { diffConfigBuilder.WithStructuredMergeDiff(true) @@ -811,18 +837,23 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1 // useDiffCache will determine if the diff should be calculated based // on the existing live state cache or not. -func useDiffCache(noCache bool, manifestInfos []*apiclient.ManifestResponse, sources []v1alpha1.ApplicationSource, app *v1alpha1.Application, manifestRevisions []string, statusRefreshTimeout time.Duration, log *log.Entry) bool { +func useDiffCache(noCache bool, manifestInfos []*apiclient.ManifestResponse, sources []v1alpha1.ApplicationSource, app *v1alpha1.Application, manifestRevisions []string, statusRefreshTimeout time.Duration, serverSideDiff bool, log *log.Entry) bool { if noCache { log.WithField("useDiffCache", "false").Debug("noCache is true") return false } - _, refreshRequested := app.IsRefreshRequested() + refreshType, refreshRequested := app.IsRefreshRequested() if refreshRequested { - log.WithField("useDiffCache", "false").Debug("refreshRequested") + log.WithField("useDiffCache", "false").Debugf("refresh type %s requested", string(refreshType)) return false } - if app.Status.Expired(statusRefreshTimeout) { + // serverSideDiff should still use cache even if status is expired. + // This is an attempt to avoid hitting k8s API server too frequently during + // app refresh with serverSideDiff is enabled. If there are negative side + // effects identified with this approach, the serverSideDiff should be removed + // from this condition. + if app.Status.Expired(statusRefreshTimeout) && !serverSideDiff { log.WithField("useDiffCache", "false").Debug("app.status.expired") return false } @@ -903,6 +934,7 @@ func NewAppStateManager( resourceTracking argo.ResourceTracking, persistResourceHealth bool, repoErrorGracePeriod time.Duration, + serverSideDiff bool, ) AppStateManager { return &appStateManager{ liveStateCache: liveStateCache, @@ -919,6 +951,7 @@ func NewAppStateManager( resourceTracking: resourceTracking, persistResourceHealth: persistResourceHealth, repoErrorGracePeriod: repoErrorGracePeriod, + serverSideDiff: serverSideDiff, } } diff --git a/controller/state_test.go b/controller/state_test.go index a240b30d688df..5d2342b601a77 100644 --- a/controller/state_test.go +++ b/controller/state_test.go @@ -1407,6 +1407,7 @@ func TestUseDiffCache(t *testing.T) { manifestRevisions []string statusRefreshTimeout time.Duration expectedUseCache bool + serverSideDiff bool } manifestInfos := func(revision string) []*apiclient.ManifestResponse { @@ -1505,6 +1506,7 @@ func TestUseDiffCache(t *testing.T) { manifestRevisions: []string{"rev1"}, statusRefreshTimeout: time.Hour * 24, expectedUseCache: true, + serverSideDiff: false, }, { testName: "will use diff cache for multisource", @@ -1548,6 +1550,7 @@ func TestUseDiffCache(t *testing.T) { manifestRevisions: []string{"rev1", "rev2"}, statusRefreshTimeout: time.Hour * 24, expectedUseCache: true, + serverSideDiff: false, }, { testName: "will return false if nocache is true", @@ -1558,6 +1561,7 @@ func TestUseDiffCache(t *testing.T) { manifestRevisions: []string{"rev1"}, statusRefreshTimeout: time.Hour * 24, expectedUseCache: false, + serverSideDiff: false, }, { testName: "will return false if requested refresh", @@ -1568,6 +1572,7 @@ func TestUseDiffCache(t *testing.T) { manifestRevisions: []string{"rev1"}, statusRefreshTimeout: time.Hour * 24, expectedUseCache: false, + serverSideDiff: false, }, { testName: "will return false if status expired", @@ -1578,6 +1583,18 @@ func TestUseDiffCache(t *testing.T) { manifestRevisions: []string{"rev1"}, statusRefreshTimeout: time.Minute, expectedUseCache: false, + serverSideDiff: false, + }, + { + testName: "will return true if status expired and server-side diff", + noCache: false, + manifestInfos: manifestInfos("rev1"), + sources: sources(), + app: app("httpbin", "rev1", false, nil), + manifestRevisions: []string{"rev1"}, + statusRefreshTimeout: time.Minute, + expectedUseCache: true, + serverSideDiff: true, }, { testName: "will return false if there is a new revision", @@ -1588,6 +1605,7 @@ func TestUseDiffCache(t *testing.T) { manifestRevisions: []string{"rev2"}, statusRefreshTimeout: time.Hour * 24, expectedUseCache: false, + serverSideDiff: false, }, { testName: "will return false if app spec repo changed", @@ -1604,6 +1622,7 @@ func TestUseDiffCache(t *testing.T) { manifestRevisions: []string{"rev1"}, statusRefreshTimeout: time.Hour * 24, expectedUseCache: false, + serverSideDiff: false, }, { testName: "will return false if app spec IgnoreDifferences changed", @@ -1626,6 +1645,7 @@ func TestUseDiffCache(t *testing.T) { manifestRevisions: []string{"rev1"}, statusRefreshTimeout: time.Hour * 24, expectedUseCache: false, + serverSideDiff: false, }, } @@ -1638,7 +1658,7 @@ func TestUseDiffCache(t *testing.T) { log := logrus.NewEntry(logger) // When - useDiffCache := useDiffCache(tc.noCache, tc.manifestInfos, tc.sources, tc.app, tc.manifestRevisions, tc.statusRefreshTimeout, log) + useDiffCache := useDiffCache(tc.noCache, tc.manifestInfos, tc.sources, tc.app, tc.manifestRevisions, tc.statusRefreshTimeout, tc.serverSideDiff, log) // Then assert.Equal(t, useDiffCache, tc.expectedUseCache) diff --git a/controller/sync.go b/controller/sync.go index 435f62e587c34..c33667a17a88d 100644 --- a/controller/sync.go +++ b/controller/sync.go @@ -57,6 +57,27 @@ func (m *appStateManager) getGVKParser(server string) (*managedfields.GvkParser, return cluster.GetGVKParser(), nil } +// getResourceOperations will return the kubectl implementation of the ResourceOperations +// interface that provides functionality to manage kubernetes resources. Returns a +// cleanup function that must be called to remove the generated kube config for this +// server. +func (m *appStateManager) getResourceOperations(server string) (kube.ResourceOperations, func(), error) { + clusterCache, err := m.liveStateCache.GetClusterCache(server) + if err != nil { + return nil, nil, fmt.Errorf("error getting cluster cache: %w", err) + } + + cluster, err := m.db.GetCluster(context.Background(), server) + if err != nil { + return nil, nil, fmt.Errorf("error getting cluster: %w", err) + } + ops, cleanup, err := m.kubectl.ManageResources(cluster.RawRestConfig(), clusterCache.GetOpenAPISchema()) + if err != nil { + return nil, nil, fmt.Errorf("error creating kubectl ResourceOperations: %w", err) + } + return ops, cleanup, nil +} + func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState) { // Sync requests might be requested with ambiguous revisions (e.g. master, HEAD, v1.2.3). // This can change meaning when resuming operations (e.g a hook sync). After calculating a diff --git a/docs/operator-manual/argocd-cmd-params-cm.yaml b/docs/operator-manual/argocd-cmd-params-cm.yaml index 5e8c04c7e50d6..dac955a9662de 100644 --- a/docs/operator-manual/argocd-cmd-params-cm.yaml +++ b/docs/operator-manual/argocd-cmd-params-cm.yaml @@ -68,6 +68,10 @@ data: controller.k8sclient.retry.base.backoff: "100" # Grace period in seconds for ignoring consecutive errors while communicating with repo server. controller.repo.error.grace.period.seconds: "180" + # Enables the server side diff feature at the application controller level. + # Diff calculation will be done by running a server side apply dryrun (when + # diff cache is unavailable). + controller.diff.server.side: "false" ## Server properties # Listen on given address for incoming connections (default "0.0.0.0") diff --git a/docs/operator-manual/server-commands/argocd-application-controller.md b/docs/operator-manual/server-commands/argocd-application-controller.md index 434c30621b8bd..1d71fc6494445 100644 --- a/docs/operator-manual/server-commands/argocd-application-controller.md +++ b/docs/operator-manual/server-commands/argocd-application-controller.md @@ -67,6 +67,7 @@ argocd-application-controller [flags] --sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379). --sentinelmaster string Redis sentinel master group name. (default "master") --server string The address and port of the Kubernetes API server + --server-side-diff-enabled Feature flag to enable ServerSide diff. Default ("false") --sharding-method string Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] (default "legacy") --status-processors int Number of application status processors (default 20) --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. diff --git a/docs/user-guide/commands/argocd_admin_app_get-reconcile-results.md b/docs/user-guide/commands/argocd_admin_app_get-reconcile-results.md index 3290098999b7c..29fa5d54d9388 100644 --- a/docs/user-guide/commands/argocd_admin_app_get-reconcile-results.md +++ b/docs/user-guide/commands/argocd_admin_app_get-reconcile-results.md @@ -32,6 +32,7 @@ argocd admin app get-reconcile-results PATH [flags] --repo-server string Repo server address. --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") --server string The address and port of the Kubernetes API server + --server-side-diff If set to "true" will use server-side diff while comparing resources. Default ("false") --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. --token string Bearer token for authentication to the API server --user string The name of the kubeconfig user to use diff --git a/docs/user-guide/diff-strategies.md b/docs/user-guide/diff-strategies.md new file mode 100644 index 0000000000000..a7b3216fa7ec7 --- /dev/null +++ b/docs/user-guide/diff-strategies.md @@ -0,0 +1,125 @@ +# Diff Strategies + +Argo CD calculates the diff between the desired state and the live +state in order to define if an Application is out-of-sync. This same +logic is also used in Argo CD UI to display the differences between +live and desired states for all resources belonging to an application. + +Argo CD currently has 3 different strategies to calculate diffs: + +- **Legacy**: This is the main diff strategy used by default. It + applies a 3-way diff based on live state, desired state and + last-applied-configuration (annotation). +- **Structured-Merge Diff**: Strategy automatically applied when + enabling Server-Side Apply sync option. +- **Server-Side Diff**: New strategy that invokes a Server-Side Apply + in dryrun mode in order to generate the predicted live state. + +## Structured-Merge Diff +*Current Status: [Beta][1] (Since v2.5.0)* + +This is diff strategy is automatically used when Server-Side Apply +sync option is enabled. It uses the [structured-merge-diff][2] library +used by Kubernetes to calculate diffs based on fields ownership. There +are some challenges using this strategy to calculate diffs for CRDs +that define default values. After different issues were identified by +the community, this strategy is being discontinued in favour of +Server-Side Diff. + +## Server-Side Diff +*Current Status: [Beta][1] (Since v2.10.0)* + +This diff strategy will execute a Server-Side Apply in dryrun mode for +each resource of the application. The response of this operation is then +compared with the live state in order to provide the diff results. The +diff results are cached and new Server-Side Apply requests to Kube API +are only triggered when: + +- An Application refresh or hard-refresh is requested. +- There is a new revision in the repo which the Argo CD Application is + targeting. +- The Argo CD Application spec changed. + +One advantage of Server-Side Diff is that Kubernetes Admission +Controllers will participate in the diff calculation. If for example +a validation webhook identifies a resource to be invalid, that will be +informed to Argo CD during the diff stage rather than during the sync +stage. + +### Enabling it + +Server-Side Diff can be enabled at the Argo CD Controller level or per +Application. + +**Enabling Server-Side Diff for all Applications** + +Add the following entry in the argocd-cmd-params-cm configmap: + +``` +controller.diff.server.side: "true" +``` + +Note: It is necessary to restart the `argocd-application-controller` +after applying this configuration. + +**Enabling Server-Side Diff for one application** + +Add the following annotation in the Argo CD Application resource: + +``` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + argocd.argoproj.io/compare-options: ServerSideDiff=true +... +``` + +**Disabling Server-Side Diff for one application** + +If Server-Side Diff is enabled globally in your Argo CD instance, it +is possible to disable it at the application level. In order to do so, +add the following annotation in the Application resource: + +``` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + argocd.argoproj.io/compare-options: ServerSideDiff=false +... +``` + +*Note: Please report any issues that forced you to disable the +Server-Side Diff feature* + +### Mutation Webhooks + +Server-Side Diff does not include changes made by mutation webhooks by +default. If you want to include mutation webhooks in Argo CD diffs add +the following annotation in the Argo CD Application resource: + +``` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + argocd.argoproj.io/compare-options: IncludeMutationWebhook=true +... +``` + +Note: This annoation is only effective when Server-Side Diff is +enabled. To enable both options for a given application add the +following annotation in the Argo CD Application resource: + +``` +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + argocd.argoproj.io/compare-options: ServerSideDiff=true,IncludeMutationWebhook=true +... +``` + +[1]: https://github.com/argoproj/argoproj/blob/main/community/feature-status.md#beta +[2]: https://github.com/kubernetes-sigs/structured-merge-diff diff --git a/go.mod b/go.mod index 03283f5c61f09..734b312579e27 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d github.com/alicebob/miniredis/v2 v2.30.4 github.com/antonmedv/expr v1.15.2 - github.com/argoproj/gitops-engine v0.7.1-0.20231102154024-c0c2dd1f6f48 + github.com/argoproj/gitops-engine v0.7.1-0.20231218194513-aba38192fb16 github.com/argoproj/notifications-engine v0.4.1-0.20231027194313-a8d185ecc0a9 github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1 github.com/aws/aws-sdk-go v1.44.317 @@ -104,7 +104,7 @@ require ( layeh.com/gopher-json v0.0.0-20190114024228-97fed8db8427 oras.land/oras-go/v2 v2.3.0 sigs.k8s.io/controller-runtime v0.14.6 - sigs.k8s.io/structured-merge-diff/v4 v4.3.0 + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 sigs.k8s.io/yaml v1.3.0 ) diff --git a/go.sum b/go.sum index 9f0d3f9a0976e..3c9e5941366b4 100644 --- a/go.sum +++ b/go.sum @@ -696,8 +696,8 @@ github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/appscode/go v0.0.0-20191119085241-0887d8ec2ecc/go.mod h1:OawnOmAL4ZX3YaPdN+8HTNwBveT1jMsqP74moa9XUbE= -github.com/argoproj/gitops-engine v0.7.1-0.20231102154024-c0c2dd1f6f48 h1:vnXMrNkBFC0H0KBkH1Jno31OVgJQR4KSd0ypEcfzQRA= -github.com/argoproj/gitops-engine v0.7.1-0.20231102154024-c0c2dd1f6f48/go.mod h1:1TchqKw9XmYYZluyEHa1dTJQoZgbV6PhabB/e8Wf3KY= +github.com/argoproj/gitops-engine v0.7.1-0.20231218194513-aba38192fb16 h1:kR15L8UsSVr7oitABKU88msQirMT0/RO/KRla1jkq/s= +github.com/argoproj/gitops-engine v0.7.1-0.20231218194513-aba38192fb16/go.mod h1:gWE8uROi7hIkWGNAVM+8FWkMfo0vZ03SLx/aFw/DBzg= github.com/argoproj/notifications-engine v0.4.1-0.20231027194313-a8d185ecc0a9 h1:1lt0VXzmLK7Vv0kaeal3S6/JIfzPyBORkUWXhiqF3l0= github.com/argoproj/notifications-engine v0.4.1-0.20231027194313-a8d185ecc0a9/go.mod h1:E/vv4+by868m0mmflaRfGBmKBtAupoF+mmyfekP8QCk= github.com/argoproj/pkg v0.13.7-0.20230626144333-d56162821bd1 h1:qsHwwOJ21K2Ao0xPju1sNuqphyMnMYkyB3ZLoLtxWpo= @@ -2715,8 +2715,8 @@ sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCY sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk= sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= -sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml b/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml index 4862721961f21..0fbf979809c97 100644 --- a/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml +++ b/manifests/base/application-controller-deployment/argocd-application-controller-deployment.yaml @@ -34,24 +34,30 @@ spec: name: argocd-cm key: timeout.hard.reconciliation optional: true + - name: ARGOCD_REPO_ERROR_GRACE_PERIOD_SECONDS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.error.grace.period.seconds + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: repo.server - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: repo.server + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.repo.server.timeout.seconds - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.server.timeout.seconds + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_STATUS_PROCESSORS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.status.processors - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.status.processors + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_OPERATION_PROCESSORS valueFrom: configMapKeyRef: @@ -78,22 +84,22 @@ spec: optional: true - name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_TIMEOUT_SECONDS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.self.heal.timeout.seconds - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.self.heal.timeout.seconds + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.repo.server.plaintext - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.server.plaintext + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.repo.server.strict.tls - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.server.strict.tls + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_PERSIST_RESOURCE_HEALTH valueFrom: configMapKeyRef: @@ -102,16 +108,16 @@ spec: optional: true - name: ARGOCD_APP_STATE_CACHE_EXPIRATION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.app.state.cache.expiration - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.app.state.cache.expiration + optional: true - name: REDIS_SERVER valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: redis.server - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: redis.server + optional: true - name: REDIS_COMPRESSION valueFrom: configMapKeyRef: @@ -120,70 +126,70 @@ spec: optional: true - name: REDISDB valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: redis.db - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: redis.db + optional: true - name: ARGOCD_DEFAULT_CACHE_EXPIRATION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.default.cache.expiration - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.default.cache.expiration + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: otlp.address - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.address + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: otlp.insecure - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.insecure + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: otlp.headers - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.headers + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: application.namespaces - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: application.namespaces + optional: true - name: ARGOCD_CONTROLLER_SHARDING_ALGORITHM valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.sharding.algorithm - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.sharding.algorithm + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_KUBECTL_PARALLELISM_LIMIT - valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.kubectl.parallelism.limit - optional: true - - name: ARGOCD_CONTROLLER_HEARTBEAT_TIME valueFrom: configMapKeyRef: name: argocd-cmd-params-cm - key: controller.heatbeat.time + key: controller.kubectl.parallelism.limit optional: true - name: ARGOCD_K8SCLIENT_RETRY_MAX valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.k8sclient.retry.max - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.k8sclient.retry.max + optional: true - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.k8sclient.retry.base.backoff - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.k8sclient.retry.base.backoff + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.diff.server.side + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-application-controller diff --git a/manifests/base/application-controller/argocd-application-controller-statefulset.yaml b/manifests/base/application-controller/argocd-application-controller-statefulset.yaml index f5e5c759e0750..62f98a1449215 100644 --- a/manifests/base/application-controller/argocd-application-controller-statefulset.yaml +++ b/manifests/base/application-controller/argocd-application-controller-statefulset.yaml @@ -43,22 +43,22 @@ spec: optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: repo.server - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: repo.server + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.repo.server.timeout.seconds - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.server.timeout.seconds + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_STATUS_PROCESSORS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.status.processors - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.status.processors + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_OPERATION_PROCESSORS valueFrom: configMapKeyRef: @@ -85,22 +85,22 @@ spec: optional: true - name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_TIMEOUT_SECONDS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.self.heal.timeout.seconds - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.self.heal.timeout.seconds + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.repo.server.plaintext - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.server.plaintext + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.repo.server.strict.tls - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.repo.server.strict.tls + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_PERSIST_RESOURCE_HEALTH valueFrom: configMapKeyRef: @@ -109,16 +109,16 @@ spec: optional: true - name: ARGOCD_APP_STATE_CACHE_EXPIRATION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.app.state.cache.expiration - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.app.state.cache.expiration + optional: true - name: REDIS_SERVER valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: redis.server - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: redis.server + optional: true - name: REDIS_COMPRESSION valueFrom: configMapKeyRef: @@ -127,64 +127,70 @@ spec: optional: true - name: REDISDB valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: redis.db - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: redis.db + optional: true - name: ARGOCD_DEFAULT_CACHE_EXPIRATION valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.default.cache.expiration - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.default.cache.expiration + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: otlp.address - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.address + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: otlp.insecure - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.insecure + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: otlp.headers - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: otlp.headers + optional: true - name: ARGOCD_APPLICATION_NAMESPACES valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: application.namespaces - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: application.namespaces + optional: true - name: ARGOCD_CONTROLLER_SHARDING_ALGORITHM valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.sharding.algorithm - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.sharding.algorithm + optional: true - name: ARGOCD_APPLICATION_CONTROLLER_KUBECTL_PARALLELISM_LIMIT valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.kubectl.parallelism.limit - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.kubectl.parallelism.limit + optional: true - name: ARGOCD_K8SCLIENT_RETRY_MAX valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.k8sclient.retry.max - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.k8sclient.retry.max + optional: true - name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF valueFrom: - configMapKeyRef: - name: argocd-cmd-params-cm - key: controller.k8sclient.retry.base.backoff - optional: true + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.k8sclient.retry.base.backoff + optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: controller.diff.server.side + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-application-controller diff --git a/manifests/core-install.yaml b/manifests/core-install.yaml index 5d2d225473452..16cc132f23754 100644 --- a/manifests/core-install.yaml +++ b/manifests/core-install.yaml @@ -21643,6 +21643,12 @@ spec: key: controller.k8sclient.retry.base.backoff name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + key: controller.diff.server.side + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-application-controller diff --git a/manifests/ha/install.yaml b/manifests/ha/install.yaml index 19f8015b04945..bae1de52d5e6f 100644 --- a/manifests/ha/install.yaml +++ b/manifests/ha/install.yaml @@ -23476,6 +23476,12 @@ spec: key: controller.k8sclient.retry.base.backoff name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + key: controller.diff.server.side + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-application-controller diff --git a/manifests/ha/namespace-install.yaml b/manifests/ha/namespace-install.yaml index 2ecd016092f1b..ad1a7baa8b017 100644 --- a/manifests/ha/namespace-install.yaml +++ b/manifests/ha/namespace-install.yaml @@ -2861,6 +2861,12 @@ spec: key: controller.k8sclient.retry.base.backoff name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + key: controller.diff.server.side + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-application-controller diff --git a/manifests/install.yaml b/manifests/install.yaml index 2f7da39bd9b12..22ba57873b694 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -22520,6 +22520,12 @@ spec: key: controller.k8sclient.retry.base.backoff name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + key: controller.diff.server.side + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-application-controller diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index d74ca00b88e4c..6fa2cdb2b6de0 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -1905,6 +1905,12 @@ spec: key: controller.k8sclient.retry.base.backoff name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF + valueFrom: + configMapKeyRef: + key: controller.diff.server.side + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-application-controller diff --git a/mkdocs.yml b/mkdocs.yml index 4a58580e29619..d4c206a01c4d1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -162,7 +162,9 @@ nav: - user-guide/multiple_sources.md - GnuPG verification: user-guide/gpg-verification.md - user-guide/auto_sync.md - - user-guide/diffing.md + - Diffing: + - Diff Strategies: user-guide/diff-strategies.md + - Diff Customization: user-guide/diffing.md - user-guide/orphaned-resources.md - user-guide/compare-options.md - user-guide/sync-options.md diff --git a/util/argo/diff/diff.go b/util/argo/diff/diff.go index 9b104719c5616..c99a04354c751 100644 --- a/util/argo/diff/diff.go +++ b/util/argo/diff/diff.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/go-logr/logr" + log "github.com/sirupsen/logrus" k8smanagedfields "k8s.io/apimachinery/pkg/util/managedfields" @@ -26,7 +27,9 @@ type DiffConfigBuilder struct { // NewDiffConfigBuilder create a new DiffConfigBuilder instance. func NewDiffConfigBuilder() *DiffConfigBuilder { return &DiffConfigBuilder{ - diffConfig: &diffConfig{}, + diffConfig: &diffConfig{ + ignoreMutationWebhook: true, + }, } } @@ -63,7 +66,6 @@ func (b *DiffConfigBuilder) WithNoCache() *DiffConfigBuilder { // WithCache sets the appstatecache.Cache and the appName in the diff config. Those the // are two objects necessary to retrieve a cached diff. func (b *DiffConfigBuilder) WithCache(s *appstatecache.Cache, appName string) *DiffConfigBuilder { - b.diffConfig.noCache = false b.diffConfig.stateCache = s b.diffConfig.appName = appName return b @@ -95,6 +97,21 @@ func (b *DiffConfigBuilder) WithManager(manager string) *DiffConfigBuilder { return b } +func (b *DiffConfigBuilder) WithServerSideDryRunner(ssdr diff.ServerSideDryRunner) *DiffConfigBuilder { + b.diffConfig.serverSideDryRunner = ssdr + return b +} + +func (b *DiffConfigBuilder) WithServerSideDiff(ssd bool) *DiffConfigBuilder { + b.diffConfig.serverSideDiff = ssd + return b +} + +func (b *DiffConfigBuilder) WithIgnoreMutationWebhook(m bool) *DiffConfigBuilder { + b.diffConfig.ignoreMutationWebhook = m + return b +} + // Build will first validate the current state of the diff config and return the // DiffConfig implementation if no errors are found. Will return nil and the error // details otherwise. @@ -140,6 +157,10 @@ type DiffConfig interface { // Manager returns the manager that should be used by the diff while // calculating the structured merge diff. Manager() string + + ServerSideDiff() bool + ServerSideDryRunner() diff.ServerSideDryRunner + IgnoreMutationWebhook() bool } // diffConfig defines the configurations used while applying diffs. @@ -156,6 +177,9 @@ type diffConfig struct { gvkParser *k8smanagedfields.GvkParser structuredMergeDiff bool manager string + serverSideDiff bool + serverSideDryRunner diff.ServerSideDryRunner + ignoreMutationWebhook bool } func (c *diffConfig) Ignores() []v1alpha1.ResourceIgnoreDifferences { @@ -194,6 +218,15 @@ func (c *diffConfig) StructuredMergeDiff() bool { func (c *diffConfig) Manager() string { return c.manager } +func (c *diffConfig) ServerSideDryRunner() diff.ServerSideDryRunner { + return c.serverSideDryRunner +} +func (c *diffConfig) ServerSideDiff() bool { + return c.serverSideDiff +} +func (c *diffConfig) IgnoreMutationWebhook() bool { + return c.ignoreMutationWebhook +} // Validate will check the current state of this diffConfig and return // error if it finds any required configuration missing. @@ -213,6 +246,9 @@ func (c *diffConfig) Validate() error { return fmt.Errorf("%s: StateCache must be set when retrieving from cache", msg) } } + if c.serverSideDiff && c.serverSideDryRunner == nil { + return fmt.Errorf("%s: serverSideDryRunner must be set when using server side diff", msg) + } return nil } @@ -254,6 +290,9 @@ func StateDiffs(lives, configs []*unstructured.Unstructured, diffConfig DiffConf diff.WithStructuredMergeDiff(diffConfig.StructuredMergeDiff()), diff.WithGVKParser(diffConfig.GVKParser()), diff.WithManager(diffConfig.Manager()), + diff.WithServerSideDiff(diffConfig.ServerSideDiff()), + diff.WithServerSideDryRunner(diffConfig.ServerSideDryRunner()), + diff.WithIgnoreMutationWebhook(diffConfig.IgnoreMutationWebhook()), } if diffConfig.Logger() != nil { @@ -282,9 +321,8 @@ func diffArrayCached(configArray []*unstructured.Unstructured, liveArray []*unst } diffByKey := map[kube.ResourceKey]*v1alpha1.ResourceDiff{} - for i := range cachedDiff { - res := cachedDiff[i] - diffByKey[kube.NewResourceKey(res.Group, res.Kind, res.Namespace, res.Name)] = cachedDiff[i] + for _, res := range cachedDiff { + diffByKey[kube.NewResourceKey(res.Group, res.Kind, res.Namespace, res.Name)] = res } diffResultList := diff.DiffResultList{ @@ -335,7 +373,12 @@ func (c *diffConfig) DiffFromCache(appName string) (bool, []*v1alpha1.ResourceDi return false, nil } cachedDiff := make([]*v1alpha1.ResourceDiff, 0) - if c.stateCache != nil && c.stateCache.GetAppManagedResources(appName, &cachedDiff) == nil { + if c.stateCache != nil { + err := c.stateCache.GetAppManagedResources(appName, &cachedDiff) + if err != nil { + log.Errorf("DiffFromCache error: error getting managed resources for app %s: %s", appName, err) + return false, nil + } return true, cachedDiff } return false, nil From 87e95b7485972cfc01b042302a37e037df7b63cc Mon Sep 17 00:00:00 2001 From: Alexy Mantha Date: Mon, 18 Dec 2023 15:38:53 -0500 Subject: [PATCH 48/66] feat(ui): add status panel extensions (#15780) * add extension Signed-off-by: Alexy Mantha * rename to status panel Signed-off-by: Alexy Mantha * wip Signed-off-by: Alexy Mantha * cleanup Signed-off-by: Alexy Mantha * add docs Signed-off-by: Alexy Mantha * add key Signed-off-by: Alexy Mantha * fix copy/paste Signed-off-by: Alexy Mantha * wip Signed-off-by: Alexy Mantha * flyout Signed-off-by: Alexy Mantha * cleanup Signed-off-by: Alexy Mantha * lint Signed-off-by: Alexy Mantha * document flyout Signed-off-by: Alexy Mantha * rename Signed-off-by: Alexy Mantha * linting Signed-off-by: Alexy Mantha --------- Signed-off-by: Alexy Mantha --- .../extensions/ui-extensions.md | 63 +++++++++++++++++++ .../application-details.tsx | 32 +++++++++- .../application-status-panel.tsx | 6 +- .../app/shared/services/extensions-service.ts | 33 +++++++++- 4 files changed, 129 insertions(+), 5 deletions(-) diff --git a/docs/developer-guide/extensions/ui-extensions.md b/docs/developer-guide/extensions/ui-extensions.md index ec1631038ad53..8d3d9dc4a3882 100644 --- a/docs/developer-guide/extensions/ui-extensions.md +++ b/docs/developer-guide/extensions/ui-extensions.md @@ -95,3 +95,66 @@ Below is an example of a simple system level extension: Since the Argo CD Application is a Kubernetes resource, application tabs can be the same as any other resource tab. Make sure to use 'argoproj.io'/'Application' as group/kind and an extension will be used to render the application-level tab. + +## Application Status Panel Extensions + +The status panel is the bar at the top of the application view where the sync status is displayed. Argo CD allows you to add new items to the status panel of an application. The extension should be registered using the `extensionsAPI.registerStatusPanelExtension` method: + +```typescript +registerStatusPanelExtension(component: StatusPanelExtensionComponent, title: string, id: string, flyout?: ExtensionComponent) +``` + +Below is an example of a simple extension: + +```typescript +((window) => { + const component = () => { + return React.createElement( + "div", + { style: { padding: "10px" } }, + "Hello World" + ); + }; + window.extensionsAPI.registerStatusPanelExtension( + component, + "My Extension", + "my_extension" + ); +})(window); +``` + +### Flyout widget + +It is also possible to add an optional flyout widget to your extension. It can be opened by calling `openFlyout()` from your extension's component. Your flyout component will then be rendered in a sliding panel, similar to the panel that opens when clicking on `History and rollback`. + +Below is an example of an extension using the flyout widget: + +```typescript +((window) => { + const component = (props: { + openFlyout: () => any + }) => { + return React.createElement( + "div", + { + style: { padding: "10px" }, + onClick: () => props.openFlyout() + }, + "Hello World" + ); + }; + const flyout = () => { + return React.createElement( + "div", + { style: { padding: "10px" } }, + "This is a flyout" + ); + }; + window.extensionsAPI.registerStatusPanelExtension( + component, + "My Extension", + "my_extension", + flyout + ); +})(window); +``` diff --git a/ui/src/app/applications/components/application-details/application-details.tsx b/ui/src/app/applications/components/application-details/application-details.tsx index 9b0a13f638c7b..a3e8175591dde 100644 --- a/ui/src/app/applications/components/application-details/application-details.tsx +++ b/ui/src/app/applications/components/application-details/application-details.tsx @@ -30,7 +30,7 @@ import {ApplicationsDetailsAppDropdown} from './application-details-app-dropdown import {useSidebarTarget} from '../../../sidebar/sidebar'; import './application-details.scss'; -import {AppViewExtension} from '../../../shared/services/extensions-service'; +import {AppViewExtension, StatusPanelExtension} from '../../../shared/services/extensions-service'; interface ApplicationDetailsState { page: number; @@ -42,6 +42,8 @@ interface ApplicationDetailsState { collapsedNodes?: string[]; extensions?: AppViewExtension[]; extensionsMap?: {[key: string]: AppViewExtension}; + statusExtensions?: StatusPanelExtension[]; + statusExtensionsMap?: {[key: string]: StatusPanelExtension}; } interface FilterInput { @@ -87,6 +89,11 @@ export class ApplicationDetails extends React.Component { extensionsMap[ext.title] = ext; }); + const statusExtensions = services.extensions.getStatusPanelExtensions(); + const statusExtensionsMap: {[key: string]: StatusPanelExtension} = {}; + statusExtensions.forEach(ext => { + statusExtensionsMap[ext.id] = ext; + }); this.state = { page: 0, groupedResources: [], @@ -95,7 +102,9 @@ export class ApplicationDetails extends React.Component this.selectNode(appFullName, 0, 'diff')} showOperation={() => this.setOperationStatusVisible(true)} showConditions={() => this.setConditionsStatusVisible(true)} + showExtension={id => this.setExtensionPanelVisible(id)} showMetadataInfo={revision => this.setState({...this.state, revision})} />
@@ -732,6 +749,13 @@ export class ApplicationDetails extends React.Component ))} + this.setExtensionPanelVisible('')}> + {this.selectedExtension !== '' && activeExtension && activeExtension.flyout && ( + + )} +
); @@ -966,6 +990,10 @@ export class ApplicationDetails extends React.Component any; showOperation?: () => any; showConditions?: () => any; + showExtension?: (id: string) => any; showMetadataInfo?: (revision: string) => any; } @@ -45,7 +46,7 @@ const sectionHeader = (info: SectionInfo, hasMultipleSources: boolean, onClick?: ); }; -export const ApplicationStatusPanel = ({application, showDiff, showOperation, showConditions, showMetadataInfo}: Props) => { +export const ApplicationStatusPanel = ({application, showDiff, showOperation, showConditions, showExtension, showMetadataInfo}: Props) => { const today = new Date(); let daysSinceLastSynchronized = 0; @@ -63,6 +64,8 @@ export const ApplicationStatusPanel = ({application, showDiff, showOperation, sh showOperation = null; } + const statusExtensions = services.extensions.getStatusPanelExtensions(); + const infos = cntByCategory.get('info'); const warnings = cntByCategory.get('warning'); const errors = cntByCategory.get('error'); @@ -203,6 +206,7 @@ export const ApplicationStatusPanel = ({application, showDiff, showOperation, sh )} + {statusExtensions && statusExtensions.map(ext => showExtension && showExtension(ext.id)} />)}
); }; diff --git a/ui/src/app/shared/services/extensions-service.ts b/ui/src/app/shared/services/extensions-service.ts index 3975fb1aec018..e26f3577b3487 100644 --- a/ui/src/app/shared/services/extensions-service.ts +++ b/ui/src/app/shared/services/extensions-service.ts @@ -6,7 +6,8 @@ import {Application, ApplicationTree, State} from '../models'; const extensions = { resourceExtentions: new Array(), systemLevelExtensions: new Array(), - appViewExtensions: new Array() + appViewExtensions: new Array(), + statusPanelExtensions: new Array() }; function registerResourceExtension(component: ExtensionComponent, group: string, kind: string, tabTitle: string, opts?: {icon: string}) { @@ -21,6 +22,10 @@ function registerAppViewExtension(component: ExtensionComponent, title: string, extensions.appViewExtensions.push({component, title, icon}); } +function registerStatusPanelExtension(component: StatusPanelExtensionComponent, title: string, id: string, flyout?: ExtensionComponent) { + extensions.statusPanelExtensions.push({component, flyout, title, id}); +} + let legacyInitialized = false; function initLegacyExtensions() { @@ -56,9 +61,18 @@ export interface AppViewExtension { icon?: string; } +export interface StatusPanelExtension { + component: StatusPanelExtensionComponent; + flyout?: StatusPanelExtensionFlyoutComponent; + title: string; + id: string; +} + export type ExtensionComponent = React.ComponentType; export type SystemExtensionComponent = React.ComponentType; export type AppViewExtensionComponent = React.ComponentType; +export type StatusPanelExtensionComponent = React.ComponentType; +export type StatusPanelExtensionFlyoutComponent = React.ComponentType; export interface Extension { component: ExtensionComponent; @@ -75,6 +89,16 @@ export interface AppViewComponentProps { tree: ApplicationTree; } +export interface StatusPanelComponentProps { + application: Application; + openFlyout: () => any; +} + +export interface StatusPanelFlyoutProps { + application: Application; + tree: ApplicationTree; +} + export class ExtensionsService { public getResourceTabs(group: string, kind: string): ResourceTabExtension[] { initLegacyExtensions(); @@ -89,6 +113,10 @@ export class ExtensionsService { public getAppViewExtensions(): AppViewExtension[] { return extensions.appViewExtensions.slice(); } + + public getStatusPanelExtensions(): StatusPanelExtension[] { + return extensions.statusPanelExtensions.slice(); + } } ((window: any) => { @@ -97,6 +125,7 @@ export class ExtensionsService { window.extensionsAPI = { registerResourceExtension, registerSystemLevelExtension, - registerAppViewExtension + registerAppViewExtension, + registerStatusPanelExtension }; })(window); From 3224102664b747e88e7330e59b3b44bc9c28cd66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Dec 2023 00:25:22 +0000 Subject: [PATCH 49/66] chore(deps): bump golang.org/x/crypto from 0.14.0 to 0.17.0 (#16645) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.14.0 to 0.17.0. - [Commits](https://github.com/golang/crypto/compare/v0.14.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 13 ++++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 734b312579e27..377f5c2592d01 100644 --- a/go.mod +++ b/go.mod @@ -80,11 +80,11 @@ require ( go.opentelemetry.io/otel v1.21.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 go.opentelemetry.io/otel/sdk v1.21.0 - golang.org/x/crypto v0.14.0 + golang.org/x/crypto v0.17.0 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 golang.org/x/oauth2 v0.11.0 golang.org/x/sync v0.3.0 - golang.org/x/term v0.13.0 + golang.org/x/term v0.15.0 google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d google.golang.org/grpc v1.59.0 google.golang.org/protobuf v1.31.0 @@ -269,8 +269,8 @@ require ( go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.17.0 - golang.org/x/sys v0.14.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 golang.org/x/tools v0.12.0 // indirect gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45 // indirect diff --git a/go.sum b/go.sum index 3c9e5941366b4..495bafe5b4053 100644 --- a/go.sum +++ b/go.sum @@ -1815,8 +1815,9 @@ golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0 golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2134,8 +2135,8 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2149,8 +2150,9 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2169,8 +2171,9 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 7847e7f393c0f83a12d4733a3a887f0875dbb086 Mon Sep 17 00:00:00 2001 From: Abhishek Veeramalla Date: Thu, 21 Dec 2023 14:04:06 +0530 Subject: [PATCH 50/66] chore: fix typo in application controller description (#16671) Signed-off-by: iam-veeramalla --- docs/developer-guide/architecture/components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/architecture/components.md b/docs/developer-guide/architecture/components.md index eb2904b531ccb..e073751da4867 100644 --- a/docs/developer-guide/architecture/components.md +++ b/docs/developer-guide/architecture/components.md @@ -71,7 +71,7 @@ and the CLI functionalities. ### Application Controller The Application Controller is responsible for reconciling the -Application resource in Kubernetes syncronizing the desired +Application resource in Kubernetes synchronizing the desired application state (provided in Git) with the live state (in Kubernetes). The Application Controller is also responsible for reconciling the Project resource. From 0e67ed89acac213a0dc358001d1fda2804b2d46d Mon Sep 17 00:00:00 2001 From: Robin Lieb Date: Fri, 22 Dec 2023 17:50:33 +0100 Subject: [PATCH 51/66] feat: add initiated by in history and rollback view (#16654) Signed-off-by: Robin Lieb --- assets/swagger.json | 3 + controller/state.go | 13 +- controller/state_test.go | 5 +- controller/sync.go | 2 +- docs/operator-manual/upgrading/2.10-2.11.md | 5 + manifests/core-install.yaml | 13 + manifests/crds/application-crd.yaml | 13 + manifests/ha/install.yaml | 13 + manifests/install.yaml | 13 + mkdocs.yml | 1 + pkg/apis/application/v1alpha1/generated.pb.go | 1422 +++++++++-------- pkg/apis/application/v1alpha1/generated.proto | 3 + .../application/v1alpha1/openapi_generated.go | 9 +- pkg/apis/application/v1alpha1/types.go | 2 + .../v1alpha1/zz_generated.deepcopy.go | 1 + .../application-deployment-history.tsx | 7 + .../initiated-by.tsx | 6 + ui/src/app/shared/models.ts | 1 + 18 files changed, 839 insertions(+), 693 deletions(-) create mode 100644 docs/operator-manual/upgrading/2.10-2.11.md create mode 100644 ui/src/app/applications/components/application-deployment-history/initiated-by.tsx diff --git a/assets/swagger.json b/assets/swagger.json index 44e67d0b3923e..a9f45fcbb1956 100644 --- a/assets/swagger.json +++ b/assets/swagger.json @@ -8499,6 +8499,9 @@ "format": "int64", "title": "ID is an auto incrementing identifier of the RevisionHistory" }, + "initiatedBy": { + "$ref": "#/definitions/v1alpha1OperationInitiator" + }, "revision": { "type": "string", "title": "Revision holds the revision the sync was performed against" diff --git a/controller/state.go b/controller/state.go index 5121fa68fcac9..704411558669b 100644 --- a/controller/state.go +++ b/controller/state.go @@ -880,7 +880,16 @@ func useDiffCache(noCache bool, manifestInfos []*apiclient.ManifestResponse, sou return true } -func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, revisions []string, sources []v1alpha1.ApplicationSource, hasMultipleSources bool, startedAt metav1.Time) error { +func (m *appStateManager) persistRevisionHistory( + app *v1alpha1.Application, + revision string, + source v1alpha1.ApplicationSource, + revisions []string, + sources []v1alpha1.ApplicationSource, + hasMultipleSources bool, + startedAt metav1.Time, + initiatedBy v1alpha1.OperationInitiator, +) error { var nextID int64 if len(app.Status.History) > 0 { nextID = app.Status.History.LastRevisionHistory().ID + 1 @@ -893,6 +902,7 @@ func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revi ID: nextID, Sources: sources, Revisions: revisions, + InitiatedBy: initiatedBy, }) } else { app.Status.History = append(app.Status.History, v1alpha1.RevisionHistory{ @@ -901,6 +911,7 @@ func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revi DeployStartedAt: &startedAt, ID: nextID, Source: source, + InitiatedBy: initiatedBy, }) } diff --git a/controller/state_test.go b/controller/state_test.go index 5d2342b601a77..1a55e25b262d1 100644 --- a/controller/state_test.go +++ b/controller/state_test.go @@ -23,6 +23,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/argoproj/argo-cd/v2/common" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" "github.com/argoproj/argo-cd/v2/test" @@ -838,7 +839,7 @@ func Test_appStateManager_persistRevisionHistory(t *testing.T) { app.Spec.RevisionHistoryLimit = &i } addHistory := func() { - err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1.Time{}) + err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1.Time{}, v1alpha1.OperationInitiator{}) assert.NoError(t, err) } addHistory() @@ -874,7 +875,7 @@ func Test_appStateManager_persistRevisionHistory(t *testing.T) { assert.Len(t, app.Status.History, 9) metav1NowTime := metav1.NewTime(time.Now()) - err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1NowTime) + err := manager.persistRevisionHistory(app, "my-revision", argoappv1.ApplicationSource{}, []string{}, []argoappv1.ApplicationSource{}, false, metav1NowTime, v1alpha1.OperationInitiator{}) assert.NoError(t, err) assert.Equal(t, app.Status.History.LastRevisionHistory().DeployStartedAt, &metav1NowTime) } diff --git a/controller/sync.go b/controller/sync.go index c33667a17a88d..2d21bf1cb1190 100644 --- a/controller/sync.go +++ b/controller/sync.go @@ -391,7 +391,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha logEntry.WithField("duration", time.Since(start)).Info("sync/terminate complete") if !syncOp.DryRun && len(syncOp.Resources) == 0 && state.Phase.Successful() { - err := m.persistRevisionHistory(app, compareResult.syncStatus.Revision, source, compareResult.syncStatus.Revisions, compareResult.syncStatus.ComparedTo.Sources, app.Spec.HasMultipleSources(), state.StartedAt) + err := m.persistRevisionHistory(app, compareResult.syncStatus.Revision, source, compareResult.syncStatus.Revisions, compareResult.syncStatus.ComparedTo.Sources, app.Spec.HasMultipleSources(), state.StartedAt, state.Operation.InitiatedBy) if err != nil { state.Phase = common.OperationError state.Message = fmt.Sprintf("failed to record sync to history: %v", err) diff --git a/docs/operator-manual/upgrading/2.10-2.11.md b/docs/operator-manual/upgrading/2.10-2.11.md new file mode 100644 index 0000000000000..4cf5c8ed02b0b --- /dev/null +++ b/docs/operator-manual/upgrading/2.10-2.11.md @@ -0,0 +1,5 @@ +# v2.10 to 2.11 + +## initiatedBy added in Application CRD + +In order to address [argoproj/argo-cd#16612](https://github.com/argoproj/argo-cd/issues/16612), initiatedBy has been added in the Application CRD. \ No newline at end of file diff --git a/manifests/core-install.yaml b/manifests/core-install.yaml index 16cc132f23754..c9028a44a1ae0 100644 --- a/manifests/core-install.yaml +++ b/manifests/core-install.yaml @@ -1726,6 +1726,19 @@ spec: description: ID is an auto incrementing identifier of the RevisionHistory format: int64 type: integer + initiatedBy: + description: InitiatedBy contains information about who initiated + the operations + properties: + automated: + description: Automated is set to true if operation was initiated + automatically by the application controller. + type: boolean + username: + description: Username contains the name of a user who started + operation + type: string + type: object revision: description: Revision holds the revision the sync was performed against diff --git a/manifests/crds/application-crd.yaml b/manifests/crds/application-crd.yaml index 962543f80eb97..f325dda7da6f7 100644 --- a/manifests/crds/application-crd.yaml +++ b/manifests/crds/application-crd.yaml @@ -1725,6 +1725,19 @@ spec: description: ID is an auto incrementing identifier of the RevisionHistory format: int64 type: integer + initiatedBy: + description: InitiatedBy contains information about who initiated + the operations + properties: + automated: + description: Automated is set to true if operation was initiated + automatically by the application controller. + type: boolean + username: + description: Username contains the name of a user who started + operation + type: string + type: object revision: description: Revision holds the revision the sync was performed against diff --git a/manifests/ha/install.yaml b/manifests/ha/install.yaml index bae1de52d5e6f..81f365bb8a86d 100644 --- a/manifests/ha/install.yaml +++ b/manifests/ha/install.yaml @@ -1726,6 +1726,19 @@ spec: description: ID is an auto incrementing identifier of the RevisionHistory format: int64 type: integer + initiatedBy: + description: InitiatedBy contains information about who initiated + the operations + properties: + automated: + description: Automated is set to true if operation was initiated + automatically by the application controller. + type: boolean + username: + description: Username contains the name of a user who started + operation + type: string + type: object revision: description: Revision holds the revision the sync was performed against diff --git a/manifests/install.yaml b/manifests/install.yaml index 22ba57873b694..3d1bbf942afb5 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -1726,6 +1726,19 @@ spec: description: ID is an auto incrementing identifier of the RevisionHistory format: int64 type: integer + initiatedBy: + description: InitiatedBy contains information about who initiated + the operations + properties: + automated: + description: Automated is set to true if operation was initiated + automatically by the application controller. + type: boolean + username: + description: Username contains the name of a user who started + operation + type: string + type: object revision: description: Revision holds the revision the sync was performed against diff --git a/mkdocs.yml b/mkdocs.yml index d4c206a01c4d1..a7e8f86e216cc 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -128,6 +128,7 @@ nav: - operator-manual/server-commands/additional-configuration-method.md - Upgrading: - operator-manual/upgrading/overview.md + - operator-manual/upgrading/2.10-2.11.md - operator-manual/upgrading/2.9-2.10.md - operator-manual/upgrading/2.8-2.9.md - operator-manual/upgrading/2.7-2.8.md diff --git a/pkg/apis/application/v1alpha1/generated.pb.go b/pkg/apis/application/v1alpha1/generated.pb.go index 2a9c0f4789bb7..cccbc3f7f15a4 100644 --- a/pkg/apis/application/v1alpha1/generated.pb.go +++ b/pkg/apis/application/v1alpha1/generated.pb.go @@ -4448,694 +4448,694 @@ func init() { } var fileDescriptor_030104ce3b95bcac = []byte{ - // 10983 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x7d, 0x70, 0x1c, 0xc9, - 0x75, 0x18, 0xae, 0xd9, 0x0f, 0x60, 0xf7, 0xe1, 0x83, 0x64, 0x93, 0xbc, 0x03, 0xa9, 0xbb, 0x03, - 0x3d, 0xf7, 0xf3, 0xe9, 0xfc, 0xd3, 0x1d, 0xe0, 0xa3, 0xef, 0xe4, 0x8b, 0xce, 0x96, 0x8c, 0x0f, - 0x12, 0x04, 0x09, 0x10, 0xb8, 0x06, 0x48, 0x4a, 0x27, 0x9f, 0x4e, 0x83, 0xdd, 0xc6, 0x62, 0x88, - 0xd9, 0x99, 0xb9, 0x99, 0x59, 0x10, 0x38, 0x4b, 0xb2, 0x64, 0xc9, 0xb6, 0x12, 0x7d, 0x9c, 0x22, - 0x25, 0xe5, 0x73, 0x12, 0x29, 0xb2, 0xe5, 0xa4, 0xec, 0x4a, 0x54, 0x71, 0x92, 0x3f, 0xe2, 0xc4, - 0x49, 0xb9, 0x6c, 0xa7, 0x52, 0x4a, 0x29, 0x29, 0xbb, 0x52, 0x2e, 0xcb, 0x49, 0x6c, 0x44, 0x62, - 0x2a, 0x95, 0x54, 0xaa, 0xe2, 0x2a, 0x27, 0xfe, 0x23, 0x61, 0xf2, 0x47, 0xaa, 0xbf, 0x7b, 0x66, - 0x67, 0x81, 0x05, 0x30, 0x20, 0x29, 0xe5, 0xfe, 0xdb, 0xed, 0xf7, 0xe6, 0xbd, 0x9e, 0x9e, 0xee, - 0xf7, 0x5e, 0xbf, 0x7e, 0xef, 0x35, 0x2c, 0xb4, 0xdc, 0x64, 0xa3, 0xb3, 0x36, 0xd1, 0x08, 0xda, - 0x93, 0x4e, 0xd4, 0x0a, 0xc2, 0x28, 0xb8, 0xcd, 0x7e, 0x3c, 0xdb, 0x68, 0x4e, 0x6e, 0x5d, 0x9c, - 0x0c, 0x37, 0x5b, 0x93, 0x4e, 0xe8, 0xc6, 0x93, 0x4e, 0x18, 0x7a, 0x6e, 0xc3, 0x49, 0xdc, 0xc0, - 0x9f, 0xdc, 0x7a, 0xce, 0xf1, 0xc2, 0x0d, 0xe7, 0xb9, 0xc9, 0x16, 0xf1, 0x49, 0xe4, 0x24, 0xa4, - 0x39, 0x11, 0x46, 0x41, 0x12, 0xa0, 0x1f, 0xd3, 0xd4, 0x26, 0x24, 0x35, 0xf6, 0xe3, 0xb5, 0x46, - 0x73, 0x62, 0xeb, 0xe2, 0x44, 0xb8, 0xd9, 0x9a, 0xa0, 0xd4, 0x26, 0x0c, 0x6a, 0x13, 0x92, 0xda, - 0xf9, 0x67, 0x8d, 0xbe, 0xb4, 0x82, 0x56, 0x30, 0xc9, 0x88, 0xae, 0x75, 0xd6, 0xd9, 0x3f, 0xf6, - 0x87, 0xfd, 0xe2, 0xcc, 0xce, 0xdb, 0x9b, 0x2f, 0xc6, 0x13, 0x6e, 0x40, 0xbb, 0x37, 0xd9, 0x08, - 0x22, 0x32, 0xb9, 0xd5, 0xd5, 0xa1, 0xf3, 0x57, 0x34, 0x0e, 0xd9, 0x4e, 0x88, 0x1f, 0xbb, 0x81, - 0x1f, 0x3f, 0x4b, 0xbb, 0x40, 0xa2, 0x2d, 0x12, 0x99, 0xaf, 0x67, 0x20, 0xe4, 0x51, 0x7a, 0x5e, - 0x53, 0x6a, 0x3b, 0x8d, 0x0d, 0xd7, 0x27, 0xd1, 0x8e, 0x7e, 0xbc, 0x4d, 0x12, 0x27, 0xef, 0xa9, - 0xc9, 0x5e, 0x4f, 0x45, 0x1d, 0x3f, 0x71, 0xdb, 0xa4, 0xeb, 0x81, 0xf7, 0xec, 0xf7, 0x40, 0xdc, - 0xd8, 0x20, 0x6d, 0xa7, 0xeb, 0xb9, 0x1f, 0xe9, 0xf5, 0x5c, 0x27, 0x71, 0xbd, 0x49, 0xd7, 0x4f, - 0xe2, 0x24, 0xca, 0x3e, 0x64, 0xbf, 0x0e, 0x23, 0x53, 0xb7, 0x56, 0xa6, 0x3a, 0xc9, 0xc6, 0x4c, - 0xe0, 0xaf, 0xbb, 0x2d, 0xf4, 0x02, 0x0c, 0x35, 0xbc, 0x4e, 0x9c, 0x90, 0xe8, 0xba, 0xd3, 0x26, - 0x63, 0xd6, 0x05, 0xeb, 0xe9, 0xfa, 0xf4, 0xe9, 0x6f, 0xee, 0x8e, 0xbf, 0xe3, 0xee, 0xee, 0xf8, - 0xd0, 0x8c, 0x06, 0x61, 0x13, 0x0f, 0xfd, 0x10, 0x0c, 0x46, 0x81, 0x47, 0xa6, 0xf0, 0xf5, 0xb1, - 0x12, 0x7b, 0xe4, 0x84, 0x78, 0x64, 0x10, 0xf3, 0x66, 0x2c, 0xe1, 0xf6, 0x1f, 0x96, 0x00, 0xa6, - 0xc2, 0x70, 0x39, 0x0a, 0x6e, 0x93, 0x46, 0x82, 0x3e, 0x02, 0x35, 0x3a, 0x74, 0x4d, 0x27, 0x71, - 0x18, 0xb7, 0xa1, 0x8b, 0x3f, 0x3c, 0xc1, 0xdf, 0x64, 0xc2, 0x7c, 0x13, 0x3d, 0x71, 0x28, 0xf6, - 0xc4, 0xd6, 0x73, 0x13, 0x4b, 0x6b, 0xf4, 0xf9, 0x45, 0x92, 0x38, 0xd3, 0x48, 0x30, 0x03, 0xdd, - 0x86, 0x15, 0x55, 0xe4, 0x43, 0x25, 0x0e, 0x49, 0x83, 0x75, 0x6c, 0xe8, 0xe2, 0xc2, 0xc4, 0x51, - 0x66, 0xe8, 0x84, 0xee, 0xf9, 0x4a, 0x48, 0x1a, 0xd3, 0xc3, 0x82, 0x73, 0x85, 0xfe, 0xc3, 0x8c, - 0x0f, 0xda, 0x82, 0x81, 0x38, 0x71, 0x92, 0x4e, 0x3c, 0x56, 0x66, 0x1c, 0xaf, 0x17, 0xc6, 0x91, - 0x51, 0x9d, 0x1e, 0x15, 0x3c, 0x07, 0xf8, 0x7f, 0x2c, 0xb8, 0xd9, 0x7f, 0x62, 0xc1, 0xa8, 0x46, - 0x5e, 0x70, 0xe3, 0x04, 0xfd, 0x64, 0xd7, 0xe0, 0x4e, 0xf4, 0x37, 0xb8, 0xf4, 0x69, 0x36, 0xb4, - 0x27, 0x05, 0xb3, 0x9a, 0x6c, 0x31, 0x06, 0xb6, 0x0d, 0x55, 0x37, 0x21, 0xed, 0x78, 0xac, 0x74, - 0xa1, 0xfc, 0xf4, 0xd0, 0xc5, 0x2b, 0x45, 0xbd, 0xe7, 0xf4, 0x88, 0x60, 0x5a, 0x9d, 0xa7, 0xe4, - 0x31, 0xe7, 0x62, 0xff, 0xda, 0xb0, 0xf9, 0x7e, 0x74, 0xc0, 0xd1, 0x73, 0x30, 0x14, 0x07, 0x9d, - 0xa8, 0x41, 0x30, 0x09, 0x83, 0x78, 0xcc, 0xba, 0x50, 0xa6, 0x53, 0x8f, 0xce, 0xd4, 0x15, 0xdd, - 0x8c, 0x4d, 0x1c, 0xf4, 0x05, 0x0b, 0x86, 0x9b, 0x24, 0x4e, 0x5c, 0x9f, 0xf1, 0x97, 0x9d, 0x5f, - 0x3d, 0x72, 0xe7, 0x65, 0xe3, 0xac, 0x26, 0x3e, 0x7d, 0x46, 0xbc, 0xc8, 0xb0, 0xd1, 0x18, 0xe3, - 0x14, 0x7f, 0xba, 0xe2, 0x9a, 0x24, 0x6e, 0x44, 0x6e, 0x48, 0xff, 0xb3, 0x39, 0x63, 0xac, 0xb8, - 0x59, 0x0d, 0xc2, 0x26, 0x1e, 0xf2, 0xa1, 0x4a, 0x57, 0x54, 0x3c, 0x56, 0x61, 0xfd, 0x9f, 0x3f, - 0x5a, 0xff, 0xc5, 0xa0, 0xd2, 0xc5, 0xaa, 0x47, 0x9f, 0xfe, 0x8b, 0x31, 0x67, 0x83, 0x3e, 0x6f, - 0xc1, 0x98, 0x58, 0xf1, 0x98, 0xf0, 0x01, 0xbd, 0xb5, 0xe1, 0x26, 0xc4, 0x73, 0xe3, 0x64, 0xac, - 0xca, 0xfa, 0x30, 0xd9, 0xdf, 0xdc, 0x9a, 0x8b, 0x82, 0x4e, 0x78, 0xcd, 0xf5, 0x9b, 0xd3, 0x17, - 0x04, 0xa7, 0xb1, 0x99, 0x1e, 0x84, 0x71, 0x4f, 0x96, 0xe8, 0xcb, 0x16, 0x9c, 0xf7, 0x9d, 0x36, - 0x89, 0x43, 0x87, 0x7e, 0x5a, 0x0e, 0x9e, 0xf6, 0x9c, 0xc6, 0x26, 0xeb, 0xd1, 0xc0, 0xe1, 0x7a, - 0x64, 0x8b, 0x1e, 0x9d, 0xbf, 0xde, 0x93, 0x34, 0xde, 0x83, 0x2d, 0xfa, 0xba, 0x05, 0xa7, 0x82, - 0x28, 0xdc, 0x70, 0x7c, 0xd2, 0x94, 0xd0, 0x78, 0x6c, 0x90, 0x2d, 0xbd, 0x0f, 0x1f, 0xed, 0x13, - 0x2d, 0x65, 0xc9, 0x2e, 0x06, 0xbe, 0x9b, 0x04, 0xd1, 0x0a, 0x49, 0x12, 0xd7, 0x6f, 0xc5, 0xd3, - 0x67, 0xef, 0xee, 0x8e, 0x9f, 0xea, 0xc2, 0xc2, 0xdd, 0xfd, 0x41, 0x3f, 0x05, 0x43, 0xf1, 0x8e, - 0xdf, 0xb8, 0xe5, 0xfa, 0xcd, 0xe0, 0x4e, 0x3c, 0x56, 0x2b, 0x62, 0xf9, 0xae, 0x28, 0x82, 0x62, - 0x01, 0x6a, 0x06, 0xd8, 0xe4, 0x96, 0xff, 0xe1, 0xf4, 0x54, 0xaa, 0x17, 0xfd, 0xe1, 0xf4, 0x64, - 0xda, 0x83, 0x2d, 0xfa, 0x79, 0x0b, 0x46, 0x62, 0xb7, 0xe5, 0x3b, 0x49, 0x27, 0x22, 0xd7, 0xc8, - 0x4e, 0x3c, 0x06, 0xac, 0x23, 0x57, 0x8f, 0x38, 0x2a, 0x06, 0xc9, 0xe9, 0xb3, 0xa2, 0x8f, 0x23, - 0x66, 0x6b, 0x8c, 0xd3, 0x7c, 0xf3, 0x16, 0x9a, 0x9e, 0xd6, 0x43, 0xc5, 0x2e, 0x34, 0x3d, 0xa9, - 0x7b, 0xb2, 0x44, 0x3f, 0x01, 0x27, 0x79, 0x93, 0x1a, 0xd9, 0x78, 0x6c, 0x98, 0x09, 0xda, 0x33, - 0x77, 0x77, 0xc7, 0x4f, 0xae, 0x64, 0x60, 0xb8, 0x0b, 0x1b, 0xbd, 0x0e, 0xe3, 0x21, 0x89, 0xda, - 0x6e, 0xb2, 0xe4, 0x7b, 0x3b, 0x52, 0x7c, 0x37, 0x82, 0x90, 0x34, 0x45, 0x77, 0xe2, 0xb1, 0x91, - 0x0b, 0xd6, 0xd3, 0xb5, 0xe9, 0x77, 0x89, 0x6e, 0x8e, 0x2f, 0xef, 0x8d, 0x8e, 0xf7, 0xa3, 0x67, - 0xff, 0xcb, 0x12, 0x9c, 0xcc, 0x2a, 0x4e, 0xf4, 0xb7, 0x2d, 0x38, 0x71, 0xfb, 0x4e, 0xb2, 0x1a, - 0x6c, 0x12, 0x3f, 0x9e, 0xde, 0xa1, 0xe2, 0x8d, 0xa9, 0x8c, 0xa1, 0x8b, 0x8d, 0x62, 0x55, 0xf4, - 0xc4, 0xd5, 0x34, 0x97, 0x4b, 0x7e, 0x12, 0xed, 0x4c, 0x3f, 0x2a, 0xde, 0xee, 0xc4, 0xd5, 0x5b, - 0xab, 0x26, 0x14, 0x67, 0x3b, 0x75, 0xfe, 0xb3, 0x16, 0x9c, 0xc9, 0x23, 0x81, 0x4e, 0x42, 0x79, - 0x93, 0xec, 0x70, 0xab, 0x0c, 0xd3, 0x9f, 0xe8, 0x55, 0xa8, 0x6e, 0x39, 0x5e, 0x87, 0x08, 0xeb, - 0x66, 0xee, 0x68, 0x2f, 0xa2, 0x7a, 0x86, 0x39, 0xd5, 0xf7, 0x96, 0x5e, 0xb4, 0xec, 0xdf, 0x2b, - 0xc3, 0x90, 0xa1, 0xdf, 0xee, 0x83, 0xc5, 0x16, 0xa4, 0x2c, 0xb6, 0xc5, 0xc2, 0x54, 0x73, 0x4f, - 0x93, 0xed, 0x4e, 0xc6, 0x64, 0x5b, 0x2a, 0x8e, 0xe5, 0x9e, 0x36, 0x1b, 0x4a, 0xa0, 0x1e, 0x84, - 0xd4, 0x22, 0xa7, 0xaa, 0xbf, 0x52, 0xc4, 0x27, 0x5c, 0x92, 0xe4, 0xa6, 0x47, 0xee, 0xee, 0x8e, - 0xd7, 0xd5, 0x5f, 0xac, 0x19, 0xd9, 0xdf, 0xb6, 0xe0, 0x8c, 0xd1, 0xc7, 0x99, 0xc0, 0x6f, 0xba, - 0xec, 0xd3, 0x5e, 0x80, 0x4a, 0xb2, 0x13, 0x4a, 0xb3, 0x5f, 0x8d, 0xd4, 0xea, 0x4e, 0x48, 0x30, - 0x83, 0x50, 0x43, 0xbf, 0x4d, 0xe2, 0xd8, 0x69, 0x91, 0xac, 0xa1, 0xbf, 0xc8, 0x9b, 0xb1, 0x84, - 0xa3, 0x08, 0x90, 0xe7, 0xc4, 0xc9, 0x6a, 0xe4, 0xf8, 0x31, 0x23, 0xbf, 0xea, 0xb6, 0x89, 0x18, - 0xe0, 0xff, 0xbf, 0xbf, 0x19, 0x43, 0x9f, 0x98, 0x7e, 0xe4, 0xee, 0xee, 0x38, 0x5a, 0xe8, 0xa2, - 0x84, 0x73, 0xa8, 0xdb, 0x5f, 0xb6, 0xe0, 0x91, 0x7c, 0x5b, 0x0c, 0x3d, 0x05, 0x03, 0x7c, 0xcb, - 0x27, 0xde, 0x4e, 0x7f, 0x12, 0xd6, 0x8a, 0x05, 0x14, 0x4d, 0x42, 0x5d, 0xe9, 0x09, 0xf1, 0x8e, - 0xa7, 0x04, 0x6a, 0x5d, 0x2b, 0x17, 0x8d, 0x43, 0x07, 0x8d, 0xfe, 0x11, 0x96, 0x9b, 0x1a, 0x34, - 0xb6, 0x49, 0x62, 0x10, 0xfb, 0x3f, 0x58, 0x70, 0xc2, 0xe8, 0xd5, 0x7d, 0x30, 0xcd, 0xfd, 0xb4, - 0x69, 0x3e, 0x5f, 0xd8, 0x7c, 0xee, 0x61, 0x9b, 0x7f, 0xde, 0x82, 0xf3, 0x06, 0xd6, 0xa2, 0x93, - 0x34, 0x36, 0x2e, 0x6d, 0x87, 0x11, 0x89, 0xe9, 0x76, 0x1a, 0x3d, 0x6e, 0xc8, 0xad, 0xe9, 0x21, - 0x41, 0xa1, 0x7c, 0x8d, 0xec, 0x70, 0x21, 0xf6, 0x0c, 0xd4, 0xf8, 0xe4, 0x0c, 0x22, 0x31, 0xe2, - 0xea, 0xdd, 0x96, 0x44, 0x3b, 0x56, 0x18, 0xc8, 0x86, 0x01, 0x26, 0x9c, 0xe8, 0x62, 0xa5, 0x6a, - 0x08, 0xe8, 0x47, 0xbc, 0xc9, 0x5a, 0xb0, 0x80, 0xd8, 0x71, 0xaa, 0x3b, 0xcb, 0x11, 0x61, 0x1f, - 0xb7, 0x79, 0xd9, 0x25, 0x5e, 0x33, 0xa6, 0xdb, 0x06, 0xc7, 0xf7, 0x83, 0x44, 0xec, 0x00, 0x8c, - 0x6d, 0xc3, 0x94, 0x6e, 0xc6, 0x26, 0x0e, 0x65, 0xea, 0x39, 0x6b, 0xc4, 0xe3, 0x23, 0x2a, 0x98, - 0x2e, 0xb0, 0x16, 0x2c, 0x20, 0xf6, 0xdd, 0x12, 0xdb, 0xa0, 0xa8, 0xa5, 0x4f, 0xee, 0xc7, 0xee, - 0x36, 0x4a, 0xc9, 0xca, 0xe5, 0xe2, 0x04, 0x17, 0xe9, 0xbd, 0xc3, 0x7d, 0x23, 0x23, 0x2e, 0x71, - 0xa1, 0x5c, 0xf7, 0xde, 0xe5, 0xfe, 0x76, 0x09, 0xc6, 0xd3, 0x0f, 0x74, 0x49, 0x5b, 0xba, 0xa5, - 0x32, 0x18, 0x65, 0x9d, 0x18, 0x06, 0x3e, 0x36, 0xf1, 0x7a, 0x08, 0xac, 0xd2, 0x71, 0x0a, 0x2c, - 0x53, 0x9e, 0x96, 0xf7, 0x91, 0xa7, 0x4f, 0xa9, 0x51, 0xaf, 0x64, 0x04, 0x58, 0x5a, 0xa7, 0x5c, - 0x80, 0x4a, 0x9c, 0x90, 0x70, 0xac, 0x9a, 0x96, 0x47, 0x2b, 0x09, 0x09, 0x31, 0x83, 0xd8, 0xff, - 0xb5, 0x04, 0x8f, 0xa6, 0xc7, 0x50, 0xab, 0x80, 0xf7, 0xa7, 0x54, 0xc0, 0xbb, 0x4d, 0x15, 0x70, - 0x6f, 0x77, 0xfc, 0x9d, 0x3d, 0x1e, 0xfb, 0x9e, 0xd1, 0x10, 0x68, 0x2e, 0x33, 0x8a, 0x93, 0xe9, - 0x51, 0xbc, 0xb7, 0x3b, 0xfe, 0x78, 0x8f, 0x77, 0xcc, 0x0c, 0xf3, 0x53, 0x30, 0x10, 0x11, 0x27, - 0x0e, 0x7c, 0x31, 0xd0, 0xea, 0x73, 0x60, 0xd6, 0x8a, 0x05, 0xd4, 0xfe, 0x37, 0xf5, 0xec, 0x60, - 0xcf, 0x71, 0x27, 0x5c, 0x10, 0x21, 0x17, 0x2a, 0xcc, 0xac, 0xe7, 0xa2, 0xe1, 0xda, 0xd1, 0x96, - 0x11, 0x55, 0x03, 0x8a, 0xf4, 0x74, 0x8d, 0x7e, 0x35, 0xda, 0x84, 0x19, 0x0b, 0xb4, 0x0d, 0xb5, - 0x86, 0xb4, 0xb6, 0x4b, 0x45, 0xf8, 0xa5, 0x84, 0xad, 0xad, 0x39, 0x0e, 0x53, 0x79, 0xad, 0x4c, - 0x74, 0xc5, 0x0d, 0x11, 0x28, 0xb7, 0xdc, 0x44, 0x7c, 0xd6, 0x23, 0xee, 0xa7, 0xe6, 0x5c, 0xe3, - 0x15, 0x07, 0xa9, 0x12, 0x99, 0x73, 0x13, 0x4c, 0xe9, 0xa3, 0x9f, 0xb5, 0x60, 0x28, 0x6e, 0xb4, - 0x97, 0xa3, 0x60, 0xcb, 0x6d, 0x92, 0x48, 0x58, 0x53, 0x47, 0x14, 0x4d, 0x2b, 0x33, 0x8b, 0x92, - 0xa0, 0xe6, 0xcb, 0xf7, 0xb7, 0x1a, 0x82, 0x4d, 0xbe, 0x74, 0x97, 0xf1, 0xa8, 0x78, 0xf7, 0x59, - 0xd2, 0x70, 0xa9, 0xfe, 0x93, 0x9b, 0x2a, 0x36, 0x53, 0x8e, 0x6c, 0x5d, 0xce, 0x76, 0x1a, 0x9b, - 0x74, 0xbd, 0xe9, 0x0e, 0xbd, 0xf3, 0xee, 0xee, 0xf8, 0xa3, 0x33, 0xf9, 0x3c, 0x71, 0xaf, 0xce, - 0xb0, 0x01, 0x0b, 0x3b, 0x9e, 0x87, 0xc9, 0xeb, 0x1d, 0xc2, 0x5c, 0x26, 0x05, 0x0c, 0xd8, 0xb2, - 0x26, 0x98, 0x19, 0x30, 0x03, 0x82, 0x4d, 0xbe, 0xe8, 0x75, 0x18, 0x68, 0x3b, 0x49, 0xe4, 0x6e, - 0x0b, 0x3f, 0xc9, 0x11, 0xed, 0xfd, 0x45, 0x46, 0x4b, 0x33, 0x67, 0x9a, 0x9a, 0x37, 0x62, 0xc1, - 0x08, 0xb5, 0xa1, 0xda, 0x26, 0x51, 0x8b, 0x8c, 0xd5, 0x8a, 0xf0, 0x09, 0x2f, 0x52, 0x52, 0x9a, - 0x61, 0x9d, 0x5a, 0x47, 0xac, 0x0d, 0x73, 0x2e, 0xe8, 0x55, 0xa8, 0xc5, 0xc4, 0x23, 0x0d, 0x6a, - 0xdf, 0xd4, 0x19, 0xc7, 0x1f, 0xe9, 0xd3, 0xd6, 0xa3, 0x86, 0xc5, 0x8a, 0x78, 0x94, 0x2f, 0x30, - 0xf9, 0x0f, 0x2b, 0x92, 0x74, 0x00, 0x43, 0xaf, 0xd3, 0x72, 0xfd, 0x31, 0x28, 0x62, 0x00, 0x97, - 0x19, 0xad, 0xcc, 0x00, 0xf2, 0x46, 0x2c, 0x18, 0xd9, 0xff, 0xc9, 0x02, 0x94, 0x16, 0x6a, 0xf7, - 0xc1, 0xa8, 0x7d, 0x3d, 0x6d, 0xd4, 0x2e, 0x14, 0x69, 0x75, 0xf4, 0xb0, 0x6b, 0x7f, 0xb3, 0x0e, - 0x19, 0x75, 0x70, 0x9d, 0xc4, 0x09, 0x69, 0xbe, 0x2d, 0xc2, 0xdf, 0x16, 0xe1, 0x6f, 0x8b, 0x70, - 0x25, 0xc2, 0xd7, 0x32, 0x22, 0xfc, 0x7d, 0xc6, 0xaa, 0xd7, 0x87, 0xaa, 0xaf, 0xa9, 0x53, 0x57, - 0xb3, 0x07, 0x06, 0x02, 0x95, 0x04, 0x57, 0x57, 0x96, 0xae, 0xe7, 0xca, 0xec, 0xd7, 0xd2, 0x32, - 0xfb, 0xa8, 0x2c, 0xfe, 0x5f, 0x90, 0xd2, 0xff, 0xc2, 0x82, 0x77, 0xa5, 0xa5, 0x97, 0x9c, 0x39, - 0xf3, 0x2d, 0x3f, 0x88, 0xc8, 0xac, 0xbb, 0xbe, 0x4e, 0x22, 0xe2, 0x37, 0x48, 0xac, 0xbc, 0x18, - 0x56, 0x2f, 0x2f, 0x06, 0x7a, 0x1e, 0x86, 0x6f, 0xc7, 0x81, 0xbf, 0x1c, 0xb8, 0xbe, 0x10, 0x41, - 0x74, 0x23, 0x7c, 0xf2, 0xee, 0xee, 0xf8, 0x30, 0x1d, 0x51, 0xd9, 0x8e, 0x53, 0x58, 0x68, 0x06, - 0x4e, 0xdd, 0x7e, 0x7d, 0xd9, 0x49, 0x0c, 0x77, 0x80, 0xdc, 0xb8, 0xb3, 0x03, 0x8b, 0xab, 0x2f, - 0x67, 0x80, 0xb8, 0x1b, 0xdf, 0xfe, 0xeb, 0x25, 0x38, 0x97, 0x79, 0x91, 0xc0, 0xf3, 0x82, 0x4e, - 0x42, 0x37, 0x35, 0xe8, 0xab, 0x16, 0x9c, 0x6c, 0xa7, 0x3d, 0x0e, 0xb1, 0x70, 0xec, 0x7e, 0xa0, - 0x30, 0x1d, 0x91, 0x71, 0x69, 0x4c, 0x8f, 0x89, 0x11, 0x3a, 0x99, 0x01, 0xc4, 0xb8, 0xab, 0x2f, - 0xe8, 0x55, 0xa8, 0xb7, 0x9d, 0xed, 0x1b, 0x61, 0xd3, 0x49, 0xe4, 0x7e, 0xb2, 0xb7, 0x1b, 0xa0, - 0x93, 0xb8, 0xde, 0x04, 0x3f, 0xae, 0x9f, 0x98, 0xf7, 0x93, 0xa5, 0x68, 0x25, 0x89, 0x5c, 0xbf, - 0xc5, 0xdd, 0x79, 0x8b, 0x92, 0x0c, 0xd6, 0x14, 0xed, 0xaf, 0x58, 0x59, 0x25, 0xa5, 0x46, 0x27, - 0x72, 0x12, 0xd2, 0xda, 0x41, 0x1f, 0x85, 0x2a, 0xdd, 0xf8, 0xc9, 0x51, 0xb9, 0x55, 0xa4, 0xe6, - 0x34, 0xbe, 0x84, 0x56, 0xa2, 0xf4, 0x5f, 0x8c, 0x39, 0x53, 0xfb, 0xab, 0xf5, 0xac, 0xb1, 0xc0, - 0x0e, 0x6f, 0x2f, 0x02, 0xb4, 0x82, 0x55, 0xd2, 0x0e, 0x3d, 0x3a, 0x2c, 0x16, 0x3b, 0x01, 0x50, - 0xbe, 0x8e, 0x39, 0x05, 0xc1, 0x06, 0x16, 0xfa, 0x8b, 0x16, 0x40, 0x4b, 0xce, 0x79, 0x69, 0x08, - 0xdc, 0x28, 0xf2, 0x75, 0xf4, 0x8a, 0xd2, 0x7d, 0x51, 0x0c, 0xb1, 0xc1, 0x1c, 0xfd, 0x8c, 0x05, - 0xb5, 0x44, 0x76, 0x9f, 0xab, 0xc6, 0xd5, 0x22, 0x7b, 0x22, 0x5f, 0x5a, 0xdb, 0x44, 0x6a, 0x48, - 0x14, 0x5f, 0xf4, 0x73, 0x16, 0x40, 0xbc, 0xe3, 0x37, 0x96, 0x03, 0xcf, 0x6d, 0xec, 0x08, 0x8d, - 0x79, 0xb3, 0x50, 0x7f, 0x8c, 0xa2, 0x3e, 0x3d, 0x4a, 0x47, 0x43, 0xff, 0xc7, 0x06, 0x67, 0xf4, - 0x71, 0xa8, 0xc5, 0x62, 0xba, 0x09, 0x1d, 0xb9, 0x5a, 0xac, 0x57, 0x88, 0xd3, 0x16, 0xe2, 0x55, - 0xfc, 0xc3, 0x8a, 0x27, 0xfa, 0x05, 0x0b, 0x4e, 0x84, 0x69, 0x3f, 0x9f, 0x50, 0x87, 0xc5, 0xc9, - 0x80, 0x8c, 0x1f, 0x71, 0xfa, 0xf4, 0xdd, 0xdd, 0xf1, 0x13, 0x99, 0x46, 0x9c, 0xed, 0x05, 0x95, - 0x80, 0x7a, 0x06, 0x2f, 0x85, 0xdc, 0xe7, 0x38, 0xa8, 0x25, 0xe0, 0x5c, 0x16, 0x88, 0xbb, 0xf1, - 0xd1, 0x32, 0x9c, 0xa1, 0xbd, 0xdb, 0xe1, 0xe6, 0xa7, 0x54, 0x2f, 0x31, 0x53, 0x86, 0xb5, 0xe9, - 0xc7, 0xc4, 0x0c, 0x61, 0x5e, 0xfd, 0x2c, 0x0e, 0xce, 0x7d, 0x12, 0xfd, 0x9e, 0x05, 0x8f, 0xb9, - 0x4c, 0x0d, 0x98, 0x0e, 0x73, 0xad, 0x11, 0xc4, 0x49, 0x2c, 0x29, 0x54, 0x56, 0xf4, 0x52, 0x3f, - 0xd3, 0xff, 0x9f, 0x78, 0x83, 0xc7, 0xe6, 0xf7, 0xe8, 0x12, 0xde, 0xb3, 0xc3, 0xe8, 0x47, 0x61, - 0x44, 0xae, 0x8b, 0x65, 0x2a, 0x82, 0x99, 0xa2, 0xad, 0x4f, 0x9f, 0xba, 0xbb, 0x3b, 0x3e, 0xb2, - 0x6a, 0x02, 0x70, 0x1a, 0xcf, 0xfe, 0x56, 0x29, 0x75, 0x1e, 0xa2, 0x9c, 0x90, 0x4c, 0xdc, 0x34, - 0xa4, 0xff, 0x47, 0x4a, 0xcf, 0x42, 0xc5, 0x8d, 0xf2, 0x2e, 0x69, 0x71, 0xa3, 0x9a, 0x62, 0x6c, - 0x30, 0xa7, 0x46, 0xe9, 0x29, 0x27, 0xeb, 0xea, 0x14, 0x12, 0xf0, 0xd5, 0x22, 0xbb, 0xd4, 0x7d, - 0x7a, 0x75, 0x4e, 0x74, 0xed, 0x54, 0x17, 0x08, 0x77, 0x77, 0xc9, 0xfe, 0x56, 0xfa, 0x0c, 0xc6, - 0x58, 0xbc, 0x7d, 0x9c, 0x2f, 0x7d, 0xc1, 0x82, 0xa1, 0x28, 0xf0, 0x3c, 0xd7, 0x6f, 0x51, 0x41, - 0x23, 0xb4, 0xe5, 0x87, 0x8e, 0x45, 0x61, 0x09, 0x89, 0xc2, 0x4c, 0x5b, 0xac, 0x79, 0x62, 0xb3, - 0x03, 0xf6, 0x9f, 0x58, 0x30, 0xd6, 0x4b, 0x20, 0x22, 0x02, 0xef, 0x94, 0xab, 0x5d, 0x45, 0x57, - 0x2c, 0xf9, 0xb3, 0xc4, 0x23, 0xca, 0xf1, 0x5c, 0x9b, 0x7e, 0x52, 0xbc, 0xe6, 0x3b, 0x97, 0x7b, - 0xa3, 0xe2, 0xbd, 0xe8, 0xa0, 0x57, 0xe0, 0xa4, 0xf1, 0x5e, 0xb1, 0x1a, 0x98, 0xfa, 0xf4, 0x04, - 0xb5, 0x40, 0xa6, 0x32, 0xb0, 0x7b, 0xbb, 0xe3, 0x8f, 0x64, 0xdb, 0x84, 0xc4, 0xee, 0xa2, 0x63, - 0xff, 0x4a, 0x29, 0xfb, 0xb5, 0x94, 0xb2, 0x7d, 0xcb, 0xea, 0xda, 0xce, 0x7f, 0xe0, 0x38, 0x14, - 0x1c, 0xdb, 0xf8, 0xab, 0x00, 0x8e, 0xde, 0x38, 0x0f, 0xf0, 0x84, 0xd8, 0xfe, 0x57, 0x15, 0xd8, - 0xa3, 0x67, 0x7d, 0x58, 0xcf, 0x07, 0x3e, 0x56, 0xfc, 0x9c, 0xa5, 0x8e, 0x9c, 0xca, 0x6c, 0x91, - 0x37, 0x8f, 0x6b, 0xec, 0xf9, 0x06, 0x26, 0xe6, 0x51, 0x0a, 0xca, 0x8d, 0x9d, 0x3e, 0xdc, 0x42, - 0x5f, 0xb3, 0xd2, 0x87, 0x66, 0x3c, 0xec, 0xcc, 0x3d, 0xb6, 0x3e, 0x19, 0x27, 0x71, 0xbc, 0x63, - 0xfa, 0xfc, 0xa6, 0xd7, 0x19, 0xdd, 0x04, 0xc0, 0xba, 0xeb, 0x3b, 0x9e, 0xfb, 0x06, 0xdd, 0x9e, - 0x54, 0x99, 0x86, 0x65, 0x26, 0xcb, 0x65, 0xd5, 0x8a, 0x0d, 0x8c, 0xf3, 0x7f, 0x01, 0x86, 0x8c, - 0x37, 0xcf, 0x09, 0xae, 0x38, 0x63, 0x06, 0x57, 0xd4, 0x8d, 0x98, 0x88, 0xf3, 0xef, 0x83, 0x93, - 0xd9, 0x0e, 0x1e, 0xe4, 0x79, 0xfb, 0x7f, 0x0e, 0x66, 0x4f, 0xb1, 0x56, 0x49, 0xd4, 0xa6, 0x5d, - 0x7b, 0xdb, 0xb3, 0xf4, 0xb6, 0x67, 0xe9, 0x6d, 0xcf, 0x92, 0x79, 0x38, 0x20, 0xbc, 0x26, 0x83, - 0xf7, 0xc9, 0x6b, 0x92, 0xf2, 0x03, 0xd5, 0x0a, 0xf7, 0x03, 0xd9, 0x77, 0xab, 0x90, 0xb2, 0xa3, - 0xf8, 0x78, 0xff, 0x10, 0x0c, 0x46, 0x24, 0x0c, 0x6e, 0xe0, 0x05, 0xa1, 0x43, 0x74, 0x00, 0x3d, - 0x6f, 0xc6, 0x12, 0x4e, 0x75, 0x4d, 0xe8, 0x24, 0x1b, 0x42, 0x89, 0x28, 0x5d, 0xb3, 0xec, 0x24, - 0x1b, 0x98, 0x41, 0xd0, 0xfb, 0x60, 0x34, 0x71, 0xa2, 0x16, 0xb5, 0xb7, 0xb7, 0xd8, 0x67, 0x15, - 0x67, 0x9d, 0x8f, 0x08, 0xdc, 0xd1, 0xd5, 0x14, 0x14, 0x67, 0xb0, 0xd1, 0xeb, 0x50, 0xd9, 0x20, - 0x5e, 0x5b, 0x0c, 0xf9, 0x4a, 0x71, 0x32, 0x9e, 0xbd, 0xeb, 0x15, 0xe2, 0xb5, 0xb9, 0x04, 0xa2, - 0xbf, 0x30, 0x63, 0x45, 0xe7, 0x5b, 0x7d, 0xb3, 0x13, 0x27, 0x41, 0xdb, 0x7d, 0x43, 0xba, 0xf8, - 0x3e, 0x50, 0x30, 0xe3, 0x6b, 0x92, 0x3e, 0xf7, 0xa5, 0xa8, 0xbf, 0x58, 0x73, 0x66, 0xfd, 0x68, - 0xba, 0x11, 0xfb, 0x54, 0x3b, 0xc2, 0x53, 0x57, 0x74, 0x3f, 0x66, 0x25, 0x7d, 0xde, 0x0f, 0xf5, - 0x17, 0x6b, 0xce, 0x68, 0x47, 0xcd, 0xfb, 0x21, 0xd6, 0x87, 0x1b, 0x05, 0xf7, 0x81, 0xcf, 0xf9, - 0xdc, 0xf9, 0xff, 0x24, 0x54, 0x1b, 0x1b, 0x4e, 0x94, 0x8c, 0x0d, 0xb3, 0x49, 0xa3, 0x7c, 0x3a, - 0x33, 0xb4, 0x11, 0x73, 0x18, 0x7a, 0x1c, 0xca, 0x11, 0x59, 0x67, 0x71, 0x9b, 0x46, 0x44, 0x0f, - 0x26, 0xeb, 0x98, 0xb6, 0xdb, 0xbf, 0x54, 0x4a, 0x9b, 0x4b, 0xe9, 0xf7, 0xe6, 0xb3, 0xbd, 0xd1, - 0x89, 0x62, 0xe9, 0xf7, 0x31, 0x66, 0x3b, 0x6b, 0xc6, 0x12, 0x8e, 0x3e, 0x69, 0xc1, 0xe0, 0xed, - 0x38, 0xf0, 0x7d, 0x92, 0x08, 0xd5, 0x74, 0xb3, 0xe0, 0xa1, 0xb8, 0xca, 0xa9, 0xeb, 0x3e, 0x88, - 0x06, 0x2c, 0xf9, 0xd2, 0xee, 0x92, 0xed, 0x86, 0xd7, 0x69, 0x76, 0x05, 0x69, 0x5c, 0xe2, 0xcd, - 0x58, 0xc2, 0x29, 0xaa, 0xeb, 0x73, 0xd4, 0x4a, 0x1a, 0x75, 0xde, 0x17, 0xa8, 0x02, 0x6e, 0xff, - 0xd5, 0x01, 0x38, 0x9b, 0xbb, 0x38, 0xa8, 0x21, 0xc3, 0x4c, 0x85, 0xcb, 0xae, 0x47, 0x64, 0x78, - 0x12, 0x33, 0x64, 0x6e, 0xaa, 0x56, 0x6c, 0x60, 0xa0, 0x9f, 0x06, 0x08, 0x9d, 0xc8, 0x69, 0x13, - 0xe5, 0x97, 0x3d, 0xb2, 0xbd, 0x40, 0xfb, 0xb1, 0x2c, 0x69, 0xea, 0xbd, 0xa9, 0x6a, 0x8a, 0xb1, - 0xc1, 0x12, 0xbd, 0x00, 0x43, 0x11, 0xf1, 0x88, 0x13, 0xb3, 0xb0, 0xdf, 0x6c, 0x0e, 0x03, 0xd6, - 0x20, 0x6c, 0xe2, 0xa1, 0xa7, 0x54, 0x24, 0x57, 0x26, 0xa2, 0x25, 0x1d, 0xcd, 0x85, 0xde, 0xb4, - 0x60, 0x74, 0xdd, 0xf5, 0x88, 0xe6, 0x2e, 0x32, 0x0e, 0x96, 0x8e, 0xfe, 0x92, 0x97, 0x4d, 0xba, - 0x5a, 0x42, 0xa6, 0x9a, 0x63, 0x9c, 0x61, 0x4f, 0x3f, 0xf3, 0x16, 0x89, 0x98, 0x68, 0x1d, 0x48, - 0x7f, 0xe6, 0x9b, 0xbc, 0x19, 0x4b, 0x38, 0x9a, 0x82, 0x13, 0xa1, 0x13, 0xc7, 0x33, 0x11, 0x69, - 0x12, 0x3f, 0x71, 0x1d, 0x8f, 0xe7, 0x03, 0xd4, 0x74, 0x3c, 0xf0, 0x72, 0x1a, 0x8c, 0xb3, 0xf8, - 0xe8, 0x83, 0xf0, 0x28, 0x77, 0x7c, 0x2c, 0xba, 0x71, 0xec, 0xfa, 0x2d, 0x3d, 0x0d, 0x84, 0xff, - 0x67, 0x5c, 0x90, 0x7a, 0x74, 0x3e, 0x1f, 0x0d, 0xf7, 0x7a, 0x1e, 0x3d, 0x03, 0xb5, 0x78, 0xd3, - 0x0d, 0x67, 0xa2, 0x66, 0xcc, 0x0e, 0x3d, 0x6a, 0xda, 0xdb, 0xb8, 0x22, 0xda, 0xb1, 0xc2, 0x40, - 0x0d, 0x18, 0xe6, 0x9f, 0x84, 0x87, 0xa2, 0x09, 0xf9, 0xf8, 0x6c, 0x4f, 0xf5, 0x28, 0x52, 0xd6, - 0x26, 0xb0, 0x73, 0xe7, 0x92, 0x3c, 0x82, 0xe1, 0x27, 0x06, 0x37, 0x0d, 0x32, 0x38, 0x45, 0xd4, - 0xfe, 0xc5, 0x52, 0x7a, 0xc7, 0x6d, 0x2e, 0x52, 0x14, 0xd3, 0xa5, 0x98, 0xdc, 0x74, 0x22, 0xe9, - 0x8d, 0x39, 0x62, 0xda, 0x82, 0xa0, 0x7b, 0xd3, 0x89, 0xcc, 0x45, 0xcd, 0x18, 0x60, 0xc9, 0x09, - 0xdd, 0x86, 0x4a, 0xe2, 0x39, 0x05, 0xe5, 0x39, 0x19, 0x1c, 0xb5, 0x03, 0x64, 0x61, 0x2a, 0xc6, - 0x8c, 0x07, 0x7a, 0x8c, 0x5a, 0xfd, 0x6b, 0xf2, 0x88, 0x44, 0x18, 0xea, 0x6b, 0x31, 0x66, 0xad, - 0xf6, 0xaf, 0x42, 0x8e, 0x5c, 0x55, 0x8a, 0x0c, 0x5d, 0x04, 0xa0, 0x1b, 0xc8, 0xe5, 0x88, 0xac, - 0xbb, 0xdb, 0xc2, 0x90, 0x50, 0x6b, 0xf7, 0xba, 0x82, 0x60, 0x03, 0x4b, 0x3e, 0xb3, 0xd2, 0x59, - 0xa7, 0xcf, 0x94, 0xba, 0x9f, 0xe1, 0x10, 0x6c, 0x60, 0xa1, 0xe7, 0x61, 0xc0, 0x6d, 0x3b, 0x2d, - 0x15, 0x82, 0xf9, 0x18, 0x5d, 0xb4, 0xf3, 0xac, 0xe5, 0xde, 0xee, 0xf8, 0xa8, 0xea, 0x10, 0x6b, - 0xc2, 0x02, 0x17, 0xfd, 0x8a, 0x05, 0xc3, 0x8d, 0xa0, 0xdd, 0x0e, 0x7c, 0xbe, 0xed, 0x12, 0x7b, - 0xc8, 0xdb, 0xc7, 0xa5, 0xe6, 0x27, 0x66, 0x0c, 0x66, 0x7c, 0x13, 0xa9, 0x12, 0xb2, 0x4c, 0x10, - 0x4e, 0xf5, 0xca, 0x5c, 0xdb, 0xd5, 0x7d, 0xd6, 0xf6, 0x6f, 0x58, 0x70, 0x8a, 0x3f, 0x6b, 0xec, - 0x06, 0x45, 0xee, 0x51, 0x70, 0xcc, 0xaf, 0xd5, 0xb5, 0x41, 0x56, 0x5e, 0xba, 0x2e, 0x38, 0xee, - 0xee, 0x24, 0x9a, 0x83, 0x53, 0xeb, 0x41, 0xd4, 0x20, 0xe6, 0x40, 0x08, 0xc1, 0xa4, 0x08, 0x5d, - 0xce, 0x22, 0xe0, 0xee, 0x67, 0xd0, 0x4d, 0x78, 0xc4, 0x68, 0x34, 0xc7, 0x81, 0xcb, 0xa6, 0x27, - 0x04, 0xb5, 0x47, 0x2e, 0xe7, 0x62, 0xe1, 0x1e, 0x4f, 0xa7, 0x1d, 0x26, 0xf5, 0x3e, 0x1c, 0x26, - 0xaf, 0xc1, 0xb9, 0x46, 0xf7, 0xc8, 0x6c, 0xc5, 0x9d, 0xb5, 0x98, 0x4b, 0xaa, 0xda, 0xf4, 0x0f, - 0x08, 0x02, 0xe7, 0x66, 0x7a, 0x21, 0xe2, 0xde, 0x34, 0xd0, 0x47, 0xa1, 0x16, 0x11, 0xf6, 0x55, - 0x62, 0x91, 0x88, 0x73, 0xc4, 0x5d, 0xb2, 0xb6, 0x40, 0x39, 0x59, 0x2d, 0x7b, 0x45, 0x43, 0x8c, - 0x15, 0x47, 0x74, 0x07, 0x06, 0x43, 0x27, 0x69, 0x6c, 0x88, 0xf4, 0x9b, 0x23, 0xc7, 0xbf, 0x28, - 0xe6, 0xcc, 0x07, 0xae, 0x27, 0xf9, 0x32, 0x67, 0x82, 0x25, 0x37, 0x6a, 0x8d, 0x34, 0x82, 0x76, - 0x18, 0xf8, 0xc4, 0x4f, 0xe2, 0xb1, 0x11, 0x6d, 0x8d, 0xcc, 0xa8, 0x56, 0x6c, 0x60, 0x9c, 0x7f, - 0x3f, 0x9c, 0xea, 0x5a, 0x78, 0x07, 0x72, 0xae, 0xcc, 0xc2, 0x23, 0xf9, 0x53, 0xfc, 0x40, 0x2e, - 0x96, 0x7f, 0x98, 0x09, 0x72, 0x35, 0xcc, 0xde, 0x3e, 0xdc, 0x75, 0x0e, 0x94, 0x89, 0xbf, 0x25, - 0x24, 0xfe, 0xe5, 0xa3, 0x8d, 0xf4, 0x25, 0x7f, 0x8b, 0xaf, 0x50, 0xe6, 0x93, 0xb8, 0xe4, 0x6f, - 0x61, 0x4a, 0x1b, 0x7d, 0xc9, 0x4a, 0x99, 0x6d, 0xdc, 0xc9, 0xf7, 0xe1, 0x63, 0xb1, 0xf3, 0xfb, - 0xb6, 0xe4, 0xec, 0x7f, 0x5d, 0x82, 0x0b, 0xfb, 0x11, 0xe9, 0x63, 0xf8, 0x9e, 0x84, 0x81, 0x98, - 0x1d, 0x5b, 0x0b, 0x11, 0x3a, 0x44, 0x67, 0x16, 0x3f, 0xc8, 0x7e, 0x0d, 0x0b, 0x10, 0xf2, 0xa0, - 0xdc, 0x76, 0x42, 0xe1, 0xfb, 0x99, 0x3f, 0x6a, 0xda, 0x0b, 0xfd, 0xef, 0x78, 0x8b, 0x4e, 0xc8, - 0x3d, 0x0a, 0x46, 0x03, 0xa6, 0x6c, 0x50, 0x02, 0x55, 0x27, 0x8a, 0x1c, 0x79, 0x46, 0x7a, 0xad, - 0x18, 0x7e, 0x53, 0x94, 0x24, 0x3f, 0x62, 0x4a, 0x35, 0x61, 0xce, 0xcc, 0xfe, 0xdc, 0x60, 0x2a, - 0xf5, 0x83, 0x1d, 0x7c, 0xc7, 0x30, 0x20, 0x5c, 0x3e, 0x56, 0xd1, 0xd9, 0x46, 0x3c, 0x77, 0x8f, - 0xed, 0xea, 0x44, 0x06, 0xb4, 0x60, 0x85, 0x3e, 0x6b, 0xb1, 0x3c, 0x63, 0x99, 0x0e, 0x23, 0xf6, - 0x52, 0xc7, 0x93, 0xf6, 0x6c, 0x66, 0x2f, 0xcb, 0x46, 0x6c, 0x72, 0xa7, 0x3a, 0x36, 0xe4, 0x19, - 0x73, 0xd9, 0x1d, 0x95, 0xcc, 0x44, 0x96, 0x70, 0xb4, 0x9d, 0x73, 0xc0, 0x5d, 0x40, 0xae, 0x6a, - 0x1f, 0x47, 0xda, 0x5f, 0xb3, 0xe0, 0x94, 0x9b, 0x3d, 0xa9, 0x14, 0x3b, 0x8f, 0x23, 0x86, 0x50, - 0xf4, 0x3e, 0x08, 0x55, 0xca, 0xb7, 0x0b, 0x84, 0xbb, 0x3b, 0x83, 0x9a, 0x50, 0x71, 0xfd, 0xf5, - 0x40, 0x98, 0x1c, 0xd3, 0x47, 0xeb, 0xd4, 0xbc, 0xbf, 0x1e, 0xe8, 0xd5, 0x4c, 0xff, 0x61, 0x46, - 0x1d, 0x2d, 0xc0, 0x99, 0x48, 0xf8, 0x86, 0xae, 0xb8, 0x31, 0xdd, 0xc1, 0x2f, 0xb8, 0x6d, 0x37, - 0x61, 0xe6, 0x42, 0x79, 0x7a, 0xec, 0xee, 0xee, 0xf8, 0x19, 0x9c, 0x03, 0xc7, 0xb9, 0x4f, 0xa1, - 0x37, 0x60, 0x50, 0x26, 0x46, 0xd7, 0x8a, 0xd8, 0xc5, 0x75, 0xcf, 0x7f, 0x35, 0x99, 0x56, 0x44, - 0x0e, 0xb4, 0x64, 0x68, 0xbf, 0x39, 0x04, 0xdd, 0x87, 0x98, 0xe8, 0x63, 0x50, 0x8f, 0x54, 0xb2, - 0xb6, 0x55, 0x84, 0x72, 0x95, 0xdf, 0x57, 0x1c, 0xa0, 0x2a, 0xc3, 0x45, 0xa7, 0x65, 0x6b, 0x8e, - 0x74, 0x7b, 0x11, 0xeb, 0xb3, 0xce, 0x02, 0xe6, 0xb6, 0xe0, 0xaa, 0xcf, 0xb1, 0x76, 0xfc, 0x06, - 0x66, 0x3c, 0x50, 0x04, 0x03, 0x1b, 0xc4, 0xf1, 0x92, 0x8d, 0x62, 0x5c, 0xee, 0x57, 0x18, 0xad, - 0x6c, 0xca, 0x0e, 0x6f, 0xc5, 0x82, 0x13, 0xda, 0x86, 0xc1, 0x0d, 0x3e, 0x01, 0x84, 0xc5, 0xbf, - 0x78, 0xd4, 0xc1, 0x4d, 0xcd, 0x2a, 0xfd, 0xb9, 0x45, 0x03, 0x96, 0xec, 0x58, 0x74, 0x8c, 0x71, - 0x7e, 0xcf, 0x97, 0x6e, 0x71, 0xd9, 0x4a, 0xfd, 0x1f, 0xde, 0x7f, 0x04, 0x86, 0x23, 0xd2, 0x08, - 0xfc, 0x86, 0xeb, 0x91, 0xe6, 0x94, 0x74, 0xa7, 0x1f, 0x24, 0xc7, 0x85, 0xed, 0x9a, 0xb1, 0x41, - 0x03, 0xa7, 0x28, 0xa2, 0xcf, 0x58, 0x30, 0xaa, 0x32, 0x3c, 0xe9, 0x07, 0x21, 0xc2, 0x7d, 0xbb, - 0x50, 0x50, 0x3e, 0x29, 0xa3, 0x39, 0x8d, 0xee, 0xee, 0x8e, 0x8f, 0xa6, 0xdb, 0x70, 0x86, 0x2f, - 0x7a, 0x05, 0x20, 0x58, 0xe3, 0x21, 0x30, 0x53, 0x89, 0xf0, 0xe5, 0x1e, 0xe4, 0x55, 0x47, 0x79, - 0xb2, 0x9b, 0xa4, 0x80, 0x0d, 0x6a, 0xe8, 0x1a, 0x00, 0x5f, 0x36, 0xab, 0x3b, 0xa1, 0xdc, 0x16, - 0xc8, 0x24, 0x25, 0x58, 0x51, 0x90, 0x7b, 0xbb, 0xe3, 0xdd, 0xbe, 0x35, 0x16, 0x66, 0x60, 0x3c, - 0x8e, 0x7e, 0x0a, 0x06, 0xe3, 0x4e, 0xbb, 0xed, 0x28, 0x4f, 0x6f, 0x81, 0xe9, 0x73, 0x9c, 0xae, - 0x21, 0x8a, 0x78, 0x03, 0x96, 0x1c, 0xd1, 0x6d, 0x2a, 0x54, 0x63, 0xe1, 0xf4, 0x63, 0xab, 0x88, - 0xdb, 0x04, 0x43, 0xec, 0x9d, 0xde, 0x23, 0x23, 0x7a, 0x70, 0x0e, 0xce, 0xbd, 0xdd, 0xf1, 0x47, - 0xd2, 0xed, 0x0b, 0x81, 0x48, 0x68, 0xcb, 0xa5, 0x89, 0xae, 0xca, 0x3a, 0x29, 0xf4, 0xb5, 0x65, - 0xfa, 0xfe, 0xd3, 0xba, 0x4e, 0x0a, 0x6b, 0xee, 0x3d, 0x66, 0xe6, 0xc3, 0x68, 0x11, 0x4e, 0x37, - 0x02, 0x3f, 0x89, 0x02, 0xcf, 0xe3, 0xc5, 0x7f, 0xf8, 0x0e, 0x8d, 0x7b, 0x82, 0xdf, 0x29, 0xba, - 0x7d, 0x7a, 0xa6, 0x1b, 0x05, 0xe7, 0x3d, 0x67, 0xfb, 0xe9, 0xd8, 0x40, 0x31, 0x38, 0xcf, 0xc3, - 0x30, 0xd9, 0x4e, 0x48, 0xe4, 0x3b, 0xde, 0x0d, 0xbc, 0x20, 0x7d, 0xa0, 0x6c, 0x0d, 0x5c, 0x32, - 0xda, 0x71, 0x0a, 0x0b, 0xd9, 0xca, 0x2d, 0x61, 0x24, 0x69, 0x72, 0xb7, 0x84, 0x74, 0x42, 0xd8, - 0xff, 0xab, 0x94, 0x32, 0xc8, 0x56, 0x23, 0x42, 0x50, 0x00, 0x55, 0x3f, 0x68, 0x2a, 0xd9, 0x7f, - 0xb5, 0x18, 0xd9, 0x7f, 0x3d, 0x68, 0x1a, 0xc5, 0x54, 0xe8, 0xbf, 0x18, 0x73, 0x3e, 0xac, 0xda, - 0x84, 0x2c, 0xcb, 0xc1, 0x00, 0x62, 0xa3, 0x51, 0x24, 0x67, 0x55, 0x6d, 0x62, 0xc9, 0x64, 0x84, - 0xd3, 0x7c, 0xd1, 0x26, 0x54, 0x37, 0x82, 0x38, 0x91, 0xdb, 0x8f, 0x23, 0xee, 0x74, 0xae, 0x04, - 0x71, 0xc2, 0xac, 0x08, 0xf5, 0xda, 0xb4, 0x25, 0xc6, 0x9c, 0x87, 0xfd, 0x9f, 0xad, 0x94, 0xc7, - 0xfb, 0x16, 0x8b, 0x93, 0xdd, 0x22, 0x3e, 0x5d, 0xd6, 0x66, 0x60, 0xd0, 0x8f, 0x66, 0xb2, 0x0e, - 0xdf, 0xd5, 0xab, 0xb4, 0xd5, 0x1d, 0x4a, 0x61, 0x82, 0x91, 0x30, 0x62, 0x88, 0x3e, 0x61, 0xa5, - 0xf3, 0x3f, 0x4b, 0x45, 0x6c, 0x30, 0xcc, 0x1c, 0xe8, 0x7d, 0x53, 0x49, 0xed, 0x2f, 0x59, 0x30, - 0x38, 0xed, 0x34, 0x36, 0x83, 0xf5, 0x75, 0xf4, 0x0c, 0xd4, 0x9a, 0x9d, 0xc8, 0x4c, 0x45, 0x55, - 0xdb, 0xfc, 0x59, 0xd1, 0x8e, 0x15, 0x06, 0x9d, 0xc3, 0xeb, 0x4e, 0x43, 0x66, 0x42, 0x97, 0xf9, - 0x1c, 0xbe, 0xcc, 0x5a, 0xb0, 0x80, 0xa0, 0x17, 0x60, 0xa8, 0xed, 0x6c, 0xcb, 0x87, 0xb3, 0xee, - 0xf6, 0x45, 0x0d, 0xc2, 0x26, 0x9e, 0xfd, 0xcf, 0x2d, 0x18, 0x9b, 0x76, 0x62, 0xb7, 0x31, 0xd5, - 0x49, 0x36, 0xa6, 0xdd, 0x64, 0xad, 0xd3, 0xd8, 0x24, 0x09, 0x4f, 0x7f, 0xa7, 0xbd, 0xec, 0xc4, - 0x74, 0x29, 0xa9, 0x7d, 0x9d, 0xea, 0xe5, 0x0d, 0xd1, 0x8e, 0x15, 0x06, 0x7a, 0x03, 0x86, 0x42, - 0x27, 0x8e, 0xef, 0x04, 0x51, 0x13, 0x93, 0xf5, 0x62, 0x8a, 0x4f, 0xac, 0x90, 0x46, 0x44, 0x12, - 0x4c, 0xd6, 0xc5, 0x91, 0xb0, 0xa6, 0x8f, 0x4d, 0x66, 0xf6, 0x17, 0x2c, 0x38, 0x37, 0x4d, 0x9c, - 0x88, 0x44, 0xac, 0x56, 0x85, 0x7a, 0x91, 0x19, 0x2f, 0xe8, 0x34, 0xd1, 0xeb, 0x50, 0x4b, 0x68, - 0x33, 0xed, 0x96, 0x55, 0x6c, 0xb7, 0xd8, 0x89, 0xee, 0xaa, 0x20, 0x8e, 0x15, 0x1b, 0xfb, 0xaf, - 0x59, 0x30, 0xcc, 0x0e, 0xc7, 0x66, 0x49, 0xe2, 0xb8, 0x5e, 0x57, 0x49, 0x27, 0xab, 0xcf, 0x92, - 0x4e, 0x17, 0xa0, 0xb2, 0x11, 0xb4, 0x49, 0xf6, 0x60, 0xf7, 0x4a, 0x40, 0xb7, 0xd5, 0x14, 0x82, - 0x9e, 0xa3, 0x1f, 0xde, 0xf5, 0x13, 0x87, 0x2e, 0x01, 0xe9, 0x7c, 0x3d, 0xc1, 0x3f, 0xba, 0x6a, - 0xc6, 0x26, 0x8e, 0xfd, 0xdb, 0x75, 0x18, 0x14, 0xa7, 0xff, 0x7d, 0x97, 0x40, 0x90, 0xfb, 0xfb, - 0x52, 0xcf, 0xfd, 0x7d, 0x0c, 0x03, 0x0d, 0x56, 0x30, 0x4e, 0x98, 0x91, 0xd7, 0x0a, 0x09, 0x17, - 0xe1, 0x35, 0xe8, 0x74, 0xb7, 0xf8, 0x7f, 0x2c, 0x58, 0xa1, 0x2f, 0x5a, 0x70, 0xa2, 0x11, 0xf8, - 0x3e, 0x69, 0x68, 0x1b, 0xa7, 0x52, 0x44, 0x54, 0xc0, 0x4c, 0x9a, 0xa8, 0x3e, 0x99, 0xc9, 0x00, - 0x70, 0x96, 0x3d, 0x7a, 0x09, 0x46, 0xf8, 0x98, 0xdd, 0x4c, 0x79, 0x8c, 0x75, 0xa5, 0x1f, 0x13, - 0x88, 0xd3, 0xb8, 0x68, 0x82, 0x7b, 0xde, 0x45, 0x4d, 0x9d, 0x01, 0xed, 0x58, 0x33, 0xaa, 0xe9, - 0x18, 0x18, 0x28, 0x02, 0x14, 0x91, 0xf5, 0x88, 0xc4, 0x1b, 0x22, 0x3a, 0x82, 0xd9, 0x57, 0x83, - 0x87, 0x4b, 0x97, 0xc6, 0x5d, 0x94, 0x70, 0x0e, 0x75, 0xb4, 0x29, 0x36, 0x98, 0xb5, 0x22, 0x64, - 0xa8, 0xf8, 0xcc, 0x3d, 0xf7, 0x99, 0xe3, 0x50, 0x8d, 0x37, 0x9c, 0xa8, 0xc9, 0xec, 0xba, 0x32, - 0x4f, 0xd1, 0x59, 0xa1, 0x0d, 0x98, 0xb7, 0xa3, 0x59, 0x38, 0x99, 0xa9, 0x53, 0x14, 0x0b, 0xcf, - 0xae, 0x4a, 0xc7, 0xc8, 0x54, 0x38, 0x8a, 0x71, 0xd7, 0x13, 0xa6, 0xf3, 0x61, 0x68, 0x1f, 0xe7, - 0xc3, 0x8e, 0x8a, 0xc1, 0xe3, 0x3e, 0xd7, 0x97, 0x0b, 0x19, 0x80, 0xbe, 0x02, 0xee, 0x3e, 0x9f, - 0x09, 0xb8, 0x1b, 0x61, 0x1d, 0xb8, 0x59, 0x4c, 0x07, 0x0e, 0x1e, 0x5d, 0xf7, 0x20, 0xa3, 0xe5, - 0xfe, 0xdc, 0x02, 0xf9, 0x5d, 0x67, 0x9c, 0xc6, 0x06, 0xa1, 0x53, 0x06, 0xbd, 0x0f, 0x46, 0xd5, - 0x16, 0x7a, 0x26, 0xe8, 0xf8, 0x3c, 0x50, 0xae, 0xac, 0x8f, 0x70, 0x71, 0x0a, 0x8a, 0x33, 0xd8, - 0x68, 0x12, 0xea, 0x74, 0x9c, 0xf8, 0xa3, 0x5c, 0xd7, 0xaa, 0x6d, 0xfa, 0xd4, 0xf2, 0xbc, 0x78, - 0x4a, 0xe3, 0xa0, 0x00, 0x4e, 0x79, 0x4e, 0x9c, 0xb0, 0x1e, 0xd0, 0x1d, 0xf5, 0x21, 0x8b, 0x15, - 0xb0, 0x98, 0xff, 0x85, 0x2c, 0x21, 0xdc, 0x4d, 0xdb, 0xfe, 0x76, 0x05, 0x46, 0x52, 0x92, 0xf1, - 0x80, 0x4a, 0xfa, 0x19, 0xa8, 0x49, 0xbd, 0x99, 0x2d, 0xab, 0xa2, 0x94, 0xab, 0xc2, 0xa0, 0x4a, - 0x6b, 0x4d, 0x6b, 0xd5, 0xac, 0x51, 0x61, 0x28, 0x5c, 0x6c, 0xe2, 0x31, 0xa1, 0x9c, 0x78, 0xf1, - 0x8c, 0xe7, 0x12, 0x3f, 0xe1, 0xdd, 0x2c, 0x46, 0x28, 0xaf, 0x2e, 0xac, 0x98, 0x44, 0xb5, 0x50, - 0xce, 0x00, 0x70, 0x96, 0x3d, 0xfa, 0xb4, 0x05, 0x23, 0xce, 0x9d, 0x58, 0x57, 0x35, 0x15, 0xa1, - 0x75, 0x47, 0x54, 0x52, 0xa9, 0x42, 0xa9, 0xdc, 0xe5, 0x9b, 0x6a, 0xc2, 0x69, 0xa6, 0xe8, 0x2d, - 0x0b, 0x10, 0xd9, 0x26, 0x0d, 0x19, 0xfc, 0x27, 0xfa, 0x32, 0x50, 0xc4, 0x4e, 0xf3, 0x52, 0x17, - 0x5d, 0x2e, 0xd5, 0xbb, 0xdb, 0x71, 0x4e, 0x1f, 0xec, 0x7f, 0x52, 0x56, 0x0b, 0x4a, 0xc7, 0x9b, - 0x3a, 0x46, 0xdc, 0x9b, 0x75, 0xf8, 0xb8, 0x37, 0x1d, 0x3f, 0xd0, 0x9d, 0x03, 0x99, 0x4a, 0x99, - 0x2a, 0x3d, 0xa0, 0x94, 0xa9, 0x9f, 0xb1, 0x52, 0x05, 0x84, 0x86, 0x2e, 0xbe, 0x52, 0x6c, 0xac, - 0xeb, 0x04, 0x8f, 0x6d, 0xc8, 0x48, 0xf7, 0x74, 0x48, 0x0b, 0x95, 0xa6, 0x06, 0xda, 0x81, 0xa4, - 0xe1, 0xbf, 0x2b, 0xc3, 0x90, 0xa1, 0x49, 0x73, 0xcd, 0x22, 0xeb, 0x21, 0x33, 0x8b, 0x4a, 0x07, - 0x30, 0x8b, 0x7e, 0x1a, 0xea, 0x0d, 0x29, 0xe5, 0x8b, 0x29, 0xa1, 0x9b, 0xd5, 0x1d, 0x5a, 0xd0, - 0xab, 0x26, 0xac, 0x79, 0xa2, 0xb9, 0x54, 0xa2, 0x8d, 0xd0, 0x10, 0x15, 0xa6, 0x21, 0xf2, 0x32, - 0x61, 0x84, 0xa6, 0xe8, 0x7e, 0x86, 0xd5, 0x99, 0x0a, 0x5d, 0xf1, 0x5e, 0x32, 0x22, 0x9d, 0xd7, - 0x99, 0x5a, 0x9e, 0x97, 0xcd, 0xd8, 0xc4, 0xb1, 0xbf, 0x6d, 0xa9, 0x8f, 0x7b, 0x1f, 0x2a, 0x2a, - 0xdc, 0x4e, 0x57, 0x54, 0xb8, 0x54, 0xc8, 0x30, 0xf7, 0x28, 0xa5, 0x70, 0x1d, 0x06, 0x67, 0x82, - 0x76, 0xdb, 0xf1, 0x9b, 0xe8, 0x07, 0x61, 0xb0, 0xc1, 0x7f, 0x0a, 0xc7, 0x0e, 0x3b, 0x1e, 0x14, - 0x50, 0x2c, 0x61, 0xe8, 0x31, 0xa8, 0x38, 0x51, 0x4b, 0x3a, 0x73, 0x58, 0x28, 0xcc, 0x54, 0xd4, - 0x8a, 0x31, 0x6b, 0xb5, 0xff, 0x41, 0x05, 0xd8, 0x09, 0xb4, 0x13, 0x91, 0xe6, 0x6a, 0xc0, 0x4a, - 0xf8, 0x1d, 0xeb, 0xa1, 0x9a, 0xde, 0x2c, 0x3d, 0xcc, 0x07, 0x6b, 0xc6, 0xe1, 0x4a, 0xf9, 0x3e, - 0x1f, 0xae, 0xf4, 0x38, 0x2f, 0xab, 0x3c, 0x44, 0xe7, 0x65, 0xf6, 0xe7, 0x2c, 0x40, 0x2a, 0x6c, - 0x41, 0x1f, 0x68, 0x4f, 0x42, 0x5d, 0x05, 0x30, 0x08, 0xc3, 0x4a, 0x8b, 0x08, 0x09, 0xc0, 0x1a, - 0xa7, 0x8f, 0x1d, 0xf2, 0x93, 0x52, 0x7e, 0x97, 0xd3, 0x51, 0xb4, 0x4c, 0xea, 0x0b, 0x71, 0x6e, - 0xff, 0x4e, 0x09, 0x1e, 0xe1, 0x2a, 0x79, 0xd1, 0xf1, 0x9d, 0x16, 0x69, 0xd3, 0x5e, 0xf5, 0x1b, - 0xa2, 0xd0, 0xa0, 0x5b, 0x33, 0x57, 0x46, 0xc5, 0x1e, 0x75, 0xed, 0xf2, 0x35, 0xc7, 0x57, 0xd9, - 0xbc, 0xef, 0x26, 0x98, 0x11, 0x47, 0x31, 0xd4, 0x64, 0xcd, 0x78, 0x21, 0x8b, 0x0b, 0x62, 0xa4, - 0xc4, 0x92, 0xd0, 0x9b, 0x04, 0x2b, 0x46, 0xd4, 0x70, 0xf5, 0x82, 0xc6, 0x26, 0x26, 0x61, 0xc0, - 0xe4, 0xae, 0x11, 0x94, 0xb8, 0x20, 0xda, 0xb1, 0xc2, 0xb0, 0x7f, 0xc7, 0x82, 0xac, 0x46, 0x32, - 0x6a, 0xa5, 0x59, 0x7b, 0xd6, 0x4a, 0x3b, 0x40, 0xb1, 0xb2, 0x9f, 0x84, 0x21, 0x27, 0xa1, 0x46, - 0x04, 0xdf, 0x76, 0x97, 0x0f, 0x77, 0xac, 0xb1, 0x18, 0x34, 0xdd, 0x75, 0x97, 0x6d, 0xb7, 0x4d, - 0x72, 0xf6, 0x7f, 0xaf, 0xc0, 0xa9, 0xae, 0xdc, 0x0d, 0xf4, 0x22, 0x0c, 0x37, 0xc4, 0xf4, 0x08, - 0xa5, 0x43, 0xab, 0x6e, 0x06, 0xb1, 0x69, 0x18, 0x4e, 0x61, 0xf6, 0x31, 0x41, 0xe7, 0xe1, 0x74, - 0x44, 0x37, 0xfa, 0x1d, 0x32, 0xb5, 0x9e, 0x90, 0x68, 0x85, 0x34, 0x02, 0xbf, 0xc9, 0x2b, 0xfa, - 0x95, 0xa7, 0x1f, 0xbd, 0xbb, 0x3b, 0x7e, 0x1a, 0x77, 0x83, 0x71, 0xde, 0x33, 0x28, 0x84, 0x11, - 0xcf, 0xb4, 0x01, 0xc5, 0x06, 0xe0, 0x50, 0xe6, 0xa3, 0xb2, 0x11, 0x52, 0xcd, 0x38, 0xcd, 0x20, - 0x6d, 0x48, 0x56, 0x1f, 0x90, 0x21, 0xf9, 0x29, 0x6d, 0x48, 0xf2, 0xf3, 0xf7, 0x0f, 0x15, 0x9c, - 0xbb, 0x73, 0xdc, 0x96, 0xe4, 0xcb, 0x50, 0x93, 0xb1, 0x49, 0x7d, 0xc5, 0xf4, 0x98, 0x74, 0x7a, - 0x48, 0xb4, 0x7b, 0x25, 0xc8, 0xd9, 0x84, 0xd0, 0x75, 0xa6, 0x35, 0x7e, 0x6a, 0x9d, 0x1d, 0x4c, - 0xeb, 0xa3, 0x6d, 0x1e, 0x97, 0xc5, 0x75, 0xdb, 0x07, 0x8b, 0xde, 0x44, 0xe9, 0x50, 0x2d, 0x95, - 0xd2, 0xa0, 0xc2, 0xb5, 0x2e, 0x02, 0x68, 0x43, 0x4d, 0x04, 0xac, 0xab, 0x63, 0x5f, 0x6d, 0xcf, - 0x61, 0x03, 0x8b, 0xee, 0xa9, 0x5d, 0x3f, 0x4e, 0x1c, 0xcf, 0xbb, 0xe2, 0xfa, 0x89, 0x70, 0x0e, - 0x2a, 0x25, 0x3e, 0xaf, 0x41, 0xd8, 0xc4, 0x3b, 0xff, 0x1e, 0xe3, 0xbb, 0x1c, 0xe4, 0x7b, 0x6e, - 0xc0, 0xb9, 0x39, 0x37, 0x51, 0x69, 0x16, 0x6a, 0x1e, 0x51, 0x3b, 0x4c, 0xa5, 0x0d, 0x59, 0x3d, - 0xd3, 0x86, 0x8c, 0x34, 0x87, 0x52, 0x3a, 0x2b, 0x23, 0x9b, 0xe6, 0x60, 0xbf, 0x08, 0x67, 0xe6, - 0xdc, 0xe4, 0xb2, 0xeb, 0x91, 0x03, 0x32, 0xb1, 0x7f, 0x6b, 0x00, 0x86, 0xcd, 0x44, 0xbd, 0x83, - 0x64, 0x3e, 0x7d, 0x81, 0x9a, 0x5a, 0xe2, 0xed, 0x5c, 0x75, 0x68, 0x76, 0xeb, 0xc8, 0x59, 0x83, - 0xf9, 0x23, 0x66, 0x58, 0x5b, 0x9a, 0x27, 0x36, 0x3b, 0x80, 0xee, 0x40, 0x75, 0x9d, 0x85, 0xe1, - 0x97, 0x8b, 0x88, 0x2c, 0xc8, 0x1b, 0x51, 0xbd, 0xcc, 0x78, 0x20, 0x3f, 0xe7, 0x47, 0x35, 0x64, - 0x94, 0xce, 0xed, 0x32, 0x42, 0x47, 0x45, 0x56, 0x97, 0xc2, 0xe8, 0x25, 0xea, 0xab, 0x87, 0x10, - 0xf5, 0x29, 0xc1, 0x3b, 0xf0, 0x80, 0x04, 0x2f, 0x4b, 0xa9, 0x48, 0x36, 0x98, 0xfd, 0x26, 0x62, - 0xdd, 0x07, 0xd9, 0x20, 0x18, 0x29, 0x15, 0x29, 0x30, 0xce, 0xe2, 0xa3, 0x8f, 0x2b, 0xd1, 0x5d, - 0x2b, 0xc2, 0xaf, 0x6a, 0xce, 0xe8, 0xe3, 0x96, 0xda, 0x9f, 0x2b, 0xc1, 0xe8, 0x9c, 0xdf, 0x59, - 0x9e, 0x5b, 0xee, 0xac, 0x79, 0x6e, 0xe3, 0x1a, 0xd9, 0xa1, 0xa2, 0x79, 0x93, 0xec, 0xcc, 0xcf, - 0x8a, 0x15, 0xa4, 0xe6, 0xcc, 0x35, 0xda, 0x88, 0x39, 0x8c, 0x0a, 0xa3, 0x75, 0xd7, 0x6f, 0x91, - 0x28, 0x8c, 0x5c, 0xe1, 0xf2, 0x34, 0x84, 0xd1, 0x65, 0x0d, 0xc2, 0x26, 0x1e, 0xa5, 0x1d, 0xdc, - 0xf1, 0x49, 0x94, 0x35, 0x64, 0x97, 0x68, 0x23, 0xe6, 0x30, 0x8a, 0x94, 0x44, 0x9d, 0x38, 0x11, - 0x93, 0x51, 0x21, 0xad, 0xd2, 0x46, 0xcc, 0x61, 0x74, 0xa5, 0xc7, 0x9d, 0x35, 0x16, 0xb8, 0x91, - 0x09, 0xac, 0x5f, 0xe1, 0xcd, 0x58, 0xc2, 0x29, 0xea, 0x26, 0xd9, 0x99, 0xa5, 0xbb, 0xde, 0x4c, - 0x7e, 0xcd, 0x35, 0xde, 0x8c, 0x25, 0x9c, 0x95, 0x22, 0x4c, 0x0f, 0xc7, 0xf7, 0x5c, 0x29, 0xc2, - 0x74, 0xf7, 0x7b, 0xec, 0x9f, 0x7f, 0xd9, 0x82, 0x61, 0x33, 0xdc, 0x0a, 0xb5, 0x32, 0x36, 0xee, - 0x52, 0x57, 0x25, 0xdb, 0x1f, 0xcf, 0xbb, 0xda, 0xab, 0xe5, 0x26, 0x41, 0x18, 0x3f, 0x4b, 0xfc, - 0x96, 0xeb, 0x13, 0x76, 0x8a, 0xce, 0xc3, 0xb4, 0x52, 0xb1, 0x5c, 0x33, 0x41, 0x93, 0x1c, 0xc2, - 0x48, 0xb6, 0x6f, 0xc1, 0xa9, 0xae, 0xa4, 0xaa, 0x3e, 0x4c, 0x8b, 0x7d, 0x53, 0x5a, 0x6d, 0x0c, - 0x43, 0x94, 0xb0, 0x2c, 0x87, 0x33, 0x03, 0xa7, 0xf8, 0x42, 0xa2, 0x9c, 0x56, 0x1a, 0x1b, 0xa4, - 0xad, 0x12, 0xe5, 0x98, 0x7f, 0xfd, 0x66, 0x16, 0x88, 0xbb, 0xf1, 0xed, 0xcf, 0x5b, 0x30, 0x92, - 0xca, 0x73, 0x2b, 0xc8, 0x08, 0x62, 0x2b, 0x2d, 0x60, 0xd1, 0x7f, 0x2c, 0x04, 0xba, 0xcc, 0x94, - 0xa9, 0x5e, 0x69, 0x1a, 0x84, 0x4d, 0x3c, 0xfb, 0x4b, 0x25, 0xa8, 0xc9, 0x08, 0x8a, 0x3e, 0xba, - 0xf2, 0x59, 0x0b, 0x46, 0xd4, 0x99, 0x06, 0x73, 0x96, 0x95, 0x8a, 0x48, 0x4a, 0xa0, 0x3d, 0x50, - 0xdb, 0x6d, 0x7f, 0x3d, 0xd0, 0x16, 0x39, 0x36, 0x99, 0xe1, 0x34, 0x6f, 0x74, 0x13, 0x20, 0xde, - 0x89, 0x13, 0xd2, 0x36, 0xdc, 0x76, 0xb6, 0xb1, 0xe2, 0x26, 0x1a, 0x41, 0x44, 0xe8, 0xfa, 0xba, - 0x1e, 0x34, 0xc9, 0x8a, 0xc2, 0xd4, 0x26, 0x94, 0x6e, 0xc3, 0x06, 0x25, 0xfb, 0xef, 0x95, 0xe0, - 0x64, 0xb6, 0x4b, 0xe8, 0x43, 0x30, 0x2c, 0xb9, 0x1b, 0xd7, 0x94, 0xc9, 0xb0, 0x91, 0x61, 0x6c, - 0xc0, 0xee, 0xed, 0x8e, 0x8f, 0x77, 0x5f, 0x13, 0x37, 0x61, 0xa2, 0xe0, 0x14, 0x31, 0x7e, 0xb0, - 0x24, 0x4e, 0x40, 0xa7, 0x77, 0xa6, 0xc2, 0x50, 0x9c, 0x0e, 0x19, 0x07, 0x4b, 0x26, 0x14, 0x67, - 0xb0, 0xd1, 0x32, 0x9c, 0x31, 0x5a, 0xae, 0x13, 0xb7, 0xb5, 0xb1, 0x16, 0x44, 0x72, 0x67, 0xf5, - 0x98, 0x0e, 0xec, 0xea, 0xc6, 0xc1, 0xb9, 0x4f, 0x52, 0x6d, 0xdf, 0x70, 0x42, 0xa7, 0xe1, 0x26, - 0x3b, 0xc2, 0x0f, 0xa9, 0x64, 0xd3, 0x8c, 0x68, 0xc7, 0x0a, 0xc3, 0x5e, 0x84, 0x4a, 0x9f, 0x33, - 0xa8, 0x2f, 0x8b, 0xfe, 0x65, 0xa8, 0x51, 0x72, 0xd2, 0xbc, 0x2b, 0x82, 0x64, 0x00, 0x35, 0x79, - 0xd3, 0x08, 0xb2, 0xa1, 0xec, 0x3a, 0xf2, 0xec, 0x4e, 0xbd, 0xd6, 0x7c, 0x1c, 0x77, 0xd8, 0x26, - 0x99, 0x02, 0xd1, 0x93, 0x50, 0x26, 0xdb, 0x61, 0xf6, 0x90, 0xee, 0xd2, 0x76, 0xe8, 0x46, 0x24, - 0xa6, 0x48, 0x64, 0x3b, 0x44, 0xe7, 0xa1, 0xe4, 0x36, 0x85, 0x92, 0x02, 0x81, 0x53, 0x9a, 0x9f, - 0xc5, 0x25, 0xb7, 0x69, 0x6f, 0x43, 0x5d, 0x5d, 0x6d, 0x82, 0x36, 0xa5, 0xec, 0xb6, 0x8a, 0x08, - 0x79, 0x92, 0x74, 0x7b, 0x48, 0xed, 0x0e, 0x80, 0x4e, 0xf8, 0x2b, 0x4a, 0xbe, 0x5c, 0x80, 0x4a, - 0x23, 0x10, 0xc9, 0xc8, 0x35, 0x4d, 0x86, 0x09, 0x6d, 0x06, 0xb1, 0x6f, 0xc1, 0xe8, 0x35, 0x3f, - 0xb8, 0xc3, 0xea, 0xb2, 0xb3, 0x32, 0x64, 0x94, 0xf0, 0x3a, 0xfd, 0x91, 0x35, 0x11, 0x18, 0x14, - 0x73, 0x98, 0xaa, 0xcf, 0x54, 0xea, 0x55, 0x9f, 0xc9, 0xfe, 0x84, 0x05, 0xc3, 0x2a, 0x73, 0x68, - 0x6e, 0x6b, 0x93, 0xd2, 0x6d, 0x45, 0x41, 0x27, 0xcc, 0xd2, 0x65, 0x97, 0x0f, 0x61, 0x0e, 0x33, - 0x53, 0xea, 0x4a, 0xfb, 0xa4, 0xd4, 0x5d, 0x80, 0xca, 0xa6, 0xeb, 0x37, 0xb3, 0xb7, 0x69, 0x5c, - 0x73, 0xfd, 0x26, 0x66, 0x10, 0xda, 0x85, 0x93, 0xaa, 0x0b, 0x52, 0x21, 0xbc, 0x08, 0xc3, 0x6b, - 0x1d, 0xd7, 0x6b, 0xca, 0xfa, 0x6a, 0x19, 0x4f, 0xc9, 0xb4, 0x01, 0xc3, 0x29, 0x4c, 0xba, 0xaf, - 0x5b, 0x73, 0x7d, 0x27, 0xda, 0x59, 0xd6, 0x1a, 0x48, 0x09, 0xa5, 0x69, 0x05, 0xc1, 0x06, 0x96, - 0xfd, 0x66, 0x19, 0x46, 0xd3, 0xf9, 0x53, 0x7d, 0x6c, 0xaf, 0x9e, 0x84, 0x2a, 0x4b, 0xa9, 0xca, - 0x7e, 0x5a, 0x5e, 0x92, 0x8c, 0xc3, 0x50, 0x0c, 0x03, 0xbc, 0x18, 0x43, 0x31, 0x37, 0xd1, 0xa8, - 0x4e, 0x2a, 0xff, 0x0a, 0x8b, 0x27, 0x13, 0xf5, 0x1f, 0x04, 0x2b, 0xf4, 0x69, 0x0b, 0x06, 0x83, - 0xd0, 0xac, 0xeb, 0xf3, 0xc1, 0x22, 0x73, 0xcb, 0x44, 0xb2, 0x8c, 0xb0, 0x88, 0xd5, 0xa7, 0x97, - 0x9f, 0x43, 0xb2, 0x3e, 0xff, 0x5e, 0x18, 0x36, 0x31, 0xf7, 0x33, 0x8a, 0x6b, 0xa6, 0x51, 0xfc, - 0x59, 0x73, 0x52, 0x88, 0xec, 0xb9, 0x3e, 0x96, 0xdb, 0x0d, 0xa8, 0x36, 0x54, 0x00, 0xc0, 0xa1, - 0xaa, 0x72, 0xaa, 0xea, 0x08, 0xec, 0x10, 0x88, 0x53, 0xb3, 0xbf, 0x6d, 0x19, 0xf3, 0x03, 0x93, - 0x78, 0xbe, 0x89, 0x22, 0x28, 0xb7, 0xb6, 0x36, 0x85, 0x29, 0x7a, 0xb5, 0xa0, 0xe1, 0x9d, 0xdb, - 0xda, 0xd4, 0x73, 0xdc, 0x6c, 0xc5, 0x94, 0x59, 0x1f, 0x4e, 0xc0, 0x54, 0x92, 0x65, 0x79, 0xff, - 0x24, 0x4b, 0xfb, 0xad, 0x12, 0x9c, 0xea, 0x9a, 0x54, 0xe8, 0x0d, 0xa8, 0x46, 0xf4, 0x2d, 0xc5, - 0xeb, 0x2d, 0x14, 0x96, 0x16, 0x19, 0xcf, 0x37, 0xb5, 0xde, 0x4d, 0xb7, 0x63, 0xce, 0x12, 0x5d, - 0x05, 0xa4, 0xc3, 0x54, 0x94, 0x07, 0x92, 0xbf, 0xf2, 0x79, 0xf1, 0x28, 0x9a, 0xea, 0xc2, 0xc0, - 0x39, 0x4f, 0xa1, 0x97, 0xb2, 0x8e, 0xcc, 0x72, 0xfa, 0xdc, 0x72, 0x2f, 0x9f, 0xa4, 0xfd, 0x4f, - 0x4b, 0x30, 0x92, 0x2a, 0xb3, 0x84, 0x3c, 0xa8, 0x11, 0x8f, 0x39, 0xf5, 0xa5, 0xb2, 0x39, 0x6a, - 0xd5, 0x62, 0xa5, 0x20, 0x2f, 0x09, 0xba, 0x58, 0x71, 0x78, 0x38, 0x0e, 0xd7, 0x5f, 0x84, 0x61, - 0xd9, 0xa1, 0x0f, 0x3a, 0x6d, 0x4f, 0x0c, 0xa0, 0x9a, 0xa3, 0x97, 0x0c, 0x18, 0x4e, 0x61, 0xda, - 0xbf, 0x5b, 0x86, 0x31, 0x7e, 0x0a, 0xd2, 0x54, 0x33, 0x6f, 0x51, 0xee, 0xb7, 0xfe, 0x92, 0x2e, - 0x86, 0xc6, 0x07, 0x72, 0xed, 0xa8, 0x97, 0x04, 0xe4, 0x33, 0xea, 0x2b, 0x32, 0xeb, 0xab, 0x99, - 0xc8, 0x2c, 0x6e, 0x76, 0xb7, 0x8e, 0xa9, 0x47, 0xdf, 0x5b, 0xa1, 0x5a, 0xbf, 0x5a, 0x82, 0x13, - 0x99, 0x1b, 0x18, 0xd0, 0x9b, 0xe9, 0xa2, 0xbd, 0x56, 0x11, 0xbe, 0xf2, 0x3d, 0x8b, 0xf2, 0x1f, - 0xac, 0x74, 0xef, 0x03, 0x5a, 0x2a, 0xf6, 0x1f, 0x94, 0x60, 0x34, 0x7d, 0x75, 0xc4, 0x43, 0x38, - 0x52, 0xef, 0x86, 0x3a, 0xab, 0x8e, 0xce, 0xae, 0xc4, 0xe4, 0x2e, 0x79, 0x5e, 0x88, 0x5a, 0x36, - 0x62, 0x0d, 0x7f, 0x28, 0x2a, 0x22, 0xdb, 0x7f, 0xc7, 0x82, 0xb3, 0xfc, 0x2d, 0xb3, 0xf3, 0xf0, - 0x2f, 0xe7, 0x8d, 0xee, 0xab, 0xc5, 0x76, 0x30, 0x53, 0xc4, 0x6f, 0xbf, 0xf1, 0x65, 0x57, 0xf1, - 0x89, 0xde, 0xa6, 0xa7, 0xc2, 0x43, 0xd8, 0xd9, 0x03, 0x4d, 0x06, 0xfb, 0x0f, 0xca, 0xa0, 0x6f, - 0x1f, 0x44, 0xae, 0xc8, 0x71, 0x2c, 0xa4, 0x98, 0xe1, 0xca, 0x8e, 0xdf, 0xd0, 0xf7, 0x1c, 0xd6, - 0x32, 0x29, 0x8e, 0x3f, 0x6f, 0xc1, 0x90, 0xeb, 0xbb, 0x89, 0xeb, 0xb0, 0x6d, 0x74, 0x31, 0x37, - 0xa3, 0x29, 0x76, 0xf3, 0x9c, 0x72, 0x10, 0x99, 0xe7, 0x38, 0x8a, 0x19, 0x36, 0x39, 0xa3, 0x8f, - 0x88, 0xe0, 0xe9, 0x72, 0x61, 0xd9, 0xb9, 0xb5, 0x4c, 0xc4, 0x74, 0x48, 0x0d, 0xaf, 0x24, 0x2a, - 0x28, 0xa9, 0x1d, 0x53, 0x52, 0xaa, 0x2e, 0xae, 0xbe, 0x07, 0x9a, 0x36, 0x63, 0xce, 0xc8, 0x8e, - 0x01, 0x75, 0x8f, 0xc5, 0x01, 0x03, 0x53, 0x27, 0xa1, 0xee, 0x74, 0x92, 0xa0, 0x4d, 0x87, 0x49, - 0x1c, 0x35, 0xe9, 0xd0, 0x5b, 0x09, 0xc0, 0x1a, 0xc7, 0x7e, 0xb3, 0x0a, 0x99, 0xa4, 0x43, 0xb4, - 0x6d, 0xde, 0x9c, 0x69, 0x15, 0x7b, 0x73, 0xa6, 0xea, 0x4c, 0xde, 0xed, 0x99, 0xa8, 0x05, 0xd5, - 0x70, 0xc3, 0x89, 0xa5, 0x59, 0xfd, 0xb2, 0xda, 0xc7, 0xd1, 0xc6, 0x7b, 0xbb, 0xe3, 0x3f, 0xd1, - 0x9f, 0xd7, 0x95, 0xce, 0xd5, 0x49, 0x5e, 0x6c, 0x44, 0xb3, 0x66, 0x34, 0x30, 0xa7, 0x7f, 0x90, - 0xbb, 0xe1, 0x3e, 0x29, 0xca, 0xc0, 0x63, 0x12, 0x77, 0xbc, 0x44, 0xcc, 0x86, 0x97, 0x0b, 0x5c, - 0x65, 0x9c, 0xb0, 0x4e, 0x97, 0xe7, 0xff, 0xb1, 0xc1, 0x14, 0x7d, 0x08, 0xea, 0x71, 0xe2, 0x44, - 0xc9, 0x21, 0x13, 0x5c, 0xd5, 0xa0, 0xaf, 0x48, 0x22, 0x58, 0xd3, 0x43, 0xaf, 0xb0, 0xda, 0xae, - 0x6e, 0xbc, 0x71, 0xc8, 0x9c, 0x07, 0x59, 0x07, 0x56, 0x50, 0xc0, 0x06, 0x35, 0x74, 0x11, 0x80, - 0xcd, 0x6d, 0x1e, 0xe8, 0x57, 0x63, 0x5e, 0x26, 0x25, 0x0a, 0xb1, 0x82, 0x60, 0x03, 0xcb, 0xfe, - 0x61, 0x48, 0xd7, 0x7b, 0x40, 0xe3, 0xb2, 0xbc, 0x04, 0xf7, 0x42, 0xb3, 0xdc, 0x85, 0x54, 0x25, - 0x88, 0xdf, 0xb0, 0xc0, 0x2c, 0x4a, 0x81, 0x5e, 0xe7, 0xd5, 0x2f, 0xac, 0x22, 0x4e, 0x0e, 0x0d, - 0xba, 0x13, 0x8b, 0x4e, 0x98, 0x39, 0xc2, 0x96, 0x25, 0x30, 0xce, 0xbf, 0x07, 0x6a, 0x12, 0x7a, - 0x20, 0xa3, 0xee, 0xe3, 0x70, 0x3a, 0x7b, 0xaf, 0xb8, 0x38, 0x75, 0xda, 0xdf, 0xf5, 0x23, 0xfd, - 0x39, 0xa5, 0x5e, 0xfe, 0x9c, 0x3e, 0xee, 0x4f, 0xfd, 0x4d, 0x0b, 0x2e, 0xec, 0x77, 0xfd, 0x39, - 0x7a, 0x0c, 0x2a, 0x77, 0x9c, 0x48, 0x16, 0xdd, 0x66, 0x82, 0xf2, 0x96, 0x13, 0xf9, 0x98, 0xb5, - 0xa2, 0x1d, 0x18, 0xe0, 0xd1, 0x60, 0xc2, 0x5a, 0x7f, 0xb9, 0xd8, 0xcb, 0xd8, 0xaf, 0x11, 0x63, - 0xbb, 0xc0, 0x23, 0xd1, 0xb0, 0x60, 0x68, 0x7f, 0xc7, 0x02, 0xb4, 0xb4, 0x45, 0xa2, 0xc8, 0x6d, - 0x1a, 0xf1, 0x6b, 0xec, 0x3a, 0x15, 0xe3, 0xda, 0x14, 0x33, 0xc5, 0x35, 0x73, 0x9d, 0x8a, 0xf1, - 0x2f, 0xff, 0x3a, 0x95, 0xd2, 0xc1, 0xae, 0x53, 0x41, 0x4b, 0x70, 0xb6, 0xcd, 0xb7, 0x1b, 0xfc, - 0x8a, 0x02, 0xbe, 0xf7, 0x50, 0x09, 0x65, 0xe7, 0xee, 0xee, 0x8e, 0x9f, 0x5d, 0xcc, 0x43, 0xc0, - 0xf9, 0xcf, 0xd9, 0xef, 0x01, 0xc4, 0xc3, 0xd6, 0x66, 0xf2, 0x62, 0x90, 0x7a, 0xba, 0x5f, 0xec, - 0xaf, 0x54, 0xe1, 0x44, 0xa6, 0x24, 0x2b, 0xdd, 0xea, 0x75, 0x07, 0x3d, 0x1d, 0x59, 0x7f, 0x77, - 0x77, 0xaf, 0xaf, 0x30, 0x2a, 0x1f, 0xaa, 0xae, 0x1f, 0x76, 0x92, 0x62, 0x72, 0x48, 0x79, 0x27, - 0xe6, 0x29, 0x41, 0xc3, 0x5d, 0x4c, 0xff, 0x62, 0xce, 0xa6, 0xc8, 0xa0, 0xac, 0x94, 0x31, 0x5e, - 0x79, 0x40, 0xee, 0x80, 0x4f, 0xea, 0x10, 0xa9, 0x6a, 0x11, 0x8e, 0xc5, 0xcc, 0x64, 0x39, 0xee, - 0xa3, 0xf6, 0x5f, 0x2f, 0xc1, 0x90, 0xf1, 0xd1, 0xd0, 0x2f, 0xa5, 0x4b, 0x36, 0x59, 0xc5, 0xbd, - 0x12, 0xa3, 0x3f, 0xa1, 0x8b, 0x32, 0xf1, 0x57, 0x7a, 0xaa, 0xbb, 0x5a, 0xd3, 0xbd, 0xdd, 0xf1, - 0x93, 0x99, 0x7a, 0x4c, 0xa9, 0x0a, 0x4e, 0xe7, 0x3f, 0x06, 0x27, 0x32, 0x64, 0x72, 0x5e, 0x79, - 0x35, 0x7d, 0x6d, 0xfc, 0x11, 0xdd, 0x52, 0xe6, 0x90, 0x7d, 0x83, 0x0e, 0x99, 0x48, 0xa3, 0x0b, - 0x3c, 0xd2, 0x87, 0x0f, 0x36, 0x93, 0x2d, 0x5b, 0xea, 0x33, 0x5b, 0xf6, 0x69, 0xa8, 0x85, 0x81, - 0xe7, 0x36, 0x5c, 0x55, 0x85, 0x90, 0xe5, 0xe7, 0x2e, 0x8b, 0x36, 0xac, 0xa0, 0xe8, 0x0e, 0xd4, - 0xd5, 0x0d, 0xfb, 0xc2, 0xbf, 0x5d, 0xd4, 0xa1, 0x8f, 0x32, 0x5a, 0xf4, 0xcd, 0xf9, 0x9a, 0x17, - 0xb2, 0x61, 0x80, 0x29, 0x41, 0x19, 0xfa, 0xcf, 0x7c, 0xef, 0x4c, 0x3b, 0xc6, 0x58, 0x40, 0xec, - 0xaf, 0xd7, 0xe1, 0x4c, 0x5e, 0x5d, 0x6c, 0xf4, 0x51, 0x18, 0xe0, 0x7d, 0x2c, 0xe6, 0xea, 0x85, - 0x3c, 0x1e, 0x73, 0x8c, 0xa0, 0xe8, 0x16, 0xfb, 0x8d, 0x05, 0x4f, 0xc1, 0xdd, 0x73, 0xd6, 0xc4, - 0x0c, 0x39, 0x1e, 0xee, 0x0b, 0x8e, 0xe6, 0xbe, 0xe0, 0x70, 0xee, 0x9e, 0xb3, 0x86, 0xb6, 0xa1, - 0xda, 0x72, 0x13, 0xe2, 0x08, 0x27, 0xc2, 0xad, 0x63, 0x61, 0x4e, 0x1c, 0x6e, 0xa5, 0xb1, 0x9f, - 0x98, 0x33, 0x44, 0x5f, 0xb3, 0xe0, 0xc4, 0x5a, 0x3a, 0x35, 0x5e, 0x08, 0x4f, 0xe7, 0x18, 0x6a, - 0x9f, 0xa7, 0x19, 0xf1, 0xfb, 0x84, 0x32, 0x8d, 0x38, 0xdb, 0x1d, 0xf4, 0x29, 0x0b, 0x06, 0xd7, - 0x5d, 0xcf, 0x28, 0x83, 0x7b, 0x0c, 0x1f, 0xe7, 0x32, 0x63, 0xa0, 0x77, 0x1c, 0xfc, 0x7f, 0x8c, - 0x25, 0xe7, 0x5e, 0x9a, 0x6a, 0xe0, 0xa8, 0x9a, 0x6a, 0xf0, 0x01, 0x69, 0xaa, 0xcf, 0x58, 0x50, - 0x57, 0x23, 0x2d, 0xd2, 0x9d, 0x3f, 0x74, 0x8c, 0x9f, 0x9c, 0x7b, 0x4e, 0xd4, 0x5f, 0xac, 0x99, - 0xa3, 0x2f, 0x5a, 0x30, 0xe4, 0xbc, 0xd1, 0x89, 0x48, 0x93, 0x6c, 0x05, 0x61, 0x2c, 0x2e, 0x23, - 0x7c, 0xb5, 0xf8, 0xce, 0x4c, 0x51, 0x26, 0xb3, 0x64, 0x6b, 0x29, 0x8c, 0x45, 0x5a, 0x92, 0x6e, - 0xc0, 0x66, 0x17, 0xec, 0xdd, 0x12, 0x8c, 0xef, 0x43, 0x01, 0xbd, 0x08, 0xc3, 0x41, 0xd4, 0x72, - 0x7c, 0xf7, 0x0d, 0xb3, 0xd6, 0x85, 0xb2, 0xb2, 0x96, 0x0c, 0x18, 0x4e, 0x61, 0x9a, 0x09, 0xd9, - 0xa5, 0x7d, 0x12, 0xb2, 0x2f, 0x40, 0x25, 0x22, 0x61, 0x90, 0xdd, 0x2c, 0xb0, 0x94, 0x00, 0x06, - 0x41, 0x8f, 0x43, 0xd9, 0x09, 0x5d, 0x11, 0x88, 0xa6, 0xf6, 0x40, 0x53, 0xcb, 0xf3, 0x98, 0xb6, - 0xa7, 0xea, 0x43, 0x54, 0xef, 0x4b, 0x7d, 0x08, 0xaa, 0x06, 0xc4, 0xd9, 0xc5, 0x80, 0x56, 0x03, - 0xe9, 0x33, 0x05, 0xfb, 0xad, 0x32, 0x3c, 0xbe, 0xe7, 0x7c, 0xd1, 0x71, 0x78, 0xd6, 0x1e, 0x71, - 0x78, 0x72, 0x78, 0x4a, 0xfb, 0x0d, 0x4f, 0xb9, 0xc7, 0xf0, 0x7c, 0x8a, 0x2e, 0x03, 0x59, 0x23, - 0xa4, 0x98, 0xeb, 0xe4, 0x7a, 0x95, 0x1c, 0x11, 0x2b, 0x40, 0x42, 0xb1, 0xe6, 0x4b, 0xf7, 0x00, - 0xa9, 0x64, 0xe4, 0x6a, 0x11, 0x6a, 0xa0, 0x67, 0xcd, 0x10, 0x3e, 0xf7, 0x7b, 0x65, 0x38, 0xdb, - 0xbf, 0x50, 0x82, 0x27, 0xfb, 0x90, 0xde, 0xe6, 0x2c, 0xb6, 0xfa, 0x9c, 0xc5, 0xdf, 0xdb, 0x9f, - 0xc9, 0xfe, 0x2b, 0x16, 0x9c, 0xef, 0xad, 0x3c, 0xd0, 0x73, 0x30, 0xb4, 0x16, 0x39, 0x7e, 0x63, - 0x83, 0x5d, 0x91, 0x29, 0x07, 0x85, 0x8d, 0xb5, 0x6e, 0xc6, 0x26, 0x0e, 0xdd, 0xde, 0xf2, 0x98, - 0x04, 0x03, 0x43, 0x26, 0x8f, 0xd2, 0xed, 0xed, 0x6a, 0x16, 0x88, 0xbb, 0xf1, 0xed, 0x3f, 0x2b, - 0xe5, 0x77, 0x8b, 0x1b, 0x19, 0x07, 0xf9, 0x4e, 0xe2, 0x2b, 0x94, 0xfa, 0x90, 0x25, 0xe5, 0xfb, - 0x2d, 0x4b, 0x2a, 0xbd, 0x64, 0x09, 0x9a, 0x85, 0x93, 0xc6, 0x15, 0x2a, 0x3c, 0x21, 0x98, 0x07, - 0xdc, 0xaa, 0x2a, 0x19, 0xcb, 0x19, 0x38, 0xee, 0x7a, 0x02, 0x3d, 0x03, 0x35, 0xd7, 0x8f, 0x49, - 0xa3, 0x13, 0xf1, 0x40, 0x6f, 0x23, 0x09, 0x6b, 0x5e, 0xb4, 0x63, 0x85, 0x61, 0xff, 0x72, 0x09, - 0xce, 0xf5, 0xb4, 0xb3, 0xee, 0x93, 0xec, 0x32, 0x3f, 0x47, 0xe5, 0xfe, 0x7c, 0x0e, 0x73, 0x90, - 0xaa, 0xfb, 0x0e, 0xd2, 0x1f, 0xf6, 0x9e, 0x98, 0xd4, 0xe6, 0xfe, 0xbe, 0x1d, 0xa5, 0x97, 0x60, - 0xc4, 0x09, 0x43, 0x8e, 0xc7, 0xe2, 0x35, 0x33, 0x55, 0x72, 0xa6, 0x4c, 0x20, 0x4e, 0xe3, 0xf6, - 0xa5, 0x3d, 0xff, 0xd8, 0x82, 0x3a, 0x26, 0xeb, 0x5c, 0x3a, 0xa0, 0xdb, 0x62, 0x88, 0xac, 0x22, - 0xea, 0x69, 0xd2, 0x81, 0x8d, 0x5d, 0x56, 0x67, 0x32, 0x6f, 0xb0, 0xbb, 0xaf, 0xda, 0x29, 0x1d, - 0xe8, 0xaa, 0x1d, 0x75, 0xd9, 0x4a, 0xb9, 0xf7, 0x65, 0x2b, 0xf6, 0x37, 0x06, 0xe9, 0xeb, 0x85, - 0xc1, 0x4c, 0x44, 0x9a, 0x31, 0xfd, 0xbe, 0x9d, 0xc8, 0x13, 0x93, 0x44, 0x7d, 0xdf, 0x1b, 0x78, - 0x01, 0xd3, 0xf6, 0xd4, 0x51, 0x4c, 0xe9, 0x40, 0x35, 0x42, 0xca, 0xfb, 0xd6, 0x08, 0x79, 0x09, - 0x46, 0xe2, 0x78, 0x63, 0x39, 0x72, 0xb7, 0x9c, 0x84, 0x5c, 0x23, 0x3b, 0xc2, 0xca, 0xd2, 0x79, - 0xfd, 0x2b, 0x57, 0x34, 0x10, 0xa7, 0x71, 0xd1, 0x1c, 0x9c, 0xd2, 0x95, 0x3a, 0x48, 0x94, 0xb0, - 0xe8, 0x7e, 0x3e, 0x13, 0x54, 0x12, 0xaf, 0xae, 0xed, 0x21, 0x10, 0x70, 0xf7, 0x33, 0x54, 0xbe, - 0xa5, 0x1a, 0x69, 0x47, 0x06, 0xd2, 0xf2, 0x2d, 0x45, 0x87, 0xf6, 0xa5, 0xeb, 0x09, 0xb4, 0x08, - 0xa7, 0xf9, 0xc4, 0x98, 0x0a, 0x43, 0xe3, 0x8d, 0x06, 0xd3, 0x75, 0x0c, 0xe7, 0xba, 0x51, 0x70, - 0xde, 0x73, 0xe8, 0x05, 0x18, 0x52, 0xcd, 0xf3, 0xb3, 0xe2, 0x14, 0x41, 0x79, 0x31, 0x14, 0x99, - 0xf9, 0x26, 0x36, 0xf1, 0xd0, 0x07, 0xe1, 0x51, 0xfd, 0x97, 0xa7, 0x80, 0xf1, 0xa3, 0xb5, 0x59, - 0x51, 0x04, 0x49, 0x5d, 0xed, 0x31, 0x97, 0x8b, 0xd6, 0xc4, 0xbd, 0x9e, 0x47, 0x6b, 0x70, 0x5e, - 0x81, 0x2e, 0xf9, 0x09, 0xcb, 0xe7, 0x88, 0xc9, 0xb4, 0x13, 0x93, 0x1b, 0x91, 0x27, 0xee, 0x46, - 0x55, 0xb7, 0x2e, 0xce, 0xb9, 0xc9, 0x95, 0x3c, 0x4c, 0xbc, 0x80, 0xf7, 0xa0, 0x82, 0x26, 0xa1, - 0x4e, 0x7c, 0x67, 0xcd, 0x23, 0x4b, 0x33, 0xf3, 0xac, 0x98, 0x92, 0x71, 0x92, 0x77, 0x49, 0x02, - 0xb0, 0xc6, 0x51, 0x11, 0xa6, 0xc3, 0x3d, 0x6f, 0x00, 0x5d, 0x86, 0x33, 0xad, 0x46, 0x48, 0x6d, - 0x0f, 0xb7, 0x41, 0xa6, 0x1a, 0x2c, 0xa0, 0x8e, 0x7e, 0x18, 0x5e, 0x60, 0x52, 0x85, 0x4f, 0xcf, - 0xcd, 0x2c, 0x77, 0xe1, 0xe0, 0xdc, 0x27, 0x59, 0xe0, 0x65, 0x14, 0x6c, 0xef, 0x8c, 0x9d, 0xce, - 0x04, 0x5e, 0xd2, 0x46, 0xcc, 0x61, 0xe8, 0x2a, 0x20, 0x16, 0x8b, 0x7f, 0x25, 0x49, 0x42, 0x65, - 0xec, 0x8c, 0x9d, 0x61, 0xaf, 0xa4, 0xc2, 0xc8, 0x2e, 0x77, 0x61, 0xe0, 0x9c, 0xa7, 0xec, 0x7f, - 0x6f, 0xc1, 0x88, 0x5a, 0xaf, 0xf7, 0x21, 0x1b, 0xc5, 0x4b, 0x67, 0xa3, 0xcc, 0x1d, 0x5d, 0xe2, - 0xb1, 0x9e, 0xf7, 0x08, 0x69, 0xfe, 0xd9, 0x21, 0x00, 0x2d, 0x15, 0x95, 0x42, 0xb2, 0x7a, 0x2a, - 0xa4, 0x87, 0x56, 0x22, 0xe5, 0x55, 0x4e, 0xa9, 0x3e, 0xd8, 0xca, 0x29, 0x2b, 0x70, 0x56, 0x9a, - 0x0b, 0xfc, 0xac, 0xe8, 0x4a, 0x10, 0x2b, 0x01, 0x57, 0x9b, 0x7e, 0x5c, 0x10, 0x3a, 0x3b, 0x9f, - 0x87, 0x84, 0xf3, 0x9f, 0x4d, 0x59, 0x29, 0x83, 0xfb, 0x59, 0x29, 0x7a, 0x4d, 0x2f, 0xac, 0xcb, - 0x3b, 0x3c, 0x32, 0x6b, 0x7a, 0xe1, 0xf2, 0x0a, 0xd6, 0x38, 0xf9, 0x82, 0xbd, 0x5e, 0x90, 0x60, - 0x87, 0x03, 0x0b, 0x76, 0x29, 0x62, 0x86, 0x7a, 0x8a, 0x18, 0xe9, 0x93, 0x1e, 0xee, 0xe9, 0x93, - 0x7e, 0x1f, 0x8c, 0xba, 0xfe, 0x06, 0x89, 0xdc, 0x84, 0x34, 0xd9, 0x5a, 0x60, 0xe2, 0xa7, 0xa6, - 0xd5, 0xfa, 0x7c, 0x0a, 0x8a, 0x33, 0xd8, 0x69, 0xb9, 0x38, 0xda, 0x87, 0x5c, 0xec, 0xa1, 0x8d, - 0x4e, 0x14, 0xa3, 0x8d, 0x4e, 0x1e, 0x5d, 0x1b, 0x9d, 0x3a, 0x56, 0x6d, 0x84, 0x0a, 0xd1, 0x46, - 0x7d, 0x09, 0x7a, 0x63, 0xfb, 0x77, 0x66, 0x9f, 0xed, 0x5f, 0x2f, 0x55, 0x74, 0xf6, 0xd0, 0xaa, - 0x28, 0x5f, 0xcb, 0x3c, 0x72, 0x28, 0x2d, 0xf3, 0x99, 0x12, 0x9c, 0xd5, 0x72, 0x98, 0xce, 0x7e, - 0x77, 0x9d, 0x4a, 0x22, 0x76, 0x0d, 0x14, 0x3f, 0xb7, 0x31, 0x92, 0xa3, 0x74, 0x9e, 0x95, 0x82, - 0x60, 0x03, 0x8b, 0xe5, 0x18, 0x91, 0x88, 0x95, 0xd1, 0xcd, 0x0a, 0xe9, 0x19, 0xd1, 0x8e, 0x15, - 0x06, 0x9d, 0x5f, 0xf4, 0xb7, 0xc8, 0xdb, 0xcc, 0x16, 0x8b, 0x9b, 0xd1, 0x20, 0x6c, 0xe2, 0xa1, - 0xa7, 0x39, 0x13, 0x26, 0x20, 0xa8, 0xa0, 0x1e, 0x16, 0xf7, 0xc2, 0x4a, 0x99, 0xa0, 0xa0, 0xb2, - 0x3b, 0x2c, 0x99, 0xac, 0xda, 0xdd, 0x1d, 0x16, 0x02, 0xa5, 0x30, 0xec, 0xff, 0x61, 0xc1, 0xb9, - 0xdc, 0xa1, 0xb8, 0x0f, 0xca, 0x77, 0x3b, 0xad, 0x7c, 0x57, 0x8a, 0xda, 0x6e, 0x18, 0x6f, 0xd1, - 0x43, 0x11, 0xff, 0x5b, 0x0b, 0x46, 0x35, 0xfe, 0x7d, 0x78, 0x55, 0x37, 0xfd, 0xaa, 0xc5, 0xed, - 0xac, 0xea, 0x5d, 0xef, 0xf6, 0xbb, 0x25, 0x50, 0x05, 0x1c, 0xa7, 0x1a, 0xb2, 0x3c, 0xee, 0x3e, - 0x27, 0x89, 0x3b, 0x30, 0xc0, 0x0e, 0x42, 0xe3, 0x62, 0x82, 0x3c, 0xd2, 0xfc, 0xd9, 0xa1, 0xaa, - 0x3e, 0x64, 0x66, 0x7f, 0x63, 0x2c, 0x18, 0xb2, 0x22, 0xcf, 0x6e, 0x4c, 0xa5, 0x79, 0x53, 0xa4, - 0x65, 0xe9, 0x22, 0xcf, 0xa2, 0x1d, 0x2b, 0x0c, 0xaa, 0x1e, 0xdc, 0x46, 0xe0, 0xcf, 0x78, 0x4e, - 0x2c, 0xef, 0x3e, 0x54, 0xea, 0x61, 0x5e, 0x02, 0xb0, 0xc6, 0x61, 0x67, 0xa4, 0x6e, 0x1c, 0x7a, - 0xce, 0x8e, 0xb1, 0x7f, 0x36, 0xea, 0x13, 0x28, 0x10, 0x36, 0xf1, 0xec, 0x36, 0x8c, 0xa5, 0x5f, - 0x62, 0x96, 0xac, 0xb3, 0x00, 0xc5, 0xbe, 0x86, 0x73, 0x12, 0xea, 0x0e, 0x7b, 0x6a, 0xa1, 0xe3, - 0x64, 0xaf, 0x2c, 0x9f, 0x92, 0x00, 0xac, 0x71, 0xec, 0x5f, 0xb3, 0xe0, 0x74, 0xce, 0xa0, 0x15, - 0x98, 0xf6, 0x96, 0x68, 0x69, 0x93, 0xa7, 0xd8, 0x7f, 0x08, 0x06, 0x9b, 0x64, 0xdd, 0x91, 0x21, - 0x70, 0x86, 0x6c, 0x9f, 0xe5, 0xcd, 0x58, 0xc2, 0xed, 0xff, 0x66, 0xc1, 0x89, 0x74, 0x5f, 0x63, - 0x96, 0x4a, 0xc2, 0x87, 0xc9, 0x8d, 0x1b, 0xc1, 0x16, 0x89, 0x76, 0xe8, 0x9b, 0x5b, 0x99, 0x54, - 0x92, 0x2e, 0x0c, 0x9c, 0xf3, 0x14, 0x2b, 0xdf, 0xda, 0x54, 0xa3, 0x2d, 0x67, 0xe4, 0xcd, 0x22, - 0x67, 0xa4, 0xfe, 0x98, 0xe6, 0x71, 0xb9, 0x62, 0x89, 0x4d, 0xfe, 0xf6, 0x77, 0x2a, 0xa0, 0xf2, - 0x62, 0x59, 0xfc, 0x51, 0x41, 0xd1, 0x5b, 0x07, 0xcd, 0x20, 0x52, 0x93, 0xa1, 0xb2, 0x57, 0x40, - 0x00, 0xf7, 0x92, 0x98, 0xae, 0x4b, 0xf5, 0x86, 0xab, 0x1a, 0x84, 0x4d, 0x3c, 0xda, 0x13, 0xcf, - 0xdd, 0x22, 0xfc, 0xa1, 0x81, 0x74, 0x4f, 0x16, 0x24, 0x00, 0x6b, 0x1c, 0xda, 0x93, 0xa6, 0xbb, - 0xbe, 0x2e, 0xb6, 0xfc, 0xaa, 0x27, 0x74, 0x74, 0x30, 0x83, 0xf0, 0x8a, 0xdc, 0xc1, 0xa6, 0xb0, - 0x82, 0x8d, 0x8a, 0xdc, 0xc1, 0x26, 0x66, 0x10, 0x6a, 0xb7, 0xf9, 0x41, 0xd4, 0x66, 0x57, 0xca, - 0x37, 0x15, 0x17, 0x61, 0xfd, 0x2a, 0xbb, 0xed, 0x7a, 0x37, 0x0a, 0xce, 0x7b, 0x8e, 0xce, 0xc0, - 0x30, 0x22, 0x4d, 0xb7, 0x91, 0x98, 0xd4, 0x20, 0x3d, 0x03, 0x97, 0xbb, 0x30, 0x70, 0xce, 0x53, - 0x68, 0x0a, 0x4e, 0xc8, 0xbc, 0x66, 0x59, 0xb5, 0x66, 0x28, 0x5d, 0x25, 0x03, 0xa7, 0xc1, 0x38, - 0x8b, 0x4f, 0xa5, 0x5a, 0x5b, 0x14, 0xac, 0x62, 0xc6, 0xb2, 0x21, 0xd5, 0x64, 0x21, 0x2b, 0xac, - 0x30, 0xec, 0x4f, 0x96, 0xa9, 0x16, 0xee, 0x51, 0xa8, 0xed, 0xbe, 0x45, 0x0b, 0xa6, 0x67, 0x64, - 0xa5, 0x8f, 0x19, 0xf9, 0x3c, 0x0c, 0xdf, 0x8e, 0x03, 0x5f, 0x45, 0xe2, 0x55, 0x7b, 0x46, 0xe2, - 0x19, 0x58, 0xf9, 0x91, 0x78, 0x03, 0x45, 0x45, 0xe2, 0x0d, 0x1e, 0x32, 0x12, 0xef, 0x5b, 0x55, - 0x50, 0x57, 0x83, 0x5c, 0x27, 0xc9, 0x9d, 0x20, 0xda, 0x74, 0xfd, 0x16, 0xcb, 0x07, 0xff, 0x9a, - 0x05, 0xc3, 0x7c, 0xbd, 0x2c, 0x98, 0x99, 0x54, 0xeb, 0x05, 0xdd, 0x39, 0x91, 0x62, 0x36, 0xb1, - 0x6a, 0x30, 0xca, 0x5c, 0xbd, 0x69, 0x82, 0x70, 0xaa, 0x47, 0xe8, 0x63, 0x00, 0xd2, 0x3f, 0xba, - 0x2e, 0x45, 0xe6, 0x7c, 0x31, 0xfd, 0xc3, 0x64, 0x5d, 0xdb, 0xc0, 0xab, 0x8a, 0x09, 0x36, 0x18, - 0xa2, 0xcf, 0xe8, 0x2c, 0x33, 0x1e, 0xb2, 0xff, 0x91, 0x63, 0x19, 0x9b, 0x7e, 0x72, 0xcc, 0x30, - 0x0c, 0xba, 0x7e, 0x8b, 0xce, 0x13, 0x11, 0xb1, 0xf4, 0xae, 0xbc, 0x5a, 0x0a, 0x0b, 0x81, 0xd3, - 0x9c, 0x76, 0x3c, 0xc7, 0x6f, 0x90, 0x68, 0x9e, 0xa3, 0x9b, 0x17, 0x4e, 0xb3, 0x06, 0x2c, 0x09, - 0x75, 0x5d, 0xaa, 0x52, 0xed, 0xe7, 0x52, 0x95, 0xf3, 0xef, 0x87, 0x53, 0x5d, 0x1f, 0xf3, 0x40, - 0x29, 0x65, 0x87, 0xcf, 0x46, 0xb3, 0xff, 0xd9, 0x80, 0x56, 0x5a, 0xd7, 0x83, 0x26, 0xbf, 0xda, - 0x23, 0xd2, 0x5f, 0x54, 0xd8, 0xb8, 0x05, 0x4e, 0x11, 0xe3, 0xd2, 0x6a, 0xd5, 0x88, 0x4d, 0x96, - 0x74, 0x8e, 0x86, 0x4e, 0x44, 0xfc, 0xe3, 0x9e, 0xa3, 0xcb, 0x8a, 0x09, 0x36, 0x18, 0xa2, 0x8d, - 0x54, 0x4e, 0xc9, 0xe5, 0xa3, 0xe7, 0x94, 0xb0, 0x2a, 0x53, 0x79, 0xd5, 0xf8, 0xbf, 0x68, 0xc1, - 0xa8, 0x9f, 0x9a, 0xb9, 0xc5, 0x84, 0x91, 0xe6, 0xaf, 0x0a, 0x7e, 0xb3, 0x54, 0xba, 0x0d, 0x67, - 0xf8, 0xe7, 0xa9, 0xb4, 0xea, 0x01, 0x55, 0x9a, 0xbe, 0x23, 0x68, 0xa0, 0xd7, 0x1d, 0x41, 0xc8, - 0x57, 0x97, 0xa4, 0x0d, 0x16, 0x7e, 0x49, 0x1a, 0xe4, 0x5c, 0x90, 0x76, 0x0b, 0xea, 0x8d, 0x88, - 0x38, 0xc9, 0x21, 0xef, 0xcb, 0x62, 0x07, 0xf4, 0x33, 0x92, 0x00, 0xd6, 0xb4, 0xec, 0xff, 0x5d, - 0x81, 0x93, 0x72, 0x44, 0x64, 0x08, 0x3a, 0xd5, 0x8f, 0x9c, 0xaf, 0x36, 0x6e, 0x95, 0x7e, 0xbc, - 0x22, 0x01, 0x58, 0xe3, 0x50, 0x7b, 0xac, 0x13, 0x93, 0xa5, 0x90, 0xf8, 0x0b, 0xee, 0x5a, 0x2c, - 0xce, 0x39, 0xd5, 0x42, 0xb9, 0xa1, 0x41, 0xd8, 0xc4, 0xa3, 0xc6, 0x38, 0xb7, 0x8b, 0xe3, 0x6c, - 0xfa, 0x8a, 0xb0, 0xb7, 0xb1, 0x84, 0xa3, 0x5f, 0xcc, 0xad, 0x1c, 0x5b, 0x4c, 0xe2, 0x56, 0x57, - 0xe4, 0xfd, 0x01, 0xaf, 0x58, 0xfc, 0x5b, 0x16, 0x9c, 0xe5, 0xad, 0x72, 0x24, 0x6f, 0x84, 0x4d, - 0x27, 0x21, 0x71, 0x31, 0x95, 0xdc, 0x73, 0xfa, 0xa7, 0x9d, 0xbc, 0x79, 0x6c, 0x71, 0x7e, 0x6f, - 0xd0, 0x9b, 0x16, 0x9c, 0xd8, 0x4c, 0xd5, 0xfc, 0x90, 0xaa, 0xe3, 0xa8, 0xe9, 0xf8, 0x29, 0xa2, - 0x7a, 0xa9, 0xa5, 0xdb, 0x63, 0x9c, 0xe5, 0x6e, 0xff, 0x99, 0x05, 0xa6, 0x18, 0xbd, 0xff, 0xa5, - 0x42, 0x0e, 0x6e, 0x0a, 0x4a, 0xeb, 0xb2, 0xda, 0xd3, 0xba, 0x7c, 0x1c, 0xca, 0x1d, 0xb7, 0x29, - 0xf6, 0x17, 0xfa, 0xf4, 0x75, 0x7e, 0x16, 0xd3, 0x76, 0xfb, 0x1f, 0x57, 0xb5, 0xdf, 0x42, 0xe4, - 0x45, 0x7d, 0x5f, 0xbc, 0xf6, 0xba, 0x2a, 0x36, 0xc6, 0xdf, 0xfc, 0x7a, 0x57, 0xb1, 0xb1, 0x1f, - 0x3b, 0x78, 0xda, 0x1b, 0x1f, 0xa0, 0x5e, 0xb5, 0xc6, 0x06, 0xf7, 0xc9, 0x79, 0xbb, 0x0d, 0x35, - 0xba, 0x05, 0x63, 0x0e, 0xc8, 0x5a, 0xaa, 0x53, 0xb5, 0x2b, 0xa2, 0xfd, 0xde, 0xee, 0xf8, 0x7b, - 0x0f, 0xde, 0x2d, 0xf9, 0x34, 0x56, 0xf4, 0x51, 0x0c, 0x75, 0xfa, 0x9b, 0xa5, 0xe7, 0x89, 0xcd, - 0xdd, 0x0d, 0x25, 0x33, 0x25, 0xa0, 0x90, 0xdc, 0x3f, 0xcd, 0x07, 0xf9, 0x50, 0x67, 0xb7, 0xd1, - 0x32, 0xa6, 0x7c, 0x0f, 0xb8, 0xac, 0x92, 0xe4, 0x24, 0xe0, 0xde, 0xee, 0xf8, 0x4b, 0x07, 0x67, - 0xaa, 0x1e, 0xc7, 0x9a, 0x85, 0xfd, 0xa5, 0x8a, 0x9e, 0xbb, 0xa2, 0xc6, 0xdc, 0xf7, 0xc5, 0xdc, - 0x7d, 0x31, 0x33, 0x77, 0x2f, 0x74, 0xcd, 0xdd, 0x51, 0x7d, 0x6b, 0x6a, 0x6a, 0x36, 0xde, 0x6f, - 0x43, 0x60, 0x7f, 0x7f, 0x03, 0xb3, 0x80, 0x5e, 0xef, 0xb8, 0x11, 0x89, 0x97, 0xa3, 0x8e, 0xef, - 0xfa, 0x2d, 0x36, 0x1d, 0x6b, 0xa6, 0x05, 0x94, 0x02, 0xe3, 0x2c, 0x3e, 0xdd, 0xd4, 0xd3, 0x6f, - 0x7e, 0xcb, 0xd9, 0xe2, 0xb3, 0xca, 0x28, 0xbb, 0xb5, 0x22, 0xda, 0xb1, 0xc2, 0xb0, 0xbf, 0xc1, - 0xce, 0xb2, 0x8d, 0xbc, 0x60, 0x3a, 0x27, 0x3c, 0x76, 0xfd, 0x2f, 0xaf, 0xd9, 0xa5, 0xe6, 0x04, - 0xbf, 0xf3, 0x97, 0xc3, 0xd0, 0x1d, 0x18, 0x5c, 0xe3, 0xf7, 0xdf, 0x15, 0x53, 0x9f, 0x5c, 0x5c, - 0xa6, 0xc7, 0x6e, 0x39, 0x91, 0x37, 0xeb, 0xdd, 0xd3, 0x3f, 0xb1, 0xe4, 0x66, 0x7f, 0xb3, 0x02, - 0x27, 0x32, 0x17, 0xc4, 0xa6, 0xaa, 0xa5, 0x96, 0xf6, 0xad, 0x96, 0xfa, 0x61, 0x80, 0x26, 0x09, - 0xbd, 0x60, 0x87, 0x99, 0x63, 0x95, 0x03, 0x9b, 0x63, 0xca, 0x82, 0x9f, 0x55, 0x54, 0xb0, 0x41, - 0x51, 0x14, 0x2a, 0xe3, 0xc5, 0x57, 0x33, 0x85, 0xca, 0x8c, 0x5b, 0x0c, 0x06, 0xee, 0xef, 0x2d, - 0x06, 0x2e, 0x9c, 0xe0, 0x5d, 0x54, 0xd9, 0xb7, 0x87, 0x48, 0xb2, 0x65, 0xf9, 0x0b, 0xb3, 0x69, - 0x32, 0x38, 0x4b, 0xf7, 0x41, 0xde, 0xff, 0x8c, 0xde, 0x0d, 0x75, 0xf9, 0x9d, 0xe3, 0xb1, 0xba, - 0xae, 0x60, 0x20, 0xa7, 0x01, 0xbb, 0x97, 0x59, 0xfc, 0xb4, 0xbf, 0x50, 0xa2, 0xd6, 0x33, 0xff, - 0xa7, 0x2a, 0xd1, 0x3c, 0x05, 0x03, 0x4e, 0x27, 0xd9, 0x08, 0xba, 0xee, 0xd0, 0x9b, 0x62, 0xad, - 0x58, 0x40, 0xd1, 0x02, 0x54, 0x9a, 0xba, 0xba, 0xc8, 0x41, 0x46, 0x51, 0x3b, 0x22, 0x9d, 0x84, - 0x60, 0x46, 0x05, 0x3d, 0x06, 0x95, 0xc4, 0x69, 0xc9, 0x44, 0x27, 0x96, 0xdc, 0xba, 0xea, 0xb4, - 0x62, 0xcc, 0x5a, 0x4d, 0xa5, 0x59, 0xd9, 0x47, 0x69, 0xbe, 0x04, 0x23, 0xb1, 0xdb, 0xf2, 0x9d, - 0xa4, 0x13, 0x11, 0xe3, 0x70, 0x4d, 0xc7, 0x4b, 0x98, 0x40, 0x9c, 0xc6, 0xb5, 0x7f, 0x6b, 0x18, - 0xce, 0xac, 0xcc, 0x2c, 0xca, 0x9a, 0xd9, 0xc7, 0x96, 0xab, 0x94, 0xc7, 0xe3, 0xfe, 0xe5, 0x2a, - 0xf5, 0xe0, 0xee, 0x19, 0xb9, 0x4a, 0x9e, 0x91, 0xab, 0x94, 0x4e, 0x1c, 0x29, 0x17, 0x91, 0x38, - 0x92, 0xd7, 0x83, 0x7e, 0x12, 0x47, 0x8e, 0x2d, 0x79, 0x69, 0xcf, 0x0e, 0x1d, 0x28, 0x79, 0x49, - 0x65, 0x76, 0x15, 0x12, 0xd2, 0xdf, 0xe3, 0x53, 0xe5, 0x66, 0x76, 0xa9, 0xac, 0x1a, 0x9e, 0xae, - 0x22, 0x04, 0xec, 0xab, 0xc5, 0x77, 0xa0, 0x8f, 0xac, 0x1a, 0x91, 0x31, 0x63, 0x66, 0x72, 0x0d, - 0x16, 0x91, 0xc9, 0x95, 0xd7, 0x9d, 0x7d, 0x33, 0xb9, 0x5e, 0x82, 0x91, 0x86, 0x17, 0xf8, 0x64, - 0x39, 0x0a, 0x92, 0xa0, 0x11, 0x78, 0xc2, 0x98, 0x56, 0x22, 0x61, 0xc6, 0x04, 0xe2, 0x34, 0x6e, - 0xaf, 0x34, 0xb0, 0xfa, 0x51, 0xd3, 0xc0, 0xe0, 0x01, 0xa5, 0x81, 0xfd, 0x9c, 0x4e, 0x58, 0x1e, - 0x62, 0x5f, 0xe4, 0xc3, 0xc5, 0x7f, 0x91, 0x7e, 0xb2, 0x96, 0xd1, 0x5b, 0xfc, 0x12, 0x3b, 0x6a, - 0x8e, 0xce, 0x04, 0x6d, 0x6a, 0x6e, 0x0d, 0xb3, 0x21, 0x79, 0xed, 0x18, 0x26, 0xec, 0xad, 0x15, - 0xcd, 0x46, 0x5d, 0x6c, 0xa7, 0x9b, 0x70, 0xba, 0x23, 0x47, 0x49, 0xa8, 0xfe, 0x4a, 0x09, 0x7e, - 0x60, 0xdf, 0x2e, 0xa0, 0x3b, 0x00, 0x89, 0xd3, 0x12, 0x13, 0x55, 0x1c, 0x53, 0x1c, 0x31, 0xa8, - 0x71, 0x55, 0xd2, 0xe3, 0x95, 0x40, 0xd4, 0x5f, 0x76, 0x00, 0x20, 0x7f, 0xb3, 0x58, 0xc6, 0xc0, - 0xeb, 0x2a, 0x98, 0x88, 0x03, 0x8f, 0x60, 0x06, 0xa1, 0xea, 0x3f, 0x22, 0x2d, 0x7d, 0xeb, 0xb2, - 0xfa, 0x7c, 0x98, 0xb5, 0x62, 0x01, 0x45, 0x2f, 0xc0, 0x90, 0xe3, 0x79, 0x3c, 0x2b, 0x85, 0xc4, - 0xe2, 0x16, 0x1b, 0x5d, 0xb9, 0x4d, 0x83, 0xb0, 0x89, 0x67, 0xff, 0x69, 0x09, 0xc6, 0xf7, 0x91, - 0x29, 0x5d, 0x79, 0x76, 0xd5, 0xbe, 0xf3, 0xec, 0x44, 0x66, 0xc0, 0x40, 0x8f, 0xcc, 0x80, 0x17, - 0x60, 0x28, 0x21, 0x4e, 0x5b, 0x84, 0x41, 0x89, 0xfd, 0xb7, 0x3e, 0x77, 0xd5, 0x20, 0x6c, 0xe2, - 0x51, 0x29, 0x36, 0xea, 0x34, 0x1a, 0x24, 0x8e, 0x65, 0xe8, 0xbf, 0xf0, 0x61, 0x16, 0x96, 0x57, - 0xc0, 0x5c, 0xc3, 0x53, 0x29, 0x16, 0x38, 0xc3, 0x32, 0x3b, 0xe0, 0xf5, 0x3e, 0x07, 0xfc, 0xeb, - 0x25, 0x78, 0x7c, 0x4f, 0xed, 0xd6, 0x77, 0x56, 0x46, 0x27, 0x26, 0x51, 0x76, 0xe2, 0xdc, 0x88, - 0x49, 0x84, 0x19, 0x84, 0x8f, 0x52, 0x18, 0x1a, 0xb7, 0x5a, 0x17, 0x9d, 0x32, 0xc4, 0x47, 0x29, - 0xc5, 0x02, 0x67, 0x58, 0x1e, 0x76, 0x5a, 0xfe, 0xdd, 0x12, 0x3c, 0xd9, 0x87, 0x0d, 0x50, 0x60, - 0x6a, 0x55, 0x3a, 0xc1, 0xad, 0xfc, 0x80, 0xf2, 0x10, 0x0f, 0x39, 0x5c, 0xdf, 0x28, 0xc1, 0xf9, - 0xde, 0xaa, 0x18, 0xfd, 0x38, 0xdd, 0xc3, 0xcb, 0xd8, 0x27, 0x33, 0x37, 0xee, 0x34, 0xdf, 0xbf, - 0xa7, 0x40, 0x38, 0x8b, 0x8b, 0x26, 0x00, 0x42, 0x27, 0xd9, 0x88, 0x2f, 0x6d, 0xbb, 0x71, 0x22, - 0x6a, 0xbf, 0x8c, 0xf2, 0x13, 0x23, 0xd9, 0x8a, 0x0d, 0x0c, 0xca, 0x8e, 0xfd, 0x9b, 0x0d, 0xae, - 0x07, 0x09, 0x7f, 0x88, 0x6f, 0x23, 0x4e, 0xcb, 0x9b, 0x32, 0x0c, 0x10, 0xce, 0xe2, 0x52, 0x76, - 0xec, 0x4c, 0x92, 0x77, 0x94, 0xef, 0x2f, 0x18, 0xbb, 0x05, 0xd5, 0x8a, 0x0d, 0x8c, 0x6c, 0xd6, - 0x5f, 0x75, 0xff, 0xac, 0x3f, 0xfb, 0x1f, 0x95, 0xe0, 0x5c, 0x4f, 0x53, 0xae, 0xbf, 0x05, 0xf8, - 0xf0, 0x65, 0xea, 0x1d, 0x6e, 0xee, 0x1c, 0x30, 0xa3, 0xec, 0x8f, 0x7b, 0xcc, 0x34, 0x91, 0x51, - 0x76, 0xf8, 0x94, 0xec, 0x87, 0x6f, 0x3c, 0xbb, 0x92, 0xc8, 0x2a, 0x07, 0x48, 0x22, 0xcb, 0x7c, - 0x8c, 0x6a, 0x9f, 0x0b, 0xf9, 0xcf, 0xcb, 0x3d, 0x87, 0x97, 0x6e, 0xfd, 0xfa, 0xf2, 0x8e, 0xce, - 0xc2, 0x49, 0xd7, 0x67, 0xb7, 0x26, 0xad, 0x74, 0xd6, 0x44, 0x39, 0x90, 0x52, 0xfa, 0xce, 0xf2, - 0xf9, 0x0c, 0x1c, 0x77, 0x3d, 0xf1, 0x10, 0x26, 0xf5, 0x1d, 0x6e, 0x48, 0x0f, 0x96, 0x56, 0x8a, - 0x96, 0xe0, 0xac, 0x1c, 0x8a, 0x0d, 0x27, 0x22, 0x4d, 0xa1, 0x46, 0x62, 0x91, 0xc6, 0x70, 0x8e, - 0xa7, 0x42, 0xe4, 0x20, 0xe0, 0xfc, 0xe7, 0xd8, 0x45, 0x35, 0x41, 0xe8, 0x36, 0xc4, 0x26, 0x47, - 0x5f, 0x54, 0x43, 0x1b, 0x31, 0x87, 0xd9, 0x1f, 0x86, 0xba, 0x7a, 0x7f, 0x1e, 0x4c, 0xad, 0x26, - 0x5d, 0x57, 0x30, 0xb5, 0x9a, 0x71, 0x06, 0x16, 0xfd, 0x5a, 0xd4, 0x24, 0xce, 0xac, 0x9e, 0x6b, - 0x64, 0x87, 0xd9, 0xc7, 0xf6, 0x8f, 0xc0, 0xb0, 0xf2, 0xb3, 0xf4, 0x7b, 0x7d, 0x8f, 0xfd, 0xa5, - 0x01, 0x18, 0x49, 0x95, 0xe4, 0x4b, 0xb9, 0x35, 0xad, 0x7d, 0xdd, 0x9a, 0x2c, 0x38, 0xbe, 0xe3, - 0xcb, 0xbb, 0xbd, 0x8c, 0xe0, 0xf8, 0x8e, 0x4f, 0x30, 0x87, 0x51, 0xf3, 0xb6, 0x19, 0xed, 0xe0, - 0x8e, 0x2f, 0x82, 0x58, 0x95, 0x79, 0x3b, 0xcb, 0x5a, 0xb1, 0x80, 0xa2, 0x4f, 0x58, 0x30, 0x1c, - 0x33, 0x9f, 0x39, 0x77, 0x0a, 0x8b, 0x49, 0x77, 0xf5, 0xe8, 0x15, 0x07, 0x55, 0xf9, 0x49, 0x16, - 0x97, 0x62, 0xb6, 0xe0, 0x14, 0x47, 0xf4, 0x69, 0x0b, 0xea, 0xea, 0x0a, 0x12, 0x71, 0x01, 0xdf, - 0x4a, 0xb1, 0x15, 0x0f, 0xb9, 0x37, 0x51, 0x1d, 0x3f, 0xa8, 0xd2, 0x73, 0x58, 0x33, 0x46, 0xb1, - 0xf2, 0xd8, 0x0e, 0x1e, 0x8f, 0xc7, 0x16, 0x72, 0xbc, 0xb5, 0xef, 0x86, 0x7a, 0xdb, 0xf1, 0xdd, - 0x75, 0x12, 0x27, 0xdc, 0x89, 0x2a, 0x0b, 0xb1, 0xca, 0x46, 0xac, 0xe1, 0x54, 0x21, 0xc7, 0xec, - 0xc5, 0x12, 0xc3, 0xeb, 0xc9, 0x14, 0xf2, 0x8a, 0x6e, 0xc6, 0x26, 0x8e, 0xe9, 0xa2, 0x85, 0x07, - 0xea, 0xa2, 0x1d, 0xda, 0xc7, 0x45, 0xfb, 0xf7, 0x2d, 0x38, 0x9b, 0xfb, 0xd5, 0x1e, 0xde, 0x70, - 0x43, 0xfb, 0xcb, 0x55, 0x38, 0x9d, 0x53, 0x5b, 0x13, 0xed, 0x98, 0xf3, 0xd9, 0x2a, 0xe2, 0xe4, - 0x3e, 0x7d, 0x10, 0x2d, 0x87, 0x31, 0x67, 0x12, 0x1f, 0xec, 0x80, 0x44, 0x1f, 0x52, 0x94, 0xef, - 0xef, 0x21, 0x85, 0x31, 0x2d, 0x2b, 0x0f, 0x74, 0x5a, 0x56, 0xf7, 0x9e, 0x96, 0xe8, 0xd7, 0x2d, - 0x18, 0x6b, 0xf7, 0x28, 0xe8, 0x2e, 0x1c, 0x8f, 0x37, 0x8f, 0xa7, 0x5c, 0xfc, 0xf4, 0x63, 0x77, - 0x77, 0xc7, 0x7b, 0xd6, 0xd1, 0xc7, 0x3d, 0x7b, 0x65, 0x7f, 0xa7, 0x0c, 0xac, 0xb0, 0x2b, 0xab, - 0x9f, 0xb6, 0x83, 0x3e, 0x6e, 0x96, 0xe8, 0xb5, 0x8a, 0x2a, 0x27, 0xcb, 0x89, 0xab, 0x12, 0xbf, - 0x7c, 0x04, 0xf3, 0x2a, 0xfe, 0x66, 0x85, 0x56, 0xa9, 0x0f, 0xa1, 0xe5, 0xc9, 0x5a, 0xc8, 0xe5, - 0xe2, 0x6b, 0x21, 0xd7, 0xb3, 0x75, 0x90, 0xf7, 0xfe, 0xc4, 0x95, 0x87, 0xf2, 0x13, 0xff, 0x0d, - 0x8b, 0x0b, 0x9e, 0xcc, 0x57, 0xd0, 0x96, 0x81, 0xb5, 0x87, 0x65, 0xf0, 0x0c, 0xd4, 0x62, 0xe2, - 0xad, 0x5f, 0x21, 0x8e, 0x27, 0x2c, 0x08, 0x7d, 0x6a, 0x2c, 0xda, 0xb1, 0xc2, 0x60, 0x97, 0xa5, - 0x7a, 0x5e, 0x70, 0xe7, 0x52, 0x3b, 0x4c, 0x76, 0x84, 0x2d, 0xa1, 0x2f, 0x4b, 0x55, 0x10, 0x6c, - 0x60, 0xd9, 0x7f, 0xb3, 0xc4, 0x67, 0xa0, 0x08, 0x3d, 0x78, 0x31, 0x73, 0xbd, 0x5d, 0xff, 0xa7, - 0xf6, 0x1f, 0x05, 0x68, 0xa8, 0x8b, 0xe1, 0xc5, 0x99, 0xd0, 0x95, 0x23, 0xdf, 0x5a, 0x2d, 0xe8, - 0xe9, 0xd7, 0xd0, 0x6d, 0xd8, 0xe0, 0x97, 0x92, 0xa5, 0xe5, 0x7d, 0x65, 0x69, 0x4a, 0xac, 0x54, - 0xf6, 0xd1, 0x76, 0x7f, 0x6a, 0x41, 0xca, 0x22, 0x42, 0x21, 0x54, 0x69, 0x77, 0x77, 0x8a, 0xb9, - 0xf3, 0xde, 0x24, 0x4d, 0x45, 0xa3, 0x98, 0xf6, 0xec, 0x27, 0xe6, 0x8c, 0x90, 0x27, 0x22, 0x14, - 0xf8, 0xa8, 0x5e, 0x2f, 0x8e, 0xe1, 0x95, 0x20, 0xd8, 0xe4, 0x07, 0x9b, 0x3a, 0xda, 0xc1, 0x7e, - 0x11, 0x4e, 0x75, 0x75, 0x8a, 0xdd, 0x64, 0x15, 0xc8, 0x8b, 0xfe, 0x8d, 0xe9, 0xca, 0xd2, 0x26, - 0x31, 0x87, 0xd9, 0xdf, 0xb0, 0xe0, 0x64, 0x96, 0x3c, 0x7a, 0xcb, 0x82, 0x53, 0x71, 0x96, 0xde, - 0x71, 0x8d, 0x9d, 0x8a, 0x32, 0xec, 0x02, 0xe1, 0xee, 0x4e, 0xd8, 0xff, 0x47, 0x4c, 0xfe, 0x5b, - 0xae, 0xdf, 0x0c, 0xee, 0x28, 0xc3, 0xc4, 0xea, 0x69, 0x98, 0xd0, 0xf5, 0xd8, 0xd8, 0x20, 0xcd, - 0x8e, 0xd7, 0x95, 0xaf, 0xb9, 0x22, 0xda, 0xb1, 0xc2, 0x60, 0xe9, 0x69, 0x1d, 0x51, 0x2c, 0x3d, - 0x33, 0x29, 0x67, 0x45, 0x3b, 0x56, 0x18, 0xe8, 0x79, 0x18, 0x36, 0x5e, 0x52, 0xce, 0x4b, 0x66, - 0x90, 0x1b, 0x2a, 0x33, 0xc6, 0x29, 0x2c, 0x34, 0x01, 0xa0, 0x8c, 0x1c, 0xa9, 0x22, 0x99, 0xa3, - 0x48, 0x49, 0xa2, 0x18, 0x1b, 0x18, 0x2c, 0x19, 0xd4, 0xeb, 0xc4, 0xcc, 0xc7, 0x3f, 0xa0, 0x0b, - 0x78, 0xce, 0x88, 0x36, 0xac, 0xa0, 0x54, 0x9a, 0xb4, 0x1d, 0xbf, 0xe3, 0x78, 0x74, 0x84, 0xc4, - 0xd6, 0x4f, 0x2d, 0xc3, 0x45, 0x05, 0xc1, 0x06, 0x16, 0x7d, 0xe3, 0xc4, 0x6d, 0x93, 0x57, 0x02, - 0x5f, 0x46, 0x87, 0xe9, 0x63, 0x1f, 0xd1, 0x8e, 0x15, 0x86, 0xfd, 0x5f, 0x2c, 0x38, 0xa1, 0x53, - 0xcb, 0xf9, 0x9d, 0xd5, 0xe6, 0x4e, 0xd5, 0xda, 0x77, 0xa7, 0x9a, 0xce, 0xb9, 0x2d, 0xf5, 0x95, - 0x73, 0x6b, 0xa6, 0xc3, 0x96, 0xf7, 0x4c, 0x87, 0xfd, 0x41, 0x7d, 0x1f, 0x2a, 0xcf, 0x9b, 0x1d, - 0xca, 0xbb, 0x0b, 0x15, 0xd9, 0x30, 0xd0, 0x70, 0x54, 0x5d, 0x95, 0x61, 0xbe, 0x77, 0x98, 0x99, - 0x62, 0x48, 0x02, 0x62, 0x2f, 0x41, 0x5d, 0x9d, 0x7e, 0xc8, 0x8d, 0xaa, 0x95, 0xbf, 0x51, 0xed, - 0x2b, 0x2d, 0x6f, 0x7a, 0xed, 0x9b, 0xdf, 0x7d, 0xe2, 0x1d, 0xbf, 0xff, 0xdd, 0x27, 0xde, 0xf1, - 0x47, 0xdf, 0x7d, 0xe2, 0x1d, 0x9f, 0xb8, 0xfb, 0x84, 0xf5, 0xcd, 0xbb, 0x4f, 0x58, 0xbf, 0x7f, - 0xf7, 0x09, 0xeb, 0x8f, 0xee, 0x3e, 0x61, 0x7d, 0xe7, 0xee, 0x13, 0xd6, 0x17, 0xff, 0xe3, 0x13, - 0xef, 0x78, 0x25, 0x37, 0x3c, 0x90, 0xfe, 0x78, 0xb6, 0xd1, 0x9c, 0xdc, 0xba, 0xc8, 0x22, 0xd4, - 0xe8, 0xf2, 0x9a, 0x34, 0xe6, 0xd4, 0xa4, 0x5c, 0x5e, 0xff, 0x37, 0x00, 0x00, 0xff, 0xff, 0x90, - 0x85, 0x5a, 0x45, 0xa5, 0xe0, 0x00, 0x00, + // 10990 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x6d, 0x70, 0x1c, 0xc9, + 0x75, 0x98, 0x66, 0x17, 0x0b, 0xec, 0x3e, 0x7c, 0x90, 0x6c, 0x92, 0x77, 0x20, 0x75, 0x77, 0xa0, + 0xe7, 0xe2, 0xd3, 0x39, 0xba, 0x03, 0x7c, 0xf4, 0x9d, 0x7c, 0xf1, 0xd9, 0x92, 0xb1, 0x00, 0x09, + 0x82, 0x04, 0x08, 0x5c, 0x03, 0x24, 0xa5, 0x93, 0x4f, 0xa7, 0xc1, 0x6e, 0x63, 0x31, 0xc4, 0xec, + 0xcc, 0xdc, 0xcc, 0x2c, 0x08, 0x9c, 0x25, 0x59, 0xb2, 0x64, 0x5b, 0x89, 0x3e, 0x4e, 0x91, 0x92, + 0xf2, 0x39, 0x89, 0x14, 0xd9, 0x72, 0x52, 0x76, 0x25, 0xaa, 0x38, 0xc9, 0x8f, 0x38, 0x71, 0x52, + 0x2e, 0xdb, 0xa9, 0x94, 0x52, 0x4a, 0xca, 0x2e, 0x97, 0xcb, 0x72, 0x12, 0x1b, 0x91, 0x98, 0x4a, + 0x25, 0x95, 0xaa, 0xb8, 0xca, 0x89, 0x7f, 0x24, 0x4c, 0x7e, 0xa4, 0xfa, 0xbb, 0x67, 0x76, 0x16, + 0x58, 0x00, 0x03, 0x92, 0x52, 0xee, 0xdf, 0x6e, 0xbf, 0x37, 0xef, 0xf5, 0xf4, 0x74, 0xbf, 0xf7, + 0xfa, 0xf5, 0x7b, 0xaf, 0x61, 0xa1, 0xe5, 0x26, 0x1b, 0x9d, 0xb5, 0xc9, 0x46, 0xd0, 0x9e, 0x72, + 0xa2, 0x56, 0x10, 0x46, 0xc1, 0x6d, 0xf6, 0xe3, 0xd9, 0x46, 0x73, 0x6a, 0xeb, 0xe2, 0x54, 0xb8, + 0xd9, 0x9a, 0x72, 0x42, 0x37, 0x9e, 0x72, 0xc2, 0xd0, 0x73, 0x1b, 0x4e, 0xe2, 0x06, 0xfe, 0xd4, + 0xd6, 0x73, 0x8e, 0x17, 0x6e, 0x38, 0xcf, 0x4d, 0xb5, 0x88, 0x4f, 0x22, 0x27, 0x21, 0xcd, 0xc9, + 0x30, 0x0a, 0x92, 0x00, 0xfd, 0xa8, 0xa6, 0x36, 0x29, 0xa9, 0xb1, 0x1f, 0xaf, 0x35, 0x9a, 0x93, + 0x5b, 0x17, 0x27, 0xc3, 0xcd, 0xd6, 0x24, 0xa5, 0x36, 0x69, 0x50, 0x9b, 0x94, 0xd4, 0xce, 0x3f, + 0x6b, 0xf4, 0xa5, 0x15, 0xb4, 0x82, 0x29, 0x46, 0x74, 0xad, 0xb3, 0xce, 0xfe, 0xb1, 0x3f, 0xec, + 0x17, 0x67, 0x76, 0xde, 0xde, 0x7c, 0x31, 0x9e, 0x74, 0x03, 0xda, 0xbd, 0xa9, 0x46, 0x10, 0x91, + 0xa9, 0xad, 0xae, 0x0e, 0x9d, 0xbf, 0xa2, 0x71, 0xc8, 0x76, 0x42, 0xfc, 0xd8, 0x0d, 0xfc, 0xf8, + 0x59, 0xda, 0x05, 0x12, 0x6d, 0x91, 0xc8, 0x7c, 0x3d, 0x03, 0x21, 0x8f, 0xd2, 0xf3, 0x9a, 0x52, + 0xdb, 0x69, 0x6c, 0xb8, 0x3e, 0x89, 0x76, 0xf4, 0xe3, 0x6d, 0x92, 0x38, 0x79, 0x4f, 0x4d, 0xf5, + 0x7a, 0x2a, 0xea, 0xf8, 0x89, 0xdb, 0x26, 0x5d, 0x0f, 0xbc, 0x67, 0xbf, 0x07, 0xe2, 0xc6, 0x06, + 0x69, 0x3b, 0x5d, 0xcf, 0xfd, 0x50, 0xaf, 0xe7, 0x3a, 0x89, 0xeb, 0x4d, 0xb9, 0x7e, 0x12, 0x27, + 0x51, 0xf6, 0x21, 0xfb, 0x75, 0x18, 0x9d, 0xbe, 0xb5, 0x32, 0xdd, 0x49, 0x36, 0x66, 0x02, 0x7f, + 0xdd, 0x6d, 0xa1, 0x17, 0x60, 0xb8, 0xe1, 0x75, 0xe2, 0x84, 0x44, 0xd7, 0x9d, 0x36, 0x19, 0xb7, + 0x2e, 0x58, 0x4f, 0xd7, 0xea, 0xa7, 0xbf, 0xb1, 0x3b, 0xf1, 0x8e, 0xbb, 0xbb, 0x13, 0xc3, 0x33, + 0x1a, 0x84, 0x4d, 0x3c, 0xf4, 0x03, 0x30, 0x14, 0x05, 0x1e, 0x99, 0xc6, 0xd7, 0xc7, 0x4b, 0xec, + 0x91, 0x13, 0xe2, 0x91, 0x21, 0xcc, 0x9b, 0xb1, 0x84, 0xdb, 0x7f, 0x58, 0x02, 0x98, 0x0e, 0xc3, + 0xe5, 0x28, 0xb8, 0x4d, 0x1a, 0x09, 0xfa, 0x30, 0x54, 0xe9, 0xd0, 0x35, 0x9d, 0xc4, 0x61, 0xdc, + 0x86, 0x2f, 0xfe, 0xe0, 0x24, 0x7f, 0x93, 0x49, 0xf3, 0x4d, 0xf4, 0xc4, 0xa1, 0xd8, 0x93, 0x5b, + 0xcf, 0x4d, 0x2e, 0xad, 0xd1, 0xe7, 0x17, 0x49, 0xe2, 0xd4, 0x91, 0x60, 0x06, 0xba, 0x0d, 0x2b, + 0xaa, 0xc8, 0x87, 0x81, 0x38, 0x24, 0x0d, 0xd6, 0xb1, 0xe1, 0x8b, 0x0b, 0x93, 0x47, 0x99, 0xa1, + 0x93, 0xba, 0xe7, 0x2b, 0x21, 0x69, 0xd4, 0x47, 0x04, 0xe7, 0x01, 0xfa, 0x0f, 0x33, 0x3e, 0x68, + 0x0b, 0x06, 0xe3, 0xc4, 0x49, 0x3a, 0xf1, 0x78, 0x99, 0x71, 0xbc, 0x5e, 0x18, 0x47, 0x46, 0xb5, + 0x3e, 0x26, 0x78, 0x0e, 0xf2, 0xff, 0x58, 0x70, 0xb3, 0xff, 0xc4, 0x82, 0x31, 0x8d, 0xbc, 0xe0, + 0xc6, 0x09, 0xfa, 0x89, 0xae, 0xc1, 0x9d, 0xec, 0x6f, 0x70, 0xe9, 0xd3, 0x6c, 0x68, 0x4f, 0x0a, + 0x66, 0x55, 0xd9, 0x62, 0x0c, 0x6c, 0x1b, 0x2a, 0x6e, 0x42, 0xda, 0xf1, 0x78, 0xe9, 0x42, 0xf9, + 0xe9, 0xe1, 0x8b, 0x57, 0x8a, 0x7a, 0xcf, 0xfa, 0xa8, 0x60, 0x5a, 0x99, 0xa7, 0xe4, 0x31, 0xe7, + 0x62, 0xff, 0xea, 0x88, 0xf9, 0x7e, 0x74, 0xc0, 0xd1, 0x73, 0x30, 0x1c, 0x07, 0x9d, 0xa8, 0x41, + 0x30, 0x09, 0x83, 0x78, 0xdc, 0xba, 0x50, 0xa6, 0x53, 0x8f, 0xce, 0xd4, 0x15, 0xdd, 0x8c, 0x4d, + 0x1c, 0xf4, 0x79, 0x0b, 0x46, 0x9a, 0x24, 0x4e, 0x5c, 0x9f, 0xf1, 0x97, 0x9d, 0x5f, 0x3d, 0x72, + 0xe7, 0x65, 0xe3, 0xac, 0x26, 0x5e, 0x3f, 0x23, 0x5e, 0x64, 0xc4, 0x68, 0x8c, 0x71, 0x8a, 0x3f, + 0x5d, 0x71, 0x4d, 0x12, 0x37, 0x22, 0x37, 0xa4, 0xff, 0xd9, 0x9c, 0x31, 0x56, 0xdc, 0xac, 0x06, + 0x61, 0x13, 0x0f, 0xf9, 0x50, 0xa1, 0x2b, 0x2a, 0x1e, 0x1f, 0x60, 0xfd, 0x9f, 0x3f, 0x5a, 0xff, + 0xc5, 0xa0, 0xd2, 0xc5, 0xaa, 0x47, 0x9f, 0xfe, 0x8b, 0x31, 0x67, 0x83, 0x3e, 0x67, 0xc1, 0xb8, + 0x58, 0xf1, 0x98, 0xf0, 0x01, 0xbd, 0xb5, 0xe1, 0x26, 0xc4, 0x73, 0xe3, 0x64, 0xbc, 0xc2, 0xfa, + 0x30, 0xd5, 0xdf, 0xdc, 0x9a, 0x8b, 0x82, 0x4e, 0x78, 0xcd, 0xf5, 0x9b, 0xf5, 0x0b, 0x82, 0xd3, + 0xf8, 0x4c, 0x0f, 0xc2, 0xb8, 0x27, 0x4b, 0xf4, 0x25, 0x0b, 0xce, 0xfb, 0x4e, 0x9b, 0xc4, 0xa1, + 0x43, 0x3f, 0x2d, 0x07, 0xd7, 0x3d, 0xa7, 0xb1, 0xc9, 0x7a, 0x34, 0x78, 0xb8, 0x1e, 0xd9, 0xa2, + 0x47, 0xe7, 0xaf, 0xf7, 0x24, 0x8d, 0xf7, 0x60, 0x8b, 0xbe, 0x66, 0xc1, 0xa9, 0x20, 0x0a, 0x37, + 0x1c, 0x9f, 0x34, 0x25, 0x34, 0x1e, 0x1f, 0x62, 0x4b, 0xef, 0x43, 0x47, 0xfb, 0x44, 0x4b, 0x59, + 0xb2, 0x8b, 0x81, 0xef, 0x26, 0x41, 0xb4, 0x42, 0x92, 0xc4, 0xf5, 0x5b, 0x71, 0xfd, 0xec, 0xdd, + 0xdd, 0x89, 0x53, 0x5d, 0x58, 0xb8, 0xbb, 0x3f, 0xe8, 0x27, 0x61, 0x38, 0xde, 0xf1, 0x1b, 0xb7, + 0x5c, 0xbf, 0x19, 0xdc, 0x89, 0xc7, 0xab, 0x45, 0x2c, 0xdf, 0x15, 0x45, 0x50, 0x2c, 0x40, 0xcd, + 0x00, 0x9b, 0xdc, 0xf2, 0x3f, 0x9c, 0x9e, 0x4a, 0xb5, 0xa2, 0x3f, 0x9c, 0x9e, 0x4c, 0x7b, 0xb0, + 0x45, 0x3f, 0x67, 0xc1, 0x68, 0xec, 0xb6, 0x7c, 0x27, 0xe9, 0x44, 0xe4, 0x1a, 0xd9, 0x89, 0xc7, + 0x81, 0x75, 0xe4, 0xea, 0x11, 0x47, 0xc5, 0x20, 0x59, 0x3f, 0x2b, 0xfa, 0x38, 0x6a, 0xb6, 0xc6, + 0x38, 0xcd, 0x37, 0x6f, 0xa1, 0xe9, 0x69, 0x3d, 0x5c, 0xec, 0x42, 0xd3, 0x93, 0xba, 0x27, 0x4b, + 0xf4, 0xe3, 0x70, 0x92, 0x37, 0xa9, 0x91, 0x8d, 0xc7, 0x47, 0x98, 0xa0, 0x3d, 0x73, 0x77, 0x77, + 0xe2, 0xe4, 0x4a, 0x06, 0x86, 0xbb, 0xb0, 0xd1, 0xeb, 0x30, 0x11, 0x92, 0xa8, 0xed, 0x26, 0x4b, + 0xbe, 0xb7, 0x23, 0xc5, 0x77, 0x23, 0x08, 0x49, 0x53, 0x74, 0x27, 0x1e, 0x1f, 0xbd, 0x60, 0x3d, + 0x5d, 0xad, 0xbf, 0x4b, 0x74, 0x73, 0x62, 0x79, 0x6f, 0x74, 0xbc, 0x1f, 0x3d, 0xfb, 0x5f, 0x97, + 0xe0, 0x64, 0x56, 0x71, 0xa2, 0xbf, 0x6b, 0xc1, 0x89, 0xdb, 0x77, 0x92, 0xd5, 0x60, 0x93, 0xf8, + 0x71, 0x7d, 0x87, 0x8a, 0x37, 0xa6, 0x32, 0x86, 0x2f, 0x36, 0x8a, 0x55, 0xd1, 0x93, 0x57, 0xd3, + 0x5c, 0x2e, 0xf9, 0x49, 0xb4, 0x53, 0x7f, 0x54, 0xbc, 0xdd, 0x89, 0xab, 0xb7, 0x56, 0x4d, 0x28, + 0xce, 0x76, 0xea, 0xfc, 0x67, 0x2c, 0x38, 0x93, 0x47, 0x02, 0x9d, 0x84, 0xf2, 0x26, 0xd9, 0xe1, + 0x56, 0x19, 0xa6, 0x3f, 0xd1, 0xab, 0x50, 0xd9, 0x72, 0xbc, 0x0e, 0x11, 0xd6, 0xcd, 0xdc, 0xd1, + 0x5e, 0x44, 0xf5, 0x0c, 0x73, 0xaa, 0x3f, 0x52, 0x7a, 0xd1, 0xb2, 0x7f, 0xb7, 0x0c, 0xc3, 0x86, + 0x7e, 0xbb, 0x0f, 0x16, 0x5b, 0x90, 0xb2, 0xd8, 0x16, 0x0b, 0x53, 0xcd, 0x3d, 0x4d, 0xb6, 0x3b, + 0x19, 0x93, 0x6d, 0xa9, 0x38, 0x96, 0x7b, 0xda, 0x6c, 0x28, 0x81, 0x5a, 0x10, 0x52, 0x8b, 0x9c, + 0xaa, 0xfe, 0x81, 0x22, 0x3e, 0xe1, 0x92, 0x24, 0x57, 0x1f, 0xbd, 0xbb, 0x3b, 0x51, 0x53, 0x7f, + 0xb1, 0x66, 0x64, 0x7f, 0xcb, 0x82, 0x33, 0x46, 0x1f, 0x67, 0x02, 0xbf, 0xe9, 0xb2, 0x4f, 0x7b, + 0x01, 0x06, 0x92, 0x9d, 0x50, 0x9a, 0xfd, 0x6a, 0xa4, 0x56, 0x77, 0x42, 0x82, 0x19, 0x84, 0x1a, + 0xfa, 0x6d, 0x12, 0xc7, 0x4e, 0x8b, 0x64, 0x0d, 0xfd, 0x45, 0xde, 0x8c, 0x25, 0x1c, 0x45, 0x80, + 0x3c, 0x27, 0x4e, 0x56, 0x23, 0xc7, 0x8f, 0x19, 0xf9, 0x55, 0xb7, 0x4d, 0xc4, 0x00, 0xff, 0xc5, + 0xfe, 0x66, 0x0c, 0x7d, 0xa2, 0xfe, 0xc8, 0xdd, 0xdd, 0x09, 0xb4, 0xd0, 0x45, 0x09, 0xe7, 0x50, + 0xb7, 0xbf, 0x64, 0xc1, 0x23, 0xf9, 0xb6, 0x18, 0x7a, 0x0a, 0x06, 0xf9, 0x96, 0x4f, 0xbc, 0x9d, + 0xfe, 0x24, 0xac, 0x15, 0x0b, 0x28, 0x9a, 0x82, 0x9a, 0xd2, 0x13, 0xe2, 0x1d, 0x4f, 0x09, 0xd4, + 0x9a, 0x56, 0x2e, 0x1a, 0x87, 0x0e, 0x1a, 0xfd, 0x23, 0x2c, 0x37, 0x35, 0x68, 0x6c, 0x93, 0xc4, + 0x20, 0xf6, 0x7f, 0xb4, 0xe0, 0x84, 0xd1, 0xab, 0xfb, 0x60, 0x9a, 0xfb, 0x69, 0xd3, 0x7c, 0xbe, + 0xb0, 0xf9, 0xdc, 0xc3, 0x36, 0xff, 0x9c, 0x05, 0xe7, 0x0d, 0xac, 0x45, 0x27, 0x69, 0x6c, 0x5c, + 0xda, 0x0e, 0x23, 0x12, 0xd3, 0xed, 0x34, 0x7a, 0xdc, 0x90, 0x5b, 0xf5, 0x61, 0x41, 0xa1, 0x7c, + 0x8d, 0xec, 0x70, 0x21, 0xf6, 0x0c, 0x54, 0xf9, 0xe4, 0x0c, 0x22, 0x31, 0xe2, 0xea, 0xdd, 0x96, + 0x44, 0x3b, 0x56, 0x18, 0xc8, 0x86, 0x41, 0x26, 0x9c, 0xe8, 0x62, 0xa5, 0x6a, 0x08, 0xe8, 0x47, + 0xbc, 0xc9, 0x5a, 0xb0, 0x80, 0xd8, 0x71, 0xaa, 0x3b, 0xcb, 0x11, 0x61, 0x1f, 0xb7, 0x79, 0xd9, + 0x25, 0x5e, 0x33, 0xa6, 0xdb, 0x06, 0xc7, 0xf7, 0x83, 0x44, 0xec, 0x00, 0x8c, 0x6d, 0xc3, 0xb4, + 0x6e, 0xc6, 0x26, 0x0e, 0x65, 0xea, 0x39, 0x6b, 0xc4, 0xe3, 0x23, 0x2a, 0x98, 0x2e, 0xb0, 0x16, + 0x2c, 0x20, 0xf6, 0xdd, 0x12, 0xdb, 0xa0, 0xa8, 0xa5, 0x4f, 0xee, 0xc7, 0xee, 0x36, 0x4a, 0xc9, + 0xca, 0xe5, 0xe2, 0x04, 0x17, 0xe9, 0xbd, 0xc3, 0x7d, 0x23, 0x23, 0x2e, 0x71, 0xa1, 0x5c, 0xf7, + 0xde, 0xe5, 0xfe, 0x56, 0x09, 0x26, 0xd2, 0x0f, 0x74, 0x49, 0x5b, 0xba, 0xa5, 0x32, 0x18, 0x65, + 0x9d, 0x18, 0x06, 0x3e, 0x36, 0xf1, 0x7a, 0x08, 0xac, 0xd2, 0x71, 0x0a, 0x2c, 0x53, 0x9e, 0x96, + 0xf7, 0x91, 0xa7, 0x4f, 0xa9, 0x51, 0x1f, 0xc8, 0x08, 0xb0, 0xb4, 0x4e, 0xb9, 0x00, 0x03, 0x71, + 0x42, 0xc2, 0xf1, 0x4a, 0x5a, 0x1e, 0xad, 0x24, 0x24, 0xc4, 0x0c, 0x62, 0xff, 0xb7, 0x12, 0x3c, + 0x9a, 0x1e, 0x43, 0xad, 0x02, 0xde, 0x97, 0x52, 0x01, 0xef, 0x36, 0x55, 0xc0, 0xbd, 0xdd, 0x89, + 0x77, 0xf6, 0x78, 0xec, 0xbb, 0x46, 0x43, 0xa0, 0xb9, 0xcc, 0x28, 0x4e, 0xa5, 0x47, 0xf1, 0xde, + 0xee, 0xc4, 0xe3, 0x3d, 0xde, 0x31, 0x33, 0xcc, 0x4f, 0xc1, 0x60, 0x44, 0x9c, 0x38, 0xf0, 0xc5, + 0x40, 0xab, 0xcf, 0x81, 0x59, 0x2b, 0x16, 0x50, 0xfb, 0xf7, 0x6b, 0xd9, 0xc1, 0x9e, 0xe3, 0x4e, + 0xb8, 0x20, 0x42, 0x2e, 0x0c, 0x30, 0xb3, 0x9e, 0x8b, 0x86, 0x6b, 0x47, 0x5b, 0x46, 0x54, 0x0d, + 0x28, 0xd2, 0xf5, 0x2a, 0xfd, 0x6a, 0xb4, 0x09, 0x33, 0x16, 0x68, 0x1b, 0xaa, 0x0d, 0x69, 0x6d, + 0x97, 0x8a, 0xf0, 0x4b, 0x09, 0x5b, 0x5b, 0x73, 0x1c, 0xa1, 0xf2, 0x5a, 0x99, 0xe8, 0x8a, 0x1b, + 0x22, 0x50, 0x6e, 0xb9, 0x89, 0xf8, 0xac, 0x47, 0xdc, 0x4f, 0xcd, 0xb9, 0xc6, 0x2b, 0x0e, 0x51, + 0x25, 0x32, 0xe7, 0x26, 0x98, 0xd2, 0x47, 0x3f, 0x63, 0xc1, 0x70, 0xdc, 0x68, 0x2f, 0x47, 0xc1, + 0x96, 0xdb, 0x24, 0x91, 0xb0, 0xa6, 0x8e, 0x28, 0x9a, 0x56, 0x66, 0x16, 0x25, 0x41, 0xcd, 0x97, + 0xef, 0x6f, 0x35, 0x04, 0x9b, 0x7c, 0xe9, 0x2e, 0xe3, 0x51, 0xf1, 0xee, 0xb3, 0xa4, 0xe1, 0x52, + 0xfd, 0x27, 0x37, 0x55, 0x6c, 0xa6, 0x1c, 0xd9, 0xba, 0x9c, 0xed, 0x34, 0x36, 0xe9, 0x7a, 0xd3, + 0x1d, 0x7a, 0xe7, 0xdd, 0xdd, 0x89, 0x47, 0x67, 0xf2, 0x79, 0xe2, 0x5e, 0x9d, 0x61, 0x03, 0x16, + 0x76, 0x3c, 0x0f, 0x93, 0xd7, 0x3b, 0x84, 0xb9, 0x4c, 0x0a, 0x18, 0xb0, 0x65, 0x4d, 0x30, 0x33, + 0x60, 0x06, 0x04, 0x9b, 0x7c, 0xd1, 0xeb, 0x30, 0xd8, 0x76, 0x92, 0xc8, 0xdd, 0x16, 0x7e, 0x92, + 0x23, 0xda, 0xfb, 0x8b, 0x8c, 0x96, 0x66, 0xce, 0x34, 0x35, 0x6f, 0xc4, 0x82, 0x11, 0x6a, 0x43, + 0xa5, 0x4d, 0xa2, 0x16, 0x19, 0xaf, 0x16, 0xe1, 0x13, 0x5e, 0xa4, 0xa4, 0x34, 0xc3, 0x1a, 0xb5, + 0x8e, 0x58, 0x1b, 0xe6, 0x5c, 0xd0, 0xab, 0x50, 0x8d, 0x89, 0x47, 0x1a, 0xd4, 0xbe, 0xa9, 0x31, + 0x8e, 0x3f, 0xd4, 0xa7, 0xad, 0x47, 0x0d, 0x8b, 0x15, 0xf1, 0x28, 0x5f, 0x60, 0xf2, 0x1f, 0x56, + 0x24, 0xe9, 0x00, 0x86, 0x5e, 0xa7, 0xe5, 0xfa, 0xe3, 0x50, 0xc4, 0x00, 0x2e, 0x33, 0x5a, 0x99, + 0x01, 0xe4, 0x8d, 0x58, 0x30, 0xb2, 0xff, 0xb3, 0x05, 0x28, 0x2d, 0xd4, 0xee, 0x83, 0x51, 0xfb, + 0x7a, 0xda, 0xa8, 0x5d, 0x28, 0xd2, 0xea, 0xe8, 0x61, 0xd7, 0xfe, 0x46, 0x0d, 0x32, 0xea, 0xe0, + 0x3a, 0x89, 0x13, 0xd2, 0x7c, 0x5b, 0x84, 0xbf, 0x2d, 0xc2, 0xdf, 0x16, 0xe1, 0x4a, 0x84, 0xaf, + 0x65, 0x44, 0xf8, 0x7b, 0x8d, 0x55, 0xaf, 0x0f, 0x55, 0x5f, 0x53, 0xa7, 0xae, 0x66, 0x0f, 0x0c, + 0x04, 0x2a, 0x09, 0xae, 0xae, 0x2c, 0x5d, 0xcf, 0x95, 0xd9, 0xaf, 0xa5, 0x65, 0xf6, 0x51, 0x59, + 0xfc, 0xff, 0x20, 0xa5, 0xff, 0x95, 0x05, 0xef, 0x4a, 0x4b, 0x2f, 0x39, 0x73, 0xe6, 0x5b, 0x7e, + 0x10, 0x91, 0x59, 0x77, 0x7d, 0x9d, 0x44, 0xc4, 0x6f, 0x90, 0x58, 0x79, 0x31, 0xac, 0x5e, 0x5e, + 0x0c, 0xf4, 0x3c, 0x8c, 0xdc, 0x8e, 0x03, 0x7f, 0x39, 0x70, 0x7d, 0x21, 0x82, 0xe8, 0x46, 0xf8, + 0xe4, 0xdd, 0xdd, 0x89, 0x11, 0x3a, 0xa2, 0xb2, 0x1d, 0xa7, 0xb0, 0xd0, 0x0c, 0x9c, 0xba, 0xfd, + 0xfa, 0xb2, 0x93, 0x18, 0xee, 0x00, 0xb9, 0x71, 0x67, 0x07, 0x16, 0x57, 0x5f, 0xce, 0x00, 0x71, + 0x37, 0xbe, 0xfd, 0x37, 0x4b, 0x70, 0x2e, 0xf3, 0x22, 0x81, 0xe7, 0x05, 0x9d, 0x84, 0x6e, 0x6a, + 0xd0, 0x57, 0x2c, 0x38, 0xd9, 0x4e, 0x7b, 0x1c, 0x62, 0xe1, 0xd8, 0x7d, 0x7f, 0x61, 0x3a, 0x22, + 0xe3, 0xd2, 0xa8, 0x8f, 0x8b, 0x11, 0x3a, 0x99, 0x01, 0xc4, 0xb8, 0xab, 0x2f, 0xe8, 0x55, 0xa8, + 0xb5, 0x9d, 0xed, 0x1b, 0x61, 0xd3, 0x49, 0xe4, 0x7e, 0xb2, 0xb7, 0x1b, 0xa0, 0x93, 0xb8, 0xde, + 0x24, 0x3f, 0xae, 0x9f, 0x9c, 0xf7, 0x93, 0xa5, 0x68, 0x25, 0x89, 0x5c, 0xbf, 0xc5, 0xdd, 0x79, + 0x8b, 0x92, 0x0c, 0xd6, 0x14, 0xed, 0x2f, 0x5b, 0x59, 0x25, 0xa5, 0x46, 0x27, 0x72, 0x12, 0xd2, + 0xda, 0x41, 0x1f, 0x81, 0x0a, 0xdd, 0xf8, 0xc9, 0x51, 0xb9, 0x55, 0xa4, 0xe6, 0x34, 0xbe, 0x84, + 0x56, 0xa2, 0xf4, 0x5f, 0x8c, 0x39, 0x53, 0xfb, 0x2b, 0xb5, 0xac, 0xb1, 0xc0, 0x0e, 0x6f, 0x2f, + 0x02, 0xb4, 0x82, 0x55, 0xd2, 0x0e, 0x3d, 0x3a, 0x2c, 0x16, 0x3b, 0x01, 0x50, 0xbe, 0x8e, 0x39, + 0x05, 0xc1, 0x06, 0x16, 0xfa, 0xcb, 0x16, 0x40, 0x4b, 0xce, 0x79, 0x69, 0x08, 0xdc, 0x28, 0xf2, + 0x75, 0xf4, 0x8a, 0xd2, 0x7d, 0x51, 0x0c, 0xb1, 0xc1, 0x1c, 0xfd, 0xb4, 0x05, 0xd5, 0x44, 0x76, + 0x9f, 0xab, 0xc6, 0xd5, 0x22, 0x7b, 0x22, 0x5f, 0x5a, 0xdb, 0x44, 0x6a, 0x48, 0x14, 0x5f, 0xf4, + 0xb3, 0x16, 0x40, 0xbc, 0xe3, 0x37, 0x96, 0x03, 0xcf, 0x6d, 0xec, 0x08, 0x8d, 0x79, 0xb3, 0x50, + 0x7f, 0x8c, 0xa2, 0x5e, 0x1f, 0xa3, 0xa3, 0xa1, 0xff, 0x63, 0x83, 0x33, 0xfa, 0x18, 0x54, 0x63, + 0x31, 0xdd, 0x84, 0x8e, 0x5c, 0x2d, 0xd6, 0x2b, 0xc4, 0x69, 0x0b, 0xf1, 0x2a, 0xfe, 0x61, 0xc5, + 0x13, 0xfd, 0xbc, 0x05, 0x27, 0xc2, 0xb4, 0x9f, 0x4f, 0xa8, 0xc3, 0xe2, 0x64, 0x40, 0xc6, 0x8f, + 0x58, 0x3f, 0x7d, 0x77, 0x77, 0xe2, 0x44, 0xa6, 0x11, 0x67, 0x7b, 0x41, 0x25, 0xa0, 0x9e, 0xc1, + 0x4b, 0x21, 0xf7, 0x39, 0x0e, 0x69, 0x09, 0x38, 0x97, 0x05, 0xe2, 0x6e, 0x7c, 0xb4, 0x0c, 0x67, + 0x68, 0xef, 0x76, 0xb8, 0xf9, 0x29, 0xd5, 0x4b, 0xcc, 0x94, 0x61, 0xb5, 0xfe, 0x98, 0x98, 0x21, + 0xcc, 0xab, 0x9f, 0xc5, 0xc1, 0xb9, 0x4f, 0xa2, 0xdf, 0xb5, 0xe0, 0x31, 0x97, 0xa9, 0x01, 0xd3, + 0x61, 0xae, 0x35, 0x82, 0x38, 0x89, 0x25, 0x85, 0xca, 0x8a, 0x5e, 0xea, 0xa7, 0xfe, 0x17, 0xc4, + 0x1b, 0x3c, 0x36, 0xbf, 0x47, 0x97, 0xf0, 0x9e, 0x1d, 0x46, 0x3f, 0x0c, 0xa3, 0x72, 0x5d, 0x2c, + 0x53, 0x11, 0xcc, 0x14, 0x6d, 0xad, 0x7e, 0xea, 0xee, 0xee, 0xc4, 0xe8, 0xaa, 0x09, 0xc0, 0x69, + 0x3c, 0xfb, 0x9b, 0xa5, 0xd4, 0x79, 0x88, 0x72, 0x42, 0x32, 0x71, 0xd3, 0x90, 0xfe, 0x1f, 0x29, + 0x3d, 0x0b, 0x15, 0x37, 0xca, 0xbb, 0xa4, 0xc5, 0x8d, 0x6a, 0x8a, 0xb1, 0xc1, 0x9c, 0x1a, 0xa5, + 0xa7, 0x9c, 0xac, 0xab, 0x53, 0x48, 0xc0, 0x57, 0x8b, 0xec, 0x52, 0xf7, 0xe9, 0xd5, 0x39, 0xd1, + 0xb5, 0x53, 0x5d, 0x20, 0xdc, 0xdd, 0x25, 0xfb, 0x9b, 0xe9, 0x33, 0x18, 0x63, 0xf1, 0xf6, 0x71, + 0xbe, 0xf4, 0x79, 0x0b, 0x86, 0xa3, 0xc0, 0xf3, 0x5c, 0xbf, 0x45, 0x05, 0x8d, 0xd0, 0x96, 0x1f, + 0x3c, 0x16, 0x85, 0x25, 0x24, 0x0a, 0x33, 0x6d, 0xb1, 0xe6, 0x89, 0xcd, 0x0e, 0xd8, 0x7f, 0x62, + 0xc1, 0x78, 0x2f, 0x81, 0x88, 0x08, 0xbc, 0x53, 0xae, 0x76, 0x15, 0x5d, 0xb1, 0xe4, 0xcf, 0x12, + 0x8f, 0x28, 0xc7, 0x73, 0xb5, 0xfe, 0xa4, 0x78, 0xcd, 0x77, 0x2e, 0xf7, 0x46, 0xc5, 0x7b, 0xd1, + 0x41, 0xaf, 0xc0, 0x49, 0xe3, 0xbd, 0x62, 0x35, 0x30, 0xb5, 0xfa, 0x24, 0xb5, 0x40, 0xa6, 0x33, + 0xb0, 0x7b, 0xbb, 0x13, 0x8f, 0x64, 0xdb, 0x84, 0xc4, 0xee, 0xa2, 0x63, 0xff, 0x72, 0x29, 0xfb, + 0xb5, 0x94, 0xb2, 0x7d, 0xcb, 0xea, 0xda, 0xce, 0xbf, 0xff, 0x38, 0x14, 0x1c, 0xdb, 0xf8, 0xab, + 0x00, 0x8e, 0xde, 0x38, 0x0f, 0xf0, 0x84, 0xd8, 0xfe, 0x37, 0x03, 0xb0, 0x47, 0xcf, 0xfa, 0xb0, + 0x9e, 0x0f, 0x7c, 0xac, 0xf8, 0x59, 0x4b, 0x1d, 0x39, 0x95, 0xd9, 0x22, 0x6f, 0x1e, 0xd7, 0xd8, + 0xf3, 0x0d, 0x4c, 0xcc, 0xa3, 0x14, 0x94, 0x1b, 0x3b, 0x7d, 0xb8, 0x85, 0xbe, 0x6a, 0xa5, 0x0f, + 0xcd, 0x78, 0xd8, 0x99, 0x7b, 0x6c, 0x7d, 0x32, 0x4e, 0xe2, 0x78, 0xc7, 0xf4, 0xf9, 0x4d, 0xaf, + 0x33, 0xba, 0x49, 0x80, 0x75, 0xd7, 0x77, 0x3c, 0xf7, 0x0d, 0xba, 0x3d, 0xa9, 0x30, 0x0d, 0xcb, + 0x4c, 0x96, 0xcb, 0xaa, 0x15, 0x1b, 0x18, 0xe7, 0xff, 0x12, 0x0c, 0x1b, 0x6f, 0x9e, 0x13, 0x5c, + 0x71, 0xc6, 0x0c, 0xae, 0xa8, 0x19, 0x31, 0x11, 0xe7, 0xdf, 0x0b, 0x27, 0xb3, 0x1d, 0x3c, 0xc8, + 0xf3, 0xf6, 0xff, 0x1a, 0xca, 0x9e, 0x62, 0xad, 0x92, 0xa8, 0x4d, 0xbb, 0xf6, 0xb6, 0x67, 0xe9, + 0x6d, 0xcf, 0xd2, 0xdb, 0x9e, 0x25, 0xf3, 0x70, 0x40, 0x78, 0x4d, 0x86, 0xee, 0x93, 0xd7, 0x24, + 0xe5, 0x07, 0xaa, 0x16, 0xee, 0x07, 0xb2, 0xef, 0x56, 0x20, 0x65, 0x47, 0xf1, 0xf1, 0xfe, 0x01, + 0x18, 0x8a, 0x48, 0x18, 0xdc, 0xc0, 0x0b, 0x42, 0x87, 0xe8, 0x00, 0x7a, 0xde, 0x8c, 0x25, 0x9c, + 0xea, 0x9a, 0xd0, 0x49, 0x36, 0x84, 0x12, 0x51, 0xba, 0x66, 0xd9, 0x49, 0x36, 0x30, 0x83, 0xa0, + 0xf7, 0xc2, 0x58, 0xe2, 0x44, 0x2d, 0x6a, 0x6f, 0x6f, 0xb1, 0xcf, 0x2a, 0xce, 0x3a, 0x1f, 0x11, + 0xb8, 0x63, 0xab, 0x29, 0x28, 0xce, 0x60, 0xa3, 0xd7, 0x61, 0x60, 0x83, 0x78, 0x6d, 0x31, 0xe4, + 0x2b, 0xc5, 0xc9, 0x78, 0xf6, 0xae, 0x57, 0x88, 0xd7, 0xe6, 0x12, 0x88, 0xfe, 0xc2, 0x8c, 0x15, + 0x9d, 0x6f, 0xb5, 0xcd, 0x4e, 0x9c, 0x04, 0x6d, 0xf7, 0x0d, 0xe9, 0xe2, 0x7b, 0x7f, 0xc1, 0x8c, + 0xaf, 0x49, 0xfa, 0xdc, 0x97, 0xa2, 0xfe, 0x62, 0xcd, 0x99, 0xf5, 0xa3, 0xe9, 0x46, 0xec, 0x53, + 0xed, 0x08, 0x4f, 0x5d, 0xd1, 0xfd, 0x98, 0x95, 0xf4, 0x79, 0x3f, 0xd4, 0x5f, 0xac, 0x39, 0xa3, + 0x1d, 0x35, 0xef, 0x87, 0x59, 0x1f, 0x6e, 0x14, 0xdc, 0x07, 0x3e, 0xe7, 0x73, 0xe7, 0xff, 0x93, + 0x50, 0x69, 0x6c, 0x38, 0x51, 0x32, 0x3e, 0xc2, 0x26, 0x8d, 0xf2, 0xe9, 0xcc, 0xd0, 0x46, 0xcc, + 0x61, 0xe8, 0x71, 0x28, 0x47, 0x64, 0x9d, 0xc5, 0x6d, 0x1a, 0x11, 0x3d, 0x98, 0xac, 0x63, 0xda, + 0x6e, 0xff, 0x62, 0x29, 0x6d, 0x2e, 0xa5, 0xdf, 0x9b, 0xcf, 0xf6, 0x46, 0x27, 0x8a, 0xa5, 0xdf, + 0xc7, 0x98, 0xed, 0xac, 0x19, 0x4b, 0x38, 0xfa, 0x84, 0x05, 0x43, 0xb7, 0xe3, 0xc0, 0xf7, 0x49, + 0x22, 0x54, 0xd3, 0xcd, 0x82, 0x87, 0xe2, 0x2a, 0xa7, 0xae, 0xfb, 0x20, 0x1a, 0xb0, 0xe4, 0x4b, + 0xbb, 0x4b, 0xb6, 0x1b, 0x5e, 0xa7, 0xd9, 0x15, 0xa4, 0x71, 0x89, 0x37, 0x63, 0x09, 0xa7, 0xa8, + 0xae, 0xcf, 0x51, 0x07, 0xd2, 0xa8, 0xf3, 0xbe, 0x40, 0x15, 0x70, 0xfb, 0xaf, 0x0f, 0xc2, 0xd9, + 0xdc, 0xc5, 0x41, 0x0d, 0x19, 0x66, 0x2a, 0x5c, 0x76, 0x3d, 0x22, 0xc3, 0x93, 0x98, 0x21, 0x73, + 0x53, 0xb5, 0x62, 0x03, 0x03, 0xfd, 0x14, 0x40, 0xe8, 0x44, 0x4e, 0x9b, 0x28, 0xbf, 0xec, 0x91, + 0xed, 0x05, 0xda, 0x8f, 0x65, 0x49, 0x53, 0xef, 0x4d, 0x55, 0x53, 0x8c, 0x0d, 0x96, 0xe8, 0x05, + 0x18, 0x8e, 0x88, 0x47, 0x9c, 0x98, 0x85, 0xfd, 0x66, 0x73, 0x18, 0xb0, 0x06, 0x61, 0x13, 0x0f, + 0x3d, 0xa5, 0x22, 0xb9, 0x32, 0x11, 0x2d, 0xe9, 0x68, 0x2e, 0xf4, 0xa6, 0x05, 0x63, 0xeb, 0xae, + 0x47, 0x34, 0x77, 0x91, 0x71, 0xb0, 0x74, 0xf4, 0x97, 0xbc, 0x6c, 0xd2, 0xd5, 0x12, 0x32, 0xd5, + 0x1c, 0xe3, 0x0c, 0x7b, 0xfa, 0x99, 0xb7, 0x48, 0xc4, 0x44, 0xeb, 0x60, 0xfa, 0x33, 0xdf, 0xe4, + 0xcd, 0x58, 0xc2, 0xd1, 0x34, 0x9c, 0x08, 0x9d, 0x38, 0x9e, 0x89, 0x48, 0x93, 0xf8, 0x89, 0xeb, + 0x78, 0x3c, 0x1f, 0xa0, 0xaa, 0xe3, 0x81, 0x97, 0xd3, 0x60, 0x9c, 0xc5, 0x47, 0x1f, 0x80, 0x47, + 0xb9, 0xe3, 0x63, 0xd1, 0x8d, 0x63, 0xd7, 0x6f, 0xe9, 0x69, 0x20, 0xfc, 0x3f, 0x13, 0x82, 0xd4, + 0xa3, 0xf3, 0xf9, 0x68, 0xb8, 0xd7, 0xf3, 0xe8, 0x19, 0xa8, 0xc6, 0x9b, 0x6e, 0x38, 0x13, 0x35, + 0x63, 0x76, 0xe8, 0x51, 0xd5, 0xde, 0xc6, 0x15, 0xd1, 0x8e, 0x15, 0x06, 0x6a, 0xc0, 0x08, 0xff, + 0x24, 0x3c, 0x14, 0x4d, 0xc8, 0xc7, 0x67, 0x7b, 0xaa, 0x47, 0x91, 0xb2, 0x36, 0x89, 0x9d, 0x3b, + 0x97, 0xe4, 0x11, 0x0c, 0x3f, 0x31, 0xb8, 0x69, 0x90, 0xc1, 0x29, 0xa2, 0xf6, 0x2f, 0x94, 0xd2, + 0x3b, 0x6e, 0x73, 0x91, 0xa2, 0x98, 0x2e, 0xc5, 0xe4, 0xa6, 0x13, 0x49, 0x6f, 0xcc, 0x11, 0xd3, + 0x16, 0x04, 0xdd, 0x9b, 0x4e, 0x64, 0x2e, 0x6a, 0xc6, 0x00, 0x4b, 0x4e, 0xe8, 0x36, 0x0c, 0x24, + 0x9e, 0x53, 0x50, 0x9e, 0x93, 0xc1, 0x51, 0x3b, 0x40, 0x16, 0xa6, 0x63, 0xcc, 0x78, 0xa0, 0xc7, + 0xa8, 0xd5, 0xbf, 0x26, 0x8f, 0x48, 0x84, 0xa1, 0xbe, 0x16, 0x63, 0xd6, 0x6a, 0xff, 0x0a, 0xe4, + 0xc8, 0x55, 0xa5, 0xc8, 0xd0, 0x45, 0x00, 0xba, 0x81, 0x5c, 0x8e, 0xc8, 0xba, 0xbb, 0x2d, 0x0c, + 0x09, 0xb5, 0x76, 0xaf, 0x2b, 0x08, 0x36, 0xb0, 0xe4, 0x33, 0x2b, 0x9d, 0x75, 0xfa, 0x4c, 0xa9, + 0xfb, 0x19, 0x0e, 0xc1, 0x06, 0x16, 0x7a, 0x1e, 0x06, 0xdd, 0xb6, 0xd3, 0x52, 0x21, 0x98, 0x8f, + 0xd1, 0x45, 0x3b, 0xcf, 0x5a, 0xee, 0xed, 0x4e, 0x8c, 0xa9, 0x0e, 0xb1, 0x26, 0x2c, 0x70, 0xd1, + 0x2f, 0x5b, 0x30, 0xd2, 0x08, 0xda, 0xed, 0xc0, 0xe7, 0xdb, 0x2e, 0xb1, 0x87, 0xbc, 0x7d, 0x5c, + 0x6a, 0x7e, 0x72, 0xc6, 0x60, 0xc6, 0x37, 0x91, 0x2a, 0x21, 0xcb, 0x04, 0xe1, 0x54, 0xaf, 0xcc, + 0xb5, 0x5d, 0xd9, 0x67, 0x6d, 0xff, 0xba, 0x05, 0xa7, 0xf8, 0xb3, 0xc6, 0x6e, 0x50, 0xe4, 0x1e, + 0x05, 0xc7, 0xfc, 0x5a, 0x5d, 0x1b, 0x64, 0xe5, 0xa5, 0xeb, 0x82, 0xe3, 0xee, 0x4e, 0xa2, 0x39, + 0x38, 0xb5, 0x1e, 0x44, 0x0d, 0x62, 0x0e, 0x84, 0x10, 0x4c, 0x8a, 0xd0, 0xe5, 0x2c, 0x02, 0xee, + 0x7e, 0x06, 0xdd, 0x84, 0x47, 0x8c, 0x46, 0x73, 0x1c, 0xb8, 0x6c, 0x7a, 0x42, 0x50, 0x7b, 0xe4, + 0x72, 0x2e, 0x16, 0xee, 0xf1, 0x74, 0xda, 0x61, 0x52, 0xeb, 0xc3, 0x61, 0xf2, 0x1a, 0x9c, 0x6b, + 0x74, 0x8f, 0xcc, 0x56, 0xdc, 0x59, 0x8b, 0xb9, 0xa4, 0xaa, 0xd6, 0xbf, 0x4f, 0x10, 0x38, 0x37, + 0xd3, 0x0b, 0x11, 0xf7, 0xa6, 0x81, 0x3e, 0x02, 0xd5, 0x88, 0xb0, 0xaf, 0x12, 0x8b, 0x44, 0x9c, + 0x23, 0xee, 0x92, 0xb5, 0x05, 0xca, 0xc9, 0x6a, 0xd9, 0x2b, 0x1a, 0x62, 0xac, 0x38, 0xa2, 0x3b, + 0x30, 0x14, 0x3a, 0x49, 0x63, 0x43, 0xa4, 0xdf, 0x1c, 0x39, 0xfe, 0x45, 0x31, 0x67, 0x3e, 0x70, + 0x3d, 0xc9, 0x97, 0x39, 0x13, 0x2c, 0xb9, 0x51, 0x6b, 0xa4, 0x11, 0xb4, 0xc3, 0xc0, 0x27, 0x7e, + 0x12, 0x8f, 0x8f, 0x6a, 0x6b, 0x64, 0x46, 0xb5, 0x62, 0x03, 0xe3, 0xfc, 0xfb, 0xe0, 0x54, 0xd7, + 0xc2, 0x3b, 0x90, 0x73, 0x65, 0x16, 0x1e, 0xc9, 0x9f, 0xe2, 0x07, 0x72, 0xb1, 0xfc, 0xe3, 0x4c, + 0x90, 0xab, 0x61, 0xf6, 0xf6, 0xe1, 0xae, 0x73, 0xa0, 0x4c, 0xfc, 0x2d, 0x21, 0xf1, 0x2f, 0x1f, + 0x6d, 0xa4, 0x2f, 0xf9, 0x5b, 0x7c, 0x85, 0x32, 0x9f, 0xc4, 0x25, 0x7f, 0x0b, 0x53, 0xda, 0xe8, + 0x8b, 0x56, 0xca, 0x6c, 0xe3, 0x4e, 0xbe, 0x0f, 0x1d, 0x8b, 0x9d, 0xdf, 0xb7, 0x25, 0x67, 0xff, + 0xdb, 0x12, 0x5c, 0xd8, 0x8f, 0x48, 0x1f, 0xc3, 0xf7, 0x24, 0x0c, 0xc6, 0xec, 0xd8, 0x5a, 0x88, + 0xd0, 0x61, 0x3a, 0xb3, 0xf8, 0x41, 0xf6, 0x6b, 0x58, 0x80, 0x90, 0x07, 0xe5, 0xb6, 0x13, 0x0a, + 0xdf, 0xcf, 0xfc, 0x51, 0xd3, 0x5e, 0xe8, 0x7f, 0xc7, 0x5b, 0x74, 0x42, 0xee, 0x51, 0x30, 0x1a, + 0x30, 0x65, 0x83, 0x12, 0xa8, 0x38, 0x51, 0xe4, 0xc8, 0x33, 0xd2, 0x6b, 0xc5, 0xf0, 0x9b, 0xa6, + 0x24, 0xf9, 0x11, 0x53, 0xaa, 0x09, 0x73, 0x66, 0xf6, 0x67, 0x87, 0x52, 0xa9, 0x1f, 0xec, 0xe0, + 0x3b, 0x86, 0x41, 0xe1, 0xf2, 0xb1, 0x8a, 0xce, 0x36, 0xe2, 0xb9, 0x7b, 0x6c, 0x57, 0x27, 0x32, + 0xa0, 0x05, 0x2b, 0xf4, 0x19, 0x8b, 0xe5, 0x19, 0xcb, 0x74, 0x18, 0xb1, 0x97, 0x3a, 0x9e, 0xb4, + 0x67, 0x33, 0x7b, 0x59, 0x36, 0x62, 0x93, 0x3b, 0xd5, 0xb1, 0x21, 0xcf, 0x98, 0xcb, 0xee, 0xa8, + 0x64, 0x26, 0xb2, 0x84, 0xa3, 0xed, 0x9c, 0x03, 0xee, 0x02, 0x72, 0x55, 0xfb, 0x38, 0xd2, 0xfe, + 0xaa, 0x05, 0xa7, 0xdc, 0xec, 0x49, 0xa5, 0xd8, 0x79, 0x1c, 0x31, 0x84, 0xa2, 0xf7, 0x41, 0xa8, + 0x52, 0xbe, 0x5d, 0x20, 0xdc, 0xdd, 0x19, 0xd4, 0x84, 0x01, 0xd7, 0x5f, 0x0f, 0x84, 0xc9, 0x51, + 0x3f, 0x5a, 0xa7, 0xe6, 0xfd, 0xf5, 0x40, 0xaf, 0x66, 0xfa, 0x0f, 0x33, 0xea, 0x68, 0x01, 0xce, + 0x44, 0xc2, 0x37, 0x74, 0xc5, 0x8d, 0xe9, 0x0e, 0x7e, 0xc1, 0x6d, 0xbb, 0x09, 0x33, 0x17, 0xca, + 0xf5, 0xf1, 0xbb, 0xbb, 0x13, 0x67, 0x70, 0x0e, 0x1c, 0xe7, 0x3e, 0x85, 0xde, 0x80, 0x21, 0x99, + 0x18, 0x5d, 0x2d, 0x62, 0x17, 0xd7, 0x3d, 0xff, 0xd5, 0x64, 0x5a, 0x11, 0x39, 0xd0, 0x92, 0xa1, + 0xfd, 0xe6, 0x30, 0x74, 0x1f, 0x62, 0xa2, 0x8f, 0x42, 0x2d, 0x52, 0xc9, 0xda, 0x56, 0x11, 0xca, + 0x55, 0x7e, 0x5f, 0x71, 0x80, 0xaa, 0x0c, 0x17, 0x9d, 0x96, 0xad, 0x39, 0xd2, 0xed, 0x45, 0xac, + 0xcf, 0x3a, 0x0b, 0x98, 0xdb, 0x82, 0xab, 0x3e, 0xc7, 0xda, 0xf1, 0x1b, 0x98, 0xf1, 0x40, 0x11, + 0x0c, 0x6e, 0x10, 0xc7, 0x4b, 0x36, 0x8a, 0x71, 0xb9, 0x5f, 0x61, 0xb4, 0xb2, 0x29, 0x3b, 0xbc, + 0x15, 0x0b, 0x4e, 0x68, 0x1b, 0x86, 0x36, 0xf8, 0x04, 0x10, 0x16, 0xff, 0xe2, 0x51, 0x07, 0x37, + 0x35, 0xab, 0xf4, 0xe7, 0x16, 0x0d, 0x58, 0xb2, 0x63, 0xd1, 0x31, 0xc6, 0xf9, 0x3d, 0x5f, 0xba, + 0xc5, 0x65, 0x2b, 0xf5, 0x7f, 0x78, 0xff, 0x61, 0x18, 0x89, 0x48, 0x23, 0xf0, 0x1b, 0xae, 0x47, + 0x9a, 0xd3, 0xd2, 0x9d, 0x7e, 0x90, 0x1c, 0x17, 0xb6, 0x6b, 0xc6, 0x06, 0x0d, 0x9c, 0xa2, 0x88, + 0x3e, 0x6d, 0xc1, 0x98, 0xca, 0xf0, 0xa4, 0x1f, 0x84, 0x08, 0xf7, 0xed, 0x42, 0x41, 0xf9, 0xa4, + 0x8c, 0x66, 0x1d, 0xdd, 0xdd, 0x9d, 0x18, 0x4b, 0xb7, 0xe1, 0x0c, 0x5f, 0xf4, 0x0a, 0x40, 0xb0, + 0xc6, 0x43, 0x60, 0xa6, 0x13, 0xe1, 0xcb, 0x3d, 0xc8, 0xab, 0x8e, 0xf1, 0x64, 0x37, 0x49, 0x01, + 0x1b, 0xd4, 0xd0, 0x35, 0x00, 0xbe, 0x6c, 0x56, 0x77, 0x42, 0xb9, 0x2d, 0x90, 0x49, 0x4a, 0xb0, + 0xa2, 0x20, 0xf7, 0x76, 0x27, 0xba, 0x7d, 0x6b, 0x2c, 0xcc, 0xc0, 0x78, 0x1c, 0xfd, 0x24, 0x0c, + 0xc5, 0x9d, 0x76, 0xdb, 0x51, 0x9e, 0xde, 0x02, 0xd3, 0xe7, 0x38, 0x5d, 0x43, 0x14, 0xf1, 0x06, + 0x2c, 0x39, 0xa2, 0xdb, 0x54, 0xa8, 0xc6, 0xc2, 0xe9, 0xc7, 0x56, 0x11, 0xb7, 0x09, 0x86, 0xd9, + 0x3b, 0xbd, 0x47, 0x46, 0xf4, 0xe0, 0x1c, 0x9c, 0x7b, 0xbb, 0x13, 0x8f, 0xa4, 0xdb, 0x17, 0x02, + 0x91, 0xd0, 0x96, 0x4b, 0x13, 0x5d, 0x95, 0x75, 0x52, 0xe8, 0x6b, 0xcb, 0xf4, 0xfd, 0xa7, 0x75, + 0x9d, 0x14, 0xd6, 0xdc, 0x7b, 0xcc, 0xcc, 0x87, 0xd1, 0x22, 0x9c, 0x6e, 0x04, 0x7e, 0x12, 0x05, + 0x9e, 0xc7, 0x8b, 0xff, 0xf0, 0x1d, 0x1a, 0xf7, 0x04, 0xbf, 0x53, 0x74, 0xfb, 0xf4, 0x4c, 0x37, + 0x0a, 0xce, 0x7b, 0xce, 0xf6, 0xd3, 0xb1, 0x81, 0x62, 0x70, 0x9e, 0x87, 0x11, 0xb2, 0x9d, 0x90, + 0xc8, 0x77, 0xbc, 0x1b, 0x78, 0x41, 0xfa, 0x40, 0xd9, 0x1a, 0xb8, 0x64, 0xb4, 0xe3, 0x14, 0x16, + 0xb2, 0x95, 0x5b, 0xc2, 0x48, 0xd2, 0xe4, 0x6e, 0x09, 0xe9, 0x84, 0xb0, 0xff, 0x77, 0x29, 0x65, + 0x90, 0xad, 0x46, 0x84, 0xa0, 0x00, 0x2a, 0x7e, 0xd0, 0x54, 0xb2, 0xff, 0x6a, 0x31, 0xb2, 0xff, + 0x7a, 0xd0, 0x34, 0x8a, 0xa9, 0xd0, 0x7f, 0x31, 0xe6, 0x7c, 0x58, 0xb5, 0x09, 0x59, 0x96, 0x83, + 0x01, 0xc4, 0x46, 0xa3, 0x48, 0xce, 0xaa, 0xda, 0xc4, 0x92, 0xc9, 0x08, 0xa7, 0xf9, 0xa2, 0x4d, + 0xa8, 0x6c, 0x04, 0x71, 0x22, 0xb7, 0x1f, 0x47, 0xdc, 0xe9, 0x5c, 0x09, 0xe2, 0x84, 0x59, 0x11, + 0xea, 0xb5, 0x69, 0x4b, 0x8c, 0x39, 0x0f, 0xfb, 0xbf, 0x58, 0x29, 0x8f, 0xf7, 0x2d, 0x16, 0x27, + 0xbb, 0x45, 0x7c, 0xba, 0xac, 0xcd, 0xc0, 0xa0, 0x1f, 0xce, 0x64, 0x1d, 0xbe, 0xab, 0x57, 0x69, + 0xab, 0x3b, 0x94, 0xc2, 0x24, 0x23, 0x61, 0xc4, 0x10, 0x7d, 0xdc, 0x4a, 0xe7, 0x7f, 0x96, 0x8a, + 0xd8, 0x60, 0x98, 0x39, 0xd0, 0xfb, 0xa6, 0x92, 0xda, 0x5f, 0xb4, 0x60, 0xa8, 0xee, 0x34, 0x36, + 0x83, 0xf5, 0x75, 0xf4, 0x0c, 0x54, 0x9b, 0x9d, 0xc8, 0x4c, 0x45, 0x55, 0xdb, 0xfc, 0x59, 0xd1, + 0x8e, 0x15, 0x06, 0x9d, 0xc3, 0xeb, 0x4e, 0x43, 0x66, 0x42, 0x97, 0xf9, 0x1c, 0xbe, 0xcc, 0x5a, + 0xb0, 0x80, 0xa0, 0x17, 0x60, 0xb8, 0xed, 0x6c, 0xcb, 0x87, 0xb3, 0xee, 0xf6, 0x45, 0x0d, 0xc2, + 0x26, 0x9e, 0xfd, 0x2f, 0x2d, 0x18, 0xaf, 0x3b, 0xb1, 0xdb, 0x98, 0xee, 0x24, 0x1b, 0x75, 0x37, + 0x59, 0xeb, 0x34, 0x36, 0x49, 0xc2, 0xd3, 0xdf, 0x69, 0x2f, 0x3b, 0x31, 0x5d, 0x4a, 0x6a, 0x5f, + 0xa7, 0x7a, 0x79, 0x43, 0xb4, 0x63, 0x85, 0x81, 0xde, 0x80, 0xe1, 0xd0, 0x89, 0xe3, 0x3b, 0x41, + 0xd4, 0xc4, 0x64, 0xbd, 0x98, 0xe2, 0x13, 0x2b, 0xa4, 0x11, 0x91, 0x04, 0x93, 0x75, 0x71, 0x24, + 0xac, 0xe9, 0x63, 0x93, 0x99, 0xfd, 0x79, 0x0b, 0xce, 0xd5, 0x89, 0x13, 0x91, 0x88, 0xd5, 0xaa, + 0x50, 0x2f, 0x32, 0xe3, 0x05, 0x9d, 0x26, 0x7a, 0x1d, 0xaa, 0x09, 0x6d, 0xa6, 0xdd, 0xb2, 0x8a, + 0xed, 0x16, 0x3b, 0xd1, 0x5d, 0x15, 0xc4, 0xb1, 0x62, 0x63, 0xff, 0x0d, 0x0b, 0x46, 0xd8, 0xe1, + 0xd8, 0x2c, 0x49, 0x1c, 0xd7, 0xeb, 0x2a, 0xe9, 0x64, 0xf5, 0x59, 0xd2, 0xe9, 0x02, 0x0c, 0x6c, + 0x04, 0x6d, 0x92, 0x3d, 0xd8, 0xbd, 0x12, 0xd0, 0x6d, 0x35, 0x85, 0xa0, 0xe7, 0xe8, 0x87, 0x77, + 0xfd, 0xc4, 0xa1, 0x4b, 0x40, 0x3a, 0x5f, 0x4f, 0xf0, 0x8f, 0xae, 0x9a, 0xb1, 0x89, 0x63, 0xff, + 0x56, 0x0d, 0x86, 0xc4, 0xe9, 0x7f, 0xdf, 0x25, 0x10, 0xe4, 0xfe, 0xbe, 0xd4, 0x73, 0x7f, 0x1f, + 0xc3, 0x60, 0x83, 0x15, 0x8c, 0x13, 0x66, 0xe4, 0xb5, 0x42, 0xc2, 0x45, 0x78, 0x0d, 0x3a, 0xdd, + 0x2d, 0xfe, 0x1f, 0x0b, 0x56, 0xe8, 0x0b, 0x16, 0x9c, 0x68, 0x04, 0xbe, 0x4f, 0x1a, 0xda, 0xc6, + 0x19, 0x28, 0x22, 0x2a, 0x60, 0x26, 0x4d, 0x54, 0x9f, 0xcc, 0x64, 0x00, 0x38, 0xcb, 0x1e, 0xbd, + 0x04, 0xa3, 0x7c, 0xcc, 0x6e, 0xa6, 0x3c, 0xc6, 0xba, 0xd2, 0x8f, 0x09, 0xc4, 0x69, 0x5c, 0x34, + 0xc9, 0x3d, 0xef, 0xa2, 0xa6, 0xce, 0xa0, 0x76, 0xac, 0x19, 0xd5, 0x74, 0x0c, 0x0c, 0x14, 0x01, + 0x8a, 0xc8, 0x7a, 0x44, 0xe2, 0x0d, 0x11, 0x1d, 0xc1, 0xec, 0xab, 0xa1, 0xc3, 0xa5, 0x4b, 0xe3, + 0x2e, 0x4a, 0x38, 0x87, 0x3a, 0xda, 0x14, 0x1b, 0xcc, 0x6a, 0x11, 0x32, 0x54, 0x7c, 0xe6, 0x9e, + 0xfb, 0xcc, 0x09, 0xa8, 0xc4, 0x1b, 0x4e, 0xd4, 0x64, 0x76, 0x5d, 0x99, 0xa7, 0xe8, 0xac, 0xd0, + 0x06, 0xcc, 0xdb, 0xd1, 0x2c, 0x9c, 0xcc, 0xd4, 0x29, 0x8a, 0x85, 0x67, 0x57, 0xa5, 0x63, 0x64, + 0x2a, 0x1c, 0xc5, 0xb8, 0xeb, 0x09, 0xd3, 0xf9, 0x30, 0xbc, 0x8f, 0xf3, 0x61, 0x47, 0xc5, 0xe0, + 0x71, 0x9f, 0xeb, 0xcb, 0x85, 0x0c, 0x40, 0x5f, 0x01, 0x77, 0x9f, 0xcb, 0x04, 0xdc, 0x8d, 0xb2, + 0x0e, 0xdc, 0x2c, 0xa6, 0x03, 0x07, 0x8f, 0xae, 0x7b, 0x90, 0xd1, 0x72, 0x7f, 0x6e, 0x81, 0xfc, + 0xae, 0x33, 0x4e, 0x63, 0x83, 0xd0, 0x29, 0x83, 0xde, 0x0b, 0x63, 0x6a, 0x0b, 0x3d, 0x13, 0x74, + 0x7c, 0x1e, 0x28, 0x57, 0xd6, 0x47, 0xb8, 0x38, 0x05, 0xc5, 0x19, 0x6c, 0x34, 0x05, 0x35, 0x3a, + 0x4e, 0xfc, 0x51, 0xae, 0x6b, 0xd5, 0x36, 0x7d, 0x7a, 0x79, 0x5e, 0x3c, 0xa5, 0x71, 0x50, 0x00, + 0xa7, 0x3c, 0x27, 0x4e, 0x58, 0x0f, 0xe8, 0x8e, 0xfa, 0x90, 0xc5, 0x0a, 0x58, 0xcc, 0xff, 0x42, + 0x96, 0x10, 0xee, 0xa6, 0x6d, 0x7f, 0x6b, 0x00, 0x46, 0x53, 0x92, 0xf1, 0x80, 0x4a, 0xfa, 0x19, + 0xa8, 0x4a, 0xbd, 0x99, 0x2d, 0xab, 0xa2, 0x94, 0xab, 0xc2, 0xa0, 0x4a, 0x6b, 0x4d, 0x6b, 0xd5, + 0xac, 0x51, 0x61, 0x28, 0x5c, 0x6c, 0xe2, 0x31, 0xa1, 0x9c, 0x78, 0xf1, 0x8c, 0xe7, 0x12, 0x3f, + 0xe1, 0xdd, 0x2c, 0x46, 0x28, 0xaf, 0x2e, 0xac, 0x98, 0x44, 0xb5, 0x50, 0xce, 0x00, 0x70, 0x96, + 0x3d, 0xfa, 0x94, 0x05, 0xa3, 0xce, 0x9d, 0x58, 0x57, 0x35, 0x15, 0xa1, 0x75, 0x47, 0x54, 0x52, + 0xa9, 0x42, 0xa9, 0xdc, 0xe5, 0x9b, 0x6a, 0xc2, 0x69, 0xa6, 0xe8, 0x2d, 0x0b, 0x10, 0xd9, 0x26, + 0x0d, 0x19, 0xfc, 0x27, 0xfa, 0x32, 0x58, 0xc4, 0x4e, 0xf3, 0x52, 0x17, 0x5d, 0x2e, 0xd5, 0xbb, + 0xdb, 0x71, 0x4e, 0x1f, 0xec, 0x7f, 0x56, 0x56, 0x0b, 0x4a, 0xc7, 0x9b, 0x3a, 0x46, 0xdc, 0x9b, + 0x75, 0xf8, 0xb8, 0x37, 0x1d, 0x3f, 0xd0, 0x9d, 0x03, 0x99, 0x4a, 0x99, 0x2a, 0x3d, 0xa0, 0x94, + 0xa9, 0x9f, 0xb6, 0x52, 0x05, 0x84, 0x86, 0x2f, 0xbe, 0x52, 0x6c, 0xac, 0xeb, 0x24, 0x8f, 0x6d, + 0xc8, 0x48, 0xf7, 0x74, 0x48, 0x0b, 0x95, 0xa6, 0x06, 0xda, 0x81, 0xa4, 0xe1, 0xbf, 0x2f, 0xc3, + 0xb0, 0xa1, 0x49, 0x73, 0xcd, 0x22, 0xeb, 0x21, 0x33, 0x8b, 0x4a, 0x07, 0x30, 0x8b, 0x7e, 0x0a, + 0x6a, 0x0d, 0x29, 0xe5, 0x8b, 0x29, 0xa1, 0x9b, 0xd5, 0x1d, 0x5a, 0xd0, 0xab, 0x26, 0xac, 0x79, + 0xa2, 0xb9, 0x54, 0xa2, 0x8d, 0xd0, 0x10, 0x03, 0x4c, 0x43, 0xe4, 0x65, 0xc2, 0x08, 0x4d, 0xd1, + 0xfd, 0x0c, 0xab, 0x33, 0x15, 0xba, 0xe2, 0xbd, 0x64, 0x44, 0x3a, 0xaf, 0x33, 0xb5, 0x3c, 0x2f, + 0x9b, 0xb1, 0x89, 0x63, 0x7f, 0xcb, 0x52, 0x1f, 0xf7, 0x3e, 0x54, 0x54, 0xb8, 0x9d, 0xae, 0xa8, + 0x70, 0xa9, 0x90, 0x61, 0xee, 0x51, 0x4a, 0xe1, 0x3a, 0x0c, 0xcd, 0x04, 0xed, 0xb6, 0xe3, 0x37, + 0xd1, 0xf7, 0xc3, 0x50, 0x83, 0xff, 0x14, 0x8e, 0x1d, 0x76, 0x3c, 0x28, 0xa0, 0x58, 0xc2, 0xd0, + 0x63, 0x30, 0xe0, 0x44, 0x2d, 0xe9, 0xcc, 0x61, 0xa1, 0x30, 0xd3, 0x51, 0x2b, 0xc6, 0xac, 0xd5, + 0xfe, 0x47, 0x03, 0xc0, 0x4e, 0xa0, 0x9d, 0x88, 0x34, 0x57, 0x03, 0x56, 0xc2, 0xef, 0x58, 0x0f, + 0xd5, 0xf4, 0x66, 0xe9, 0x61, 0x3e, 0x58, 0x33, 0x0e, 0x57, 0xca, 0xf7, 0xf9, 0x70, 0xa5, 0xc7, + 0x79, 0xd9, 0xc0, 0x43, 0x74, 0x5e, 0x66, 0x7f, 0xd6, 0x02, 0xa4, 0xc2, 0x16, 0xf4, 0x81, 0xf6, + 0x14, 0xd4, 0x54, 0x00, 0x83, 0x30, 0xac, 0xb4, 0x88, 0x90, 0x00, 0xac, 0x71, 0xfa, 0xd8, 0x21, + 0x3f, 0x29, 0xe5, 0x77, 0x39, 0x1d, 0x45, 0xcb, 0xa4, 0xbe, 0x10, 0xe7, 0xf6, 0x6f, 0x97, 0xe0, + 0x11, 0xae, 0x92, 0x17, 0x1d, 0xdf, 0x69, 0x91, 0x36, 0xed, 0x55, 0xbf, 0x21, 0x0a, 0x0d, 0xba, + 0x35, 0x73, 0x65, 0x54, 0xec, 0x51, 0xd7, 0x2e, 0x5f, 0x73, 0x7c, 0x95, 0xcd, 0xfb, 0x6e, 0x82, + 0x19, 0x71, 0x14, 0x43, 0x55, 0xd6, 0x8c, 0x17, 0xb2, 0xb8, 0x20, 0x46, 0x4a, 0x2c, 0x09, 0xbd, + 0x49, 0xb0, 0x62, 0x44, 0x0d, 0x57, 0x2f, 0x68, 0x6c, 0x62, 0x12, 0x06, 0x4c, 0xee, 0x1a, 0x41, + 0x89, 0x0b, 0xa2, 0x1d, 0x2b, 0x0c, 0xfb, 0xb7, 0x2d, 0xc8, 0x6a, 0x24, 0xa3, 0x56, 0x9a, 0xb5, + 0x67, 0xad, 0xb4, 0x03, 0x14, 0x2b, 0xfb, 0x09, 0x18, 0x76, 0x12, 0x6a, 0x44, 0xf0, 0x6d, 0x77, + 0xf9, 0x70, 0xc7, 0x1a, 0x8b, 0x41, 0xd3, 0x5d, 0x77, 0xd9, 0x76, 0xdb, 0x24, 0x67, 0xff, 0x8f, + 0x01, 0x38, 0xd5, 0x95, 0xbb, 0x81, 0x5e, 0x84, 0x91, 0x86, 0x98, 0x1e, 0xa1, 0x74, 0x68, 0xd5, + 0xcc, 0x20, 0x36, 0x0d, 0xc3, 0x29, 0xcc, 0x3e, 0x26, 0xe8, 0x3c, 0x9c, 0x8e, 0xe8, 0x46, 0xbf, + 0x43, 0xa6, 0xd7, 0x13, 0x12, 0xad, 0x90, 0x46, 0xe0, 0x37, 0x79, 0x45, 0xbf, 0x72, 0xfd, 0xd1, + 0xbb, 0xbb, 0x13, 0xa7, 0x71, 0x37, 0x18, 0xe7, 0x3d, 0x83, 0x42, 0x18, 0xf5, 0x4c, 0x1b, 0x50, + 0x6c, 0x00, 0x0e, 0x65, 0x3e, 0x2a, 0x1b, 0x21, 0xd5, 0x8c, 0xd3, 0x0c, 0xd2, 0x86, 0x64, 0xe5, + 0x01, 0x19, 0x92, 0x9f, 0xd4, 0x86, 0x24, 0x3f, 0x7f, 0xff, 0x60, 0xc1, 0xb9, 0x3b, 0xc7, 0x6d, + 0x49, 0xbe, 0x0c, 0x55, 0x19, 0x9b, 0xd4, 0x57, 0x4c, 0x8f, 0x49, 0xa7, 0x87, 0x44, 0xbb, 0x57, + 0x82, 0x9c, 0x4d, 0x08, 0x5d, 0x67, 0x5a, 0xe3, 0xa7, 0xd6, 0xd9, 0xc1, 0xb4, 0x3e, 0xda, 0xe6, + 0x71, 0x59, 0x5c, 0xb7, 0x7d, 0xa0, 0xe8, 0x4d, 0x94, 0x0e, 0xd5, 0x52, 0x29, 0x0d, 0x2a, 0x5c, + 0xeb, 0x22, 0x80, 0x36, 0xd4, 0x44, 0xc0, 0xba, 0x3a, 0xf6, 0xd5, 0xf6, 0x1c, 0x36, 0xb0, 0xe8, + 0x9e, 0xda, 0xf5, 0xe3, 0xc4, 0xf1, 0xbc, 0x2b, 0xae, 0x9f, 0x08, 0xe7, 0xa0, 0x52, 0xe2, 0xf3, + 0x1a, 0x84, 0x4d, 0xbc, 0xf3, 0xef, 0x31, 0xbe, 0xcb, 0x41, 0xbe, 0xe7, 0x06, 0x9c, 0x9b, 0x73, + 0x13, 0x95, 0x66, 0xa1, 0xe6, 0x11, 0xb5, 0xc3, 0x54, 0xda, 0x90, 0xd5, 0x33, 0x6d, 0xc8, 0x48, + 0x73, 0x28, 0xa5, 0xb3, 0x32, 0xb2, 0x69, 0x0e, 0xf6, 0x8b, 0x70, 0x66, 0xce, 0x4d, 0x2e, 0xbb, + 0x1e, 0x39, 0x20, 0x13, 0xfb, 0x37, 0x07, 0x61, 0xc4, 0x4c, 0xd4, 0x3b, 0x48, 0xe6, 0xd3, 0xe7, + 0xa9, 0xa9, 0x25, 0xde, 0xce, 0x55, 0x87, 0x66, 0xb7, 0x8e, 0x9c, 0x35, 0x98, 0x3f, 0x62, 0x86, + 0xb5, 0xa5, 0x79, 0x62, 0xb3, 0x03, 0xe8, 0x0e, 0x54, 0xd6, 0x59, 0x18, 0x7e, 0xb9, 0x88, 0xc8, + 0x82, 0xbc, 0x11, 0xd5, 0xcb, 0x8c, 0x07, 0xf2, 0x73, 0x7e, 0x54, 0x43, 0x46, 0xe9, 0xdc, 0x2e, + 0x23, 0x74, 0x54, 0x64, 0x75, 0x29, 0x8c, 0x5e, 0xa2, 0xbe, 0x72, 0x08, 0x51, 0x9f, 0x12, 0xbc, + 0x83, 0x0f, 0x48, 0xf0, 0xb2, 0x94, 0x8a, 0x64, 0x83, 0xd9, 0x6f, 0x22, 0xd6, 0x7d, 0x88, 0x0d, + 0x82, 0x91, 0x52, 0x91, 0x02, 0xe3, 0x2c, 0x3e, 0xfa, 0x98, 0x12, 0xdd, 0xd5, 0x22, 0xfc, 0xaa, + 0xe6, 0x8c, 0x3e, 0x6e, 0xa9, 0xfd, 0xd9, 0x12, 0x8c, 0xcd, 0xf9, 0x9d, 0xe5, 0xb9, 0xe5, 0xce, + 0x9a, 0xe7, 0x36, 0xae, 0x91, 0x1d, 0x2a, 0x9a, 0x37, 0xc9, 0xce, 0xfc, 0xac, 0x58, 0x41, 0x6a, + 0xce, 0x5c, 0xa3, 0x8d, 0x98, 0xc3, 0xa8, 0x30, 0x5a, 0x77, 0xfd, 0x16, 0x89, 0xc2, 0xc8, 0x15, + 0x2e, 0x4f, 0x43, 0x18, 0x5d, 0xd6, 0x20, 0x6c, 0xe2, 0x51, 0xda, 0xc1, 0x1d, 0x9f, 0x44, 0x59, + 0x43, 0x76, 0x89, 0x36, 0x62, 0x0e, 0xa3, 0x48, 0x49, 0xd4, 0x89, 0x13, 0x31, 0x19, 0x15, 0xd2, + 0x2a, 0x6d, 0xc4, 0x1c, 0x46, 0x57, 0x7a, 0xdc, 0x59, 0x63, 0x81, 0x1b, 0x99, 0xc0, 0xfa, 0x15, + 0xde, 0x8c, 0x25, 0x9c, 0xa2, 0x6e, 0x92, 0x9d, 0x59, 0xba, 0xeb, 0xcd, 0xe4, 0xd7, 0x5c, 0xe3, + 0xcd, 0x58, 0xc2, 0x59, 0x29, 0xc2, 0xf4, 0x70, 0x7c, 0xd7, 0x95, 0x22, 0x4c, 0x77, 0xbf, 0xc7, + 0xfe, 0xf9, 0x97, 0x2c, 0x18, 0x31, 0xc3, 0xad, 0x50, 0x2b, 0x63, 0xe3, 0x2e, 0x75, 0x55, 0xb2, + 0xfd, 0xb1, 0xbc, 0xab, 0xbd, 0x5a, 0x6e, 0x12, 0x84, 0xf1, 0xb3, 0xc4, 0x6f, 0xb9, 0x3e, 0x61, + 0xa7, 0xe8, 0x3c, 0x4c, 0x2b, 0x15, 0xcb, 0x35, 0x13, 0x34, 0xc9, 0x21, 0x8c, 0x64, 0xfb, 0x16, + 0x9c, 0xea, 0x4a, 0xaa, 0xea, 0xc3, 0xb4, 0xd8, 0x37, 0xa5, 0xd5, 0xc6, 0x30, 0x4c, 0x09, 0xcb, + 0x72, 0x38, 0x33, 0x70, 0x8a, 0x2f, 0x24, 0xca, 0x69, 0xa5, 0xb1, 0x41, 0xda, 0x2a, 0x51, 0x8e, + 0xf9, 0xd7, 0x6f, 0x66, 0x81, 0xb8, 0x1b, 0xdf, 0xfe, 0x9c, 0x05, 0xa3, 0xa9, 0x3c, 0xb7, 0x82, + 0x8c, 0x20, 0xb6, 0xd2, 0x02, 0x16, 0xfd, 0xc7, 0x42, 0xa0, 0xcb, 0x4c, 0x99, 0xea, 0x95, 0xa6, + 0x41, 0xd8, 0xc4, 0xb3, 0xbf, 0x58, 0x82, 0xaa, 0x8c, 0xa0, 0xe8, 0xa3, 0x2b, 0x9f, 0xb1, 0x60, + 0x54, 0x9d, 0x69, 0x30, 0x67, 0x59, 0xa9, 0x88, 0xa4, 0x04, 0xda, 0x03, 0xb5, 0xdd, 0xf6, 0xd7, + 0x03, 0x6d, 0x91, 0x63, 0x93, 0x19, 0x4e, 0xf3, 0x46, 0x37, 0x01, 0xe2, 0x9d, 0x38, 0x21, 0x6d, + 0xc3, 0x6d, 0x67, 0x1b, 0x2b, 0x6e, 0xb2, 0x11, 0x44, 0x84, 0xae, 0xaf, 0xeb, 0x41, 0x93, 0xac, + 0x28, 0x4c, 0x6d, 0x42, 0xe9, 0x36, 0x6c, 0x50, 0xb2, 0xff, 0x41, 0x09, 0x4e, 0x66, 0xbb, 0x84, + 0x3e, 0x08, 0x23, 0x92, 0xbb, 0x71, 0x4d, 0x99, 0x0c, 0x1b, 0x19, 0xc1, 0x06, 0xec, 0xde, 0xee, + 0xc4, 0x44, 0xf7, 0x35, 0x71, 0x93, 0x26, 0x0a, 0x4e, 0x11, 0xe3, 0x07, 0x4b, 0xe2, 0x04, 0xb4, + 0xbe, 0x33, 0x1d, 0x86, 0xe2, 0x74, 0xc8, 0x38, 0x58, 0x32, 0xa1, 0x38, 0x83, 0x8d, 0x96, 0xe1, + 0x8c, 0xd1, 0x72, 0x9d, 0xb8, 0xad, 0x8d, 0xb5, 0x20, 0x92, 0x3b, 0xab, 0xc7, 0x74, 0x60, 0x57, + 0x37, 0x0e, 0xce, 0x7d, 0x92, 0x6a, 0xfb, 0x86, 0x13, 0x3a, 0x0d, 0x37, 0xd9, 0x11, 0x7e, 0x48, + 0x25, 0x9b, 0x66, 0x44, 0x3b, 0x56, 0x18, 0xf6, 0x22, 0x0c, 0xf4, 0x39, 0x83, 0xfa, 0xb2, 0xe8, + 0x5f, 0x86, 0x2a, 0x25, 0x27, 0xcd, 0xbb, 0x22, 0x48, 0x06, 0x50, 0x95, 0x37, 0x8d, 0x20, 0x1b, + 0xca, 0xae, 0x23, 0xcf, 0xee, 0xd4, 0x6b, 0xcd, 0xc7, 0x71, 0x87, 0x6d, 0x92, 0x29, 0x10, 0x3d, + 0x09, 0x65, 0xb2, 0x1d, 0x66, 0x0f, 0xe9, 0x2e, 0x6d, 0x87, 0x6e, 0x44, 0x62, 0x8a, 0x44, 0xb6, + 0x43, 0x74, 0x1e, 0x4a, 0x6e, 0x53, 0x28, 0x29, 0x10, 0x38, 0xa5, 0xf9, 0x59, 0x5c, 0x72, 0x9b, + 0xf6, 0x36, 0xd4, 0xd4, 0xd5, 0x26, 0x68, 0x53, 0xca, 0x6e, 0xab, 0x88, 0x90, 0x27, 0x49, 0xb7, + 0x87, 0xd4, 0xee, 0x00, 0xe8, 0x84, 0xbf, 0xa2, 0xe4, 0xcb, 0x05, 0x18, 0x68, 0x04, 0x22, 0x19, + 0xb9, 0xaa, 0xc9, 0x30, 0xa1, 0xcd, 0x20, 0xf6, 0x2d, 0x18, 0xbb, 0xe6, 0x07, 0x77, 0x58, 0x5d, + 0x76, 0x56, 0x86, 0x8c, 0x12, 0x5e, 0xa7, 0x3f, 0xb2, 0x26, 0x02, 0x83, 0x62, 0x0e, 0x53, 0xf5, + 0x99, 0x4a, 0xbd, 0xea, 0x33, 0xd9, 0x1f, 0xb7, 0x60, 0x44, 0x65, 0x0e, 0xcd, 0x6d, 0x6d, 0x52, + 0xba, 0xad, 0x28, 0xe8, 0x84, 0x59, 0xba, 0xec, 0xf2, 0x21, 0xcc, 0x61, 0x66, 0x4a, 0x5d, 0x69, + 0x9f, 0x94, 0xba, 0x0b, 0x30, 0xb0, 0xe9, 0xfa, 0xcd, 0xec, 0x6d, 0x1a, 0xd7, 0x5c, 0xbf, 0x89, + 0x19, 0x84, 0x76, 0xe1, 0xa4, 0xea, 0x82, 0x54, 0x08, 0x2f, 0xc2, 0xc8, 0x5a, 0xc7, 0xf5, 0x9a, + 0xb2, 0xbe, 0x5a, 0xc6, 0x53, 0x52, 0x37, 0x60, 0x38, 0x85, 0x49, 0xf7, 0x75, 0x6b, 0xae, 0xef, + 0x44, 0x3b, 0xcb, 0x5a, 0x03, 0x29, 0xa1, 0x54, 0x57, 0x10, 0x6c, 0x60, 0xd9, 0x6f, 0x96, 0x61, + 0x2c, 0x9d, 0x3f, 0xd5, 0xc7, 0xf6, 0xea, 0x49, 0xa8, 0xb0, 0x94, 0xaa, 0xec, 0xa7, 0xe5, 0x25, + 0xc9, 0x38, 0x0c, 0xc5, 0x30, 0xc8, 0x8b, 0x31, 0x14, 0x73, 0x13, 0x8d, 0xea, 0xa4, 0xf2, 0xaf, + 0xb0, 0x78, 0x32, 0x51, 0xff, 0x41, 0xb0, 0x42, 0x9f, 0xb2, 0x60, 0x28, 0x08, 0xcd, 0xba, 0x3e, + 0x1f, 0x28, 0x32, 0xb7, 0x4c, 0x24, 0xcb, 0x08, 0x8b, 0x58, 0x7d, 0x7a, 0xf9, 0x39, 0x24, 0xeb, + 0xf3, 0x3f, 0x02, 0x23, 0x26, 0xe6, 0x7e, 0x46, 0x71, 0xd5, 0x34, 0x8a, 0x3f, 0x63, 0x4e, 0x0a, + 0x91, 0x3d, 0xd7, 0xc7, 0x72, 0xbb, 0x01, 0x95, 0x86, 0x0a, 0x00, 0x38, 0x54, 0x55, 0x4e, 0x55, + 0x1d, 0x81, 0x1d, 0x02, 0x71, 0x6a, 0xf6, 0xb7, 0x2c, 0x63, 0x7e, 0x60, 0x12, 0xcf, 0x37, 0x51, + 0x04, 0xe5, 0xd6, 0xd6, 0xa6, 0x30, 0x45, 0xaf, 0x16, 0x34, 0xbc, 0x73, 0x5b, 0x9b, 0x7a, 0x8e, + 0x9b, 0xad, 0x98, 0x32, 0xeb, 0xc3, 0x09, 0x98, 0x4a, 0xb2, 0x2c, 0xef, 0x9f, 0x64, 0x69, 0xbf, + 0x55, 0x82, 0x53, 0x5d, 0x93, 0x0a, 0xbd, 0x01, 0x95, 0x88, 0xbe, 0xa5, 0x78, 0xbd, 0x85, 0xc2, + 0xd2, 0x22, 0xe3, 0xf9, 0xa6, 0xd6, 0xbb, 0xe9, 0x76, 0xcc, 0x59, 0xa2, 0xab, 0x80, 0x74, 0x98, + 0x8a, 0xf2, 0x40, 0xf2, 0x57, 0x3e, 0x2f, 0x1e, 0x45, 0xd3, 0x5d, 0x18, 0x38, 0xe7, 0x29, 0xf4, + 0x52, 0xd6, 0x91, 0x59, 0x4e, 0x9f, 0x5b, 0xee, 0xe5, 0x93, 0xb4, 0xff, 0x79, 0x09, 0x46, 0x53, + 0x65, 0x96, 0x90, 0x07, 0x55, 0xe2, 0x31, 0xa7, 0xbe, 0x54, 0x36, 0x47, 0xad, 0x5a, 0xac, 0x14, + 0xe4, 0x25, 0x41, 0x17, 0x2b, 0x0e, 0x0f, 0xc7, 0xe1, 0xfa, 0x8b, 0x30, 0x22, 0x3b, 0xf4, 0x01, + 0xa7, 0xed, 0x89, 0x01, 0x54, 0x73, 0xf4, 0x92, 0x01, 0xc3, 0x29, 0x4c, 0xfb, 0x77, 0xca, 0x30, + 0xce, 0x4f, 0x41, 0x9a, 0x6a, 0xe6, 0x2d, 0xca, 0xfd, 0xd6, 0x5f, 0xd1, 0xc5, 0xd0, 0xf8, 0x40, + 0xae, 0x1d, 0xf5, 0x92, 0x80, 0x7c, 0x46, 0x7d, 0x45, 0x66, 0x7d, 0x25, 0x13, 0x99, 0xc5, 0xcd, + 0xee, 0xd6, 0x31, 0xf5, 0xe8, 0xbb, 0x2b, 0x54, 0xeb, 0x57, 0x4a, 0x70, 0x22, 0x73, 0x03, 0x03, + 0x7a, 0x33, 0x5d, 0xb4, 0xd7, 0x2a, 0xc2, 0x57, 0xbe, 0x67, 0x51, 0xfe, 0x83, 0x95, 0xee, 0x7d, + 0x40, 0x4b, 0xc5, 0xfe, 0x83, 0x12, 0x8c, 0xa5, 0xaf, 0x8e, 0x78, 0x08, 0x47, 0xea, 0xdd, 0x50, + 0x63, 0xd5, 0xd1, 0xd9, 0x95, 0x98, 0xdc, 0x25, 0xcf, 0x0b, 0x51, 0xcb, 0x46, 0xac, 0xe1, 0x0f, + 0x45, 0x45, 0x64, 0xfb, 0xef, 0x59, 0x70, 0x96, 0xbf, 0x65, 0x76, 0x1e, 0xfe, 0xd5, 0xbc, 0xd1, + 0x7d, 0xb5, 0xd8, 0x0e, 0x66, 0x8a, 0xf8, 0xed, 0x37, 0xbe, 0xec, 0x2a, 0x3e, 0xd1, 0xdb, 0xf4, + 0x54, 0x78, 0x08, 0x3b, 0x7b, 0xa0, 0xc9, 0x60, 0xff, 0x41, 0x19, 0xf4, 0xed, 0x83, 0xc8, 0x15, + 0x39, 0x8e, 0x85, 0x14, 0x33, 0x5c, 0xd9, 0xf1, 0x1b, 0xfa, 0x9e, 0xc3, 0x6a, 0x26, 0xc5, 0xf1, + 0xe7, 0x2c, 0x18, 0x76, 0x7d, 0x37, 0x71, 0x1d, 0xb6, 0x8d, 0x2e, 0xe6, 0x66, 0x34, 0xc5, 0x6e, + 0x9e, 0x53, 0x0e, 0x22, 0xf3, 0x1c, 0x47, 0x31, 0xc3, 0x26, 0x67, 0xf4, 0x61, 0x11, 0x3c, 0x5d, + 0x2e, 0x2c, 0x3b, 0xb7, 0x9a, 0x89, 0x98, 0x0e, 0xa9, 0xe1, 0x95, 0x44, 0x05, 0x25, 0xb5, 0x63, + 0x4a, 0x4a, 0xd5, 0xc5, 0xd5, 0xf7, 0x40, 0xd3, 0x66, 0xcc, 0x19, 0xd9, 0x31, 0xa0, 0xee, 0xb1, + 0x38, 0x60, 0x60, 0xea, 0x14, 0xd4, 0x9c, 0x4e, 0x12, 0xb4, 0xe9, 0x30, 0x89, 0xa3, 0x26, 0x1d, + 0x7a, 0x2b, 0x01, 0x58, 0xe3, 0xd8, 0x6f, 0x56, 0x20, 0x93, 0x74, 0x88, 0xb6, 0xcd, 0x9b, 0x33, + 0xad, 0x62, 0x6f, 0xce, 0x54, 0x9d, 0xc9, 0xbb, 0x3d, 0x13, 0xb5, 0xa0, 0x12, 0x6e, 0x38, 0xb1, + 0x34, 0xab, 0x5f, 0x56, 0xfb, 0x38, 0xda, 0x78, 0x6f, 0x77, 0xe2, 0xc7, 0xfb, 0xf3, 0xba, 0xd2, + 0xb9, 0x3a, 0xc5, 0x8b, 0x8d, 0x68, 0xd6, 0x8c, 0x06, 0xe6, 0xf4, 0x0f, 0x72, 0x37, 0xdc, 0x27, + 0x44, 0x19, 0x78, 0x4c, 0xe2, 0x8e, 0x97, 0x88, 0xd9, 0xf0, 0x72, 0x81, 0xab, 0x8c, 0x13, 0xd6, + 0xe9, 0xf2, 0xfc, 0x3f, 0x36, 0x98, 0xa2, 0x0f, 0x42, 0x2d, 0x4e, 0x9c, 0x28, 0x39, 0x64, 0x82, + 0xab, 0x1a, 0xf4, 0x15, 0x49, 0x04, 0x6b, 0x7a, 0xe8, 0x15, 0x56, 0xdb, 0xd5, 0x8d, 0x37, 0x0e, + 0x99, 0xf3, 0x20, 0xeb, 0xc0, 0x0a, 0x0a, 0xd8, 0xa0, 0x86, 0x2e, 0x02, 0xb0, 0xb9, 0xcd, 0x03, + 0xfd, 0xaa, 0xcc, 0xcb, 0xa4, 0x44, 0x21, 0x56, 0x10, 0x6c, 0x60, 0xd9, 0x3f, 0x08, 0xe9, 0x7a, + 0x0f, 0x68, 0x42, 0x96, 0x97, 0xe0, 0x5e, 0x68, 0x96, 0xbb, 0x90, 0xaa, 0x04, 0xf1, 0xeb, 0x16, + 0x98, 0x45, 0x29, 0xd0, 0xeb, 0xbc, 0xfa, 0x85, 0x55, 0xc4, 0xc9, 0xa1, 0x41, 0x77, 0x72, 0xd1, + 0x09, 0x33, 0x47, 0xd8, 0xb2, 0x04, 0xc6, 0xf9, 0xf7, 0x40, 0x55, 0x42, 0x0f, 0x64, 0xd4, 0x7d, + 0x0c, 0x4e, 0x67, 0xef, 0x15, 0x17, 0xa7, 0x4e, 0xfb, 0xbb, 0x7e, 0xa4, 0x3f, 0xa7, 0xd4, 0xcb, + 0x9f, 0xd3, 0xc7, 0xfd, 0xa9, 0xbf, 0x61, 0xc1, 0x85, 0xfd, 0xae, 0x3f, 0x47, 0x8f, 0xc1, 0xc0, + 0x1d, 0x27, 0x92, 0x45, 0xb7, 0x99, 0xa0, 0xbc, 0xe5, 0x44, 0x3e, 0x66, 0xad, 0x68, 0x07, 0x06, + 0x79, 0x34, 0x98, 0xb0, 0xd6, 0x5f, 0x2e, 0xf6, 0x32, 0xf6, 0x6b, 0xc4, 0xd8, 0x2e, 0xf0, 0x48, + 0x34, 0x2c, 0x18, 0xda, 0xdf, 0xb6, 0x00, 0x2d, 0x6d, 0x91, 0x28, 0x72, 0x9b, 0x46, 0xfc, 0x1a, + 0xbb, 0x4e, 0xc5, 0xb8, 0x36, 0xc5, 0x4c, 0x71, 0xcd, 0x5c, 0xa7, 0x62, 0xfc, 0xcb, 0xbf, 0x4e, + 0xa5, 0x74, 0xb0, 0xeb, 0x54, 0xd0, 0x12, 0x9c, 0x6d, 0xf3, 0xed, 0x06, 0xbf, 0xa2, 0x80, 0xef, + 0x3d, 0x54, 0x42, 0xd9, 0xb9, 0xbb, 0xbb, 0x13, 0x67, 0x17, 0xf3, 0x10, 0x70, 0xfe, 0x73, 0xf6, + 0x7b, 0x00, 0xf1, 0xb0, 0xb5, 0x99, 0xbc, 0x18, 0xa4, 0x9e, 0xee, 0x17, 0xfb, 0xcb, 0x15, 0x38, + 0x91, 0x29, 0xc9, 0x4a, 0xb7, 0x7a, 0xdd, 0x41, 0x4f, 0x47, 0xd6, 0xdf, 0xdd, 0xdd, 0xeb, 0x2b, + 0x8c, 0xca, 0x87, 0x8a, 0xeb, 0x87, 0x9d, 0xa4, 0x98, 0x1c, 0x52, 0xde, 0x89, 0x79, 0x4a, 0xd0, + 0x70, 0x17, 0xd3, 0xbf, 0x98, 0xb3, 0x29, 0x32, 0x28, 0x2b, 0x65, 0x8c, 0x0f, 0x3c, 0x20, 0x77, + 0xc0, 0x27, 0x74, 0x88, 0x54, 0xa5, 0x08, 0xc7, 0x62, 0x66, 0xb2, 0x1c, 0xf7, 0x51, 0xfb, 0xaf, + 0x95, 0x60, 0xd8, 0xf8, 0x68, 0xe8, 0x17, 0xd3, 0x25, 0x9b, 0xac, 0xe2, 0x5e, 0x89, 0xd1, 0x9f, + 0xd4, 0x45, 0x99, 0xf8, 0x2b, 0x3d, 0xd5, 0x5d, 0xad, 0xe9, 0xde, 0xee, 0xc4, 0xc9, 0x4c, 0x3d, + 0xa6, 0x54, 0x05, 0xa7, 0xf3, 0x1f, 0x85, 0x13, 0x19, 0x32, 0x39, 0xaf, 0xbc, 0x9a, 0xbe, 0x36, + 0xfe, 0x88, 0x6e, 0x29, 0x73, 0xc8, 0xbe, 0x4e, 0x87, 0x4c, 0xa4, 0xd1, 0x05, 0x1e, 0xe9, 0xc3, + 0x07, 0x9b, 0xc9, 0x96, 0x2d, 0xf5, 0x99, 0x2d, 0xfb, 0x34, 0x54, 0xc3, 0xc0, 0x73, 0x1b, 0xae, + 0xaa, 0x42, 0xc8, 0xf2, 0x73, 0x97, 0x45, 0x1b, 0x56, 0x50, 0x74, 0x07, 0x6a, 0xea, 0x86, 0x7d, + 0xe1, 0xdf, 0x2e, 0xea, 0xd0, 0x47, 0x19, 0x2d, 0xfa, 0xe6, 0x7c, 0xcd, 0x0b, 0xd9, 0x30, 0xc8, + 0x94, 0xa0, 0x0c, 0xfd, 0x67, 0xbe, 0x77, 0xa6, 0x1d, 0x63, 0x2c, 0x20, 0xf6, 0xd7, 0x6a, 0x70, + 0x26, 0xaf, 0x2e, 0x36, 0xfa, 0x08, 0x0c, 0xf2, 0x3e, 0x16, 0x73, 0xf5, 0x42, 0x1e, 0x8f, 0x39, + 0x46, 0x50, 0x74, 0x8b, 0xfd, 0xc6, 0x82, 0xa7, 0xe0, 0xee, 0x39, 0x6b, 0x62, 0x86, 0x1c, 0x0f, + 0xf7, 0x05, 0x47, 0x73, 0x5f, 0x70, 0x38, 0x77, 0xcf, 0x59, 0x43, 0xdb, 0x50, 0x69, 0xb9, 0x09, + 0x71, 0x84, 0x13, 0xe1, 0xd6, 0xb1, 0x30, 0x27, 0x0e, 0xb7, 0xd2, 0xd8, 0x4f, 0xcc, 0x19, 0xa2, + 0xaf, 0x5a, 0x70, 0x62, 0x2d, 0x9d, 0x1a, 0x2f, 0x84, 0xa7, 0x73, 0x0c, 0xb5, 0xcf, 0xd3, 0x8c, + 0xf8, 0x7d, 0x42, 0x99, 0x46, 0x9c, 0xed, 0x0e, 0xfa, 0xa4, 0x05, 0x43, 0xeb, 0xae, 0x67, 0x94, + 0xc1, 0x3d, 0x86, 0x8f, 0x73, 0x99, 0x31, 0xd0, 0x3b, 0x0e, 0xfe, 0x3f, 0xc6, 0x92, 0x73, 0x2f, + 0x4d, 0x35, 0x78, 0x54, 0x4d, 0x35, 0xf4, 0x80, 0x34, 0xd5, 0xa7, 0x2d, 0xa8, 0xa9, 0x91, 0x16, + 0xe9, 0xce, 0x1f, 0x3c, 0xc6, 0x4f, 0xce, 0x3d, 0x27, 0xea, 0x2f, 0xd6, 0xcc, 0xd1, 0x17, 0x2c, + 0x18, 0x76, 0xde, 0xe8, 0x44, 0xa4, 0x49, 0xb6, 0x82, 0x30, 0x16, 0x97, 0x11, 0xbe, 0x5a, 0x7c, + 0x67, 0xa6, 0x29, 0x93, 0x59, 0xb2, 0xb5, 0x14, 0xc6, 0x22, 0x2d, 0x49, 0x37, 0x60, 0xb3, 0x0b, + 0xf6, 0x6e, 0x09, 0x26, 0xf6, 0xa1, 0x80, 0x5e, 0x84, 0x91, 0x20, 0x6a, 0x39, 0xbe, 0xfb, 0x86, + 0x59, 0xeb, 0x42, 0x59, 0x59, 0x4b, 0x06, 0x0c, 0xa7, 0x30, 0xcd, 0x84, 0xec, 0xd2, 0x3e, 0x09, + 0xd9, 0x17, 0x60, 0x20, 0x22, 0x61, 0x90, 0xdd, 0x2c, 0xb0, 0x94, 0x00, 0x06, 0x41, 0x8f, 0x43, + 0xd9, 0x09, 0x5d, 0x11, 0x88, 0xa6, 0xf6, 0x40, 0xd3, 0xcb, 0xf3, 0x98, 0xb6, 0xa7, 0xea, 0x43, + 0x54, 0xee, 0x4b, 0x7d, 0x08, 0xaa, 0x06, 0xc4, 0xd9, 0xc5, 0xa0, 0x56, 0x03, 0xe9, 0x33, 0x05, + 0xfb, 0xad, 0x32, 0x3c, 0xbe, 0xe7, 0x7c, 0xd1, 0x71, 0x78, 0xd6, 0x1e, 0x71, 0x78, 0x72, 0x78, + 0x4a, 0xfb, 0x0d, 0x4f, 0xb9, 0xc7, 0xf0, 0x7c, 0x92, 0x2e, 0x03, 0x59, 0x23, 0xa4, 0x98, 0xeb, + 0xe4, 0x7a, 0x95, 0x1c, 0x11, 0x2b, 0x40, 0x42, 0xb1, 0xe6, 0x4b, 0xf7, 0x00, 0xa9, 0x64, 0xe4, + 0x4a, 0x11, 0x6a, 0xa0, 0x67, 0xcd, 0x10, 0x3e, 0xf7, 0x7b, 0x65, 0x38, 0xdb, 0x3f, 0x5f, 0x82, + 0x27, 0xfb, 0x90, 0xde, 0xe6, 0x2c, 0xb6, 0xfa, 0x9c, 0xc5, 0xdf, 0xdd, 0x9f, 0xc9, 0xfe, 0x6b, + 0x16, 0x9c, 0xef, 0xad, 0x3c, 0xd0, 0x73, 0x30, 0xbc, 0x16, 0x39, 0x7e, 0x63, 0x83, 0x5d, 0x91, + 0x29, 0x07, 0x85, 0x8d, 0xb5, 0x6e, 0xc6, 0x26, 0x0e, 0xdd, 0xde, 0xf2, 0x98, 0x04, 0x03, 0x43, + 0x26, 0x8f, 0xd2, 0xed, 0xed, 0x6a, 0x16, 0x88, 0xbb, 0xf1, 0xed, 0x3f, 0x2b, 0xe5, 0x77, 0x8b, + 0x1b, 0x19, 0x07, 0xf9, 0x4e, 0xe2, 0x2b, 0x94, 0xfa, 0x90, 0x25, 0xe5, 0xfb, 0x2d, 0x4b, 0x06, + 0x7a, 0xc9, 0x12, 0x34, 0x0b, 0x27, 0x8d, 0x2b, 0x54, 0x78, 0x42, 0x30, 0x0f, 0xb8, 0x55, 0x55, + 0x32, 0x96, 0x33, 0x70, 0xdc, 0xf5, 0x04, 0x7a, 0x06, 0xaa, 0xae, 0x1f, 0x93, 0x46, 0x27, 0xe2, + 0x81, 0xde, 0x46, 0x12, 0xd6, 0xbc, 0x68, 0xc7, 0x0a, 0xc3, 0xfe, 0xa5, 0x12, 0x9c, 0xeb, 0x69, + 0x67, 0xdd, 0x27, 0xd9, 0x65, 0x7e, 0x8e, 0x81, 0xfb, 0xf3, 0x39, 0xcc, 0x41, 0xaa, 0xec, 0x3b, + 0x48, 0x7f, 0xd8, 0x7b, 0x62, 0x52, 0x9b, 0xfb, 0x7b, 0x76, 0x94, 0x5e, 0x82, 0x51, 0x27, 0x0c, + 0x39, 0x1e, 0x8b, 0xd7, 0xcc, 0x54, 0xc9, 0x99, 0x36, 0x81, 0x38, 0x8d, 0xdb, 0x97, 0xf6, 0xfc, + 0x63, 0x0b, 0x6a, 0x98, 0xac, 0x73, 0xe9, 0x80, 0x6e, 0x8b, 0x21, 0xb2, 0x8a, 0xa8, 0xa7, 0x49, + 0x07, 0x36, 0x76, 0x59, 0x9d, 0xc9, 0xbc, 0xc1, 0xee, 0xbe, 0x6a, 0xa7, 0x74, 0xa0, 0xab, 0x76, + 0xd4, 0x65, 0x2b, 0xe5, 0xde, 0x97, 0xad, 0xd8, 0x5f, 0x1f, 0xa2, 0xaf, 0x17, 0x06, 0x33, 0x11, + 0x69, 0xc6, 0xf4, 0xfb, 0x76, 0x22, 0x4f, 0x4c, 0x12, 0xf5, 0x7d, 0x6f, 0xe0, 0x05, 0x4c, 0xdb, + 0x53, 0x47, 0x31, 0xa5, 0x03, 0xd5, 0x08, 0x29, 0xef, 0x5b, 0x23, 0xe4, 0x25, 0x18, 0x8d, 0xe3, + 0x8d, 0xe5, 0xc8, 0xdd, 0x72, 0x12, 0x72, 0x8d, 0xec, 0x08, 0x2b, 0x4b, 0xe7, 0xf5, 0xaf, 0x5c, + 0xd1, 0x40, 0x9c, 0xc6, 0x45, 0x73, 0x70, 0x4a, 0x57, 0xea, 0x20, 0x51, 0xc2, 0xa2, 0xfb, 0xf9, + 0x4c, 0x50, 0x49, 0xbc, 0xba, 0xb6, 0x87, 0x40, 0xc0, 0xdd, 0xcf, 0x50, 0xf9, 0x96, 0x6a, 0xa4, + 0x1d, 0x19, 0x4c, 0xcb, 0xb7, 0x14, 0x1d, 0xda, 0x97, 0xae, 0x27, 0xd0, 0x22, 0x9c, 0xe6, 0x13, + 0x63, 0x3a, 0x0c, 0x8d, 0x37, 0x1a, 0x4a, 0xd7, 0x31, 0x9c, 0xeb, 0x46, 0xc1, 0x79, 0xcf, 0xa1, + 0x17, 0x60, 0x58, 0x35, 0xcf, 0xcf, 0x8a, 0x53, 0x04, 0xe5, 0xc5, 0x50, 0x64, 0xe6, 0x9b, 0xd8, + 0xc4, 0x43, 0x1f, 0x80, 0x47, 0xf5, 0x5f, 0x9e, 0x02, 0xc6, 0x8f, 0xd6, 0x66, 0x45, 0x11, 0x24, + 0x75, 0xb5, 0xc7, 0x5c, 0x2e, 0x5a, 0x13, 0xf7, 0x7a, 0x1e, 0xad, 0xc1, 0x79, 0x05, 0xba, 0xe4, + 0x27, 0x2c, 0x9f, 0x23, 0x26, 0x75, 0x27, 0x26, 0x37, 0x22, 0x4f, 0xdc, 0x8d, 0xaa, 0x6e, 0x5d, + 0x9c, 0x73, 0x93, 0x2b, 0x79, 0x98, 0x78, 0x01, 0xef, 0x41, 0x05, 0x4d, 0x41, 0x8d, 0xf8, 0xce, + 0x9a, 0x47, 0x96, 0x66, 0xe6, 0x59, 0x31, 0x25, 0xe3, 0x24, 0xef, 0x92, 0x04, 0x60, 0x8d, 0xa3, + 0x22, 0x4c, 0x47, 0x7a, 0xde, 0x00, 0xba, 0x0c, 0x67, 0x5a, 0x8d, 0x90, 0xda, 0x1e, 0x6e, 0x83, + 0x4c, 0x37, 0x58, 0x40, 0x1d, 0xfd, 0x30, 0xbc, 0xc0, 0xa4, 0x0a, 0x9f, 0x9e, 0x9b, 0x59, 0xee, + 0xc2, 0xc1, 0xb9, 0x4f, 0xb2, 0xc0, 0xcb, 0x28, 0xd8, 0xde, 0x19, 0x3f, 0x9d, 0x09, 0xbc, 0xa4, + 0x8d, 0x98, 0xc3, 0xd0, 0x55, 0x40, 0x2c, 0x16, 0xff, 0x4a, 0x92, 0x84, 0xca, 0xd8, 0x19, 0x3f, + 0xc3, 0x5e, 0x49, 0x85, 0x91, 0x5d, 0xee, 0xc2, 0xc0, 0x39, 0x4f, 0xd9, 0xff, 0xc1, 0x82, 0x51, + 0xb5, 0x5e, 0xef, 0x43, 0x36, 0x8a, 0x97, 0xce, 0x46, 0x99, 0x3b, 0xba, 0xc4, 0x63, 0x3d, 0xef, + 0x11, 0xd2, 0xfc, 0x33, 0xc3, 0x00, 0x5a, 0x2a, 0x2a, 0x85, 0x64, 0xf5, 0x54, 0x48, 0x0f, 0xad, + 0x44, 0xca, 0xab, 0x9c, 0x52, 0x79, 0xb0, 0x95, 0x53, 0x56, 0xe0, 0xac, 0x34, 0x17, 0xf8, 0x59, + 0xd1, 0x95, 0x20, 0x56, 0x02, 0xae, 0x5a, 0x7f, 0x5c, 0x10, 0x3a, 0x3b, 0x9f, 0x87, 0x84, 0xf3, + 0x9f, 0x4d, 0x59, 0x29, 0x43, 0xfb, 0x59, 0x29, 0x7a, 0x4d, 0x2f, 0xac, 0xcb, 0x3b, 0x3c, 0x32, + 0x6b, 0x7a, 0xe1, 0xf2, 0x0a, 0xd6, 0x38, 0xf9, 0x82, 0xbd, 0x56, 0x90, 0x60, 0x87, 0x03, 0x0b, + 0x76, 0x29, 0x62, 0x86, 0x7b, 0x8a, 0x18, 0xe9, 0x93, 0x1e, 0xe9, 0xe9, 0x93, 0x7e, 0x2f, 0x8c, + 0xb9, 0xfe, 0x06, 0x89, 0xdc, 0x84, 0x34, 0xd9, 0x5a, 0x60, 0xe2, 0xa7, 0xaa, 0xd5, 0xfa, 0x7c, + 0x0a, 0x8a, 0x33, 0xd8, 0x69, 0xb9, 0x38, 0xd6, 0x87, 0x5c, 0xec, 0xa1, 0x8d, 0x4e, 0x14, 0xa3, + 0x8d, 0x4e, 0x1e, 0x5d, 0x1b, 0x9d, 0x3a, 0x56, 0x6d, 0x84, 0x0a, 0xd1, 0x46, 0x7d, 0x09, 0x7a, + 0x63, 0xfb, 0x77, 0x66, 0x9f, 0xed, 0x5f, 0x2f, 0x55, 0x74, 0xf6, 0xd0, 0xaa, 0x28, 0x5f, 0xcb, + 0x3c, 0x72, 0x28, 0x2d, 0xf3, 0xe9, 0x12, 0x9c, 0xd5, 0x72, 0x98, 0xce, 0x7e, 0x77, 0x9d, 0x4a, + 0x22, 0x76, 0x0d, 0x14, 0x3f, 0xb7, 0x31, 0x92, 0xa3, 0x74, 0x9e, 0x95, 0x82, 0x60, 0x03, 0x8b, + 0xe5, 0x18, 0x91, 0x88, 0x95, 0xd1, 0xcd, 0x0a, 0xe9, 0x19, 0xd1, 0x8e, 0x15, 0x06, 0x9d, 0x5f, + 0xf4, 0xb7, 0xc8, 0xdb, 0xcc, 0x16, 0x8b, 0x9b, 0xd1, 0x20, 0x6c, 0xe2, 0xa1, 0xa7, 0x39, 0x13, + 0x26, 0x20, 0xa8, 0xa0, 0x1e, 0x11, 0xf7, 0xc2, 0x4a, 0x99, 0xa0, 0xa0, 0xb2, 0x3b, 0x2c, 0x99, + 0xac, 0xd2, 0xdd, 0x1d, 0x16, 0x02, 0xa5, 0x30, 0xec, 0xff, 0x69, 0xc1, 0xb9, 0xdc, 0xa1, 0xb8, + 0x0f, 0xca, 0x77, 0x3b, 0xad, 0x7c, 0x57, 0x8a, 0xda, 0x6e, 0x18, 0x6f, 0xd1, 0x43, 0x11, 0xff, + 0x3b, 0x0b, 0xc6, 0x34, 0xfe, 0x7d, 0x78, 0x55, 0x37, 0xfd, 0xaa, 0xc5, 0xed, 0xac, 0x6a, 0x5d, + 0xef, 0xf6, 0x3b, 0x25, 0x50, 0x05, 0x1c, 0xa7, 0x1b, 0xb2, 0x3c, 0xee, 0x3e, 0x27, 0x89, 0x3b, + 0x30, 0xc8, 0x0e, 0x42, 0xe3, 0x62, 0x82, 0x3c, 0xd2, 0xfc, 0xd9, 0xa1, 0xaa, 0x3e, 0x64, 0x66, + 0x7f, 0x63, 0x2c, 0x18, 0xb2, 0x22, 0xcf, 0x6e, 0x4c, 0xa5, 0x79, 0x53, 0xa4, 0x65, 0xe9, 0x22, + 0xcf, 0xa2, 0x1d, 0x2b, 0x0c, 0xaa, 0x1e, 0xdc, 0x46, 0xe0, 0xcf, 0x78, 0x4e, 0x2c, 0xef, 0x3e, + 0x54, 0xea, 0x61, 0x5e, 0x02, 0xb0, 0xc6, 0x61, 0x67, 0xa4, 0x6e, 0x1c, 0x7a, 0xce, 0x8e, 0xb1, + 0x7f, 0x36, 0xea, 0x13, 0x28, 0x10, 0x36, 0xf1, 0xec, 0x36, 0x8c, 0xa7, 0x5f, 0x62, 0x96, 0xac, + 0xb3, 0x00, 0xc5, 0xbe, 0x86, 0x73, 0x0a, 0x6a, 0x0e, 0x7b, 0x6a, 0xa1, 0xe3, 0x64, 0xaf, 0x2c, + 0x9f, 0x96, 0x00, 0xac, 0x71, 0xec, 0x5f, 0xb5, 0xe0, 0x74, 0xce, 0xa0, 0x15, 0x98, 0xf6, 0x96, + 0x68, 0x69, 0x93, 0xa7, 0xd8, 0x7f, 0x00, 0x86, 0x9a, 0x64, 0xdd, 0x91, 0x21, 0x70, 0x86, 0x6c, + 0x9f, 0xe5, 0xcd, 0x58, 0xc2, 0xed, 0xff, 0x6e, 0xc1, 0x89, 0x74, 0x5f, 0x63, 0x96, 0x4a, 0xc2, + 0x87, 0xc9, 0x8d, 0x1b, 0xc1, 0x16, 0x89, 0x76, 0xe8, 0x9b, 0x5b, 0x99, 0x54, 0x92, 0x2e, 0x0c, + 0x9c, 0xf3, 0x14, 0x2b, 0xdf, 0xda, 0x54, 0xa3, 0x2d, 0x67, 0xe4, 0xcd, 0x22, 0x67, 0xa4, 0xfe, + 0x98, 0xe6, 0x71, 0xb9, 0x62, 0x89, 0x4d, 0xfe, 0xf6, 0xb7, 0x07, 0x40, 0xe5, 0xc5, 0xb2, 0xf8, + 0xa3, 0x82, 0xa2, 0xb7, 0x0e, 0x9a, 0x41, 0xa4, 0x26, 0xc3, 0xc0, 0x5e, 0x01, 0x01, 0xdc, 0x4b, + 0x62, 0xba, 0x2e, 0xd5, 0x1b, 0xae, 0x6a, 0x10, 0x36, 0xf1, 0x68, 0x4f, 0x3c, 0x77, 0x8b, 0xf0, + 0x87, 0x06, 0xd3, 0x3d, 0x59, 0x90, 0x00, 0xac, 0x71, 0x68, 0x4f, 0x9a, 0xee, 0xfa, 0xba, 0xd8, + 0xf2, 0xab, 0x9e, 0xd0, 0xd1, 0xc1, 0x0c, 0xc2, 0x2b, 0x72, 0x07, 0x9b, 0xc2, 0x0a, 0x36, 0x2a, + 0x72, 0x07, 0x9b, 0x98, 0x41, 0xa8, 0xdd, 0xe6, 0x07, 0x51, 0x9b, 0x5d, 0x29, 0xdf, 0x54, 0x5c, + 0x84, 0xf5, 0xab, 0xec, 0xb6, 0xeb, 0xdd, 0x28, 0x38, 0xef, 0x39, 0x3a, 0x03, 0xc3, 0x88, 0x34, + 0xdd, 0x46, 0x62, 0x52, 0x83, 0xf4, 0x0c, 0x5c, 0xee, 0xc2, 0xc0, 0x39, 0x4f, 0xa1, 0x69, 0x38, + 0x21, 0xf3, 0x9a, 0x65, 0xd5, 0x9a, 0xe1, 0x74, 0x95, 0x0c, 0x9c, 0x06, 0xe3, 0x2c, 0x3e, 0x95, + 0x6a, 0x6d, 0x51, 0xb0, 0x8a, 0x19, 0xcb, 0x86, 0x54, 0x93, 0x85, 0xac, 0xb0, 0xc2, 0xb0, 0x3f, + 0x51, 0xa6, 0x5a, 0xb8, 0x47, 0xa1, 0xb6, 0xfb, 0x16, 0x2d, 0x98, 0x9e, 0x91, 0x03, 0x7d, 0xcc, + 0xc8, 0xe7, 0x61, 0xe4, 0x76, 0x1c, 0xf8, 0x2a, 0x12, 0xaf, 0xd2, 0x33, 0x12, 0xcf, 0xc0, 0xca, + 0x8f, 0xc4, 0x1b, 0x2c, 0x2a, 0x12, 0x6f, 0xe8, 0x90, 0x91, 0x78, 0xdf, 0xac, 0x80, 0xba, 0x1a, + 0xe4, 0x3a, 0x49, 0xee, 0x04, 0xd1, 0xa6, 0xeb, 0xb7, 0x58, 0x3e, 0xf8, 0x57, 0x2d, 0x18, 0xe1, + 0xeb, 0x65, 0xc1, 0xcc, 0xa4, 0x5a, 0x2f, 0xe8, 0xce, 0x89, 0x14, 0xb3, 0xc9, 0x55, 0x83, 0x51, + 0xe6, 0xea, 0x4d, 0x13, 0x84, 0x53, 0x3d, 0x42, 0x1f, 0x05, 0x90, 0xfe, 0xd1, 0x75, 0x29, 0x32, + 0xe7, 0x8b, 0xe9, 0x1f, 0x26, 0xeb, 0xda, 0x06, 0x5e, 0x55, 0x4c, 0xb0, 0xc1, 0x10, 0x7d, 0x5a, + 0x67, 0x99, 0xf1, 0x90, 0xfd, 0x0f, 0x1f, 0xcb, 0xd8, 0xf4, 0x93, 0x63, 0x86, 0x61, 0xc8, 0xf5, + 0x5b, 0x74, 0x9e, 0x88, 0x88, 0xa5, 0x77, 0xe5, 0xd5, 0x52, 0x58, 0x08, 0x9c, 0x66, 0xdd, 0xf1, + 0x1c, 0xbf, 0x41, 0xa2, 0x79, 0x8e, 0x6e, 0x5e, 0x38, 0xcd, 0x1a, 0xb0, 0x24, 0xd4, 0x75, 0xa9, + 0x4a, 0xa5, 0x9f, 0x4b, 0x55, 0xce, 0xbf, 0x0f, 0x4e, 0x75, 0x7d, 0xcc, 0x03, 0xa5, 0x94, 0x1d, + 0x3e, 0x1b, 0xcd, 0xfe, 0x17, 0x83, 0x5a, 0x69, 0x5d, 0x0f, 0x9a, 0xfc, 0x6a, 0x8f, 0x48, 0x7f, + 0x51, 0x61, 0xe3, 0x16, 0x38, 0x45, 0x8c, 0x4b, 0xab, 0x55, 0x23, 0x36, 0x59, 0xd2, 0x39, 0x1a, + 0x3a, 0x11, 0xf1, 0x8f, 0x7b, 0x8e, 0x2e, 0x2b, 0x26, 0xd8, 0x60, 0x88, 0x36, 0x52, 0x39, 0x25, + 0x97, 0x8f, 0x9e, 0x53, 0xc2, 0xaa, 0x4c, 0xe5, 0x55, 0xe3, 0xff, 0x82, 0x05, 0x63, 0x7e, 0x6a, + 0xe6, 0x16, 0x13, 0x46, 0x9a, 0xbf, 0x2a, 0xf8, 0xcd, 0x52, 0xe9, 0x36, 0x9c, 0xe1, 0x9f, 0xa7, + 0xd2, 0x2a, 0x07, 0x54, 0x69, 0xfa, 0x8e, 0xa0, 0xc1, 0x5e, 0x77, 0x04, 0x21, 0x5f, 0x5d, 0x92, + 0x36, 0x54, 0xf8, 0x25, 0x69, 0x90, 0x73, 0x41, 0xda, 0x2d, 0xa8, 0x35, 0x22, 0xe2, 0x24, 0x87, + 0xbc, 0x2f, 0x8b, 0x1d, 0xd0, 0xcf, 0x48, 0x02, 0x58, 0xd3, 0xb2, 0xff, 0xcf, 0x00, 0x9c, 0x94, + 0x23, 0x22, 0x43, 0xd0, 0xa9, 0x7e, 0xe4, 0x7c, 0xb5, 0x71, 0xab, 0xf4, 0xe3, 0x15, 0x09, 0xc0, + 0x1a, 0x87, 0xda, 0x63, 0x9d, 0x98, 0x2c, 0x85, 0xc4, 0x5f, 0x70, 0xd7, 0x62, 0x71, 0xce, 0xa9, + 0x16, 0xca, 0x0d, 0x0d, 0xc2, 0x26, 0x1e, 0x35, 0xc6, 0xb9, 0x5d, 0x1c, 0x67, 0xd3, 0x57, 0x84, + 0xbd, 0x8d, 0x25, 0x1c, 0xfd, 0x42, 0x6e, 0xe5, 0xd8, 0x62, 0x12, 0xb7, 0xba, 0x22, 0xef, 0x0f, + 0x78, 0xc5, 0xe2, 0xdf, 0xb1, 0xe0, 0x2c, 0x6f, 0x95, 0x23, 0x79, 0x23, 0x6c, 0x3a, 0x09, 0x89, + 0x8b, 0xa9, 0xe4, 0x9e, 0xd3, 0x3f, 0xed, 0xe4, 0xcd, 0x63, 0x8b, 0xf3, 0x7b, 0x83, 0xde, 0xb4, + 0xe0, 0xc4, 0x66, 0xaa, 0xe6, 0x87, 0x54, 0x1d, 0x47, 0x4d, 0xc7, 0x4f, 0x11, 0xd5, 0x4b, 0x2d, + 0xdd, 0x1e, 0xe3, 0x2c, 0x77, 0xfb, 0xcf, 0x2c, 0x30, 0xc5, 0xe8, 0xfd, 0x2f, 0x15, 0x72, 0x70, + 0x53, 0x50, 0x5a, 0x97, 0x95, 0x9e, 0xd6, 0xe5, 0xe3, 0x50, 0xee, 0xb8, 0x4d, 0xb1, 0xbf, 0xd0, + 0xa7, 0xaf, 0xf3, 0xb3, 0x98, 0xb6, 0xdb, 0xff, 0xb4, 0xa2, 0xfd, 0x16, 0x22, 0x2f, 0xea, 0x7b, + 0xe2, 0xb5, 0xd7, 0x55, 0xb1, 0x31, 0xfe, 0xe6, 0xd7, 0xbb, 0x8a, 0x8d, 0xfd, 0xe8, 0xc1, 0xd3, + 0xde, 0xf8, 0x00, 0xf5, 0xaa, 0x35, 0x36, 0xb4, 0x4f, 0xce, 0xdb, 0x6d, 0xa8, 0xd2, 0x2d, 0x18, + 0x73, 0x40, 0x56, 0x53, 0x9d, 0xaa, 0x5e, 0x11, 0xed, 0xf7, 0x76, 0x27, 0x7e, 0xe4, 0xe0, 0xdd, + 0x92, 0x4f, 0x63, 0x45, 0x1f, 0xc5, 0x50, 0xa3, 0xbf, 0x59, 0x7a, 0x9e, 0xd8, 0xdc, 0xdd, 0x50, + 0x32, 0x53, 0x02, 0x0a, 0xc9, 0xfd, 0xd3, 0x7c, 0x90, 0x0f, 0x35, 0x76, 0x1b, 0x2d, 0x63, 0xca, + 0xf7, 0x80, 0xcb, 0x2a, 0x49, 0x4e, 0x02, 0xee, 0xed, 0x4e, 0xbc, 0x74, 0x70, 0xa6, 0xea, 0x71, + 0xac, 0x59, 0xd8, 0x5f, 0x1c, 0xd0, 0x73, 0x57, 0xd4, 0x98, 0xfb, 0x9e, 0x98, 0xbb, 0x2f, 0x66, + 0xe6, 0xee, 0x85, 0xae, 0xb9, 0x3b, 0xa6, 0x6f, 0x4d, 0x4d, 0xcd, 0xc6, 0xfb, 0x6d, 0x08, 0xec, + 0xef, 0x6f, 0x60, 0x16, 0xd0, 0xeb, 0x1d, 0x37, 0x22, 0xf1, 0x72, 0xd4, 0xf1, 0x5d, 0xbf, 0xc5, + 0xa6, 0x63, 0xd5, 0xb4, 0x80, 0x52, 0x60, 0x9c, 0xc5, 0xa7, 0x9b, 0x7a, 0xfa, 0xcd, 0x6f, 0x39, + 0x5b, 0x7c, 0x56, 0x19, 0x65, 0xb7, 0x56, 0x44, 0x3b, 0x56, 0x18, 0xf6, 0xd7, 0xd9, 0x59, 0xb6, + 0x91, 0x17, 0x4c, 0xe7, 0x84, 0xc7, 0xae, 0xff, 0xe5, 0x35, 0xbb, 0xd4, 0x9c, 0xe0, 0x77, 0xfe, + 0x72, 0x18, 0xba, 0x03, 0x43, 0x6b, 0xfc, 0xfe, 0xbb, 0x62, 0xea, 0x93, 0x8b, 0xcb, 0xf4, 0xd8, + 0x2d, 0x27, 0xf2, 0x66, 0xbd, 0x7b, 0xfa, 0x27, 0x96, 0xdc, 0xec, 0xdf, 0xaf, 0xc0, 0x89, 0xcc, + 0x05, 0xb1, 0xa9, 0x6a, 0xa9, 0xa5, 0x7d, 0xab, 0xa5, 0x7e, 0x08, 0xa0, 0x49, 0x42, 0x2f, 0xd8, + 0x61, 0xe6, 0xd8, 0xc0, 0x81, 0xcd, 0x31, 0x65, 0xc1, 0xcf, 0x2a, 0x2a, 0xd8, 0xa0, 0x28, 0x0a, + 0x95, 0xf1, 0xe2, 0xab, 0x99, 0x42, 0x65, 0xc6, 0x2d, 0x06, 0x83, 0xf7, 0xf7, 0x16, 0x03, 0x17, + 0x4e, 0xf0, 0x2e, 0xaa, 0xec, 0xdb, 0x43, 0x24, 0xd9, 0xb2, 0xfc, 0x85, 0xd9, 0x34, 0x19, 0x9c, + 0xa5, 0xfb, 0x20, 0xef, 0x7f, 0x46, 0xef, 0x86, 0x9a, 0xfc, 0xce, 0xf1, 0x78, 0x4d, 0x57, 0x30, + 0x90, 0xd3, 0x80, 0xdd, 0xcb, 0x2c, 0x7e, 0x76, 0x15, 0x12, 0x80, 0x07, 0x55, 0x48, 0xc0, 0xfe, + 0x7c, 0x89, 0xda, 0xf1, 0xbc, 0x5f, 0xaa, 0x26, 0xce, 0x53, 0x30, 0xe8, 0x74, 0x92, 0x8d, 0xa0, + 0xeb, 0x36, 0xbf, 0x69, 0xd6, 0x8a, 0x05, 0x14, 0x2d, 0xc0, 0x40, 0x53, 0xd7, 0x39, 0x39, 0xc8, + 0xf7, 0xd4, 0x2e, 0x51, 0x27, 0x21, 0x98, 0x51, 0x41, 0x8f, 0xc1, 0x40, 0xe2, 0xb4, 0x64, 0xca, + 0x15, 0x4b, 0xb3, 0x5d, 0x75, 0x5a, 0x31, 0x66, 0xad, 0xa6, 0xfa, 0x1e, 0xd8, 0x47, 0x7d, 0xbf, + 0x04, 0xa3, 0xb1, 0xdb, 0xf2, 0x9d, 0xa4, 0x13, 0x11, 0xe3, 0x98, 0x4f, 0x47, 0x6e, 0x98, 0x40, + 0x9c, 0xc6, 0xb5, 0x7f, 0x73, 0x04, 0xce, 0xac, 0xcc, 0x2c, 0xca, 0xea, 0xdd, 0xc7, 0x96, 0x35, + 0x95, 0xc7, 0xe3, 0xfe, 0x65, 0x4d, 0xf5, 0xe0, 0xee, 0x19, 0x59, 0x53, 0x9e, 0x91, 0x35, 0x95, + 0x4e, 0x61, 0x29, 0x17, 0x91, 0xc2, 0x92, 0xd7, 0x83, 0x7e, 0x52, 0x58, 0x8e, 0x2d, 0x8d, 0x6a, + 0xcf, 0x0e, 0x1d, 0x28, 0x8d, 0x4a, 0xe5, 0x98, 0x15, 0x92, 0x5c, 0xd0, 0xe3, 0x53, 0xe5, 0xe6, + 0x98, 0xa9, 0xfc, 0x1e, 0x9e, 0x38, 0x23, 0x44, 0xfd, 0xab, 0xc5, 0x77, 0xa0, 0x8f, 0xfc, 0x1e, + 0x91, 0xbb, 0x63, 0xe6, 0x94, 0x0d, 0x15, 0x91, 0x53, 0x96, 0xd7, 0x9d, 0x7d, 0x73, 0xca, 0x5e, + 0x82, 0xd1, 0x86, 0x17, 0xf8, 0x64, 0x39, 0x0a, 0x92, 0xa0, 0x11, 0x78, 0xc2, 0xac, 0x57, 0x22, + 0x61, 0xc6, 0x04, 0xe2, 0x34, 0x6e, 0xaf, 0x84, 0xb4, 0xda, 0x51, 0x13, 0xd2, 0xe0, 0x01, 0x25, + 0xa4, 0xfd, 0xac, 0x4e, 0x9d, 0x1e, 0x66, 0x5f, 0xe4, 0x43, 0xc5, 0x7f, 0x91, 0x7e, 0xf2, 0xa7, + 0xd1, 0x5b, 0xfc, 0x3a, 0x3d, 0x6a, 0x18, 0xcf, 0x04, 0x6d, 0x6a, 0xf8, 0x8d, 0xb0, 0x21, 0x79, + 0xed, 0x18, 0x26, 0xec, 0xad, 0x15, 0xcd, 0x46, 0x5d, 0xb1, 0xa7, 0x9b, 0x70, 0xba, 0x23, 0x47, + 0x49, 0xed, 0xfe, 0x72, 0x09, 0xbe, 0x6f, 0xdf, 0x2e, 0xa0, 0x3b, 0x00, 0x89, 0xd3, 0x12, 0x13, + 0x55, 0x1c, 0x98, 0x1c, 0x31, 0xbc, 0x72, 0x55, 0xd2, 0xe3, 0x35, 0x49, 0xd4, 0x5f, 0x76, 0x14, + 0x21, 0x7f, 0xb3, 0xa8, 0xca, 0xc0, 0xeb, 0x2a, 0xdd, 0x88, 0x03, 0x8f, 0x60, 0x06, 0xa1, 0xea, + 0x3f, 0x22, 0x2d, 0x7d, 0xff, 0xb3, 0xfa, 0x7c, 0x98, 0xb5, 0x62, 0x01, 0x45, 0x2f, 0xc0, 0xb0, + 0xe3, 0x79, 0x3c, 0x3f, 0x86, 0xc4, 0xe2, 0x3e, 0x1d, 0x5d, 0x43, 0x4e, 0x83, 0xb0, 0x89, 0x67, + 0xff, 0x69, 0x09, 0x26, 0xf6, 0x91, 0x29, 0x5d, 0x19, 0x7f, 0x95, 0xbe, 0x33, 0xfe, 0x44, 0x8e, + 0xc2, 0x60, 0x8f, 0x1c, 0x85, 0x17, 0x60, 0x38, 0x21, 0x4e, 0x5b, 0x04, 0x64, 0x09, 0x4f, 0x80, + 0x3e, 0x01, 0xd6, 0x20, 0x6c, 0xe2, 0x51, 0x29, 0x36, 0xe6, 0x34, 0x1a, 0x24, 0x8e, 0x65, 0x12, + 0x82, 0xf0, 0xa6, 0x16, 0x96, 0xe1, 0xc0, 0x9c, 0xd4, 0xd3, 0x29, 0x16, 0x38, 0xc3, 0x32, 0x3b, + 0xe0, 0xb5, 0x3e, 0x07, 0xfc, 0x6b, 0x25, 0x78, 0x7c, 0x4f, 0xed, 0xd6, 0x77, 0x7e, 0x48, 0x27, + 0x26, 0x51, 0x76, 0xe2, 0xdc, 0x88, 0x49, 0x84, 0x19, 0x84, 0x8f, 0x52, 0x18, 0x1a, 0xf7, 0x6b, + 0x17, 0x9d, 0xbc, 0xc4, 0x47, 0x29, 0xc5, 0x02, 0x67, 0x58, 0x1e, 0x76, 0x5a, 0xfe, 0xfd, 0x12, + 0x3c, 0xd9, 0x87, 0x0d, 0x50, 0x60, 0x92, 0x57, 0x3a, 0xd5, 0xae, 0xfc, 0x80, 0x32, 0x22, 0x0f, + 0x39, 0x5c, 0x5f, 0x2f, 0xc1, 0xf9, 0xde, 0xaa, 0x18, 0xfd, 0x18, 0x9c, 0x88, 0x54, 0x14, 0x96, + 0x99, 0xa5, 0x77, 0x9a, 0x7b, 0x12, 0x52, 0x20, 0x9c, 0xc5, 0x45, 0x93, 0x00, 0xa1, 0x93, 0x6c, + 0xc4, 0x97, 0xb6, 0xdd, 0x38, 0x11, 0x55, 0x68, 0xc6, 0xf8, 0xd9, 0x95, 0x6c, 0xc5, 0x06, 0x06, + 0x65, 0xc7, 0xfe, 0xcd, 0x06, 0xd7, 0x83, 0x84, 0x3f, 0xc4, 0xb7, 0x11, 0xa7, 0xe5, 0x9d, 0x1d, + 0x06, 0x08, 0x67, 0x71, 0x29, 0x3b, 0x76, 0x3a, 0xca, 0x3b, 0xca, 0xf7, 0x17, 0x8c, 0xdd, 0x82, + 0x6a, 0xc5, 0x06, 0x46, 0x36, 0xff, 0xb0, 0xb2, 0x7f, 0xfe, 0xa1, 0xfd, 0x4f, 0x4a, 0x70, 0xae, + 0xa7, 0x29, 0xd7, 0xdf, 0x02, 0x7c, 0xf8, 0x72, 0x06, 0x0f, 0x37, 0x77, 0x0e, 0x98, 0xdb, 0xf6, + 0xc7, 0x3d, 0x66, 0x9a, 0xc8, 0x6d, 0x3b, 0x7c, 0x72, 0xf8, 0xc3, 0x37, 0x9e, 0x5d, 0xe9, 0x6c, + 0x03, 0x07, 0x48, 0x67, 0xcb, 0x7c, 0x8c, 0x4a, 0x9f, 0x0b, 0xf9, 0xcf, 0xcb, 0x3d, 0x87, 0x97, + 0x6e, 0xfd, 0xfa, 0xf2, 0xd3, 0xce, 0xc2, 0x49, 0xd7, 0x67, 0xf7, 0x37, 0xad, 0x74, 0xd6, 0x44, + 0x61, 0x92, 0x52, 0xfa, 0xf6, 0xf4, 0xf9, 0x0c, 0x1c, 0x77, 0x3d, 0xf1, 0x10, 0xa6, 0x17, 0x1e, + 0x6e, 0x48, 0x0f, 0x96, 0xe0, 0x8a, 0x96, 0xe0, 0xac, 0x1c, 0x8a, 0x0d, 0x27, 0x22, 0x4d, 0xa1, + 0x46, 0x62, 0x91, 0x50, 0x71, 0x8e, 0x27, 0x65, 0xe4, 0x20, 0xe0, 0xfc, 0xe7, 0xd8, 0x95, 0x39, + 0x41, 0xe8, 0x36, 0xc4, 0x26, 0x47, 0x5f, 0x99, 0x43, 0x1b, 0x31, 0x87, 0xd9, 0x1f, 0x82, 0x9a, + 0x7a, 0x7f, 0x1e, 0xd6, 0xad, 0x26, 0x5d, 0x57, 0x58, 0xb7, 0x9a, 0x71, 0x06, 0x16, 0xfd, 0x5a, + 0xd4, 0x24, 0xce, 0xac, 0x9e, 0x6b, 0x64, 0x87, 0xd9, 0xc7, 0xf6, 0x0f, 0xc1, 0x88, 0xf2, 0xb3, + 0xf4, 0x7b, 0x91, 0x90, 0xfd, 0xc5, 0x41, 0x18, 0x4d, 0x15, 0x07, 0x4c, 0x39, 0x58, 0xad, 0x7d, + 0x1d, 0xac, 0x2c, 0x4c, 0xbf, 0xe3, 0xcb, 0x5b, 0xc6, 0x8c, 0x30, 0xfd, 0x8e, 0x4f, 0x30, 0x87, + 0x51, 0xf3, 0xb6, 0x19, 0xed, 0xe0, 0x8e, 0x2f, 0xc2, 0x69, 0x95, 0x79, 0x3b, 0xcb, 0x5a, 0xb1, + 0x80, 0xa2, 0x8f, 0x5b, 0x30, 0x12, 0x33, 0xef, 0x3d, 0x77, 0x4f, 0x8b, 0x49, 0x77, 0xf5, 0xe8, + 0xb5, 0x0f, 0x55, 0x21, 0x4c, 0x16, 0x21, 0x63, 0xb6, 0xe0, 0x14, 0x47, 0xf4, 0x29, 0x0b, 0x6a, + 0xea, 0x32, 0x14, 0x71, 0x15, 0xe0, 0x4a, 0xb1, 0xb5, 0x17, 0xb9, 0x5f, 0x53, 0x1d, 0x84, 0xa8, + 0x22, 0x78, 0x58, 0x33, 0x46, 0xb1, 0xf2, 0x1d, 0x0f, 0x1d, 0x8f, 0xef, 0x18, 0x72, 0xfc, 0xc6, + 0xef, 0x86, 0x5a, 0xdb, 0xf1, 0xdd, 0x75, 0x12, 0x27, 0xdc, 0x9d, 0x2b, 0x4b, 0xc2, 0xca, 0x46, + 0xac, 0xe1, 0x54, 0x21, 0xc7, 0xec, 0xc5, 0x12, 0xc3, 0xff, 0xca, 0x14, 0xf2, 0x8a, 0x6e, 0xc6, + 0x26, 0x8e, 0xe9, 0x2c, 0x86, 0x07, 0xea, 0x2c, 0x1e, 0xde, 0xdb, 0x59, 0x6c, 0xff, 0x43, 0x0b, + 0xce, 0xe6, 0x7e, 0xb5, 0x87, 0x37, 0xf0, 0xd1, 0xfe, 0x52, 0x05, 0x4e, 0xe7, 0x54, 0xf9, 0x44, + 0x3b, 0xe6, 0x7c, 0xb6, 0x8a, 0x88, 0x21, 0x48, 0x1f, 0x89, 0xcb, 0x61, 0xcc, 0x99, 0xc4, 0x07, + 0x3b, 0xaa, 0xd1, 0xc7, 0x25, 0xe5, 0xfb, 0x7b, 0x5c, 0x62, 0x4c, 0xcb, 0x81, 0x07, 0x3a, 0x2d, + 0x2b, 0xfb, 0x9c, 0x61, 0xfc, 0x9a, 0x05, 0xe3, 0xed, 0x1e, 0xa5, 0xe5, 0x85, 0xe3, 0xf1, 0xe6, + 0xf1, 0x14, 0xae, 0xaf, 0x3f, 0x76, 0x77, 0x77, 0xa2, 0x67, 0x45, 0x7f, 0xdc, 0xb3, 0x57, 0xf6, + 0xb7, 0xcb, 0xc0, 0x4a, 0xcc, 0xb2, 0x4a, 0x6e, 0x3b, 0xe8, 0x63, 0x66, 0xb1, 0x60, 0xab, 0xa8, + 0xc2, 0xb6, 0x9c, 0xb8, 0x2a, 0x36, 0xcc, 0x47, 0x30, 0xaf, 0xf6, 0x70, 0x56, 0x68, 0x95, 0xfa, + 0x10, 0x5a, 0x9e, 0xac, 0xca, 0x5c, 0x2e, 0xbe, 0x2a, 0x73, 0x2d, 0x5b, 0x91, 0x79, 0xef, 0x4f, + 0x3c, 0xf0, 0x50, 0x7e, 0xe2, 0xbf, 0x65, 0x71, 0xc1, 0x93, 0xf9, 0x0a, 0xda, 0x32, 0xb0, 0xf6, + 0xb0, 0x0c, 0x9e, 0x81, 0x6a, 0x4c, 0xbc, 0xf5, 0x2b, 0xc4, 0xf1, 0x84, 0x05, 0xa1, 0xcf, 0xaf, + 0x45, 0x3b, 0x56, 0x18, 0xec, 0xda, 0x56, 0xcf, 0x0b, 0xee, 0x5c, 0x6a, 0x87, 0xc9, 0x8e, 0xb0, + 0x25, 0xf4, 0xb5, 0xad, 0x0a, 0x82, 0x0d, 0x2c, 0xfb, 0x6f, 0x97, 0xf8, 0x0c, 0x14, 0x41, 0x10, + 0x2f, 0x66, 0x2e, 0xda, 0xeb, 0x3f, 0x7e, 0xe0, 0x23, 0x00, 0x0d, 0x75, 0x45, 0xbd, 0x38, 0x13, + 0xba, 0x72, 0xe4, 0xfb, 0xb3, 0x05, 0x3d, 0xfd, 0x1a, 0xba, 0x0d, 0x1b, 0xfc, 0x52, 0xb2, 0xb4, + 0xbc, 0xaf, 0x2c, 0x4d, 0x89, 0x95, 0x81, 0x7d, 0xb4, 0xdd, 0x9f, 0x5a, 0x90, 0xb2, 0x88, 0x50, + 0x08, 0x15, 0xda, 0xdd, 0x9d, 0x62, 0x6e, 0xdf, 0x37, 0x49, 0x53, 0xd1, 0x28, 0xa6, 0x3d, 0xfb, + 0x89, 0x39, 0x23, 0xe4, 0x89, 0x58, 0x09, 0x3e, 0xaa, 0xd7, 0x8b, 0x63, 0x78, 0x25, 0x08, 0x36, + 0xf9, 0xc1, 0xa6, 0x8e, 0xbb, 0xb0, 0x5f, 0x84, 0x53, 0x5d, 0x9d, 0x62, 0x77, 0x6a, 0x05, 0x54, + 0xfb, 0x64, 0xa6, 0x2b, 0x4b, 0xe0, 0xc4, 0x1c, 0x66, 0x7f, 0xdd, 0x82, 0x93, 0x59, 0xf2, 0xe8, + 0x2d, 0x0b, 0x4e, 0xc5, 0x59, 0x7a, 0xc7, 0x35, 0x76, 0x2a, 0xde, 0xb1, 0x0b, 0x84, 0xbb, 0x3b, + 0x61, 0xff, 0x5f, 0x31, 0xf9, 0x6f, 0xb9, 0x7e, 0x33, 0xb8, 0xa3, 0x0c, 0x13, 0xab, 0xa7, 0x61, + 0x42, 0xd7, 0x63, 0x63, 0x83, 0x34, 0x3b, 0x5e, 0x57, 0xe6, 0xe8, 0x8a, 0x68, 0xc7, 0x0a, 0x83, + 0x25, 0xca, 0x75, 0x44, 0xd9, 0xf6, 0xcc, 0xa4, 0x9c, 0x15, 0xed, 0x58, 0x61, 0xa0, 0xe7, 0x61, + 0xc4, 0x78, 0x49, 0x39, 0x2f, 0x99, 0x41, 0x6e, 0xa8, 0xcc, 0x18, 0xa7, 0xb0, 0xd0, 0x24, 0x80, + 0x32, 0x72, 0xa4, 0x8a, 0x64, 0x8e, 0x22, 0x25, 0x89, 0x62, 0x6c, 0x60, 0xb0, 0xb4, 0x54, 0xaf, + 0x13, 0x33, 0x1f, 0xff, 0xa0, 0x2e, 0x25, 0x3a, 0x23, 0xda, 0xb0, 0x82, 0x52, 0x69, 0xd2, 0x76, + 0xfc, 0x8e, 0xe3, 0xd1, 0x11, 0x12, 0x5b, 0x3f, 0xb5, 0x0c, 0x17, 0x15, 0x04, 0x1b, 0x58, 0xf4, + 0x8d, 0x13, 0xb7, 0x4d, 0x5e, 0x09, 0x7c, 0x19, 0xa7, 0xa6, 0x8f, 0x7d, 0x44, 0x3b, 0x56, 0x18, + 0xf6, 0x7f, 0xb5, 0xe0, 0x84, 0x4e, 0x72, 0xe7, 0xb7, 0x67, 0x9b, 0x3b, 0x55, 0x6b, 0xdf, 0x9d, + 0x6a, 0x3a, 0xfb, 0xb7, 0xd4, 0x57, 0xf6, 0xaf, 0x99, 0x98, 0x5b, 0xde, 0x33, 0x31, 0xf7, 0xfb, + 0xf5, 0xcd, 0xac, 0x3c, 0x83, 0x77, 0x38, 0xef, 0x56, 0x56, 0x64, 0xc3, 0x60, 0xc3, 0x51, 0x15, + 0x5e, 0x46, 0xf8, 0xde, 0x61, 0x66, 0x9a, 0x21, 0x09, 0x88, 0xbd, 0x04, 0x35, 0x75, 0xfa, 0x21, + 0x37, 0xaa, 0x56, 0xfe, 0x46, 0xb5, 0xaf, 0x04, 0xc1, 0xfa, 0xda, 0x37, 0xbe, 0xf3, 0xc4, 0x3b, + 0x7e, 0xef, 0x3b, 0x4f, 0xbc, 0xe3, 0x8f, 0xbe, 0xf3, 0xc4, 0x3b, 0x3e, 0x7e, 0xf7, 0x09, 0xeb, + 0x1b, 0x77, 0x9f, 0xb0, 0x7e, 0xef, 0xee, 0x13, 0xd6, 0x1f, 0xdd, 0x7d, 0xc2, 0xfa, 0xf6, 0xdd, + 0x27, 0xac, 0x2f, 0xfc, 0xa7, 0x27, 0xde, 0xf1, 0x4a, 0x6e, 0xa0, 0x22, 0xfd, 0xf1, 0x6c, 0xa3, + 0x39, 0xb5, 0x75, 0x91, 0xc5, 0xca, 0xd1, 0xe5, 0x35, 0x65, 0xcc, 0xa9, 0x29, 0xb9, 0xbc, 0xfe, + 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2b, 0x8e, 0xba, 0x30, 0x2f, 0xe1, 0x00, 0x00, } func (m *AWSAuthConfig) Marshal() (dAtA []byte, err error) { @@ -12794,6 +12794,16 @@ func (m *RevisionHistory) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.InitiatedBy.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 if len(m.Revisions) > 0 { for iNdEx := len(m.Revisions) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Revisions[iNdEx]) @@ -17226,6 +17236,8 @@ func (m *RevisionHistory) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } + l = m.InitiatedBy.Size() + n += 1 + l + sovGenerated(uint64(l)) return n } @@ -19972,6 +19984,7 @@ func (this *RevisionHistory) String() string { `DeployStartedAt:` + strings.Replace(fmt.Sprintf("%v", this.DeployStartedAt), "Time", "v1.Time", 1) + `,`, `Sources:` + repeatedStringForSources + `,`, `Revisions:` + fmt.Sprintf("%v", this.Revisions) + `,`, + `InitiatedBy:` + strings.Replace(strings.Replace(this.InitiatedBy.String(), "OperationInitiator", "OperationInitiator", 1), `&`, ``, 1) + `,`, `}`, }, "") return s @@ -45766,6 +45779,39 @@ func (m *RevisionHistory) Unmarshal(dAtA []byte) error { } m.Revisions = append(m.Revisions, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field InitiatedBy", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.InitiatedBy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/pkg/apis/application/v1alpha1/generated.proto b/pkg/apis/application/v1alpha1/generated.proto index c080c04c3a088..a2b17373de259 100644 --- a/pkg/apis/application/v1alpha1/generated.proto +++ b/pkg/apis/application/v1alpha1/generated.proto @@ -1895,6 +1895,9 @@ message RevisionHistory { // Revisions holds the revision of each source in sources field the sync was performed against repeated string revisions = 9; + + // InitiatedBy contains information about who initiated the operations + optional OperationInitiator initiatedBy = 10; } // RevisionMetadata contains metadata for a specific revision in a Git repository diff --git a/pkg/apis/application/v1alpha1/openapi_generated.go b/pkg/apis/application/v1alpha1/openapi_generated.go index 723ea884cb75b..2274e252a4148 100644 --- a/pkg/apis/application/v1alpha1/openapi_generated.go +++ b/pkg/apis/application/v1alpha1/openapi_generated.go @@ -6662,12 +6662,19 @@ func schema_pkg_apis_application_v1alpha1_RevisionHistory(ref common.ReferenceCa }, }, }, + "initiatedBy": { + SchemaProps: spec.SchemaProps{ + Description: "InitiatedBy contains information about who initiated the operations", + Default: map[string]interface{}{}, + Ref: ref("github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OperationInitiator"), + }, + }, }, Required: []string{"deployedAt", "id"}, }, }, Dependencies: []string{ - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSource", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ApplicationSource", "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.OperationInitiator", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, } } diff --git a/pkg/apis/application/v1alpha1/types.go b/pkg/apis/application/v1alpha1/types.go index 49be82a443bc4..e271003ad6ad0 100644 --- a/pkg/apis/application/v1alpha1/types.go +++ b/pkg/apis/application/v1alpha1/types.go @@ -1401,6 +1401,8 @@ type RevisionHistory struct { Sources ApplicationSources `json:"sources,omitempty" protobuf:"bytes,8,opt,name=sources"` // Revisions holds the revision of each source in sources field the sync was performed against Revisions []string `json:"revisions,omitempty" protobuf:"bytes,9,opt,name=revisions"` + // InitiatedBy contains information about who initiated the operations + InitiatedBy OperationInitiator `json:"initiatedBy,omitempty" protobuf:"bytes,10,opt,name=initiatedBy"` } // ApplicationWatchEvent contains information about application change. diff --git a/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go index c10b610cbd5a7..8c851067a6be3 100644 --- a/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go @@ -3689,6 +3689,7 @@ func (in *RevisionHistory) DeepCopyInto(out *RevisionHistory) { *out = make([]string, len(*in)) copy(*out, *in) } + out.InitiatedBy = in.InitiatedBy return } diff --git a/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx b/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx index 55734b69ea0c4..37908fb1a35b8 100644 --- a/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx +++ b/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx @@ -1,4 +1,5 @@ import {DataLoader, DropDownMenu, Duration} from 'argo-ui'; +import {InitiatedBy} from './initiated-by'; import * as moment from 'moment'; import * as React from 'react'; import {Revision, Timestamp} from '../../../shared/components'; @@ -42,6 +43,12 @@ export const ApplicationDeploymentHistory = ({
{(info.deployStartedAt && ) || 'Unknown'} +
+
+ Initiated by: +
+ +

Active for: diff --git a/ui/src/app/applications/components/application-deployment-history/initiated-by.tsx b/ui/src/app/applications/components/application-deployment-history/initiated-by.tsx new file mode 100644 index 0000000000000..f691389b5daca --- /dev/null +++ b/ui/src/app/applications/components/application-deployment-history/initiated-by.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; + +export const InitiatedBy = (props: {username: string; automated: boolean}) => { + const initiator = props.automated ? 'automated sync policy' : props.username || 'Unknown'; + return {initiator}; +}; diff --git a/ui/src/app/shared/models.ts b/ui/src/app/shared/models.ts index 861513523c7a3..823c61c34dc9a 100644 --- a/ui/src/app/shared/models.ts +++ b/ui/src/app/shared/models.ts @@ -297,6 +297,7 @@ export interface RevisionHistory { sources: ApplicationSource[]; deployStartedAt: models.Time; deployedAt: models.Time; + initiatedBy: OperationInitiator; } export type SyncStatusCode = 'Unknown' | 'Synced' | 'OutOfSync'; From 7d1f6a1e94b43d85d93a4964e48ab576ac02c763 Mon Sep 17 00:00:00 2001 From: Sanchaai Mathiyarasan Date: Thu, 28 Dec 2023 05:15:36 -0500 Subject: [PATCH 52/66] Update contributors-quickstart.md to include minikube as local cluster alternative. (#16690) Signed-off-by: Sanchaai Mathiyarasan --- docs/developer-guide/contributors-quickstart.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/developer-guide/contributors-quickstart.md b/docs/developer-guide/contributors-quickstart.md index 0e98fab7ec940..cb3fe67e6e212 100644 --- a/docs/developer-guide/contributors-quickstart.md +++ b/docs/developer-guide/contributors-quickstart.md @@ -23,16 +23,29 @@ git clone https://github.com/argoproj/argo-cd.git -### Install or Upgrade `kind` (Optional - Should work with any local cluster) +### Install or Upgrade a Tool for Running Local Clusters (e.g. kind or minikube) + +#### Installation guide for kind: +#### Installation guide for minikube: + + + ### Start Your Local Cluster +For example, if you are using kind: ```shell kind create cluster ``` +Or, if you are using minikube: + +```shell +minikube start +``` + ### Install Argo CD ```shell From 20f718248998e698370fa43bb74f548072835c29 Mon Sep 17 00:00:00 2001 From: Mark Estiller Date: Thu, 28 Dec 2023 05:17:00 -0500 Subject: [PATCH 53/66] Update contributors-quickstart.md to include a link to Go's installation guide (#16691) Signed-off-by: Mark Estiller --- docs/developer-guide/contributors-quickstart.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/developer-guide/contributors-quickstart.md b/docs/developer-guide/contributors-quickstart.md index cb3fe67e6e212..a7646a6cf5f25 100644 --- a/docs/developer-guide/contributors-quickstart.md +++ b/docs/developer-guide/contributors-quickstart.md @@ -9,6 +9,8 @@ and the [toolchain guide](toolchain-guide.md). ### Install Go + + Install version 1.18 or newer (Verify version by running `go version`) ### Clone the Argo CD repo From f0f4a4e438eeff1ee985e4a0535b9597175aefa1 Mon Sep 17 00:00:00 2001 From: Nenad Strainovic Date: Thu, 28 Dec 2023 12:32:15 +0100 Subject: [PATCH 54/66] Multiple hook delete policies can be specified as a comma separated list (#16659) Based on https://github.com/argoproj/gitops-engine/blob/master/pkg/sync/hook/delete_policy.go#L13 multiple hook delete policies are also allowed. Signed-off-by: Nenad Strainovic --- docs/user-guide/resource_hooks.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/user-guide/resource_hooks.md b/docs/user-guide/resource_hooks.md index 74098b1810011..a6fdaf8bd2e05 100644 --- a/docs/user-guide/resource_hooks.md +++ b/docs/user-guide/resource_hooks.md @@ -62,6 +62,7 @@ metadata: argocd.argoproj.io/hook: PostSync argocd.argoproj.io/hook-delete-policy: HookSucceeded ``` +Multiple hook delete policies can be specified as a comma separated list. The following policies define when the hook will be deleted. From 80ca563909505ed18fea65e5e47ad6c9db87603e Mon Sep 17 00:00:00 2001 From: Noam Gal Date: Sun, 31 Dec 2023 18:46:58 +0200 Subject: [PATCH 55/66] fix broken link (#16722) Signed-off-by: Noam Gal --- docs/operator-manual/notifications/triggers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/operator-manual/notifications/triggers.md b/docs/operator-manual/notifications/triggers.md index c3e2dc601296b..02d0228c40997 100644 --- a/docs/operator-manual/notifications/triggers.md +++ b/docs/operator-manual/notifications/triggers.md @@ -1,7 +1,7 @@ The trigger defines the condition when the notification should be sent. The definition includes name, condition and notification templates reference. The condition is a predicate expression that returns true if the notification should be sent. The trigger condition evaluation is powered by [antonmedv/expr](https://github.com/antonmedv/expr). -The condition language syntax is described at [Language-Definition.md](https://github.com/antonmedv/expr/blob/master/docs/Language-Definition.md). +The condition language syntax is described at [language-definition.md](https://github.com/antonmedv/expr/blob/master/docs/language-definition.md). The trigger is configured in the `argocd-notifications-cm` ConfigMap. For example the following trigger sends a notification when application sync status changes to `Unknown` using the `app-sync-status` template: From a40330f5c80b413e22d1001ae27d75b3b70cbb72 Mon Sep 17 00:00:00 2001 From: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Date: Wed, 3 Jan 2024 12:08:24 -0500 Subject: [PATCH 56/66] docs: configmap items are strings (#16737) Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --- docs/operator-manual/notifications/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/operator-manual/notifications/index.md b/docs/operator-manual/notifications/index.md index 3609089e23d08..eccca906ae91b 100644 --- a/docs/operator-manual/notifications/index.md +++ b/docs/operator-manual/notifications/index.md @@ -67,7 +67,7 @@ metadata: name: argocd-cmd-params-cm data: application.namespaces: app-team-one, app-team-two - notificationscontroller.selfservice.enabled: true + notificationscontroller.selfservice.enabled: "true" ``` To use this feature, you can deploy configmap named `argocd-notifications-cm` and possibly a secret `argocd-notifications-secret` in the namespace where the Argo CD application lives. From 1975074de5fb80cb56c94add93ba55979df67b98 Mon Sep 17 00:00:00 2001 From: Nicholas Morey Date: Wed, 3 Jan 2024 12:58:26 -0500 Subject: [PATCH 57/66] docs: remove core install commands from getting started (#16735) * docs: remove core install commands from getting started I often accidentally run the core install commands when quickly copying and pasting commands from the getting started guide, which leads to confusion. I've also spent plenty of time helping newcomers to Argo CD who have done the same and are confused when they can't reach the UI. Given that this is a "getting started" guide, it's ideal to provide only the commands required. I've removed the commands and left the link out to the core install page for those who are interested in going down that path. Signed-off-by: Nicholas Morey * fix: use link to install commands in core docs Signed-off-by: Nicholas Morey * fix: use tip and improve wording Signed-off-by: Nicholas Morey --------- Signed-off-by: Nicholas Morey --- docs/getting_started.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index d81bd08897ad8..1000206eaf972 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -22,12 +22,8 @@ This will create a new namespace, `argocd`, where Argo CD services and applicati The installation manifests include `ClusterRoleBinding` resources that reference `argocd` namespace. If you are installing Argo CD into a different namespace then make sure to update the namespace reference. -If you are not interested in UI, SSO, multi-cluster features then you can install [core](operator-manual/installation.md#core) Argo CD components only: - -```bash -kubectl create namespace argocd -kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/core-install.yaml -``` +!!! tip + If you are not interested in UI, SSO, and multi-cluster features, then you can install only the [core](operator-manual/core/#installing) Argo CD components. This default installation will have a self-signed certificate and cannot be accessed without a bit of extra work. Do one of: From d5955508da5e1c1d26a2526d826bafe4f697b162 Mon Sep 17 00:00:00 2001 From: Greg Werner Date: Thu, 4 Jan 2024 00:35:33 -0500 Subject: [PATCH 58/66] Update USERS.md with IllumiDesk (#16742) Signed-off-by: Greg Werner --- USERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/USERS.md b/USERS.md index ea0c44ae1ff4b..9059df8450c33 100644 --- a/USERS.md +++ b/USERS.md @@ -128,6 +128,7 @@ Currently, the following organizations are **officially** using Argo CD: 1. [IBM](https://www.ibm.com/) 1. [Ibotta](https://home.ibotta.com) 1. [IITS-Consulting](https://iits-consulting.de) +1. [IllumiDesk](https://www.illumidesk.com) 1. [imaware](https://imaware.health) 1. [Indeed](https://indeed.com) 1. [Index Exchange](https://www.indexexchange.com/) From 1372529d5674405d214e8d96f91388a52db76b63 Mon Sep 17 00:00:00 2001 From: Yi Cai Date: Fri, 5 Jan 2024 11:53:08 -0500 Subject: [PATCH 59/66] fix(ui):Fixed log horizontal scroll for issue #16411 (#16727) * Fixed log horizontal scroll Signed-off-by: Yi Cai * Updated log line-height Signed-off-by: Yi Cai --------- Signed-off-by: Yi Cai --- .../components/pod-logs-viewer/pod-logs-viewer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/app/applications/components/pod-logs-viewer/pod-logs-viewer.tsx b/ui/src/app/applications/components/pod-logs-viewer/pod-logs-viewer.tsx index 1ef2d83815821..309287fab2f37 100644 --- a/ui/src/app/applications/components/pod-logs-viewer/pod-logs-viewer.tsx +++ b/ui/src/app/applications/components/pod-logs-viewer/pod-logs-viewer.tsx @@ -149,9 +149,9 @@ export const PodsLogsViewer = (props: PodLogsProps) => { const logsContent = (width: number, height: number, isWrapped: boolean) => (
{logs.map((log, lineNum) => ( -
+                
{renderLog(log, lineNum)} -
+
))}
); From 4afddf71cce23105b0989a497054c5faa85dfa92 Mon Sep 17 00:00:00 2001 From: Alexander Matyushentsev Date: Fri, 5 Jan 2024 08:53:52 -0800 Subject: [PATCH 60/66] feat: webhook should use 'rename' to copy app manifests of previous commit (#16754) Signed-off-by: Alexander Matyushentsev --- .../commands/argocd_application_controller.go | 12 +- .../commands/argocd_repo_server.go | 6 +- cmd/argocd-server/commands/argocd_server.go | 13 +- cmd/argocd/commands/headless/headless.go | 6 + .../server-commands/argocd-server.md | 147 ++++++++++-------- reposerver/cache/cache.go | 9 +- server/cache/cache.go | 3 +- server/server.go | 3 +- util/cache/appstate/cache.go | 3 +- util/cache/cache.go | 110 ++++++++++--- util/cache/client.go | 1 + util/cache/inmemory.go | 10 ++ util/cache/mocks/cacheclient.go | 11 +- util/cache/redis.go | 4 + util/cache/twolevelclient.go | 8 + util/webhook/webhook.go | 10 +- 16 files changed, 243 insertions(+), 113 deletions(-) diff --git a/cmd/argocd-application-controller/commands/argocd_application_controller.go b/cmd/argocd-application-controller/commands/argocd_application_controller.go index 796a645f03393..d5ef88a1702b6 100644 --- a/cmd/argocd-application-controller/commands/argocd_application_controller.go +++ b/cmd/argocd-application-controller/commands/argocd_application_controller.go @@ -6,11 +6,12 @@ import ( "math" "time" - "github.com/argoproj/argo-cd/v2/pkg/ratelimiter" "github.com/argoproj/pkg/stats" "github.com/redis/go-redis/v9" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + kubeerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" @@ -20,6 +21,7 @@ import ( "github.com/argoproj/argo-cd/v2/controller/sharding" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" + "github.com/argoproj/argo-cd/v2/pkg/ratelimiter" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" cacheutil "github.com/argoproj/argo-cd/v2/util/cache" appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate" @@ -31,8 +33,6 @@ import ( "github.com/argoproj/argo-cd/v2/util/settings" "github.com/argoproj/argo-cd/v2/util/tls" "github.com/argoproj/argo-cd/v2/util/trace" - kubeerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const ( @@ -227,8 +227,10 @@ func NewCommand() *cobra.Command { command.Flags().Float64Var(&workqueueRateLimit.BackoffFactor, "wq-backoff-factor", env.ParseFloat64FromEnv("WORKQUEUE_BACKOFF_FACTOR", 1.5, 0, math.MaxFloat64), "Set Workqueue Per Item Rate Limiter Backoff Factor, default is 1.5") command.Flags().BoolVar(&enableDynamicClusterDistribution, "dynamic-cluster-distribution-enabled", env.ParseBoolFromEnv(common.EnvEnableDynamicClusterDistribution, false), "Enables dynamic cluster distribution.") command.Flags().BoolVar(&serverSideDiff, "server-side-diff-enabled", env.ParseBoolFromEnv(common.EnvServerSideDiff, false), "Feature flag to enable ServerSide diff. Default (\"false\")") - cacheSource = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) { - redisClient = client + cacheSource = appstatecache.AddCacheFlagsToCmd(&command, cacheutil.Options{ + OnClientCreated: func(client *redis.Client) { + redisClient = client + }, }) return &command } diff --git a/cmd/argocd-repo-server/commands/argocd_repo_server.go b/cmd/argocd-repo-server/commands/argocd_repo_server.go index 2a16d192e01bd..84b50e7cd5ab9 100644 --- a/cmd/argocd-repo-server/commands/argocd_repo_server.go +++ b/cmd/argocd-repo-server/commands/argocd_repo_server.go @@ -210,8 +210,10 @@ func NewCommand() *cobra.Command { command.Flags().StringVar(&helmManifestMaxExtractedSize, "helm-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of helm manifest archives when extracted") command.Flags().BoolVar(&disableManifestMaxExtractedSize, "disable-helm-manifest-max-extracted-size", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE", false), "Disable maximum size of helm manifest archives when extracted") tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command) - cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) { - redisClient = client + cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, cacheutil.Options{ + OnClientCreated: func(client *redis.Client) { + redisClient = client + }, }) return &command } diff --git a/cmd/argocd-server/commands/argocd_server.go b/cmd/argocd-server/commands/argocd_server.go index 6eeb5b299ce0f..72fe765c32c56 100644 --- a/cmd/argocd-server/commands/argocd_server.go +++ b/cmd/argocd-server/commands/argocd_server.go @@ -18,8 +18,10 @@ import ( "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned" "github.com/argoproj/argo-cd/v2/reposerver/apiclient" + reposervercache "github.com/argoproj/argo-cd/v2/reposerver/cache" "github.com/argoproj/argo-cd/v2/server" servercache "github.com/argoproj/argo-cd/v2/server/cache" + cacheutil "github.com/argoproj/argo-cd/v2/util/cache" "github.com/argoproj/argo-cd/v2/util/cli" "github.com/argoproj/argo-cd/v2/util/dex" "github.com/argoproj/argo-cd/v2/util/env" @@ -64,6 +66,7 @@ func NewCommand() *cobra.Command { enableGZip bool tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error) cacheSrc func() (*servercache.Cache, error) + repoServerCacheSrc func() (*reposervercache.Cache, error) frameOptions string contentSecurityPolicy string repoServerPlaintext bool @@ -105,6 +108,8 @@ func NewCommand() *cobra.Command { errors.CheckError(err) cache, err := cacheSrc() errors.CheckError(err) + repoServerCache, err := repoServerCacheSrc() + errors.CheckError(err) kubeclientset := kubernetes.NewForConfigOrDie(config) @@ -183,6 +188,7 @@ func NewCommand() *cobra.Command { EnableGZip: enableGZip, TLSConfigCustomizer: tlsConfigCustomizer, Cache: cache, + RepoServerCache: repoServerCache, XFrameOptions: frameOptions, ContentSecurityPolicy: contentSecurityPolicy, RedisClient: redisClient, @@ -254,8 +260,11 @@ func NewCommand() *cobra.Command { command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", env.StringsFromEnv("ARGOCD_APPLICATION_NAMESPACES", []string{}, ","), "List of additional namespaces where application resources can be managed in") command.Flags().BoolVar(&enableProxyExtension, "enable-proxy-extension", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_PROXY_EXTENSION", false), "Enable Proxy Extension feature") tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(command) - cacheSrc = servercache.AddCacheFlagsToCmd(command, func(client *redis.Client) { - redisClient = client + cacheSrc = servercache.AddCacheFlagsToCmd(command, cacheutil.Options{ + OnClientCreated: func(client *redis.Client) { + redisClient = client + }, }) + repoServerCacheSrc = reposervercache.AddCacheFlagsToCmd(command, cacheutil.Options{FlagPrefix: "repo-server-"}) return command } diff --git a/cmd/argocd/commands/headless/headless.go b/cmd/argocd/commands/headless/headless.go index 5c9828fc9f131..d48019a2216b9 100644 --- a/cmd/argocd/commands/headless/headless.go +++ b/cmd/argocd/commands/headless/headless.go @@ -78,6 +78,12 @@ func (c *forwardCacheClient) Set(item *cache.Item) error { }) } +func (c *forwardCacheClient) Rename(oldKey string, newKey string, expiration time.Duration) error { + return c.doLazy(func(client cache.CacheClient) error { + return client.Rename(oldKey, newKey, expiration) + }) +} + func (c *forwardCacheClient) Get(key string, obj interface{}) error { return c.doLazy(func(client cache.CacheClient) error { return client.Get(key, obj) diff --git a/docs/operator-manual/server-commands/argocd-server.md b/docs/operator-manual/server-commands/argocd-server.md index e3dcc937243df..1da27d735e1cd 100644 --- a/docs/operator-manual/server-commands/argocd-server.md +++ b/docs/operator-manual/server-commands/argocd-server.md @@ -25,73 +25,86 @@ argocd-server [flags] ### Options ``` - --address string Listen on given address (default "0.0.0.0") - --app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s) - --application-namespaces strings List of additional namespaces where application resources can be managed in - --as string Username to impersonate for the operation - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --as-uid string UID to impersonate for the operation - --basehref string Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from / (default "/") - --certificate-authority string Path to a cert file for the certificate authority - --client-certificate string Path to a client certificate file for TLS - --client-key string Path to a client key file for TLS - --cluster string The name of the kubeconfig cluster to use - --connection-status-cache-expiration duration Cache expiration for cluster/repo connection status (default 1h0m0s) - --content-security-policy value Set Content-Security-Policy header in HTTP responses to value. To disable, set to "". (default "frame-ancestors 'self';") - --context string The name of the kubeconfig context to use - --default-cache-expiration duration Cache expiration default (default 24h0m0s) - --dex-server string Dex server address (default "argocd-dex-server:5556") - --dex-server-plaintext Use a plaintext client (non-TLS) to connect to dex server - --dex-server-strict-tls Perform strict validation of TLS certificates when connecting to dex server - --disable-auth Disable client authentication - --disable-compression If true, opt-out of response compression for all requests to the server - --enable-gzip Enable GZIP compression (default true) - --enable-proxy-extension Enable Proxy Extension feature - --gloglevel int Set the glog logging level - -h, --help help for argocd-server - --insecure Run server without TLS - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --kubeconfig string Path to a kube config. Only required if out-of-cluster - --logformat string Set the logging format. One of: text|json (default "text") - --login-attempts-expiration duration Cache expiration for failed login attempts (default 24h0m0s) - --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") - --metrics-address string Listen for metrics on given address (default "0.0.0.0") - --metrics-port int Start metrics on given port (default 8083) - -n, --namespace string If present, the namespace scope for this CLI request - --oidc-cache-expiration duration Cache expiration for OIDC state (default 3m0s) - --otlp-address string OpenTelemetry collector address to send traces to - --otlp-attrs strings List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value) - --otlp-headers stringToString List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2) (default []) - --otlp-insecure OpenTelemetry collector insecure mode (default true) - --password string Password for basic authentication to the API server - --port int Listen on given port (default 8080) - --proxy-url string If provided, this URL will be used to connect via proxy - --redis string Redis server hostname and port (e.g. argocd-redis:6379). - --redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation. - --redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt). - --redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt). - --redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none) (default "gzip") - --redis-insecure-skip-tls-verify Skip Redis server certificate validation. - --redis-use-tls Use TLS when connecting to Redis. - --redisdb int Redis database. - --repo-server string Repo server address (default "argocd-repo-server:8081") - --repo-server-plaintext Use a plaintext client (non-TLS) to connect to repository server - --repo-server-strict-tls Perform strict validation of TLS certificates when connecting to repo server - --repo-server-timeout-seconds int Repo server RPC call timeout seconds. (default 60) - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - --rootpath string Used if Argo CD is running behind reverse proxy under subpath different from / - --sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379). - --sentinelmaster string Redis sentinel master group name. (default "master") - --server string The address and port of the Kubernetes API server - --staticassets string Directory path that contains additional static assets (default "/shared/app") - --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. - --tlsciphers string The list of acceptable ciphers to be used when establishing TLS connections. Use 'list' to list available ciphers. (default "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:TLS_RSA_WITH_AES_256_GCM_SHA384") - --tlsmaxversion string The maximum SSL/TLS version that is acceptable (one of: 1.0|1.1|1.2|1.3) (default "1.3") - --tlsminversion string The minimum SSL/TLS version that is acceptable (one of: 1.0|1.1|1.2|1.3) (default "1.2") - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use - --username string Username for basic authentication to the API server - --x-frame-options value Set X-Frame-Options header in HTTP responses to value. To disable, set to "". (default "sameorigin") + --address string Listen on given address (default "0.0.0.0") + --app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s) + --application-namespaces strings List of additional namespaces where application resources can be managed in + --as string Username to impersonate for the operation + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --as-uid string UID to impersonate for the operation + --basehref string Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from / (default "/") + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --connection-status-cache-expiration duration Cache expiration for cluster/repo connection status (default 1h0m0s) + --content-security-policy value Set Content-Security-Policy header in HTTP responses to value. To disable, set to "". (default "frame-ancestors 'self';") + --context string The name of the kubeconfig context to use + --default-cache-expiration duration Cache expiration default (default 24h0m0s) + --dex-server string Dex server address (default "argocd-dex-server:5556") + --dex-server-plaintext Use a plaintext client (non-TLS) to connect to dex server + --dex-server-strict-tls Perform strict validation of TLS certificates when connecting to dex server + --disable-auth Disable client authentication + --disable-compression If true, opt-out of response compression for all requests to the server + --enable-gzip Enable GZIP compression (default true) + --enable-proxy-extension Enable Proxy Extension feature + --gloglevel int Set the glog logging level + -h, --help help for argocd-server + --insecure Run server without TLS + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to a kube config. Only required if out-of-cluster + --logformat string Set the logging format. One of: text|json (default "text") + --login-attempts-expiration duration Cache expiration for failed login attempts (default 24h0m0s) + --loglevel string Set the logging level. One of: debug|info|warn|error (default "info") + --metrics-address string Listen for metrics on given address (default "0.0.0.0") + --metrics-port int Start metrics on given port (default 8083) + -n, --namespace string If present, the namespace scope for this CLI request + --oidc-cache-expiration duration Cache expiration for OIDC state (default 3m0s) + --otlp-address string OpenTelemetry collector address to send traces to + --otlp-attrs strings List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value) + --otlp-headers stringToString List of OpenTelemetry collector extra headers sent with traces, headers are comma-separated key-value pairs(e.g. key1=value1,key2=value2) (default []) + --otlp-insecure OpenTelemetry collector insecure mode (default true) + --password string Password for basic authentication to the API server + --port int Listen on given port (default 8080) + --proxy-url string If provided, this URL will be used to connect via proxy + --redis string Redis server hostname and port (e.g. argocd-redis:6379). + --redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation. + --redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt). + --redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt). + --redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none) (default "gzip") + --redis-insecure-skip-tls-verify Skip Redis server certificate validation. + --redis-use-tls Use TLS when connecting to Redis. + --redisdb int Redis database. + --repo-cache-expiration duration Cache expiration for repo state, incl. app lists, app details, manifest generation, revision meta-data (default 24h0m0s) + --repo-server string Repo server address (default "argocd-repo-server:8081") + --repo-server-default-cache-expiration duration Cache expiration default (default 24h0m0s) + --repo-server-plaintext Use a plaintext client (non-TLS) to connect to repository server + --repo-server-redis string Redis server hostname and port (e.g. argocd-redis:6379). + --repo-server-redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation. + --repo-server-redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt). + --repo-server-redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt). + --repo-server-redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none) (default "gzip") + --repo-server-redis-insecure-skip-tls-verify Skip Redis server certificate validation. + --repo-server-redis-use-tls Use TLS when connecting to Redis. + --repo-server-redisdb int Redis database. + --repo-server-sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379). + --repo-server-sentinelmaster string Redis sentinel master group name. (default "master") + --repo-server-strict-tls Perform strict validation of TLS certificates when connecting to repo server + --repo-server-timeout-seconds int Repo server RPC call timeout seconds. (default 60) + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + --revision-cache-expiration duration Cache expiration for cached revision (default 3m0s) + --rootpath string Used if Argo CD is running behind reverse proxy under subpath different from / + --sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379). + --sentinelmaster string Redis sentinel master group name. (default "master") + --server string The address and port of the Kubernetes API server + --staticassets string Directory path that contains additional static assets (default "/shared/app") + --tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used. + --tlsciphers string The list of acceptable ciphers to be used when establishing TLS connections. Use 'list' to list available ciphers. (default "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:TLS_RSA_WITH_AES_256_GCM_SHA384") + --tlsmaxversion string The maximum SSL/TLS version that is acceptable (one of: 1.0|1.1|1.2|1.3) (default "1.3") + --tlsminversion string The minimum SSL/TLS version that is acceptable (one of: 1.0|1.1|1.2|1.3) (default "1.2") + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use + --username string Username for basic authentication to the API server + --x-frame-options value Set X-Frame-Options header in HTTP responses to value. To disable, set to "". (default "sameorigin") ``` ### SEE ALSO diff --git a/reposerver/cache/cache.go b/reposerver/cache/cache.go index 79d3a02b62750..4437bd3ac0dd7 100644 --- a/reposerver/cache/cache.go +++ b/reposerver/cache/cache.go @@ -12,7 +12,6 @@ import ( "github.com/argoproj/gitops-engine/pkg/utils/text" "github.com/go-git/go-git/v5/plumbing" - "github.com/redis/go-redis/v9" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -44,7 +43,7 @@ func NewCache(cache *cacheutil.Cache, repoCacheExpiration time.Duration, revisio return &Cache{cache, repoCacheExpiration, revisionCacheExpiration} } -func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...func(client *redis.Client)) func() (*Cache, error) { +func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...cacheutil.Options) func() (*Cache, error) { var repoCacheExpiration time.Duration var revisionCacheExpiration time.Duration @@ -225,6 +224,12 @@ func LogDebugManifestCacheKeyFields(message string, reason string, revision stri } } +func (c *Cache) SetNewRevisionManifests(newRevision string, revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, refSourceCommitSHAs ResolvedRevisions) error { + oldKey := manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs) + newKey := manifestCacheKey(newRevision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs) + return c.cache.RenameItem(oldKey, newKey, c.repoCacheExpiration) +} + func (c *Cache) GetManifests(revision string, appSrc *appv1.ApplicationSource, srcRefs appv1.RefTargetRevisionMapping, clusterInfo ClusterRuntimeInfo, namespace string, trackingMethod string, appLabelKey string, appName string, res *CachedManifestResponse, refSourceCommitSHAs ResolvedRevisions) error { err := c.cache.GetItem(manifestCacheKey(revision, appSrc, srcRefs, namespace, trackingMethod, appLabelKey, appName, clusterInfo, refSourceCommitSHAs), res) diff --git a/server/cache/cache.go b/server/cache/cache.go index ccbebd256be78..c2042c3f0e8d1 100644 --- a/server/cache/cache.go +++ b/server/cache/cache.go @@ -6,7 +6,6 @@ import ( "math" "time" - "github.com/redis/go-redis/v9" "github.com/spf13/cobra" appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -33,7 +32,7 @@ func NewCache( return &Cache{cache, connectionStatusCacheExpiration, oidcCacheExpiration, loginAttemptsExpiration} } -func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...func(client *redis.Client)) func() (*Cache, error) { +func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...cacheutil.Options) func() (*Cache, error) { var connectionStatusCacheExpiration time.Duration var oidcCacheExpiration time.Duration var loginAttemptsExpiration time.Duration diff --git a/server/server.go b/server/server.go index 0f9b0ddadd800..6ebbc9723167f 100644 --- a/server/server.go +++ b/server/server.go @@ -213,6 +213,7 @@ type ArgoCDServerOpts struct { AppClientset appclientset.Interface RepoClientset repoapiclient.Clientset Cache *servercache.Cache + RepoServerCache *repocache.Cache RedisClient *redis.Client TLSConfigCustomizer tlsutil.ConfigCustomizer XFrameOptions string @@ -1028,7 +1029,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl // Webhook handler for git events (Note: cache timeouts are hardcoded because API server does not write to cache and not really using them) argoDB := db.NewDB(a.Namespace, a.settingsMgr, a.KubeClientset) - acdWebhookHandler := webhook.NewHandler(a.Namespace, a.ArgoCDServerOpts.ApplicationNamespaces, a.AppClientset, a.settings, a.settingsMgr, repocache.NewCache(a.Cache.GetCache(), 24*time.Hour, 3*time.Minute), a.Cache, argoDB) + acdWebhookHandler := webhook.NewHandler(a.Namespace, a.ArgoCDServerOpts.ApplicationNamespaces, a.AppClientset, a.settings, a.settingsMgr, a.RepoServerCache, a.Cache, argoDB) mux.HandleFunc("/api/webhook", acdWebhookHandler.Handler) diff --git a/util/cache/appstate/cache.go b/util/cache/appstate/cache.go index d59d31befb12e..bb161a429eff9 100644 --- a/util/cache/appstate/cache.go +++ b/util/cache/appstate/cache.go @@ -6,7 +6,6 @@ import ( "sort" "time" - "github.com/redis/go-redis/v9" "github.com/spf13/cobra" appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" @@ -29,7 +28,7 @@ func NewCache(cache *cacheutil.Cache, appStateCacheExpiration time.Duration) *Ca return &Cache{cache, appStateCacheExpiration} } -func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...func(client *redis.Client)) func() (*Cache, error) { +func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...cacheutil.Options) func() (*Cache, error) { var appStateCacheExpiration time.Duration cmd.Flags().DurationVar(&appStateCacheExpiration, "app-state-cache-expiration", env.ParseDurationFromEnv("ARGOCD_APP_STATE_CACHE_EXPIRATION", 1*time.Hour, 0, 10*time.Hour), "Cache expiration for app state") diff --git a/util/cache/cache.go b/util/cache/cache.go index c9cb8c3b8607a..9ac058756f4ca 100644 --- a/util/cache/cache.go +++ b/util/cache/cache.go @@ -5,17 +5,17 @@ import ( "fmt" "math" "os" + "strings" "time" "crypto/tls" "crypto/x509" - "github.com/redis/go-redis/v9" - "github.com/spf13/cobra" - "github.com/argoproj/argo-cd/v2/common" certutil "github.com/argoproj/argo-cd/v2/util/cert" "github.com/argoproj/argo-cd/v2/util/env" + "github.com/redis/go-redis/v9" + "github.com/spf13/cobra" ) const ( @@ -77,8 +77,52 @@ func buildFailoverRedisClient(sentinelMaster, password, username string, redisDB return client } +type Options struct { + FlagPrefix string + OnClientCreated func(client *redis.Client) +} + +func (o *Options) callOnClientCreated(client *redis.Client) { + if o.OnClientCreated != nil { + o.OnClientCreated(client) + } +} + +func (o *Options) getEnvPrefix() string { + return strings.Replace(strings.ToUpper(o.FlagPrefix), "-", "_", -1) +} + +func mergeOptions(opts ...Options) Options { + var result Options + for _, o := range opts { + if o.FlagPrefix != "" { + result.FlagPrefix = o.FlagPrefix + } + if o.OnClientCreated != nil { + result.OnClientCreated = o.OnClientCreated + } + } + return result +} + +func getFlagVal[T any](cmd *cobra.Command, o Options, name string, getVal func(name string) (T, error)) func() T { + return func() T { + var res T + var err error + if o.FlagPrefix != "" && cmd.Flags().Changed(o.FlagPrefix+name) { + res, err = getVal(o.FlagPrefix + name) + } else { + res, err = getVal(name) + } + if err != nil { + panic(err) + } + return res + } +} + // AddCacheFlagsToCmd adds flags which control caching to the specified command -func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...func(client *redis.Client)) func() (*Cache, error) { +func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...Options) func() (*Cache, error) { redisAddress := "" sentinelAddresses := make([]string, 0) sentinelMaster := "" @@ -89,20 +133,44 @@ func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...func(client *redis.Client)) redisUseTLS := false insecureRedis := false compressionStr := "" + opt := mergeOptions(opts...) var defaultCacheExpiration time.Duration - cmd.Flags().StringVar(&redisAddress, "redis", env.StringFromEnv("REDIS_SERVER", ""), "Redis server hostname and port (e.g. argocd-redis:6379). ") - cmd.Flags().IntVar(&redisDB, "redisdb", env.ParseNumFromEnv("REDISDB", 0, 0, math.MaxInt32), "Redis database.") - cmd.Flags().StringArrayVar(&sentinelAddresses, "sentinel", []string{}, "Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379). ") - cmd.Flags().StringVar(&sentinelMaster, "sentinelmaster", "master", "Redis sentinel master group name.") - cmd.Flags().DurationVar(&defaultCacheExpiration, "default-cache-expiration", env.ParseDurationFromEnv("ARGOCD_DEFAULT_CACHE_EXPIRATION", 24*time.Hour, 0, math.MaxInt64), "Cache expiration default") - cmd.Flags().BoolVar(&redisUseTLS, "redis-use-tls", false, "Use TLS when connecting to Redis. ") - cmd.Flags().StringVar(&redisClientCertificate, "redis-client-certificate", "", "Path to Redis client certificate (e.g. /etc/certs/redis/client.crt).") - cmd.Flags().StringVar(&redisClientKey, "redis-client-key", "", "Path to Redis client key (e.g. /etc/certs/redis/client.crt).") - cmd.Flags().BoolVar(&insecureRedis, "redis-insecure-skip-tls-verify", false, "Skip Redis server certificate validation.") - cmd.Flags().StringVar(&redisCACertificate, "redis-ca-certificate", "", "Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation.") - cmd.Flags().StringVar(&compressionStr, CLIFlagRedisCompress, env.StringFromEnv("REDIS_COMPRESSION", string(RedisCompressionGZip)), "Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none)") + cmd.Flags().StringVar(&redisAddress, opt.FlagPrefix+"redis", env.StringFromEnv(opt.getEnvPrefix()+"REDIS_SERVER", ""), "Redis server hostname and port (e.g. argocd-redis:6379). ") + redisAddressSrc := getFlagVal(cmd, opt, "redis", cmd.Flags().GetString) + cmd.Flags().IntVar(&redisDB, opt.FlagPrefix+"redisdb", env.ParseNumFromEnv(opt.getEnvPrefix()+"REDISDB", 0, 0, math.MaxInt32), "Redis database.") + redisDBSrc := getFlagVal(cmd, opt, "redisdb", cmd.Flags().GetInt) + cmd.Flags().StringArrayVar(&sentinelAddresses, opt.FlagPrefix+"sentinel", []string{}, "Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379). ") + sentinelAddressesSrc := getFlagVal(cmd, opt, "sentinel", cmd.Flags().GetStringArray) + cmd.Flags().StringVar(&sentinelMaster, opt.FlagPrefix+"sentinelmaster", "master", "Redis sentinel master group name.") + sentinelMasterSrc := getFlagVal(cmd, opt, "sentinelmaster", cmd.Flags().GetString) + cmd.Flags().DurationVar(&defaultCacheExpiration, opt.FlagPrefix+"default-cache-expiration", env.ParseDurationFromEnv("ARGOCD_DEFAULT_CACHE_EXPIRATION", 24*time.Hour, 0, math.MaxInt64), "Cache expiration default") + defaultCacheExpirationSrc := getFlagVal(cmd, opt, "default-cache-expiration", cmd.Flags().GetDuration) + cmd.Flags().BoolVar(&redisUseTLS, opt.FlagPrefix+"redis-use-tls", false, "Use TLS when connecting to Redis. ") + redisUseTLSSrc := getFlagVal(cmd, opt, "redis-use-tls", cmd.Flags().GetBool) + cmd.Flags().StringVar(&redisClientCertificate, opt.FlagPrefix+"redis-client-certificate", "", "Path to Redis client certificate (e.g. /etc/certs/redis/client.crt).") + redisClientCertificateSrc := getFlagVal(cmd, opt, "redis-client-certificate", cmd.Flags().GetString) + cmd.Flags().StringVar(&redisClientKey, opt.FlagPrefix+"redis-client-key", "", "Path to Redis client key (e.g. /etc/certs/redis/client.crt).") + redisClientKeySrc := getFlagVal(cmd, opt, "redis-client-key", cmd.Flags().GetString) + cmd.Flags().BoolVar(&insecureRedis, opt.FlagPrefix+"redis-insecure-skip-tls-verify", false, "Skip Redis server certificate validation.") + insecureRedisSrc := getFlagVal(cmd, opt, "redis-insecure-skip-tls-verify", cmd.Flags().GetBool) + cmd.Flags().StringVar(&redisCACertificate, opt.FlagPrefix+"redis-ca-certificate", "", "Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation.") + redisCACertificateSrc := getFlagVal(cmd, opt, "redis-ca-certificate", cmd.Flags().GetString) + cmd.Flags().StringVar(&compressionStr, opt.FlagPrefix+CLIFlagRedisCompress, env.StringFromEnv(opt.getEnvPrefix()+"REDIS_COMPRESSION", string(RedisCompressionGZip)), "Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none)") + compressionStrSrc := getFlagVal(cmd, opt, CLIFlagRedisCompress, cmd.Flags().GetString) return func() (*Cache, error) { + redisAddress := redisAddressSrc() + redisDB := redisDBSrc() + sentinelAddresses := sentinelAddressesSrc() + sentinelMaster := sentinelMasterSrc() + defaultCacheExpiration := defaultCacheExpirationSrc() + redisUseTLS := redisUseTLSSrc() + redisClientCertificate := redisClientCertificateSrc() + redisClientKey := redisClientKeySrc() + insecureRedis := insecureRedisSrc() + redisCACertificate := redisCACertificateSrc() + compressionStr := compressionStrSrc() + var tlsConfig *tls.Config = nil if redisUseTLS { tlsConfig = &tls.Config{} @@ -138,9 +206,7 @@ func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...func(client *redis.Client)) } if len(sentinelAddresses) > 0 { client := buildFailoverRedisClient(sentinelMaster, password, username, redisDB, maxRetries, tlsConfig, sentinelAddresses) - for i := range opts { - opts[i](client) - } + opt.callOnClientCreated(client) return NewCache(NewRedisCache(client, defaultCacheExpiration, compression)), nil } if redisAddress == "" { @@ -148,9 +214,7 @@ func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...func(client *redis.Client)) } client := buildRedisClient(redisAddress, password, username, redisDB, maxRetries, tlsConfig) - for i := range opts { - opts[i](client) - } + opt.callOnClientCreated(client) return NewCache(NewRedisCache(client, defaultCacheExpiration, compression)), nil } } @@ -168,6 +232,10 @@ func (c *Cache) SetClient(client CacheClient) { c.client = client } +func (c *Cache) RenameItem(oldKey string, newKey string, expiration time.Duration) error { + return c.client.Rename(fmt.Sprintf("%s|%s", oldKey, common.CacheVersion), fmt.Sprintf("%s|%s", newKey, common.CacheVersion), expiration) +} + func (c *Cache) SetItem(key string, item interface{}, expiration time.Duration, delete bool) error { key = fmt.Sprintf("%s|%s", key, common.CacheVersion) if delete { diff --git a/util/cache/client.go b/util/cache/client.go index 434c2a8da187a..c8c7b4a6baa80 100644 --- a/util/cache/client.go +++ b/util/cache/client.go @@ -17,6 +17,7 @@ type Item struct { type CacheClient interface { Set(item *Item) error + Rename(oldKey string, newKey string, expiration time.Duration) error Get(key string, obj interface{}) error Delete(key string) error OnUpdated(ctx context.Context, key string, callback func() error) error diff --git a/util/cache/inmemory.go b/util/cache/inmemory.go index f75688c275546..6d970c1d4f567 100644 --- a/util/cache/inmemory.go +++ b/util/cache/inmemory.go @@ -37,6 +37,16 @@ func (i *InMemoryCache) Set(item *Item) error { return nil } +func (i *InMemoryCache) Rename(oldKey string, newKey string, expiration time.Duration) error { + bufIf, found := i.memCache.Get(oldKey) + if !found { + return ErrCacheMiss + } + i.memCache.Set(newKey, bufIf, expiration) + i.memCache.Delete(oldKey) + return nil +} + // HasSame returns true if key with the same value already present in cache func (i *InMemoryCache) HasSame(key string, obj interface{}) (bool, error) { var buf bytes.Buffer diff --git a/util/cache/mocks/cacheclient.go b/util/cache/mocks/cacheclient.go index e653847ec49a8..2fdd9fc37f8be 100644 --- a/util/cache/mocks/cacheclient.go +++ b/util/cache/mocks/cacheclient.go @@ -4,8 +4,9 @@ import ( "context" "time" - cache "github.com/argoproj/argo-cd/v2/util/cache" "github.com/stretchr/testify/mock" + + "github.com/argoproj/argo-cd/v2/util/cache" ) type MockCacheClient struct { @@ -15,6 +16,14 @@ type MockCacheClient struct { WriteDelay time.Duration } +func (c *MockCacheClient) Rename(oldKey string, newKey string, expiration time.Duration) error { + args := c.Called(oldKey, newKey, expiration) + if len(args) > 0 && args.Get(0) != nil { + return args.Get(0).(error) + } + return c.BaseCache.Rename(oldKey, newKey, expiration) +} + func (c *MockCacheClient) Set(item *cache.Item) error { args := c.Called(item) if len(args) > 0 && args.Get(0) != nil { diff --git a/util/cache/redis.go b/util/cache/redis.go index c5365c4984e21..7d5303bb3a9fa 100644 --- a/util/cache/redis.go +++ b/util/cache/redis.go @@ -95,6 +95,10 @@ func (r *redisCache) unmarshal(data []byte, obj interface{}) error { return nil } +func (r *redisCache) Rename(oldKey string, newKey string, _ time.Duration) error { + return r.client.Rename(context.TODO(), r.getKey(oldKey), r.getKey(newKey)).Err() +} + func (r *redisCache) Set(item *Item) error { expiration := item.Expiration if expiration == 0 { diff --git a/util/cache/twolevelclient.go b/util/cache/twolevelclient.go index 14a4279e87c89..f221099844876 100644 --- a/util/cache/twolevelclient.go +++ b/util/cache/twolevelclient.go @@ -18,6 +18,14 @@ type twoLevelClient struct { externalCache CacheClient } +func (c *twoLevelClient) Rename(oldKey string, newKey string, expiration time.Duration) error { + err := c.inMemoryCache.Rename(oldKey, newKey, expiration) + if err != nil { + log.Warnf("Failed to move key '%s' in in-memory cache: %v", oldKey, err) + } + return c.externalCache.Rename(oldKey, newKey, expiration) +} + // Set stores the given value in both in-memory and external cache. // Skip storing the value in external cache if the same value already exists in memory to avoid requesting external cache. func (c *twoLevelClient) Set(item *Item) error { diff --git a/util/webhook/webhook.go b/util/webhook/webhook.go index 9955540ea04a9..25bd92e11802c 100644 --- a/util/webhook/webhook.go +++ b/util/webhook/webhook.go @@ -349,18 +349,12 @@ func (a *ArgoCDWebhookHandler) storePreviouslyCachedManifests(app *v1alpha1.Appl return fmt.Errorf("error getting ref sources: %w", err) } source := app.Spec.GetSource() - cache.LogDebugManifestCacheKeyFields("getting manifests cache", "webhook app revision changed", change.shaBefore, &source, refSources, &clusterInfo, app.Spec.Destination.Namespace, trackingMethod, appInstanceLabelKey, app.Name, nil) + cache.LogDebugManifestCacheKeyFields("moving manifests cache", "webhook app revision changed", change.shaBefore, &source, refSources, &clusterInfo, app.Spec.Destination.Namespace, trackingMethod, appInstanceLabelKey, app.Name, nil) - var cachedManifests cache.CachedManifestResponse - if err := a.repoCache.GetManifests(change.shaBefore, &source, refSources, &clusterInfo, app.Spec.Destination.Namespace, trackingMethod, appInstanceLabelKey, app.Name, &cachedManifests, nil); err != nil { + if err := a.repoCache.SetNewRevisionManifests(change.shaAfter, change.shaBefore, &source, refSources, &clusterInfo, app.Spec.Destination.Namespace, trackingMethod, appInstanceLabelKey, app.Name, nil); err != nil { return err } - cache.LogDebugManifestCacheKeyFields("setting manifests cache", "webhook app revision changed", change.shaAfter, &source, refSources, &clusterInfo, app.Spec.Destination.Namespace, trackingMethod, appInstanceLabelKey, app.Name, nil) - - if err = a.repoCache.SetManifests(change.shaAfter, &source, refSources, &clusterInfo, app.Spec.Destination.Namespace, trackingMethod, appInstanceLabelKey, app.Name, &cachedManifests, nil); err != nil { - return err - } return nil } From c4ac5aaa972e8080c186f9b5145114ca7217b28d Mon Sep 17 00:00:00 2001 From: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> Date: Fri, 5 Jan 2024 15:13:35 -0500 Subject: [PATCH 61/66] docs: add context to configmap example (#16763) Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com> --- docs/user-guide/diff-strategies.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/user-guide/diff-strategies.md b/docs/user-guide/diff-strategies.md index a7b3216fa7ec7..2890fe64cbb0e 100644 --- a/docs/user-guide/diff-strategies.md +++ b/docs/user-guide/diff-strategies.md @@ -56,7 +56,13 @@ Application. Add the following entry in the argocd-cmd-params-cm configmap: ``` -controller.diff.server.side: "true" +apiVersion: v1 +kind: ConfigMap +metadata: + name: argocd-cmd-params-cm +data: + controller.diff.server.side: "true" +... ``` Note: It is necessary to restart the `argocd-application-controller` From ecbd24da1074f03b49d20994e01ddaf7c0c73b27 Mon Sep 17 00:00:00 2001 From: mfreeman451 Date: Fri, 5 Jan 2024 18:04:54 -0600 Subject: [PATCH 62/66] docs: Update signed-release-assets.md (#16755) Missing \ in example Signed-off-by: mfreeman451 --- docs/operator-manual/signed-release-assets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/operator-manual/signed-release-assets.md b/docs/operator-manual/signed-release-assets.md index 9aec6bb071047..b4e4f3fc97418 100644 --- a/docs/operator-manual/signed-release-assets.md +++ b/docs/operator-manual/signed-release-assets.md @@ -92,7 +92,7 @@ The attestation payload contains a non-forgeable provenance which is base64 enco ```bash slsa-verifier verify-image "$IMAGE" \ --source-uri github.com/argoproj/argo-cd \ - --source-tag v2.7.0 + --source-tag v2.7.0 \ --print-provenance | jq ``` From 40760eb8528766745328e67e124e2cf1f56d8d93 Mon Sep 17 00:00:00 2001 From: Lie Ryan Date: Mon, 8 Jan 2024 03:48:54 +1100 Subject: [PATCH 63/66] Document restarting argocd after modifying argocd-cm (#12405) Signed-off-by: Lie Ryan Co-authored-by: Blake Pettersson --- docs/user-guide/kustomize.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/user-guide/kustomize.md b/docs/user-guide/kustomize.md index 9c2bf1fc655a4..647e753649cce 100644 --- a/docs/user-guide/kustomize.md +++ b/docs/user-guide/kustomize.md @@ -131,6 +131,9 @@ data: kustomize.buildOptions: --load-restrictor LoadRestrictionsNone kustomize.buildOptions.v4.4.0: --output /tmp ``` + +After modifying `kustomize.buildOptions`, you may need to restart ArgoCD for the changes to take effect. + ## Custom Kustomize versions Argo CD supports using multiple Kustomize versions simultaneously and specifies required version per application. From c5b9c670737544f497c2f648d06e4540f568505f Mon Sep 17 00:00:00 2001 From: Alexander Matyushentsev Date: Mon, 8 Jan 2024 16:04:46 -0800 Subject: [PATCH 64/66] fix: support specifying username/password for redis holding manifests in argocd-server (#16786) Signed-off-by: Alexander Matyushentsev --- util/cache/cache.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/util/cache/cache.go b/util/cache/cache.go index 9ac058756f4ca..d34fba5d38f7b 100644 --- a/util/cache/cache.go +++ b/util/cache/cache.go @@ -199,6 +199,14 @@ func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...Options) func() (*Cache, err } password := os.Getenv(envRedisPassword) username := os.Getenv(envRedisUsername) + if opt.FlagPrefix != "" { + if val := os.Getenv(opt.getEnvPrefix() + envRedisUsername); val != "" { + username = val + } + if val := os.Getenv(opt.getEnvPrefix() + envRedisPassword); val != "" { + password = val + } + } maxRetries := env.ParseNumFromEnv(envRedisRetryCount, defaultRedisRetryCount, 0, math.MaxInt32) compression, err := CompressionTypeFromString(compressionStr) if err != nil { From 8ebe1cd3c441aab85c13cc03d297b846c9886b99 Mon Sep 17 00:00:00 2001 From: Ishita Sequeira <46771830+ishitasequeira@users.noreply.github.com> Date: Mon, 8 Jan 2024 21:58:34 -0500 Subject: [PATCH 65/66] fix: add list permission deployments (#16785) * add list permissions for deployments to application controller Signed-off-by: ishitasequeira * revert redis-ha chart changes Signed-off-by: ishitasequeira * revert redis-ha chart changes Signed-off-by: ishitasequeira --------- Signed-off-by: ishitasequeira --- .../argocd-application-controller-role.yaml | 8 ++++++++ manifests/core-install.yaml | 8 ++++++++ manifests/ha/install.yaml | 8 ++++++++ manifests/ha/namespace-install.yaml | 8 ++++++++ manifests/install.yaml | 8 ++++++++ manifests/namespace-install.yaml | 8 ++++++++ 6 files changed, 48 insertions(+) diff --git a/manifests/base/application-controller/argocd-application-controller-role.yaml b/manifests/base/application-controller/argocd-application-controller-role.yaml index 27e0bc7bfe9cb..a672268eb1dd9 100644 --- a/manifests/base/application-controller/argocd-application-controller-role.yaml +++ b/manifests/base/application-controller/argocd-application-controller-role.yaml @@ -36,3 +36,11 @@ rules: verbs: - create - list +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch diff --git a/manifests/core-install.yaml b/manifests/core-install.yaml index c9028a44a1ae0..08d7d972e6362 100644 --- a/manifests/core-install.yaml +++ b/manifests/core-install.yaml @@ -20595,6 +20595,14 @@ rules: verbs: - create - list +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/manifests/ha/install.yaml b/manifests/ha/install.yaml index 81f365bb8a86d..a7086ae8a6c06 100644 --- a/manifests/ha/install.yaml +++ b/manifests/ha/install.yaml @@ -20631,6 +20631,14 @@ rules: verbs: - create - list +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/manifests/ha/namespace-install.yaml b/manifests/ha/namespace-install.yaml index ad1a7baa8b017..01a8da2ffd7b9 100644 --- a/manifests/ha/namespace-install.yaml +++ b/manifests/ha/namespace-install.yaml @@ -109,6 +109,14 @@ rules: verbs: - create - list +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/manifests/install.yaml b/manifests/install.yaml index 3d1bbf942afb5..8d30e076d8bf7 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -20622,6 +20622,14 @@ rules: verbs: - create - list +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 6fa2cdb2b6de0..76301680f195a 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -100,6 +100,14 @@ rules: verbs: - create - list +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role From 20246596962814b732f20b8cc638156df80c4066 Mon Sep 17 00:00:00 2001 From: mugi <62197019+mugioka@users.noreply.github.com> Date: Tue, 9 Jan 2024 12:57:27 +0900 Subject: [PATCH 66/66] chore(manifests): add ClsuterRole/ClusterRoleBinding for applicationset controller. (#16699) Closes https://github.com/argoproj/argo-cd/issues/16698. Signed-off-by: mugioka --- USERS.md | 1 + ...applicationset-controller-clusterrole.yaml | 88 +++++++++++++++++++ ...tionset-controller-clusterrolebinding.yaml | 16 ++++ .../kustomization.yaml | 6 ++ 4 files changed, 111 insertions(+) create mode 100644 manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrole.yaml create mode 100644 manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrolebinding.yaml create mode 100644 manifests/cluster-rbac/applicationset-controller/kustomization.yaml diff --git a/USERS.md b/USERS.md index 9059df8450c33..60dd3b881c10b 100644 --- a/USERS.md +++ b/USERS.md @@ -40,6 +40,7 @@ Currently, the following organizations are **officially** using Argo CD: 1. [Boozt](https://www.booztgroup.com/) 1. [Boticario](https://www.boticario.com.br/) 1. [Bulder Bank](https://bulderbank.no) +1. [CAM](https://cam-inc.co.jp) 1. [Camptocamp](https://camptocamp.com) 1. [Candis](https://www.candis.io) 1. [Capital One](https://www.capitalone.com) diff --git a/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrole.yaml b/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrole.yaml new file mode 100644 index 0000000000000..259a48e7aee9e --- /dev/null +++ b/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrole.yaml @@ -0,0 +1,88 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: argocd-applicationset-controller + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: applicationset-controller + name: argocd-applicationset-controller +rules: +- apiGroups: + - argoproj.io + resources: + - applications + - applicationsets + - applicationsets/finalizers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - argoproj.io + resources: + - applicationsets/status + verbs: + - get + - patch + - update +- apiGroups: + - argoproj.io + resources: + - appprojects + verbs: + - get +- apiGroups: + - "" + resources: + - events + verbs: + - create + - get + - list + - patch + - watch +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - update + - delete + - get + - list + - patch + - watch +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch +- apiGroups: + - apps + - extensions + resources: + - deployments + verbs: + - get + - list + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch diff --git a/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrolebinding.yaml b/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrolebinding.yaml new file mode 100644 index 0000000000000..820f16f472e4e --- /dev/null +++ b/manifests/cluster-rbac/applicationset-controller/argocd-applicationset-controller-clusterrolebinding.yaml @@ -0,0 +1,16 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: argocd-applicationset-controller + app.kubernetes.io/part-of: argocd + app.kubernetes.io/component: applicationset-controller + name: argocd-applicationset-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: argocd-applicationset-controller +subjects: +- kind: ServiceAccount + name: argocd-applicationset-controller + namespace: argocd diff --git a/manifests/cluster-rbac/applicationset-controller/kustomization.yaml b/manifests/cluster-rbac/applicationset-controller/kustomization.yaml new file mode 100644 index 0000000000000..b8f18c57a14f7 --- /dev/null +++ b/manifests/cluster-rbac/applicationset-controller/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- argocd-applicationset-controller-clusterrole.yaml +- argocd-applicationset-controller-clusterrolebinding.yaml