diff --git a/.github/workflows/ci-build.yaml b/.github/workflows/ci-build.yaml index 7445a30fd9eca..6e08b1a6ed51a 100644 --- a/.github/workflows/ci-build.yaml +++ b/.github/workflows/ci-build.yaml @@ -354,6 +354,7 @@ jobs: ARGOCD_E2E_K3S: "true" ARGOCD_IN_CI: "true" ARGOCD_E2E_APISERVER_PORT: "8088" + ARGOCD_APPLICATION_NAMESPACES: "argocd-e2e-external" ARGOCD_SERVER: "127.0.0.1:8088" GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }} diff --git a/Makefile b/Makefile index 4a1aaf5803611..0ac46b965bc29 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ ARGOCD_E2E_DEX_PORT?=5556 ARGOCD_E2E_YARN_HOST?=localhost ARGOCD_E2E_DISABLE_AUTH?= -ARGOCD_E2E_TEST_TIMEOUT?=30m +ARGOCD_E2E_TEST_TIMEOUT?=45m ARGOCD_IN_CI?=false ARGOCD_TEST_E2E?=true @@ -81,6 +81,7 @@ define run-in-test-server -e ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} \ -e ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} \ -e ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} \ + -e ARGOCD_APPLICATION_NAMESPACES \ -e GITHUB_TOKEN \ -v ${DOCKER_SRC_MOUNT} \ -v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \ @@ -118,7 +119,7 @@ endef # define exec-in-test-server - docker exec -it -u $(shell id -u):$(shell id -g) -e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) argocd-test-server $(1) + docker exec -it -u $(shell id -u):$(shell id -g) -e ARGOCD_E2E_RECORD=$(ARGOCD_E2E_RECORD) -e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) argocd-test-server $(1) endef PATH:=$(PATH):$(PWD)/hack @@ -405,7 +406,7 @@ test-e2e: test-e2e-local: cli-local # NO_PROXY ensures all tests don't go out through a proxy if one is configured on the test system export GO111MODULE=off - ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v ./test/e2e + ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v ./test/e2e # Spawns a shell in the test server container for debugging purposes debug-test-server: test-tools-image @@ -426,6 +427,7 @@ start-e2e: test-tools-image .PHONY: start-e2e-local start-e2e-local: mod-vendor-local dep-ui-local cli-local kubectl create ns argocd-e2e || true + kubectl create ns argocd-e2e-external || true kubectl config set-context --current --namespace=argocd-e2e kustomize build test/manifests/base | kubectl apply -f - kubectl apply -f https://raw.githubusercontent.com/open-cluster-management/api/a6845f2ebcb186ec26b832f60c988537a58f3859/cluster/v1alpha1/0000_04_clusters.open-cluster-management.io_placementdecisions.crd.yaml @@ -446,6 +448,7 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local ARGOCD_ZJWT_FEATURE_FLAG=always \ ARGOCD_IN_CI=$(ARGOCD_IN_CI) \ BIN_MODE=$(ARGOCD_BIN_MODE) \ + ARGOCD_APPLICATION_NAMESPACES=argocd-e2e-external \ ARGOCD_E2E_TEST=true \ goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START} @@ -477,6 +480,7 @@ start-local: mod-vendor-local dep-ui-local cli-local ARGOCD_IN_CI=false \ ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \ ARGOCD_E2E_TEST=false \ + ARGOCD_APPLICATION_NAMESPACES=$(ARGOCD_APPLICATION_NAMESPACES) \ goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START} # Run goreman start with exclude option , provide exclude env variable with list of services diff --git a/Procfile b/Procfile index 9ea47074823b8..56c9440177bfc 100644 --- a/Procfile +++ b/Procfile @@ -1,5 +1,5 @@ -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}" -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}" +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:-''}" +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 && 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} redis:$(grep "image: redis" manifests/base/redis/argocd-redis-deployment.yaml | cut -d':' -f3) --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi" repo-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_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} 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-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --otlp-address=${ARGOCD_OTLP_ADDRESS}" diff --git a/assets/swagger.json b/assets/swagger.json index b6c539a991d1b..728d6eb08532f 100644 --- a/assets/swagger.json +++ b/assets/swagger.json @@ -265,6 +265,12 @@ "description": "the repoURL to restrict returned list applications.", "name": "repo", "in": "query" + }, + { + "type": "string", + "description": "the application's namespace.", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -407,6 +413,11 @@ "type": "string", "name": "kind", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -463,6 +474,11 @@ "type": "string", "name": "kind", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -529,6 +545,12 @@ "description": "the repoURL to restrict returned list applications.", "name": "repo", "in": "query" + }, + { + "type": "string", + "description": "the application's namespace.", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -568,6 +590,11 @@ "type": "string", "name": "propagationPolicy", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -651,6 +678,11 @@ "type": "string", "name": "resourceUID", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -758,6 +790,11 @@ "type": "boolean", "name": "previous", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -803,6 +840,11 @@ "type": "string", "name": "revision", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -834,6 +876,11 @@ "name": "name", "in": "path", "required": true + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -942,6 +989,11 @@ "type": "boolean", "name": "previous", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -1007,6 +1059,11 @@ "type": "string", "name": "kind", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -1074,6 +1131,11 @@ "type": "string", "name": "patchType", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -1138,6 +1200,11 @@ "type": "boolean", "name": "orphan", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -1194,6 +1261,11 @@ "type": "string", "name": "kind", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -1256,6 +1328,11 @@ "type": "string", "name": "kind", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -1295,6 +1372,12 @@ "name": "revision", "in": "path", "required": true + }, + { + "type": "string", + "description": "the application's namespace.", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -1378,6 +1461,11 @@ "type": "boolean", "name": "validate", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -1448,6 +1536,11 @@ "name": "name", "in": "path", "required": true + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -3167,6 +3260,12 @@ "description": "the repoURL to restrict returned list applications.", "name": "repo", "in": "query" + }, + { + "type": "string", + "description": "the application's namespace.", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -3232,6 +3331,11 @@ "type": "string", "name": "kind", "in": "query" + }, + { + "type": "string", + "name": "appNamespace", + "in": "query" } ], "responses": { @@ -3390,6 +3494,9 @@ "type": "object", "title": "ApplicationPatchRequest is a request to patch an application", "properties": { + "appNamespace": { + "type": "string" + }, "name": { "type": "string" }, @@ -3415,6 +3522,9 @@ "applicationApplicationRollbackRequest": { "type": "object", "properties": { + "appNamespace": { + "type": "string" + }, "dryRun": { "type": "boolean" }, @@ -3434,6 +3544,9 @@ "type": "object", "title": "ApplicationSyncRequest is a request to apply the config state to live state", "properties": { + "appNamespace": { + "type": "string" + }, "dryRun": { "type": "boolean" }, @@ -3704,6 +3817,9 @@ "$ref": "#/definitions/v1alpha1ConfigManagementPlugin" } }, + "controllerNamespace": { + "type": "string" + }, "dexConfig": { "$ref": "#/definitions/clusterDexConfig" }, @@ -4755,6 +4871,13 @@ "$ref": "#/definitions/v1alpha1SignatureKey" } }, + "sourceNamespaces": { + "type": "array", + "title": "SourceNamespaces defines the namespaces application resources are allowed to be created in", + "items": { + "type": "string" + } + }, "sourceRepos": { "type": "array", "title": "SourceRepos contains list of repository URLs which can be used for deployment", diff --git a/cmd/argocd-application-controller/commands/argocd_application_controller.go b/cmd/argocd-application-controller/commands/argocd_application_controller.go index 4bb4c4a355594..a410029d399a1 100644 --- a/cmd/argocd-application-controller/commands/argocd_application_controller.go +++ b/cmd/argocd-application-controller/commands/argocd_application_controller.go @@ -60,6 +60,7 @@ func NewCommand() *cobra.Command { repoServerPlaintext bool repoServerStrictTLS bool otlpAddress string + applicationNamespaces []string ) var command = cobra.Command{ Use: cliName, @@ -148,7 +149,8 @@ func NewCommand() *cobra.Command { metricsCacheExpiration, metricsAplicationLabels, kubectlParallelismLimit, - clusterFilter) + clusterFilter, + applicationNamespaces) errors.CheckError(err) cacheutil.CollectMetrics(redisClient, appController.GetMetricsServer()) @@ -189,6 +191,7 @@ 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().StringSliceVar(&applicationNamespaces, "application-namespaces", []string{}, "List of additional namespaces that applications are allowed to be created in") cacheSrc = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) { redisClient = client }) diff --git a/cmd/argocd-server/commands/argocd_server.go b/cmd/argocd-server/commands/argocd_server.go index 289f93466cdb9..65ad4e872b2f6 100644 --- a/cmd/argocd-server/commands/argocd_server.go +++ b/cmd/argocd-server/commands/argocd_server.go @@ -70,6 +70,7 @@ func NewCommand() *cobra.Command { dexServerPlaintext bool dexServerStrictTLS bool staticAssetsDir string + applicationNamespaces []string ) var command = &cobra.Command{ Use: cliName, @@ -182,6 +183,7 @@ func NewCommand() *cobra.Command { ContentSecurityPolicy: contentSecurityPolicy, RedisClient: redisClient, StaticAssetsDir: staticAssetsDir, + ApplicationNamespaces: applicationNamespaces, } stats.RegisterStackDumper() @@ -232,6 +234,7 @@ func NewCommand() *cobra.Command { command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_SERVER_REPO_SERVER_STRICT_TLS", false), "Perform strict validation of TLS certificates when connecting to repo server") command.Flags().BoolVar(&dexServerPlaintext, "dex-server-plaintext", env.ParseBoolFromEnv("ARGOCD_SERVER_DEX_SERVER_PLAINTEXT", false), "Use a plaintext client (non-TLS) to connect to dex server") command.Flags().BoolVar(&dexServerStrictTLS, "dex-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_SERVER_DEX_SERVER_STRICT_TLS", false), "Perform strict validation of TLS certificates when connecting to dex server") + command.Flags().StringSliceVar(&applicationNamespaces, "application-namespaces", []string{}, "List of namespaces where application resources can exist") tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(command) cacheSrc = servercache.AddCacheFlagsToCmd(command, func(client *redis.Client) { redisClient = client diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index 9bbcd6ba0705a..144016399ffc5 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -112,6 +112,7 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra. labels []string annotations []string setFinalizer bool + appNamespace string ) var command = &cobra.Command{ Use: "create APPNAME", @@ -146,6 +147,9 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra. c.HelpFunc()(c, args) os.Exit(1) } + if appNamespace != "" { + app.Namespace = appNamespace + } if setFinalizer { app.Finalizers = append(app.Finalizers, "resources-finalizer.argocd.argoproj.io") } @@ -190,6 +194,7 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra. if err != nil { log.Fatal(err) } + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Namespace where the application will be created in") cmdutil.AddAppFlags(command, &appOpts) return command } @@ -274,8 +279,13 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com acdClient := headless.NewClientOrDie(clientOpts, c) conn, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) - appName := args[0] - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName, Refresh: getRefreshType(refresh, hardRefresh)}) + + appName, appNs := argo.ParseAppQualifiedName(args[0], "") + app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{ + Name: &appName, + Refresh: getRefreshType(refresh, hardRefresh), + AppNamespace: &appNs, + }) errors.CheckError(err) pConn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie() @@ -354,7 +364,7 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co acdClient := headless.NewClientOrDie(clientOpts, c) conn, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) - appName := args[0] + appName, appNs := argo.ParseAppQualifiedName(args[0], "") retry := true for retry { @@ -372,6 +382,7 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co Filter: &filter, Container: pointer.String(container), Previous: pointer.Bool(previous), + AppNamespace: &appNs, }) if err != nil { log.Fatalf("failed to get pod logs: %v", err) @@ -419,7 +430,7 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } func printAppSummaryTable(app *argoappv1.Application, appURL string, windows *argoappv1.SyncWindows) { - fmt.Printf(printOpFmtStr, "Name:", app.Name) + fmt.Printf(printOpFmtStr, "Name:", app.QualifiedName()) fmt.Printf(printOpFmtStr, "Project:", app.Spec.GetProject()) fmt.Printf(printOpFmtStr, "Server:", getServer(app)) fmt.Printf(printOpFmtStr, "Namespace:", app.Spec.Destination.Namespace) @@ -590,11 +601,11 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com c.HelpFunc()(c, args) os.Exit(1) } - appName := args[0] + appName, appNs := argo.ParseAppQualifiedName(args[0], "") argocdClient := headless.NewClientOrDie(clientOpts, c) conn, appIf := argocdClient.NewApplicationClientOrDie() defer argoio.Close(conn) - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName}) + app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName, AppNamespace: &appNs}) errors.CheckError(err) visited := cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts) if visited == 0 { @@ -604,9 +615,10 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com } setParameterOverrides(app, appOpts.Parameters) _, err = appIf.UpdateSpec(ctx, &applicationpkg.ApplicationUpdateSpecRequest{ - Name: &app.Name, - Spec: &app.Spec, - Validate: &appOpts.Validate, + Name: &app.Name, + Spec: &app.Spec, + Validate: &appOpts.Validate, + AppNamespace: &appNs, }) errors.CheckError(err) }, @@ -652,10 +664,10 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C c.HelpFunc()(c, args) os.Exit(1) } - appName := args[0] + appName, appNs := argo.ParseAppQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName}) + app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName, AppNamespace: &appNs}) errors.CheckError(err) updated, nothingToUnset := unset(&app.Spec.Source, opts) @@ -669,9 +681,10 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts) _, err = appIf.UpdateSpec(ctx, &applicationpkg.ApplicationUpdateSpecRequest{ - Name: &app.Name, - Spec: &app.Spec, - Validate: &appOpts.Validate, + Name: &app.Name, + Spec: &app.Spec, + Validate: &appOpts.Validate, + AppNamespace: &appNs, }) errors.CheckError(err) }, @@ -803,7 +816,6 @@ func getLocalObjects(ctx context.Context, app *argoappv1.Application, local, loc func getLocalObjectsString(ctx context.Context, app *argoappv1.Application, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions, configManagementPlugins []*argoappv1.ConfigManagementPlugin, trackingMethod string) []string { - res, err := repository.GenerateManifests(ctx, local, localRepoRoot, app.Spec.Source.TargetRevision, &repoapiclient.ManifestRequest{ Repo: &argoappv1.Repository{Repo: app.Spec.Source.RepoURL}, AppLabelKey: appLabelKey, @@ -882,10 +894,14 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co clientset := headless.NewClientOrDie(clientOpts, c) conn, appIf := clientset.NewApplicationClientOrDie() defer argoio.Close(conn) - appName := args[0] - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName, Refresh: getRefreshType(refresh, hardRefresh)}) + appName, appNs := argo.ParseAppQualifiedName(args[0], "") + app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{ + Name: &appName, + Refresh: getRefreshType(refresh, hardRefresh), + AppNamespace: &appNs, + }) errors.CheckError(err) - resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName}) + resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName, AppNamespace: &appNs}) errors.CheckError(err) conn, settingsIf := clientset.NewSettingsClientOrDie() defer argoio.Close(conn) @@ -894,8 +910,9 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co diffOption := &DifferenceOption{} if revision != "" { q := applicationpkg.ApplicationManifestQuery{ - Name: &appName, - Revision: &revision, + Name: &appName, + Revision: &revision, + AppNamespace: &appNs, } res, err := appIf.GetManifests(ctx, &q) errors.CheckError(err) @@ -943,7 +960,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, resources items := make([]objKeyLiveTarget, 0) if diffOptions.local != "" { localObjs := groupObjsByKey(getLocalObjects(ctx, app, diffOptions.local, diffOptions.localRepoRoot, argoSettings.AppLabelKey, diffOptions.cluster.Info.ServerVersion, diffOptions.cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins, argoSettings.TrackingMethod), liveObjs, app.Spec.Destination.Namespace) - items = groupObjsForDiff(resources, localObjs, items, argoSettings, appName) + items = groupObjsForDiff(resources, localObjs, items, argoSettings, app.InstanceName(argoSettings.ControllerNamespace)) } else if diffOptions.revision != "" { var unstructureds []*unstructured.Unstructured for _, mfst := range diffOptions.res.Manifests { @@ -952,7 +969,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, resources unstructureds = append(unstructureds, obj) } groupedObjs := groupObjsByKey(unstructureds, liveObjs, app.Spec.Destination.Namespace) - items = groupObjsForDiff(resources, groupedObjs, items, argoSettings, appName) + items = groupObjsForDiff(resources, groupedObjs, items, argoSettings, app.Name) } else { for i := range resources.Items { res := resources.Items[i] @@ -1072,9 +1089,11 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. if promptFlag.Changed && promptFlag.Value.String() == "true" { noPrompt = true } - for _, appName := range args { + for _, appFullName := range args { + appName, appNs := argo.ParseAppQualifiedName(appFullName, "") appDeleteReq := applicationpkg.ApplicationDeleteRequest{ - Name: &appName, + Name: &appName, + AppNamespace: &appNs, } if c.Flag("cascade").Changed { appDeleteReq.Cascade = &cascade @@ -1085,10 +1104,10 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. if cascade && isTerminal && !noPrompt { var lowercaseAnswer string if numOfApps == 1 { - lowercaseAnswer = cli.AskToProceedS("Are you sure you want to delete '" + appName + "' and all its resources? [y/n]") + lowercaseAnswer = cli.AskToProceedS("Are you sure you want to delete '" + appFullName + "' and all its resources? [y/n]") } else { if !isConfirmAll { - lowercaseAnswer = cli.AskToProceedS("Are you sure you want to delete '" + appName + "' and all its resources? [y/n/A] where 'A' is to delete all specified apps and their resources without prompting") + lowercaseAnswer = cli.AskToProceedS("Are you sure you want to delete '" + appFullName + "' and all its resources? [y/n/A] where 'A' is to delete all specified apps and their resources without prompting") if lowercaseAnswer == "a" { lowercaseAnswer = "y" isConfirmAll = true @@ -1100,9 +1119,9 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. if lowercaseAnswer == "y" { _, err := appIf.Delete(ctx, &appDeleteReq) errors.CheckError(err) - fmt.Printf("application '%s' deleted\n", appName) + fmt.Printf("application '%s' deleted\n", appFullName) } else { - fmt.Println("The command to delete '" + appName + "' was cancelled.") + fmt.Println("The command to delete '" + appFullName + "' was cancelled.") } } else { _, err := appIf.Delete(ctx, &appDeleteReq) @@ -1120,7 +1139,7 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. // Print simple list of application names func printApplicationNames(apps []argoappv1.Application) { for _, app := range apps { - fmt.Println(app.Name) + fmt.Println(app.QualifiedName()) } } @@ -1138,7 +1157,7 @@ func printApplicationTable(apps []argoappv1.Application, output *string) { _, _ = fmt.Fprintf(w, fmtStr, headers...) for _, app := range apps { vals := []interface{}{ - app.Name, + app.QualifiedName(), getServer(&app), app.Spec.Destination.Namespace, app.Spec.GetProject(), @@ -1158,10 +1177,11 @@ func printApplicationTable(apps []argoappv1.Application, output *string) { // NewApplicationListCommand returns a new instance of an `argocd app list` command func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( - output string - selector string - projects []string - repo string + output string + selector string + projects []string + repo string + appNamespace string ) var command = &cobra.Command{ Use: "list", @@ -1176,7 +1196,10 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - apps, err := appIf.List(ctx, &applicationpkg.ApplicationQuery{Selector: pointer.String(selector)}) + apps, err := appIf.List(ctx, &applicationpkg.ApplicationQuery{ + Selector: pointer.String(selector), + AppNamespace: &appNamespace, + }) errors.CheckError(err) appList := apps.Items if len(projects) != 0 { @@ -1202,6 +1225,7 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co command.Flags().StringVarP(&selector, "selector", "l", "", "List apps by label") command.Flags().StringArrayVarP(&projects, "project", "p", []string{}, "Filter by project name") command.Flags().StringVarP(&repo, "repo", "r", "", "List apps by source repo URL") + command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Only list applications in namespace") return command } @@ -1442,16 +1466,18 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } for _, i := range list.Items { - appNames = append(appNames, i.Name) + appNames = append(appNames, i.QualifiedName()) } } - for _, appName := range appNames { + for _, appQualifiedName := range appNames { + appName, appNs := argo.ParseAppQualifiedName(appQualifiedName, "") if len(selectedLabels) > 0 { q := applicationpkg.ApplicationManifestQuery{ - Name: &appName, - Revision: &revision, + Name: &appName, + AppNamespace: &appNs, + Revision: &revision, } res, err := appIf.GetManifests(ctx, &q) @@ -1484,7 +1510,10 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co var localObjsStrings []string diffOption := &DifferenceOption{} if local != "" { - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName}) + app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{ + Name: &appName, + AppNamespace: &appNs, + }) errors.CheckError(err) if app.Spec.SyncPolicy != nil && app.Spec.SyncPolicy.Automated != nil && !dryRun { log.Fatal("Cannot use local sync when Automatic Sync Policy is enabled except with --dry-run") @@ -1527,14 +1556,15 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } syncReq := applicationpkg.ApplicationSyncRequest{ - Name: &appName, - DryRun: &dryRun, - Revision: &revision, - Resources: selectedResources, - Prune: &prune, - Manifests: localObjsStrings, - Infos: getInfos(infos), - SyncOptions: syncOptionsFactory(), + Name: &appName, + AppNamespace: &appNs, + DryRun: &dryRun, + Revision: &revision, + Resources: selectedResources, + Prune: &prune, + Manifests: localObjsStrings, + Infos: getInfos(infos), + SyncOptions: syncOptionsFactory(), } switch strategy { @@ -1558,20 +1588,26 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co } } if diffChanges { - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName}) + app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{ + Name: &appName, + AppNamespace: &appNs, + }) errors.CheckError(err) - resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName}) + resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ + ApplicationName: &appName, + AppNamespace: &appNs, + }) errors.CheckError(err) conn, settingsIf := acdClient.NewSettingsClientOrDie() defer argoio.Close(conn) argoSettings, err := settingsIf.Get(ctx, &settingspkg.SettingsQuery{}) errors.CheckError(err) foundDiffs := false - fmt.Printf("====== Previewing differences between live and desired state of application %s ======\n", appName) - foundDiffs = findandPrintDiff(ctx, app, resources, argoSettings, appName, diffOption) + fmt.Printf("====== Previewing differences between live and desired state of application %s ======\n", appQualifiedName) + foundDiffs = findandPrintDiff(ctx, app, resources, argoSettings, appQualifiedName, diffOption) if foundDiffs { if !diffChangesConfirm { - yesno := cli.AskToProceed(fmt.Sprintf("Please review changes to application %s shown above. Do you want to continue the sync process? (y/n): ", appName)) + yesno := cli.AskToProceed(fmt.Sprintf("Please review changes to application %s shown above. Do you want to continue the sync process? (y/n): ", appQualifiedName)) if !yesno { os.Exit(0) } @@ -1584,7 +1620,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co errors.CheckError(err) if !async { - app, err := waitOnApplicationStatus(ctx, acdClient, appName, timeout, watchOpts{operation: true}, selectedResources) + app, err := waitOnApplicationStatus(ctx, acdClient, appQualifiedName, timeout, watchOpts{operation: true}, selectedResources) errors.CheckError(err) if !dryRun { @@ -1783,12 +1819,18 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, // time when the sync status lags behind when an operation completes refresh := false + appRealName, appNs := argo.ParseAppQualifiedName(appName, "") + printFinalStatus := func(app *argoappv1.Application) *argoappv1.Application { var err error if refresh { conn, appClient := acdClient.NewApplicationClientOrDie() refreshType := string(argoappv1.RefreshTypeNormal) - app, err = appClient.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName, Refresh: &refreshType}) + app, err = appClient.Get(ctx, &applicationpkg.ApplicationQuery{ + Name: &appRealName, + Refresh: &refreshType, + AppNamespace: &appNs, + }) errors.CheckError(err) _ = conn.Close() } @@ -1821,7 +1863,10 @@ func waitOnApplicationStatus(ctx context.Context, acdClient argocdclient.Client, prevStates := make(map[string]*resourceState) conn, appClient := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) - app, err := appClient.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName}) + app, err := appClient.Get(ctx, &applicationpkg.ApplicationQuery{ + Name: &appRealName, + AppNamespace: &appNs, + }) errors.CheckError(err) appEventCh := acdClient.WatchApplicationWithRetry(ctx, appName, app.ResourceVersion) for appEvent := range appEventCh { @@ -1965,8 +2010,11 @@ func NewApplicationHistoryCommand(clientOpts *argocdclient.ClientOptions) *cobra } conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - appName := args[0] - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName}) + appName, appNs := argo.ParseAppQualifiedName(args[0], "") + app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{ + Name: &appName, + AppNamespace: &appNs, + }) errors.CheckError(err) if output == "id" { printApplicationHistoryIds(app.Status.History) @@ -2012,7 +2060,7 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr c.HelpFunc()(c, args) os.Exit(1) } - appName := args[0] + appName, appNs := argo.ParseAppQualifiedName(args[0], "") var err error depID := -1 if len(args) > 1 { @@ -2022,20 +2070,24 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr acdClient := headless.NewClientOrDie(clientOpts, c) conn, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName}) + app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{ + Name: &appName, + AppNamespace: &appNs, + }) errors.CheckError(err) depInfo, err := findRevisionHistory(app, int64(depID)) errors.CheckError(err) _, err = appIf.Rollback(ctx, &applicationpkg.ApplicationRollbackRequest{ - Name: &appName, - Id: pointer.Int64(depInfo.ID), - Prune: pointer.Bool(prune), + Name: &appName, + AppNamespace: &appNs, + Id: pointer.Int64(depInfo.ID), + Prune: pointer.Bool(prune), }) errors.CheckError(err) - _, err = waitOnApplicationStatus(ctx, acdClient, appName, timeout, watchOpts{ + _, err = waitOnApplicationStatus(ctx, acdClient, app.QualifiedName(), timeout, watchOpts{ operation: true, }, nil) errors.CheckError(err) @@ -2090,11 +2142,14 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob c.HelpFunc()(c, args) os.Exit(1) } - appName := args[0] + appName, appNs := argo.ParseAppQualifiedName(args[0], "") clientset := headless.NewClientOrDie(clientOpts, c) conn, appIf := clientset.NewApplicationClientOrDie() defer argoio.Close(conn) - resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName}) + resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ + ApplicationName: &appName, + AppNamespace: &appNs, + }) errors.CheckError(err) var unstructureds []*unstructured.Unstructured @@ -2117,8 +2172,9 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob unstructureds = getLocalObjects(context.Background(), app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, cluster.Info.APIVersions, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins, argoSettings.TrackingMethod) } else if revision != "" { q := applicationpkg.ApplicationManifestQuery{ - Name: &appName, - Revision: pointer.String(revision), + Name: &appName, + AppNamespace: &appNs, + Revision: pointer.String(revision), } res, err := appIf.GetManifests(ctx, &q) errors.CheckError(err) @@ -2167,10 +2223,13 @@ func NewApplicationTerminateOpCommand(clientOpts *argocdclient.ClientOptions) *c c.HelpFunc()(c, args) os.Exit(1) } - appName := args[0] + appName, appNs := argo.ParseAppQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - _, err := appIf.TerminateOperation(ctx, &applicationpkg.OperationTerminateRequest{Name: &appName}) + _, err := appIf.TerminateOperation(ctx, &applicationpkg.OperationTerminateRequest{ + Name: &appName, + AppNamespace: &appNs, + }) errors.CheckError(err) fmt.Printf("Application '%s' operation terminating\n", appName) }, @@ -2189,10 +2248,13 @@ func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co c.HelpFunc()(c, args) os.Exit(1) } - appName := args[0] + appName, appNs := argo.ParseAppQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName}) + app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{ + Name: &appName, + AppNamespace: &appNs, + }) errors.CheckError(err) appData, err := json.Marshal(app.Spec) errors.CheckError(err) @@ -2212,7 +2274,12 @@ func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co var appOpts cmdutil.AppOptions cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts) - _, err = appIf.UpdateSpec(ctx, &applicationpkg.ApplicationUpdateSpecRequest{Name: &app.Name, Spec: &updatedSpec, Validate: &appOpts.Validate}) + _, err = appIf.UpdateSpec(ctx, &applicationpkg.ApplicationUpdateSpecRequest{ + Name: &appName, + Spec: &updatedSpec, + Validate: &appOpts.Validate, + AppNamespace: &appNs, + }) if err != nil { return fmt.Errorf("Failed to update application spec:\n%v", err) } @@ -2243,14 +2310,15 @@ func NewApplicationPatchCommand(clientOpts *argocdclient.ClientOptions) *cobra.C c.HelpFunc()(c, args) os.Exit(1) } - appName := args[0] + appName, appNs := argo.ParseAppQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) patchedApp, err := appIf.Patch(ctx, &applicationpkg.ApplicationPatchRequest{ - Name: &appName, - Patch: &patch, - PatchType: &patchType, + Name: &appName, + Patch: &patch, + PatchType: &patchType, + AppNamespace: &appNs, }) errors.CheckError(err) diff --git a/cmd/argocd/commands/app_actions.go b/cmd/argocd/commands/app_actions.go index 28217438cf3b2..a1c86c1b1efe4 100644 --- a/cmd/argocd/commands/app_actions.go +++ b/cmd/argocd/commands/app_actions.go @@ -17,6 +17,7 @@ import ( "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" + "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/io" ) @@ -62,10 +63,13 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt c.HelpFunc()(c, args) os.Exit(1) } - appName := args[0] + appName, appNs := argo.ParseAppQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer io.Close(conn) - resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName}) + resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ + ApplicationName: &appName, + AppNamespace: &appNs, + }) errors.CheckError(err) filteredObjects, err := util.FilterResources(command.Flags().Changed("group"), resources.Items, group, kind, namespace, resourceName, true) errors.CheckError(err) @@ -75,6 +79,7 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt gvk := obj.GroupVersionKind() availActionsForResource, err := appIf.ListResourceActions(ctx, &applicationpkg.ApplicationResourceRequest{ Name: &appName, + AppNamespace: &appNs, Namespace: pointer.String(obj.GetNamespace()), ResourceName: pointer.String(obj.GetName()), Group: pointer.String(gvk.Group), @@ -147,12 +152,15 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti c.HelpFunc()(c, args) os.Exit(1) } - appName := args[0] + appName, appNs := argo.ParseAppQualifiedName(args[0], "") actionName := args[1] conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer io.Close(conn) - resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName}) + resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ + ApplicationName: &appName, + AppNamespace: &appNs, + }) errors.CheckError(err) filteredObjects, err := util.FilterResources(command.Flags().Changed("group"), resources.Items, group, kind, namespace, resourceName, all) errors.CheckError(err) @@ -169,6 +177,7 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti objResourceName := obj.GetName() _, err := appIf.RunResourceAction(ctx, &applicationpkg.ResourceActionRunRequest{ Name: &appName, + AppNamespace: &appNs, Namespace: pointer.String(obj.GetNamespace()), ResourceName: pointer.String(objResourceName), Group: pointer.String(gvk.Group), diff --git a/cmd/argocd/commands/app_resources.go b/cmd/argocd/commands/app_resources.go index 5bbfa8a8c02b8..02c1054b6372d 100644 --- a/cmd/argocd/commands/app_resources.go +++ b/cmd/argocd/commands/app_resources.go @@ -16,6 +16,7 @@ import ( "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" + "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/errors" argoio "github.com/argoproj/argo-cd/v2/util/io" @@ -53,11 +54,14 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions) c.HelpFunc()(c, args) os.Exit(1) } - appName := args[0] + appName, appNs := argo.ParseAppQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName}) + resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ + ApplicationName: &appName, + AppNamespace: &appNs, + }) errors.CheckError(err) objectsToPatch, err := util.FilterResources(command.Flags().Changed("group"), resources.Items, group, kind, namespace, resourceName, all) errors.CheckError(err) @@ -66,6 +70,7 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions) gvk := obj.GroupVersionKind() _, err = appIf.PatchResource(ctx, &applicationpkg.ApplicationResourcePatchRequest{ Name: &appName, + AppNamespace: &appNs, Namespace: pointer.String(obj.GetNamespace()), ResourceName: pointer.String(obj.GetName()), Version: pointer.String(gvk.Version), @@ -111,11 +116,14 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions) c.HelpFunc()(c, args) os.Exit(1) } - appName := args[0] + appName, appNs := argo.ParseAppQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName}) + resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ + ApplicationName: &appName, + AppNamespace: &appNs, + }) errors.CheckError(err) objectsToDelete, err := util.FilterResources(command.Flags().Changed("group"), resources.Items, group, kind, namespace, resourceName, all) errors.CheckError(err) @@ -124,6 +132,7 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions) gvk := obj.GroupVersionKind() _, err = appIf.DeleteResource(ctx, &applicationpkg.ApplicationResourceDeleteRequest{ Name: &appName, + AppNamespace: &appNs, Namespace: pointer.String(obj.GetNamespace()), ResourceName: pointer.String(obj.GetName()), Version: pointer.String(gvk.Version), @@ -173,10 +182,13 @@ func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions) os.Exit(1) } listAll := !c.Flag("orphaned").Changed - appName := args[0] + appName, appNs := argo.ParseAppQualifiedName(args[0], "") conn, appIf := headless.NewClientOrDie(clientOpts, c).NewApplicationClientOrDie() defer argoio.Close(conn) - appResourceTree, err := appIf.ResourceTree(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName}) + appResourceTree, err := appIf.ResourceTree(ctx, &applicationpkg.ResourcesQuery{ + ApplicationName: &appName, + AppNamespace: &appNs, + }) errors.CheckError(err) printResources(listAll, orphaned, appResourceTree) }, diff --git a/cmd/argocd/commands/app_test.go b/cmd/argocd/commands/app_test.go index 5f74fabb43ba2..1d3d3d6134ac4 100644 --- a/cmd/argocd/commands/app_test.go +++ b/cmd/argocd/commands/app_test.go @@ -552,7 +552,7 @@ func TestPrintAppSummaryTable(t *testing.T) { return nil }) - expectation := `Name: test + expectation := `Name: argocd/test Project: default Server: local Namespace: argocd diff --git a/cmd/util/app.go b/cmd/util/app.go index eb181f9b12c11..baa7238665c12 100644 --- a/cmd/util/app.go +++ b/cmd/util/app.go @@ -21,6 +21,7 @@ import ( "github.com/argoproj/argo-cd/v2/pkg/apis/application" argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/config" "github.com/argoproj/argo-cd/v2/util/errors" "github.com/argoproj/argo-cd/v2/util/text/label" @@ -561,6 +562,7 @@ func constructAppsFromStdin() ([]*argoappv1.Application, error) { func constructAppsBaseOnName(appName string, labels, annotations, args []string, appOpts AppOptions, flags *pflag.FlagSet) ([]*argoappv1.Application, error) { var app *argoappv1.Application + // read arguments if len(args) == 1 { if appName != "" && appName != args[0] { @@ -568,13 +570,15 @@ func constructAppsBaseOnName(appName string, labels, annotations, args []string, } appName = args[0] } + appName, appNs := argo.ParseAppQualifiedName(appName, "") app = &argoappv1.Application{ TypeMeta: v1.TypeMeta{ Kind: application.ApplicationKind, APIVersion: application.Group + "/v1alpha1", }, ObjectMeta: v1.ObjectMeta{ - Name: appName, + Name: appName, + Namespace: appNs, }, } SetAppSpecOptions(flags, &app.Spec, &appOpts) diff --git a/cmd/util/project.go b/cmd/util/project.go index 09e90caf5b5b8..ff6f6c60338a3 100644 --- a/cmd/util/project.go +++ b/cmd/util/project.go @@ -20,10 +20,11 @@ import ( ) type ProjectOpts struct { - Description string - destinations []string - Sources []string - SignatureKeys []string + Description string + destinations []string + Sources []string + SignatureKeys []string + SourceNamespaces []string orphanedResourcesEnabled bool orphanedResourcesWarn bool @@ -45,6 +46,7 @@ func AddProjFlags(command *cobra.Command, opts *ProjectOpts) { command.Flags().StringArrayVar(&opts.deniedClusterResources, "deny-cluster-resource", []string{}, "List of denied cluster level resources") command.Flags().StringArrayVar(&opts.allowedNamespacedResources, "allow-namespaced-resource", []string{}, "List of allowed namespaced resources") command.Flags().StringArrayVar(&opts.deniedNamespacedResources, "deny-namespaced-resource", []string{}, "List of denied namespaced resources") + command.Flags().StringSliceVar(&opts.SourceNamespaces, "source-namespaces", []string{}, "List of source namespaces for applications") } @@ -104,6 +106,10 @@ func (opts *ProjectOpts) GetSignatureKeys() []v1alpha1.SignatureKey { return signatureKeys } +func (opts *ProjectOpts) GetSourceNamespaces() []string { + return opts.SourceNamespaces +} + func GetOrphanedResourcesSettings(flagSet *pflag.FlagSet, opts ProjectOpts) *v1alpha1.OrphanedResourcesMonitorSettings { warnChanged := flagSet.Changed("orphaned-resources-warn") if opts.orphanedResourcesEnabled || warnChanged { @@ -156,6 +162,8 @@ func SetProjSpecOptions(flags *pflag.FlagSet, spec *v1alpha1.AppProjectSpec, pro spec.NamespaceResourceWhitelist = projOpts.GetAllowedNamespacedResources() case "deny-namespaced-resource": spec.NamespaceResourceBlacklist = projOpts.GetDeniedNamespacedResources() + case "source-namespaces": + spec.SourceNamespaces = projOpts.GetSourceNamespaces() } }) if flags.Changed("orphaned-resources") || flags.Changed("orphaned-resources-warn") { @@ -197,6 +205,5 @@ func ConstructAppProj(fileURL string, args []string, opts ProjectOpts, c *cobra. proj.Name = args[0] } SetProjSpecOptions(c.Flags(), &proj.Spec, &opts) - return &proj, nil } diff --git a/controller/appcontroller.go b/controller/appcontroller.go index 0c071c2b6dec9..eb3c3d166b1b4 100644 --- a/controller/appcontroller.go +++ b/controller/appcontroller.go @@ -113,6 +113,7 @@ type ApplicationController struct { kubectlSemaphore *semaphore.Weighted clusterFilter func(cluster *appv1.Cluster) bool projByNameCache sync.Map + applicationNamespaces []string } // NewApplicationController creates new instance of ApplicationController. @@ -132,6 +133,7 @@ func NewApplicationController( metricsApplicationLabels []string, kubectlParallelismLimit int64, clusterFilter func(cluster *appv1.Cluster) bool, + applicationNamespaces []string, ) (*ApplicationController, error) { log.Infof("appResyncPeriod=%v, appHardResyncPeriod=%v", appResyncPeriod, appHardResyncPeriod) db := db.NewDB(namespace, settingsMgr, kubeClientset) @@ -156,6 +158,7 @@ func NewApplicationController( selfHealTimeout: selfHealTimeout, clusterFilter: clusterFilter, projByNameCache: sync.Map{}, + applicationNamespaces: applicationNamespaces, } if kubectlParallelismLimit > 0 { ctrl.kubectlSemaphore = semaphore.NewWeighted(kubectlParallelismLimit) @@ -270,6 +273,9 @@ type appProjCache struct { appProj *appv1.AppProject } +// GetAppProject gets an AppProject from the cache. If the AppProject is not +// yet cached, retrieves the AppProject from the K8s control plane and stores +// in the cache. func (projCache *appProjCache) GetAppProject(ctx context.Context) (*appv1.AppProject, error) { projCache.lock.Lock() defer projCache.lock.Unlock() @@ -284,9 +290,21 @@ func (projCache *appProjCache) GetAppProject(ctx context.Context) (*appv1.AppPro return projCache.appProj, nil } +// getAppProj gets the AppProject for the given Application app. func (ctrl *ApplicationController) getAppProj(app *appv1.Application) (*appv1.AppProject, error) { projCache, _ := ctrl.projByNameCache.LoadOrStore(app.Spec.GetProject(), ctrl.newAppProjCache(app.Spec.GetProject())) - return projCache.(*appProjCache).GetAppProject(context.TODO()) + proj, err := projCache.(*appProjCache).GetAppProject(context.TODO()) + if err != nil { + if apierr.IsNotFound(err) { + return nil, err + } else { + return nil, fmt.Errorf("could not retrieve AppProject '%s' from cache: %v", app.Spec.Project, err) + } + } + if !proj.IsAppNamespacePermitted(app, ctrl.namespace) { + return nil, argo.ErrProjectNotPermitted(app.GetName(), app.GetNamespace(), proj.GetName()) + } + return proj, nil } func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]bool, ref v1.ObjectReference) { @@ -300,13 +318,17 @@ func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]b continue } - managedByApp[app.Name] = true + managedByApp[app.InstanceName(ctrl.namespace)] = true } } } for appName, isManagedResource := range managedByApp { - obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(ctrl.namespace + "/" + appName) - if app, ok := obj.(*appv1.Application); exists && err == nil && ok && isSelfReferencedApp(app, ref) { + // The appName is given as _, but the indexer needs it + // format / + appKey := ctrl.toAppKey(appName) + obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey) + app, ok := obj.(*appv1.Application) + if exists && err == nil && ok && isSelfReferencedApp(app, ref) { // Don't force refresh app if related resource is application itself. This prevents infinite reconciliation loop. continue } @@ -316,6 +338,13 @@ func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]b continue } + // Enforce application's permission for the source namespace + _, err = ctrl.getAppProj(app) + if err != nil { + log.Errorf("Unable to determine project for app '%s': %v", app.QualifiedName(), err) + continue + } + level := ComparisonWithNothing if isManagedResource { level = CompareWithRecent @@ -330,10 +359,10 @@ func (ctrl *ApplicationController) handleObjectUpdated(managedByApp map[string]b } else { resKey = "(cluster-scoped)/" + ref.Name } - log.Debugf("Refreshing app %s for change in cluster of object %s of type %s/%s", appName, resKey, ref.APIVersion, ref.Kind) + log.Debugf("Refreshing app %s for change in cluster of object %s of type %s/%s", appKey, resKey, ref.APIVersion, ref.Kind) } - ctrl.requestAppRefresh(appName, &level, nil) + ctrl.requestAppRefresh(app.QualifiedName(), &level, nil) } } @@ -348,11 +377,11 @@ func (ctrl *ApplicationController) setAppManagedResources(a *appv1.Application, if err != nil { return nil, fmt.Errorf("error getting resource tree: %s", err) } - err = ctrl.cache.SetAppResourcesTree(a.Name, tree) + err = ctrl.cache.SetAppResourcesTree(a.InstanceName(ctrl.namespace), tree) if err != nil { return nil, fmt.Errorf("error setting app resource tree: %s", err) } - err = ctrl.cache.SetAppManagedResources(a.Name, managedResources) + err = ctrl.cache.SetAppManagedResources(a.InstanceName(ctrl.namespace), managedResources) if err != nil { return nil, fmt.Errorf("error setting app managed resources: %s", err) } @@ -443,7 +472,8 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, k, func(child appv1.ResourceNode, appName string) bool { belongToAnotherApp := false if appName != "" { - if _, exists, err := ctrl.appInformer.GetIndexer().GetByKey(ctrl.namespace + "/" + appName); exists && err == nil { + appKey := ctrl.toAppKey(appName) + if _, exists, err := ctrl.appInformer.GetIndexer().GetByKey(appKey); exists && err == nil { belongToAnotherApp = true } } @@ -474,7 +504,6 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed if err != nil { return nil, err } - return &appv1.ApplicationTree{Nodes: nodes, OrphanedNodes: orphanedNodes, Hosts: hosts}, nil } @@ -705,15 +734,16 @@ func (ctrl *ApplicationController) Run(ctx context.Context, statusProcessors int <-ctx.Done() } +// requestAppRefresh adds a request for given app to the refresh queue. appName +// needs to be the qualified name of the application, i.e. /. func (ctrl *ApplicationController) requestAppRefresh(appName string, compareWith *CompareWith, after *time.Duration) { - key := fmt.Sprintf("%s/%s", ctrl.namespace, appName) - + key := ctrl.toAppKey(appName) if compareWith != nil && after != nil { ctrl.appComparisonTypeRefreshQueue.AddAfter(fmt.Sprintf("%s/%d", key, compareWith), *after) } else { if compareWith != nil { ctrl.refreshRequestedAppsMutex.Lock() - ctrl.refreshRequestedApps[appName] = compareWith.Max(ctrl.refreshRequestedApps[appName]) + ctrl.refreshRequestedApps[key] = compareWith.Max(ctrl.refreshRequestedApps[key]) ctrl.refreshRequestedAppsMutex.Unlock() } if after != nil { @@ -770,7 +800,7 @@ func (ctrl *ApplicationController) processAppOperationQueueItem() (processNext b // If we get here, we are about process an operation but we cannot rely on informer since it might has stale data. // So always retrieve the latest version to ensure it is not stale to avoid unnecessary syncing. // We cannot rely on informer since applications might be updated by both application controller and api server. - freshApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace).Get(context.Background(), app.ObjectMeta.Name, metav1.GetOptions{}) + freshApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.ObjectMeta.Namespace).Get(context.Background(), app.ObjectMeta.Name, metav1.GetOptions{}) if err != nil { log.Errorf("Failed to retrieve latest application state: %v", err) return @@ -816,7 +846,7 @@ func (ctrl *ApplicationController) processAppComparisonTypeQueueItem() (processN log.Warnf("Unable to parse comparison type: %v", err) return } else { - ctrl.requestAppRefresh(parts[1], CompareWith(compareWith).Pointer(), nil) + ctrl.requestAppRefresh(ctrl.toAppQualifiedName(parts[1], parts[0]), CompareWith(compareWith).Pointer(), nil) } } return @@ -910,7 +940,7 @@ func (ctrl *ApplicationController) getPermittedAppLiveObjects(app *appv1.Applica } func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Application) ([]*unstructured.Unstructured, error) { - logCtx := log.WithField("application", app.Name) + 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{}) @@ -1019,11 +1049,15 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic logCtx.Infof("Resource entries removed from undefined cluster") } - ctrl.projectRefreshQueue.Add(fmt.Sprintf("%s/%s", app.Namespace, app.Spec.GetProject())) + ctrl.projectRefreshQueue.Add(fmt.Sprintf("%s/%s", ctrl.namespace, app.Spec.GetProject())) return objs, nil } func (ctrl *ApplicationController) removeCascadeFinalizer(app *appv1.Application) error { + _, err := ctrl.getAppProj(app) + if err != nil { + return err + } app.UnSetCascadedDeletion() var patch []byte patch, _ = json.Marshal(map[string]interface{}{ @@ -1032,7 +1066,7 @@ func (ctrl *ApplicationController) removeCascadeFinalizer(app *appv1.Application }, }) - _, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{}) + _, err = ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{}) return err } @@ -1061,7 +1095,7 @@ func (ctrl *ApplicationController) setAppCondition(app *appv1.Application, condi } func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Application) { - logCtx := log.WithField("application", app.Name) + logCtx := log.WithField("application", app.QualifiedName()) var state *appv1.OperationState // Recover from any unexpected panics and automatically set the status to be failed defer func() { @@ -1092,7 +1126,7 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli retryAfter := time.Until(retryAt) if retryAfter > 0 { logCtx.Infof("Skipping retrying in-progress operation. Attempting again at: %s", retryAt.Format(time.RFC3339)) - ctrl.requestAppRefresh(app.Name, CompareWithLatest.Pointer(), &retryAfter) + ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatest.Pointer(), &retryAfter) return } else { // retrying operation. remove previous failure time in app since it is used as a trigger @@ -1118,11 +1152,24 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli ctrl.appStateManager.SyncAppState(app, state) } + // Check whether application is allowed to use project + _, err := ctrl.getAppProj(app) + if err != nil { + state.Phase = synccommon.OperationError + state.Message = err.Error() + } + if state.Phase == synccommon.OperationRunning { // It's possible for an app to be terminated while we were operating on it. We do not want // to clobber the Terminated state with Running. Get the latest app state to check for this. - freshApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace).Get(context.Background(), app.ObjectMeta.Name, metav1.GetOptions{}) + freshApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Get(context.Background(), app.ObjectMeta.Name, metav1.GetOptions{}) if err == nil { + // App may have lost permissions to use the project meanwhile. + _, err = ctrl.getAppProj(freshApp) + if err != nil { + state.Phase = synccommon.OperationFailed + state.Message = fmt.Sprintf("operation not allowed: %v", err) + } if freshApp.Status.OperationState != nil && freshApp.Status.OperationState.Phase == synccommon.OperationTerminating { state.Phase = synccommon.OperationTerminating state.Message = "operation is terminating" @@ -1155,7 +1202,7 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli // sync/health information if _, err := cache.MetaNamespaceKeyFunc(app); err == nil { // force app refresh with using CompareWithLatest comparison type and trigger app reconciliation loop - ctrl.requestAppRefresh(app.Name, CompareWithLatest.Pointer(), nil) + ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatest.Pointer(), nil) } else { logCtx.Warnf("Fails to requeue application: %v", err) } @@ -1183,7 +1230,7 @@ func (ctrl *ApplicationController) setOperationState(app *appv1.Application, sta patch["operation"] = nil } if reflect.DeepEqual(app.Status.OperationState, state) { - log.Infof("No operation updates necessary to '%s'. Skipping patch", app.Name) + log.Infof("No operation updates necessary to '%s'. Skipping patch", app.QualifiedName()) return nil } patchJSON, err := json.Marshal(patch) @@ -1197,7 +1244,7 @@ func (ctrl *ApplicationController) setOperationState(app *appv1.Application, sta } } - appClient := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace) + appClient := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace) _, err = appClient.Patch(context.Background(), app.Name, types.MergePatchType, patchJSON, metav1.PatchOptions{}) if err != nil { // Stop retrying updating deleted application @@ -1206,7 +1253,7 @@ func (ctrl *ApplicationController) setOperationState(app *appv1.Application, sta } return err } - log.Infof("updated '%s' operation (phase: %s)", app.Name, state.Phase) + log.Infof("updated '%s' operation (phase: %s)", app.QualifiedName(), state.Phase) if state.Phase.Completed() { eventInfo := argo.EventInfo{Reason: argo.EventReasonOperationCompleted} var messages []string @@ -1268,7 +1315,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo } app := origApp.DeepCopy() - logCtx := log.WithFields(log.Fields{"application": app.Name}) + logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()}) startTime := time.Now() defer func() { reconcileDuration := time.Since(startTime) @@ -1284,13 +1331,13 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo if comparisonLevel == ComparisonWithNothing { managedResources := make([]*appv1.ResourceDiff, 0) - if err := ctrl.cache.GetAppManagedResources(app.Name, &managedResources); err != nil { + if err := ctrl.cache.GetAppManagedResources(app.InstanceName(ctrl.namespace), &managedResources); err != nil { logCtx.Warnf("Failed to get cached managed resources for tree reconciliation, fall back to full reconciliation") } else { var tree *appv1.ApplicationTree if tree, err = ctrl.getResourceTree(app, managedResources); err == nil { app.Status.Summary = tree.GetSummary() - if err := ctrl.cache.SetAppResourcesTree(app.Name, tree); err != nil { + if err := ctrl.cache.SetAppResourcesTree(app.InstanceName(ctrl.namespace), tree); err != nil { logCtx.Errorf("Failed to cache resources tree: %v", err) return } @@ -1307,10 +1354,10 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo app.Status.Health.Status = health.HealthStatusUnknown ctrl.persistAppStatus(origApp, &app.Status) - if err := ctrl.cache.SetAppResourcesTree(app.Name, &appv1.ApplicationTree{}); err != nil { + if err := ctrl.cache.SetAppResourcesTree(app.InstanceName(ctrl.namespace), &appv1.ApplicationTree{}); err != nil { log.Warnf("failed to set app resource tree: %v", err) } - if err := ctrl.cache.SetAppManagedResources(app.Name, nil); err != nil { + if err := ctrl.cache.SetAppManagedResources(app.InstanceName(ctrl.namespace), nil); err != nil { log.Warnf("failed to set app managed resources tree: %v", err) } return @@ -1383,7 +1430,7 @@ func resourceStatusKey(res appv1.ResourceStatus) string { // Additionally returns whether full refresh was requested or not. // If full refresh is requested then target and live state should be reconciled, else only live state tree should be updated. func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application, statusRefreshTimeout, statusHardRefreshTimeout time.Duration) (bool, appv1.RefreshType, CompareWith) { - logCtx := log.WithFields(log.Fields{"application": app.Name}) + logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()}) var reason string compareWith := CompareWithLatest refreshType := appv1.RefreshTypeNormal @@ -1413,7 +1460,7 @@ func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application, } } else if !app.Spec.Destination.Equals(app.Status.Sync.ComparedTo.Destination) { reason = "spec.destination differs" - } else if requested, level := ctrl.isRefreshRequested(app.Name); requested { + } else if requested, level := ctrl.isRefreshRequested(app.QualifiedName()); requested { compareWith = level reason = "controller refresh requested" } @@ -1460,7 +1507,7 @@ func (ctrl *ApplicationController) refreshAppConditions(app *appv1.Application) // normalizeApplication normalizes an application.spec and additionally persists updates if it changed func (ctrl *ApplicationController) normalizeApplication(orig, app *appv1.Application) { - logCtx := log.WithFields(log.Fields{"application": app.Name}) + logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()}) app.Spec = *argo.NormalizeApplicationSpec(&app.Spec) patch, modified, err := diff.CreateTwoWayMergePatch(orig, app, appv1.Application{}) if err != nil { @@ -1478,7 +1525,7 @@ func (ctrl *ApplicationController) normalizeApplication(orig, app *appv1.Applica // persistAppStatus persists updates to application status. If no changes were made, it is a no-op func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, newStatus *appv1.ApplicationStatus) { - logCtx := log.WithFields(log.Fields{"application": orig.Name}) + logCtx := log.WithFields(log.Fields{"application": orig.QualifiedName()}) if orig.Status.Sync.Status != newStatus.Sync.Status { message := fmt.Sprintf("Updated sync status: %s -> %s", orig.Status.Sync.Status, newStatus.Sync.Status) ctrl.auditLogger.LogAppEvent(orig, argo.EventInfo{Reason: argo.EventReasonResourceUpdated, Type: v1.EventTypeNormal}, message) @@ -1521,7 +1568,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus * if app.Spec.SyncPolicy == nil || app.Spec.SyncPolicy.Automated == nil { return nil } - logCtx := log.WithFields(log.Fields{"application": app.Name}) + logCtx := log.WithFields(log.Fields{"application": app.QualifiedName()}) if app.Operation != nil { logCtx.Infof("Skipping auto-sync: another operation is in progress") return nil @@ -1592,7 +1639,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus * } } else { logCtx.Infof("Skipping auto-sync: already attempted sync to %s with timeout %v (retrying in %v)", desiredCommitSHA, ctrl.selfHealTimeout, retryAfter) - ctrl.requestAppRefresh(app.Name, CompareWithLatest.Pointer(), &retryAfter) + ctrl.requestAppRefresh(app.QualifiedName(), CompareWithLatest.Pointer(), &retryAfter) return nil } @@ -1669,10 +1716,22 @@ func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool { return ctrl.clusterFilter(cluster) } + // Only process given app if it exists in a watched namespace, or in the + // control plane's namespace. + if app.Namespace != ctrl.namespace && !glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false) { + return false + } + return true } func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.SharedIndexInformer, applisters.ApplicationLister) { + watchNamespace := ctrl.namespace + // If we have at least one additional namespace configured, we need to + // watch on them all. + if len(ctrl.applicationNamespaces) > 0 { + watchNamespace = "" + } refreshTimeout := ctrl.statusRefreshTimeout if ctrl.statusHardRefreshTimeout.Seconds() != 0 && (ctrl.statusHardRefreshTimeout < ctrl.statusRefreshTimeout) { refreshTimeout = ctrl.statusHardRefreshTimeout @@ -1680,10 +1739,23 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar informer := cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (apiruntime.Object, error) { - return ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace).List(context.TODO(), options) + // We are only interested in apps that exist in namespaces the + // user wants to be enabled. + appList, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(watchNamespace).List(context.TODO(), options) + if err != nil { + return nil, err + } + newItems := []appv1.Application{} + for _, app := range appList.Items { + if ctrl.namespace == app.Namespace || glob.MatchStringInList(ctrl.applicationNamespaces, app.Namespace, false) { + newItems = append(newItems, app) + } + } + appList.Items = newItems + return appList, nil }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - return ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace).Watch(context.TODO(), options) + return ctrl.applicationClientset.ArgoprojV1alpha1().Applications(watchNamespace).Watch(context.TODO(), options) }, }, &appv1.Application{}, @@ -1701,6 +1773,12 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil { ctrl.setAppCondition(app, appv1.ApplicationCondition{Type: appv1.ApplicationConditionInvalidSpecError, Message: err.Error()}) } + + // If the application is not allowed to use the project, + // log an error. + if _, err := ctrl.getAppProj(app); err != nil { + ctrl.setAppCondition(app, appv1.ApplicationCondition{Type: appv1.ApplicationConditionUnknownError, Message: err.Error()}) + } } return cache.MetaNamespaceIndexFunc(obj) @@ -1748,10 +1826,10 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar oldApp, oldOK := old.(*appv1.Application) newApp, newOK := new.(*appv1.Application) if oldOK && newOK && automatedSyncEnabled(oldApp, newApp) { - log.WithField("application", newApp.Name).Info("Enabled automated sync") + log.WithField("application", newApp.QualifiedName()).Info("Enabled automated sync") compareWith = CompareWithLatest.Pointer() } - ctrl.requestAppRefresh(newApp.Name, compareWith, nil) + ctrl.requestAppRefresh(newApp.QualifiedName(), compareWith, nil) ctrl.appOperationQueue.Add(key) }, DeleteFunc: func(obj interface{}) { @@ -1771,7 +1849,7 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar } func (ctrl *ApplicationController) RegisterClusterSecretUpdater(ctx context.Context) { - updater := NewClusterInfoUpdater(ctrl.stateCache, ctrl.db, ctrl.appLister.Applications(ctrl.namespace), ctrl.cache, ctrl.clusterFilter) + updater := NewClusterInfoUpdater(ctrl.stateCache, ctrl.db, ctrl.appLister.Applications(""), ctrl.cache, ctrl.clusterFilter, ctrl.getAppProj, ctrl.namespace) go updater.Run(ctx) } @@ -1804,3 +1882,21 @@ func automatedSyncEnabled(oldApp *appv1.Application, newApp *appv1.Application) // nothing changed return false } + +// toAppKey returns the application key from a given appName, that is, it will +// replace underscores with forward-slashes to become a / +// format. If the appName is an unqualified name (such as, "app"), it will use +// the controller's namespace in the key. +func (ctrl *ApplicationController) toAppKey(appName string) string { + if !strings.Contains(appName, "_") && !strings.Contains(appName, "/") { + return ctrl.namespace + "/" + appName + } else if strings.Contains(appName, "/") { + return appName + } else { + return strings.ReplaceAll(appName, "_", "/") + } +} + +func (ctrl *ApplicationController) toAppQualifiedName(appName, appNamespace string) string { + return fmt.Sprintf("%s/%s", appNamespace, appName) +} diff --git a/controller/appcontroller_test.go b/controller/appcontroller_test.go index 8c224483e6fbb..282cb9a3e3eef 100644 --- a/controller/appcontroller_test.go +++ b/controller/appcontroller_test.go @@ -110,6 +110,7 @@ func newFakeController(data *fakeData) *ApplicationController { []string{}, 0, nil, + []string{}, ) if err != nil { panic(err) @@ -754,15 +755,17 @@ func TestHandleAppUpdated(t *testing.T) { app := newFakeApp() app.Spec.Destination.Namespace = test.FakeArgoCDNamespace app.Spec.Destination.Server = argoappv1.KubernetesInternalAPIServerAddr - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) + proj := defaultProj.DeepCopy() + proj.Spec.SourceNamespaces = []string{test.FakeArgoCDNamespace} + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, proj}}) - ctrl.handleObjectUpdated(map[string]bool{app.Name: true}, kube.GetObjectRef(kube.MustToUnstructured(app))) - isRequested, level := ctrl.isRefreshRequested(app.Name) + ctrl.handleObjectUpdated(map[string]bool{app.InstanceName(ctrl.namespace): true}, kube.GetObjectRef(kube.MustToUnstructured(app))) + isRequested, level := ctrl.isRefreshRequested(app.QualifiedName()) assert.False(t, isRequested) assert.Equal(t, ComparisonWithNothing, level) - ctrl.handleObjectUpdated(map[string]bool{app.Name: true}, corev1.ObjectReference{UID: "test", Kind: kube.DeploymentKind, Name: "test", Namespace: "default"}) - isRequested, level = ctrl.isRefreshRequested(app.Name) + ctrl.handleObjectUpdated(map[string]bool{app.InstanceName(ctrl.namespace): true}, corev1.ObjectReference{UID: "test", Kind: kube.DeploymentKind, Name: "test", Namespace: "default"}) + isRequested, level = ctrl.isRefreshRequested(app.QualifiedName()) assert.True(t, isRequested) assert.Equal(t, CompareWithRecent, level) } @@ -785,11 +788,11 @@ func TestHandleOrphanedResourceUpdated(t *testing.T) { ctrl.handleObjectUpdated(map[string]bool{}, corev1.ObjectReference{UID: "test", Kind: kube.DeploymentKind, Name: "test", Namespace: test.FakeArgoCDNamespace}) - isRequested, level := ctrl.isRefreshRequested(app1.Name) + isRequested, level := ctrl.isRefreshRequested(app1.QualifiedName()) assert.True(t, isRequested) assert.Equal(t, CompareWithRecent, level) - isRequested, level = ctrl.isRefreshRequested(app2.Name) + isRequested, level = ctrl.isRefreshRequested(app2.QualifiedName()) assert.True(t, isRequested) assert.Equal(t, CompareWithRecent, level) } @@ -1097,7 +1100,7 @@ func TestFinalizeProjectDeletion_DoesNotHaveApplications(t *testing.T) { func TestProcessRequestedAppOperation_FailedNoRetries(t *testing.T) { app := newFakeApp() - app.Spec.Project = "invalid-project" + app.Spec.Project = "default" app.Operation = &argoappv1.Operation{ Sync: &argoappv1.SyncOperation{}, } @@ -1123,7 +1126,10 @@ func TestProcessRequestedAppOperation_InvalidDestination(t *testing.T) { app.Operation = &argoappv1.Operation{ Sync: &argoappv1.SyncOperation{}, } - ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}}) + proj := defaultProj + proj.Name = "test-project" + proj.Spec.SourceNamespaces = []string{test.FakeArgoCDNamespace} + ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &proj}}) fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset) receivedPatch := map[string]interface{}{} func() { @@ -1311,3 +1317,22 @@ func TestMetricsExpiration(t *testing.T) { ctrl = newFakeController(&fakeData{apps: []runtime.Object{app}, metricsCacheExpiration: 10 * time.Second}) assert.True(t, ctrl.metricsServer.HasExpiration()) } + +func TestToAppKey(t *testing.T) { + ctrl := newFakeController(&fakeData{}) + tests := []struct { + name string + input string + expected string + }{ + {"From instance name", "foo_bar", "foo/bar"}, + {"From qualified name", "foo/bar", "foo/bar"}, + {"From unqualified name", "bar", ctrl.namespace + "/bar"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, ctrl.toAppKey(tt.input)) + }) + } +} diff --git a/controller/cache/cache.go b/controller/cache/cache.go index c288aceb2310d..3f4086cb0965e 100644 --- a/controller/cache/cache.go +++ b/controller/cache/cache.go @@ -529,7 +529,7 @@ func (c *liveStateCache) GetManagedLiveObjs(a *appv1.Application, targetObjs []* return nil, err } return clusterInfo.GetManagedLiveObjs(targetObjs, func(r *clustercache.Resource) bool { - return resInfo(r).AppName == a.Name + return resInfo(r).AppName == a.InstanceName(c.settingsMgr.GetNamespace()) }) } diff --git a/controller/clusterinfoupdater.go b/controller/clusterinfoupdater.go index d34873c0ddadb..111f74497604a 100644 --- a/controller/clusterinfoupdater.go +++ b/controller/clusterinfoupdater.go @@ -28,6 +28,8 @@ type clusterInfoUpdater struct { appLister v1alpha1.ApplicationNamespaceLister cache *appstatecache.Cache clusterFilter func(cluster *appv1.Cluster) bool + projGetter func(app *appv1.Application) (*appv1.AppProject, error) + namespace string } func NewClusterInfoUpdater( @@ -35,9 +37,11 @@ func NewClusterInfoUpdater( db db.ArgoDB, appLister v1alpha1.ApplicationNamespaceLister, cache *appstatecache.Cache, - clusterFilter func(cluster *appv1.Cluster) bool) *clusterInfoUpdater { + clusterFilter func(cluster *appv1.Cluster) bool, + projGetter func(app *appv1.Application) (*appv1.AppProject, error), + namespace string) *clusterInfoUpdater { - return &clusterInfoUpdater{infoSource, db, appLister, cache, clusterFilter} + return &clusterInfoUpdater{infoSource, db, appLister, cache, clusterFilter, projGetter, namespace} } func (c *clusterInfoUpdater) Run(ctx context.Context) { @@ -93,6 +97,12 @@ func (c *clusterInfoUpdater) updateClusterInfo(cluster appv1.Cluster, info *cach } var appCount int64 for _, a := range apps { + if c.projGetter != nil { + proj, err := c.projGetter(a) + if err != nil || !proj.IsAppNamespacePermitted(a, c.namespace) { + continue + } + } if err := argo.ValidateDestination(context.Background(), &a.Spec.Destination, c.db); err != nil { continue } diff --git a/controller/clusterinfoupdater_test.go b/controller/clusterinfoupdater_test.go index 9b99f8b03b34e..60f074d2cfd37 100644 --- a/controller/clusterinfoupdater_test.go +++ b/controller/clusterinfoupdater_test.go @@ -86,7 +86,7 @@ func TestClusterSecretUpdater(t *testing.T) { } lister := applisters.NewApplicationLister(appInformer.GetIndexer()).Applications(fakeNamespace) - updater := NewClusterInfoUpdater(nil, argoDB, lister, appCache, nil) + updater := NewClusterInfoUpdater(nil, argoDB, lister, appCache, nil, nil, fakeNamespace) err = updater.updateClusterInfo(*cluster, info) assert.NoError(t, err, "Invoking updateClusterInfo failed.") diff --git a/controller/state.go b/controller/state.go index a3a31a7a1c2dd..7a2c606ede0da 100644 --- a/controller/state.go +++ b/controller/state.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "reflect" + "strings" "time" "github.com/argoproj/gitops-engine/pkg/diff" @@ -177,7 +178,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1 NoCache: noCache, NoRevisionCache: noRevisionCache, AppLabelKey: appLabelKey, - AppName: app.Name, + AppName: app.InstanceName(m.namespace), Namespace: app.Spec.Destination.Namespace, ApplicationSource: &source, Plugins: tools, @@ -200,7 +201,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1 } ts.AddCheckpoint("unmarshal_ms") - logCtx := log.WithField("application", app.Name) + logCtx := log.WithField("application", app.QualifiedName()) for k, v := range ts.Timings() { logCtx = logCtx.WithField(k, v.Milliseconds()) } @@ -349,7 +350,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap failedToLoadObjs := false conditions := make([]v1alpha1.ApplicationCondition, 0) - logCtx := log.WithField("application", app.Name) + logCtx := log.WithField("application", app.QualifiedName()) logCtx.Infof("Comparing app state (cluster: %s, namespace: %s)", app.Spec.Destination.Server, app.Spec.Destination.Namespace) var targetObjs []*unstructured.Unstructured @@ -427,10 +428,11 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap for _, liveObj := range liveObjByKey { if liveObj != nil { appInstanceName := m.resourceTracking.GetAppName(liveObj, appLabelKey, trackingMethod) - if appInstanceName != "" && appInstanceName != app.Name { + if appInstanceName != "" && appInstanceName != app.InstanceName(m.namespace) { + fqInstanceName := strings.ReplaceAll(appInstanceName, "_", "/") conditions = append(conditions, v1alpha1.ApplicationCondition{ Type: v1alpha1.ApplicationConditionSharedResourceWarning, - Message: fmt.Sprintf("%s/%s is part of applications %s and %s", liveObj.GetKind(), liveObj.GetName(), app.Name, appInstanceName), + Message: fmt.Sprintf("%s/%s is part of applications %s and %s", liveObj.GetKind(), liveObj.GetName(), app.QualifiedName(), fqInstanceName), LastTransitionTime: &now, }) } @@ -644,7 +646,7 @@ func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revi if err != nil { return err } - _, err = m.appclientset.ArgoprojV1alpha1().Applications(m.namespace).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{}) + _, err = m.appclientset.ArgoprojV1alpha1().Applications(app.Namespace).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{}) return err } diff --git a/controller/state_test.go b/controller/state_test.go index a2e7aa4816b82..055dc2f72e007 100644 --- a/controller/state_test.go +++ b/controller/state_test.go @@ -374,7 +374,7 @@ func TestSetManagedResourcesWithResourcesOfAnotherApp(t *testing.T) { tree, err := ctrl.setAppManagedResources(app1, &comparisonResult{managedResources: make([]managedResource, 0)}) assert.NoError(t, err) - assert.Equal(t, len(tree.OrphanedNodes), 0) + assert.Equal(t, 0, len(tree.OrphanedNodes)) } func TestReturnUnknownComparisonStateOnSettingLoadError(t *testing.T) { @@ -399,6 +399,7 @@ func TestReturnUnknownComparisonStateOnSettingLoadError(t *testing.T) { func TestSetManagedResourcesKnownOrphanedResourceExceptions(t *testing.T) { proj := defaultProj.DeepCopy() proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{} + proj.Spec.SourceNamespaces = []string{"default"} app := newFakeApp() app.Namespace = "default" diff --git a/controller/sync.go b/controller/sync.go index db41495e73f45..a738b9f02304b 100644 --- a/controller/sync.go +++ b/controller/sync.go @@ -109,7 +109,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha revision = syncOp.Revision } - proj, err := argo.GetAppProject(&app.Spec, listersv1alpha1.NewAppProjectLister(m.projInformer.GetIndexer()), m.namespace, m.settingsMgr, m.db, context.TODO()) + proj, err := argo.GetAppProject(app, listersv1alpha1.NewAppProjectLister(m.projInformer.GetIndexer()), m.namespace, m.settingsMgr, m.db, context.TODO()) if err != nil { state.Phase = common.OperationError state.Message = fmt.Sprintf("Failed to load application project: %v", err) @@ -157,7 +157,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha } syncId := fmt.Sprintf("%05d-%s", syncIdPrefix, randSuffix) - logEntry := log.WithFields(log.Fields{"application": app.Name, "syncId": syncId}) + logEntry := log.WithFields(log.Fields{"application": app.QualifiedName(), "syncId": syncId}) initialResourcesRes := make([]common.ResourceSyncResult, 0) for i, res := range syncRes.Resources { key := kube.ResourceKey{Group: res.Group, Kind: res.Kind, Namespace: res.Namespace, Name: res.Name} diff --git a/docs/operator-manual/server-commands/argocd-application-controller.md b/docs/operator-manual/server-commands/argocd-application-controller.md index d1fc4d0e99c14..853501707b35c 100644 --- a/docs/operator-manual/server-commands/argocd-application-controller.md +++ b/docs/operator-manual/server-commands/argocd-application-controller.md @@ -16,6 +16,7 @@ argocd-application-controller [flags] --app-hard-resync int Time period in seconds for application hard resync. --app-resync int Time period in seconds for application resync. (default 180) --app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s) + --application-namespaces strings List of additional namespaces that applications are allowed to be created 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 diff --git a/docs/operator-manual/server-commands/argocd-server.md b/docs/operator-manual/server-commands/argocd-server.md index 7165c3696df36..f0fa08a0dc27e 100644 --- a/docs/operator-manual/server-commands/argocd-server.md +++ b/docs/operator-manual/server-commands/argocd-server.md @@ -14,6 +14,7 @@ argocd-server [flags] ``` --app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s) + --application-namespaces strings List of namespaces where application resources can exist --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 diff --git a/docs/user-guide/commands/argocd_admin_proj_generate-spec.md b/docs/user-guide/commands/argocd_admin_proj_generate-spec.md index 7881e3e281d09..4227f17459c64 100644 --- a/docs/user-guide/commands/argocd_admin_proj_generate-spec.md +++ b/docs/user-guide/commands/argocd_admin_proj_generate-spec.md @@ -22,6 +22,7 @@ argocd admin proj generate-spec PROJECT [flags] --orphaned-resources-warn Specifies if applications should have a warning condition when orphaned resources detected -o, --output string Output format. One of: json|yaml (default "yaml") --signature-keys strings GnuPG public key IDs for commit signature verification + --source-namespaces strings List of source namespaces for applications -s, --src stringArray Permitted source repository URL ``` diff --git a/docs/user-guide/commands/argocd_app_create.md b/docs/user-guide/commands/argocd_app_create.md index 900145f57e0ea..e4e5b1d10c6a8 100644 --- a/docs/user-guide/commands/argocd_app_create.md +++ b/docs/user-guide/commands/argocd_app_create.md @@ -33,6 +33,7 @@ argocd app create APPNAME [flags] ``` --allow-empty Set allow zero live resources when sync is automated --annotations stringArray Set metadata annotations (e.g. example=value) + -N, --app-namespace string Namespace where the application will be created in --auto-prune Set automatic pruning when sync is automated --config-management-plugin string Config management plugin name --dest-name string K8s cluster Name (e.g. minikube) diff --git a/docs/user-guide/commands/argocd_app_list.md b/docs/user-guide/commands/argocd_app_list.md index 8e38361e8483b..b318086c58192 100644 --- a/docs/user-guide/commands/argocd_app_list.md +++ b/docs/user-guide/commands/argocd_app_list.md @@ -19,11 +19,12 @@ argocd app list [flags] ### Options ``` - -h, --help help for list - -o, --output string Output format. One of: wide|name|json|yaml (default "wide") - -p, --project stringArray Filter by project name - -r, --repo string List apps by source repo URL - -l, --selector string List apps by label + -N, --app-namespace string Only list applications in namespace + -h, --help help for list + -o, --output string Output format. One of: wide|name|json|yaml (default "wide") + -p, --project stringArray Filter by project name + -r, --repo string List apps by source repo URL + -l, --selector string List apps by label ``` ### Options inherited from parent commands diff --git a/docs/user-guide/commands/argocd_proj_create.md b/docs/user-guide/commands/argocd_proj_create.md index ee42fb7266b63..fa9ac85afe9f5 100644 --- a/docs/user-guide/commands/argocd_proj_create.md +++ b/docs/user-guide/commands/argocd_proj_create.md @@ -20,6 +20,7 @@ argocd proj create PROJECT [flags] --orphaned-resources Enables orphaned resources monitoring --orphaned-resources-warn Specifies if applications should have a warning condition when orphaned resources detected --signature-keys strings GnuPG public key IDs for commit signature verification + --source-namespaces strings List of source namespaces for applications -s, --src stringArray Permitted source repository URL --upsert Allows to override a project with the same name even if supplied project spec is different from existing spec ``` diff --git a/docs/user-guide/commands/argocd_proj_set.md b/docs/user-guide/commands/argocd_proj_set.md index 697673395f544..04a87ae2bcf14 100644 --- a/docs/user-guide/commands/argocd_proj_set.md +++ b/docs/user-guide/commands/argocd_proj_set.md @@ -19,6 +19,7 @@ argocd proj set PROJECT [flags] --orphaned-resources Enables orphaned resources monitoring --orphaned-resources-warn Specifies if applications should have a warning condition when orphaned resources detected --signature-keys strings GnuPG public key IDs for commit signature verification + --source-namespaces strings List of source namespaces for applications -s, --src stringArray Permitted source repository URL ``` diff --git a/manifests/cluster-rbac/server/argocd-server-clusterrole.yaml b/manifests/cluster-rbac/server/argocd-server-clusterrole.yaml index 79bedbd72a42f..779e674a608c9 100644 --- a/manifests/cluster-rbac/server/argocd-server-clusterrole.yaml +++ b/manifests/cluster-rbac/server/argocd-server-clusterrole.yaml @@ -28,3 +28,11 @@ rules: - pods/log verbs: - get # supports viewing pod logs from UI +- apiGroups: + - "argoproj.io" + resources: + - "applications" + verbs: + - get + - list + - watch diff --git a/manifests/core-install.yaml b/manifests/core-install.yaml index 6ed734b29b47e..f8b51e259009a 100644 --- a/manifests/core-install.yaml +++ b/manifests/core-install.yaml @@ -9121,6 +9121,12 @@ spec: - keyID type: object type: array + sourceNamespaces: + description: SourceNamespaces defines the namespaces application resources + are allowed to be created in + items: + type: string + type: array sourceRepos: description: SourceRepos contains list of repository URLs which can be used for deployment diff --git a/manifests/crds/appproject-crd.yaml b/manifests/crds/appproject-crd.yaml index 79179e8e67a3b..f510ed215638a 100644 --- a/manifests/crds/appproject-crd.yaml +++ b/manifests/crds/appproject-crd.yaml @@ -221,6 +221,12 @@ spec: - keyID type: object type: array + sourceNamespaces: + description: SourceNamespaces defines the namespaces application resources + are allowed to be created in + items: + type: string + type: array sourceRepos: description: SourceRepos contains list of repository URLs which can be used for deployment diff --git a/manifests/ha/install.yaml b/manifests/ha/install.yaml index 31d9679516690..1e8b101a407b0 100644 --- a/manifests/ha/install.yaml +++ b/manifests/ha/install.yaml @@ -9121,6 +9121,12 @@ spec: - keyID type: object type: array + sourceNamespaces: + description: SourceNamespaces defines the namespaces application resources + are allowed to be created in + items: + type: string + type: array sourceRepos: description: SourceRepos contains list of repository URLs which can be used for deployment @@ -9573,6 +9579,14 @@ rules: - pods/log verbs: - get +- apiGroups: + - argoproj.io + resources: + - applications + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/manifests/install.yaml b/manifests/install.yaml index 9b2228a81f273..eb62636d9e8b0 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -9121,6 +9121,12 @@ spec: - keyID type: object type: array + sourceNamespaces: + description: SourceNamespaces defines the namespaces application resources + are allowed to be created in + items: + type: string + type: array sourceRepos: description: SourceRepos contains list of repository URLs which can be used for deployment @@ -9532,6 +9538,14 @@ rules: - pods/log verbs: - get +- apiGroups: + - argoproj.io + resources: + - applications + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/pkg/apiclient/apiclient.go b/pkg/apiclient/apiclient.go index f51863ce4ab5a..bba7b88076197 100644 --- a/pkg/apiclient/apiclient.go +++ b/pkg/apiclient/apiclient.go @@ -44,6 +44,7 @@ import ( versionpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/version" "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/util/argo" "github.com/argoproj/argo-cd/v2/util/env" grpc_util "github.com/argoproj/argo-cd/v2/util/grpc" http_util "github.com/argoproj/argo-cd/v2/util/http" @@ -765,13 +766,18 @@ func (c *client) NewAccountClientOrDie() (io.Closer, accountpkg.AccountServiceCl func (c *client) WatchApplicationWithRetry(ctx context.Context, appName string, revision string) chan *argoappv1.ApplicationWatchEvent { appEventsCh := make(chan *argoappv1.ApplicationWatchEvent) cancelled := false + appName, appNs := argo.ParseAppQualifiedName(appName, "") go func() { defer close(appEventsCh) for !cancelled { conn, appIf, err := c.NewApplicationClient() if err == nil { var wc applicationpkg.ApplicationService_WatchClient - wc, err = appIf.Watch(ctx, &applicationpkg.ApplicationQuery{Name: &appName, ResourceVersion: &revision}) + wc, err = appIf.Watch(ctx, &applicationpkg.ApplicationQuery{ + Name: &appName, + AppNamespace: &appNs, + ResourceVersion: &revision, + }) if err == nil { for { var appEvent *v1alpha1.ApplicationWatchEvent diff --git a/pkg/apiclient/application/application.pb.go b/pkg/apiclient/application/application.pb.go index fd0f9829aca41..9215edb927944 100644 --- a/pkg/apiclient/application/application.pb.go +++ b/pkg/apiclient/application/application.pb.go @@ -49,7 +49,9 @@ type ApplicationQuery struct { // the selector to restrict returned list to applications only with matched labels Selector *string `protobuf:"bytes,5,opt,name=selector" json:"selector,omitempty"` // the repoURL to restrict returned list applications - Repo *string `protobuf:"bytes,6,opt,name=repo" json:"repo,omitempty"` + Repo *string `protobuf:"bytes,6,opt,name=repo" json:"repo,omitempty"` + // the application's namespace + AppNamespace *string `protobuf:"bytes,7,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -130,9 +132,17 @@ func (m *ApplicationQuery) GetRepo() string { return "" } +func (m *ApplicationQuery) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type NodeQuery struct { // the application's name Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + AppNamespace *string `protobuf:"bytes,2,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -178,11 +188,20 @@ func (m *NodeQuery) GetName() string { return "" } +func (m *NodeQuery) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type RevisionMetadataQuery struct { // the application's name Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` // the revision of the app - Revision *string `protobuf:"bytes,2,req,name=revision" json:"revision,omitempty"` + Revision *string `protobuf:"bytes,2,req,name=revision" json:"revision,omitempty"` + // the application's namespace + AppNamespace *string `protobuf:"bytes,3,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -235,12 +254,20 @@ func (m *RevisionMetadataQuery) GetRevision() string { return "" } +func (m *RevisionMetadataQuery) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + // ApplicationEventsQuery is a query for application resource events type ApplicationResourceEventsQuery struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` ResourceNamespace *string `protobuf:"bytes,2,opt,name=resourceNamespace" json:"resourceNamespace,omitempty"` ResourceName *string `protobuf:"bytes,3,opt,name=resourceName" json:"resourceName,omitempty"` ResourceUID *string `protobuf:"bytes,4,opt,name=resourceUID" json:"resourceUID,omitempty"` + AppNamespace *string `protobuf:"bytes,5,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -307,10 +334,18 @@ func (m *ApplicationResourceEventsQuery) GetResourceUID() string { return "" } +func (m *ApplicationResourceEventsQuery) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + // ManifestQuery is a query for manifest resources type ApplicationManifestQuery struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Revision *string `protobuf:"bytes,2,opt,name=revision" json:"revision,omitempty"` + AppNamespace *string `protobuf:"bytes,3,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -363,6 +398,13 @@ func (m *ApplicationManifestQuery) GetRevision() string { return "" } +func (m *ApplicationManifestQuery) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type ApplicationResponse struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -524,6 +566,7 @@ type ApplicationDeleteRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Cascade *bool `protobuf:"varint,2,opt,name=cascade" json:"cascade,omitempty"` PropagationPolicy *string `protobuf:"bytes,3,opt,name=propagationPolicy" json:"propagationPolicy,omitempty"` + AppNamespace *string `protobuf:"bytes,4,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -583,6 +626,13 @@ func (m *ApplicationDeleteRequest) GetPropagationPolicy() string { return "" } +func (m *ApplicationDeleteRequest) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type SyncOptions struct { Items []string `protobuf:"bytes,1,rep,name=items" json:"items,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -642,6 +692,7 @@ type ApplicationSyncRequest struct { Infos []*v1alpha1.Info `protobuf:"bytes,9,rep,name=infos" json:"infos,omitempty"` RetryStrategy *v1alpha1.RetryStrategy `protobuf:"bytes,10,opt,name=retryStrategy" json:"retryStrategy,omitempty"` SyncOptions *SyncOptions `protobuf:"bytes,11,opt,name=syncOptions" json:"syncOptions,omitempty"` + AppNamespace *string `protobuf:"bytes,12,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -750,11 +801,19 @@ func (m *ApplicationSyncRequest) GetSyncOptions() *SyncOptions { return nil } +func (m *ApplicationSyncRequest) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + // ApplicationUpdateSpecRequest is a request to update application spec type ApplicationUpdateSpecRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Spec *v1alpha1.ApplicationSpec `protobuf:"bytes,2,req,name=spec" json:"spec,omitempty"` Validate *bool `protobuf:"varint,3,opt,name=validate" json:"validate,omitempty"` + AppNamespace *string `protobuf:"bytes,4,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -814,11 +873,19 @@ func (m *ApplicationUpdateSpecRequest) GetValidate() bool { return false } +func (m *ApplicationUpdateSpecRequest) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + // ApplicationPatchRequest is a request to patch an application type ApplicationPatchRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Patch *string `protobuf:"bytes,2,req,name=patch" json:"patch,omitempty"` PatchType *string `protobuf:"bytes,3,req,name=patchType" json:"patchType,omitempty"` + AppNamespace *string `protobuf:"bytes,5,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -878,11 +945,19 @@ func (m *ApplicationPatchRequest) GetPatchType() string { return "" } +func (m *ApplicationPatchRequest) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type ApplicationRollbackRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Id *int64 `protobuf:"varint,2,req,name=id" json:"id,omitempty"` DryRun *bool `protobuf:"varint,3,opt,name=dryRun" json:"dryRun,omitempty"` Prune *bool `protobuf:"varint,4,opt,name=prune" json:"prune,omitempty"` + AppNamespace *string `protobuf:"bytes,6,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -949,6 +1024,13 @@ func (m *ApplicationRollbackRequest) GetPrune() bool { return false } +func (m *ApplicationRollbackRequest) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type ApplicationResourceRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"` @@ -956,6 +1038,7 @@ type ApplicationResourceRequest struct { Version *string `protobuf:"bytes,4,req,name=version" json:"version,omitempty"` Group *string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"` Kind *string `protobuf:"bytes,6,req,name=kind" json:"kind,omitempty"` + AppNamespace *string `protobuf:"bytes,7,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1036,6 +1119,13 @@ func (m *ApplicationResourceRequest) GetKind() string { return "" } +func (m *ApplicationResourceRequest) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type ApplicationResourcePatchRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"` @@ -1045,6 +1135,7 @@ type ApplicationResourcePatchRequest struct { Kind *string `protobuf:"bytes,6,req,name=kind" json:"kind,omitempty"` Patch *string `protobuf:"bytes,7,req,name=patch" json:"patch,omitempty"` PatchType *string `protobuf:"bytes,8,req,name=patchType" json:"patchType,omitempty"` + AppNamespace *string `protobuf:"bytes,9,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1139,6 +1230,13 @@ func (m *ApplicationResourcePatchRequest) GetPatchType() string { return "" } +func (m *ApplicationResourcePatchRequest) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type ApplicationResourceDeleteRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"` @@ -1148,6 +1246,7 @@ type ApplicationResourceDeleteRequest struct { Kind *string `protobuf:"bytes,6,req,name=kind" json:"kind,omitempty"` Force *bool `protobuf:"varint,7,opt,name=force" json:"force,omitempty"` Orphan *bool `protobuf:"varint,8,opt,name=orphan" json:"orphan,omitempty"` + AppNamespace *string `protobuf:"bytes,9,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1242,6 +1341,13 @@ func (m *ApplicationResourceDeleteRequest) GetOrphan() bool { return false } +func (m *ApplicationResourceDeleteRequest) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type ResourceActionRunRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"` @@ -1250,6 +1356,7 @@ type ResourceActionRunRequest struct { Group *string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"` Kind *string `protobuf:"bytes,6,req,name=kind" json:"kind,omitempty"` Action *string `protobuf:"bytes,7,req,name=action" json:"action,omitempty"` + AppNamespace *string `protobuf:"bytes,8,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1337,6 +1444,13 @@ func (m *ResourceActionRunRequest) GetAction() string { return "" } +func (m *ResourceActionRunRequest) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type ResourceActionsListResponse struct { Actions []*v1alpha1.ResourceAction `protobuf:"bytes,1,rep,name=actions" json:"actions,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -1446,6 +1560,7 @@ type ApplicationPodLogsQuery struct { Group *string `protobuf:"bytes,12,opt,name=group" json:"group,omitempty"` ResourceName *string `protobuf:"bytes,13,opt,name=resourceName" json:"resourceName,omitempty"` Previous *bool `protobuf:"varint,14,opt,name=previous" json:"previous,omitempty"` + AppNamespace *string `protobuf:"bytes,15,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1582,6 +1697,13 @@ func (m *ApplicationPodLogsQuery) GetPrevious() bool { return false } +func (m *ApplicationPodLogsQuery) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type LogEntry struct { Content *string `protobuf:"bytes,1,req,name=content" json:"content,omitempty"` // deprecated in favor of timeStampStr since meta.v1.Time don't support nano time @@ -1664,6 +1786,7 @@ func (m *LogEntry) GetPodName() string { type OperationTerminateRequest struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` + AppNamespace *string `protobuf:"bytes,2,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1709,8 +1832,16 @@ func (m *OperationTerminateRequest) GetName() string { return "" } +func (m *OperationTerminateRequest) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type ApplicationSyncWindowsQuery struct { Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` + AppNamespace *string `protobuf:"bytes,2,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1756,6 +1887,13 @@ func (m *ApplicationSyncWindowsQuery) GetName() string { return "" } +func (m *ApplicationSyncWindowsQuery) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type ApplicationSyncWindowsResponse struct { ActiveWindows []*ApplicationSyncWindow `protobuf:"bytes,1,rep,name=activeWindows" json:"activeWindows,omitempty"` AssignedWindows []*ApplicationSyncWindow `protobuf:"bytes,2,rep,name=assignedWindows" json:"assignedWindows,omitempty"` @@ -1936,6 +2074,7 @@ type ResourcesQuery struct { Version *string `protobuf:"bytes,4,opt,name=version" json:"version,omitempty"` Group *string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"` Kind *string `protobuf:"bytes,6,opt,name=kind" json:"kind,omitempty"` + AppNamespace *string `protobuf:"bytes,7,opt,name=appNamespace" json:"appNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2016,6 +2155,13 @@ func (m *ResourcesQuery) GetKind() string { return "" } +func (m *ResourcesQuery) GetAppNamespace() string { + if m != nil && m.AppNamespace != nil { + return *m.AppNamespace + } + return "" +} + type ManagedResourcesResponse struct { Items []*v1alpha1.ResourceDiff `protobuf:"bytes,1,rep,name=items" json:"items,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -2100,147 +2246,152 @@ func init() { } var fileDescriptor_df6e82b174b5eaec = []byte{ - // 2236 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x8f, 0x1b, 0x49, - 0x15, 0x57, 0xcd, 0xa7, 0xfd, 0x9c, 0xcf, 0xda, 0x4d, 0xe8, 0xed, 0x4c, 0x66, 0x47, 0x95, 0xaf, - 0xc9, 0x24, 0x63, 0x27, 0x26, 0x42, 0xd9, 0x59, 0x10, 0x64, 0x77, 0xc3, 0x6c, 0x96, 0x99, 0xd9, - 0xd0, 0x93, 0x10, 0xb4, 0x1c, 0xa0, 0xb6, 0xbb, 0xec, 0x69, 0xc6, 0xee, 0xea, 0x74, 0xb7, 0x1d, - 0x59, 0x21, 0x97, 0x45, 0xdc, 0x10, 0x48, 0xb0, 0x07, 0x84, 0x10, 0x42, 0xac, 0x56, 0xe2, 0x06, - 0x5c, 0x56, 0x48, 0x5c, 0xe0, 0xc2, 0x87, 0xc4, 0x01, 0xc1, 0x3f, 0x00, 0x11, 0x27, 0x2e, 0x5c, - 0x39, 0xa2, 0xaa, 0xae, 0x6a, 0x57, 0x7b, 0xec, 0xb6, 0xc3, 0x78, 0xb5, 0xb9, 0xf5, 0x2b, 0x57, - 0xbd, 0xf7, 0xab, 0x57, 0xbf, 0x7a, 0xaf, 0xde, 0x93, 0xe1, 0x7c, 0xcc, 0xa2, 0x2e, 0x8b, 0x6a, - 0x34, 0x0c, 0x5b, 0xbe, 0x4b, 0x13, 0x9f, 0x07, 0xe6, 0x77, 0x35, 0x8c, 0x78, 0xc2, 0x71, 0xc5, - 0x18, 0xb2, 0x97, 0x9a, 0x9c, 0x37, 0x5b, 0xac, 0x46, 0x43, 0xbf, 0x46, 0x83, 0x80, 0x27, 0x72, - 0x38, 0x4e, 0xa7, 0xda, 0x64, 0xff, 0x66, 0x5c, 0xf5, 0xb9, 0xfc, 0xd5, 0xe5, 0x11, 0xab, 0x75, - 0xaf, 0xd7, 0x9a, 0x2c, 0x60, 0x11, 0x4d, 0x98, 0xa7, 0xe6, 0xdc, 0xe8, 0xcf, 0x69, 0x53, 0x77, - 0xcf, 0x0f, 0x58, 0xd4, 0xab, 0x85, 0xfb, 0x4d, 0x31, 0x10, 0xd7, 0xda, 0x2c, 0xa1, 0xc3, 0x56, - 0x6d, 0x35, 0xfd, 0x64, 0xaf, 0xf3, 0x6e, 0xd5, 0xe5, 0xed, 0x1a, 0x8d, 0x9a, 0x3c, 0x8c, 0xf8, - 0x37, 0xe5, 0xc7, 0xba, 0xeb, 0xd5, 0xba, 0xf5, 0xbe, 0x02, 0x73, 0x2f, 0xdd, 0xeb, 0xb4, 0x15, - 0xee, 0xd1, 0x83, 0xda, 0x6e, 0x8f, 0xd1, 0x16, 0xb1, 0x90, 0x2b, 0xdf, 0xc8, 0x4f, 0x3f, 0xe1, - 0x51, 0xcf, 0xf8, 0x4c, 0xd5, 0x90, 0x8f, 0x10, 0x9c, 0xb8, 0xd5, 0xb7, 0xf7, 0xe5, 0x0e, 0x8b, - 0x7a, 0x18, 0xc3, 0x5c, 0x40, 0xdb, 0xcc, 0x42, 0x2b, 0x68, 0xb5, 0xec, 0xc8, 0x6f, 0x6c, 0xc1, - 0x62, 0xc4, 0x1a, 0x11, 0x8b, 0xf7, 0xac, 0x19, 0x39, 0xac, 0x45, 0x6c, 0x43, 0x49, 0x18, 0x67, - 0x6e, 0x12, 0x5b, 0xb3, 0x2b, 0xb3, 0xab, 0x65, 0x27, 0x93, 0xf1, 0x2a, 0x1c, 0x8f, 0x58, 0xcc, - 0x3b, 0x91, 0xcb, 0xbe, 0xc2, 0xa2, 0xd8, 0xe7, 0x81, 0x35, 0x27, 0x57, 0x0f, 0x0e, 0x0b, 0x2d, - 0x31, 0x6b, 0x31, 0x37, 0xe1, 0x91, 0x35, 0x2f, 0xa7, 0x64, 0xb2, 0xc0, 0x23, 0x80, 0x5b, 0x0b, - 0x29, 0x1e, 0xf1, 0x4d, 0x5e, 0x86, 0xf2, 0x0e, 0xf7, 0xd8, 0x48, 0xc0, 0x64, 0x13, 0x4e, 0x39, - 0xac, 0xeb, 0x0b, 0xe5, 0xdb, 0x2c, 0xa1, 0x1e, 0x4d, 0xe8, 0xe0, 0xe4, 0x99, 0x6c, 0x77, 0x36, - 0x94, 0x22, 0x35, 0xd9, 0x9a, 0x91, 0xe3, 0x99, 0x4c, 0x7e, 0x81, 0x60, 0xd9, 0x70, 0x91, 0xa3, - 0x80, 0xdf, 0xee, 0xb2, 0x20, 0x89, 0x47, 0xab, 0xbc, 0x0a, 0x27, 0xf5, 0x1e, 0x77, 0x68, 0x9b, - 0xc5, 0x21, 0x75, 0x99, 0x72, 0xdd, 0xc1, 0x1f, 0x30, 0x81, 0x23, 0xe6, 0xa0, 0x35, 0x2b, 0x27, - 0xe6, 0xc6, 0xf0, 0x0a, 0x54, 0xb4, 0x7c, 0xff, 0xce, 0x1b, 0xca, 0x91, 0xe6, 0x10, 0x79, 0x0b, - 0x2c, 0x03, 0xe9, 0x36, 0x0d, 0xfc, 0x06, 0x8b, 0x93, 0x49, 0xb7, 0x8d, 0x72, 0xdb, 0x3e, 0x05, - 0x2f, 0xe4, 0x77, 0x1d, 0xf2, 0x20, 0x66, 0xe4, 0xb7, 0x28, 0x67, 0xe3, 0xf5, 0x88, 0xd1, 0x84, - 0x39, 0xec, 0x61, 0x87, 0xc5, 0x09, 0xde, 0x07, 0xf3, 0xa6, 0x49, 0x53, 0x95, 0xfa, 0x9d, 0x6a, - 0x9f, 0xaa, 0x55, 0x4d, 0x55, 0xf9, 0xf1, 0x75, 0xd7, 0xab, 0x76, 0xeb, 0xd5, 0x70, 0xbf, 0x59, - 0x15, 0xc4, 0xaf, 0x9a, 0x17, 0x57, 0x13, 0xbf, 0x6a, 0x82, 0x30, 0xb5, 0xe3, 0xd3, 0xb0, 0xd0, - 0x09, 0x63, 0x16, 0x25, 0x12, 0x7a, 0xc9, 0x51, 0x92, 0xd8, 0x54, 0x97, 0xb6, 0x7c, 0x8f, 0x26, - 0xa9, 0x1b, 0x4b, 0x4e, 0x26, 0x93, 0x0f, 0xf2, 0xe8, 0xef, 0x87, 0xde, 0x27, 0x85, 0xde, 0x44, - 0x39, 0x33, 0x80, 0xb2, 0x9b, 0x03, 0xf9, 0x06, 0x6b, 0xb1, 0x3e, 0xc8, 0x61, 0xc7, 0x68, 0xc1, - 0xa2, 0x4b, 0x63, 0x97, 0x7a, 0x5a, 0x95, 0x16, 0x05, 0x09, 0xc3, 0x88, 0x87, 0xb4, 0x29, 0x35, - 0xdd, 0xe5, 0x2d, 0xdf, 0xed, 0x29, 0x6e, 0x1d, 0xfc, 0x81, 0x9c, 0x83, 0xca, 0x6e, 0x2f, 0x70, - 0xdf, 0x0e, 0x65, 0x40, 0xc4, 0x2f, 0xc2, 0xbc, 0x9f, 0xb0, 0x76, 0x6c, 0x21, 0x79, 0xab, 0x53, - 0x81, 0xfc, 0x77, 0x0e, 0x4e, 0x1b, 0xe8, 0xc4, 0x82, 0x22, 0x6c, 0x05, 0x14, 0x13, 0x27, 0xe8, - 0x45, 0x3d, 0xa7, 0x13, 0xa8, 0x73, 0x52, 0x92, 0x30, 0x1c, 0x46, 0x9d, 0x80, 0x49, 0x8a, 0x97, - 0x9c, 0x54, 0xc0, 0x0d, 0x28, 0xc5, 0x89, 0x08, 0x81, 0xcd, 0x9e, 0x8c, 0x10, 0x95, 0xfa, 0x5b, - 0x87, 0x3b, 0x1b, 0x01, 0x7d, 0x57, 0x69, 0x74, 0x32, 0xdd, 0xf8, 0x21, 0x94, 0xf5, 0x9d, 0x8a, - 0xad, 0xc5, 0x95, 0xd9, 0xd5, 0x4a, 0x7d, 0xf7, 0xf0, 0x86, 0xde, 0x0e, 0x45, 0xf8, 0x36, 0xe2, - 0x87, 0xd3, 0xb7, 0x82, 0x97, 0xa0, 0xdc, 0x56, 0x97, 0x35, 0xb6, 0x4a, 0xd2, 0xdb, 0xfd, 0x01, - 0xfc, 0x55, 0x98, 0xf7, 0x83, 0x06, 0x8f, 0xad, 0xb2, 0x04, 0xf3, 0xda, 0xe1, 0xc0, 0xdc, 0x09, - 0x1a, 0xdc, 0x49, 0x15, 0xe2, 0x87, 0x70, 0x34, 0x62, 0x49, 0xd4, 0xd3, 0x5e, 0xb0, 0x40, 0xfa, - 0xf5, 0x4b, 0x87, 0xb3, 0xe0, 0x98, 0x2a, 0x9d, 0xbc, 0x05, 0xbc, 0x01, 0x95, 0xb8, 0xcf, 0x31, - 0xab, 0x22, 0x0d, 0x5a, 0x39, 0x45, 0x06, 0x07, 0x1d, 0x73, 0x32, 0xf9, 0x35, 0x82, 0xa5, 0x03, - 0xb7, 0x77, 0x37, 0x64, 0x85, 0x04, 0xa4, 0x30, 0x17, 0x87, 0xcc, 0x95, 0x61, 0xbd, 0x52, 0xdf, - 0x9e, 0xda, 0x75, 0x96, 0x76, 0xa5, 0xea, 0xc2, 0x88, 0x43, 0xe1, 0x53, 0xc6, 0xa2, 0xbb, 0x34, - 0x71, 0xf7, 0x8a, 0xd0, 0x0a, 0xea, 0x8b, 0x39, 0x2a, 0x0b, 0xa5, 0x82, 0xe0, 0x87, 0xfc, 0xb8, - 0xd7, 0x0b, 0x85, 0x05, 0xf1, 0x4b, 0x7f, 0x80, 0x04, 0x60, 0x9b, 0x61, 0x86, 0xb7, 0x5a, 0xef, - 0x52, 0x77, 0xbf, 0xc8, 0xca, 0x31, 0x98, 0xf1, 0x3d, 0x69, 0x62, 0xd6, 0x99, 0xf1, 0xbd, 0x67, - 0xbb, 0x88, 0xe2, 0xcd, 0x60, 0x0f, 0x49, 0x88, 0x45, 0x06, 0x97, 0xa0, 0x1c, 0x0c, 0x24, 0xc1, - 0xfe, 0xc0, 0x90, 0xe4, 0x37, 0x73, 0x20, 0xf9, 0x59, 0xb0, 0xd8, 0xcd, 0x5e, 0x10, 0xe2, 0x67, - 0x2d, 0x0a, 0x90, 0xcd, 0x88, 0x77, 0x42, 0xf5, 0x6c, 0x48, 0x05, 0x81, 0x62, 0xdf, 0x0f, 0x3c, - 0x6b, 0x21, 0x45, 0x21, 0xbe, 0xc9, 0x7f, 0x10, 0xbc, 0x3c, 0x04, 0xf8, 0xd8, 0x43, 0x79, 0x2e, - 0xd0, 0xf7, 0xa9, 0xb1, 0x38, 0x92, 0x1a, 0xa5, 0x41, 0x6a, 0xfc, 0x1b, 0xc1, 0xca, 0x90, 0x1d, - 0x8f, 0x4f, 0x29, 0xcf, 0xcd, 0x96, 0x1b, 0x3c, 0x72, 0x99, 0xb5, 0x98, 0xf2, 0x4f, 0x0a, 0x82, - 0xad, 0x3c, 0x0a, 0xf7, 0x68, 0x60, 0x95, 0x52, 0xb6, 0xa6, 0x12, 0xf9, 0x0b, 0x02, 0x4b, 0xef, - 0xf0, 0x96, 0x2b, 0xf7, 0xdb, 0x09, 0x9e, 0xff, 0x4d, 0x9e, 0x86, 0x05, 0x2a, 0xd1, 0xaa, 0x83, - 0x55, 0x12, 0xf9, 0x0e, 0x82, 0x33, 0xf9, 0xed, 0xc4, 0x5b, 0x7e, 0x9c, 0xe8, 0x97, 0x18, 0x6e, - 0xc0, 0x62, 0x3a, 0x33, 0x4d, 0xd0, 0x95, 0xfa, 0xd6, 0x61, 0xc3, 0x76, 0xce, 0x75, 0x5a, 0x39, - 0x79, 0x05, 0xce, 0x0c, 0xbd, 0xed, 0x0a, 0x86, 0x0d, 0x25, 0x9d, 0xaa, 0x94, 0x73, 0x33, 0x99, - 0xfc, 0x71, 0x36, 0x1f, 0xfd, 0xb8, 0xb7, 0xc5, 0x9b, 0x05, 0x6f, 0xe6, 0xe2, 0x03, 0xb1, 0x60, - 0x31, 0xe4, 0x9e, 0xf1, 0x3c, 0xd6, 0xa2, 0x58, 0xe7, 0xf2, 0x20, 0xa1, 0xa2, 0x12, 0x53, 0xef, - 0xe2, 0xfe, 0x80, 0x38, 0xc8, 0xd8, 0x0f, 0x5c, 0xb6, 0xcb, 0x5c, 0x1e, 0x78, 0xb1, 0x3c, 0x91, - 0x59, 0x27, 0x37, 0x86, 0xdf, 0x84, 0xb2, 0x94, 0xef, 0xf9, 0x6d, 0x26, 0xeb, 0x8c, 0x4a, 0x7d, - 0xad, 0x9a, 0x96, 0x79, 0x55, 0xb3, 0xcc, 0xeb, 0xfb, 0x50, 0x94, 0x79, 0xd5, 0xee, 0xf5, 0xaa, - 0x58, 0xe1, 0xf4, 0x17, 0x0b, 0x2c, 0x09, 0xf5, 0x5b, 0x5b, 0x7e, 0x20, 0x9f, 0x0f, 0xc2, 0x54, - 0x7f, 0x40, 0x1c, 0x76, 0x83, 0xb7, 0x5a, 0xfc, 0x91, 0xe6, 0x6e, 0x2a, 0x89, 0x55, 0x9d, 0x20, - 0xf1, 0x5b, 0xd2, 0x7e, 0x39, 0xdd, 0x41, 0x36, 0x20, 0x57, 0xf9, 0xad, 0x84, 0x45, 0x32, 0x41, - 0x97, 0x1d, 0x25, 0x65, 0x74, 0xaa, 0xa4, 0x75, 0x8f, 0xbe, 0x33, 0x29, 0xf1, 0x8e, 0x98, 0xc4, - 0x1b, 0x24, 0xf3, 0xd1, 0x21, 0xf5, 0x85, 0x2c, 0xe4, 0x58, 0xd7, 0xe7, 0x9d, 0xd8, 0x3a, 0x96, - 0xa6, 0x31, 0x2d, 0x93, 0xdf, 0x21, 0x28, 0x6d, 0xf1, 0xe6, 0xed, 0x20, 0x89, 0x7a, 0xf2, 0xbd, - 0xc9, 0x83, 0x84, 0x05, 0xfa, 0xc4, 0xb5, 0x28, 0xdc, 0x98, 0xf8, 0x6d, 0xb6, 0x9b, 0xd0, 0x76, - 0xa8, 0x32, 0xee, 0x33, 0xb9, 0x31, 0x5b, 0x2c, 0xb6, 0xd6, 0xa2, 0x71, 0x22, 0x6f, 0x5d, 0xc9, - 0x91, 0xdf, 0x62, 0x13, 0xd9, 0x84, 0xdd, 0x24, 0x52, 0x57, 0x2e, 0x37, 0x66, 0x92, 0x64, 0x3e, - 0xc5, 0xa6, 0x44, 0x52, 0x83, 0x97, 0xb2, 0x47, 0xd8, 0x3d, 0x16, 0xb5, 0xfd, 0x80, 0x16, 0xc6, - 0x40, 0x72, 0x3d, 0x47, 0x7c, 0xf1, 0x2a, 0x79, 0xe0, 0x07, 0x1e, 0x7f, 0x34, 0x9a, 0xc0, 0xe4, - 0x6f, 0xf9, 0x5a, 0xd1, 0x58, 0x93, 0xdd, 0x97, 0x37, 0xe1, 0xa8, 0xb8, 0x59, 0x5d, 0xa6, 0x7e, - 0x50, 0x97, 0x97, 0xe4, 0x2e, 0xe5, 0x50, 0x1d, 0x4e, 0x7e, 0x21, 0xde, 0x82, 0xe3, 0x34, 0x8e, - 0xfd, 0x66, 0xc0, 0x3c, 0xad, 0x6b, 0x66, 0x62, 0x5d, 0x83, 0x4b, 0xd3, 0x22, 0x42, 0xce, 0x50, - 0x3e, 0xd7, 0x22, 0xf9, 0x36, 0x82, 0x53, 0x43, 0x95, 0x64, 0xfc, 0x43, 0x46, 0x38, 0x13, 0x85, - 0xbc, 0xbb, 0xc7, 0xbc, 0x4e, 0x8b, 0xe9, 0x52, 0x5a, 0xcb, 0xe2, 0x37, 0xaf, 0x93, 0x9e, 0x80, - 0x0a, 0xa7, 0x99, 0x8c, 0x97, 0x01, 0xda, 0x34, 0xe8, 0xd0, 0x96, 0x84, 0x30, 0x27, 0x21, 0x18, - 0x23, 0x64, 0x09, 0xec, 0x61, 0xc7, 0xa7, 0xca, 0xd2, 0x5f, 0x21, 0x38, 0xa6, 0x43, 0x93, 0x3a, - 0x9f, 0x55, 0x38, 0x6e, 0xb8, 0x61, 0xa7, 0x7f, 0x54, 0x83, 0xc3, 0x63, 0xc2, 0x8e, 0x3e, 0xe7, - 0xd9, 0x7c, 0x37, 0xa4, 0x9b, 0xeb, 0x67, 0x4c, 0x1c, 0xf7, 0xb3, 0x8b, 0x4a, 0xbe, 0x05, 0xd6, - 0x36, 0x0d, 0x68, 0x93, 0x79, 0x19, 0xf0, 0x8c, 0x24, 0xdf, 0x30, 0x4b, 0xaf, 0x43, 0x17, 0x3a, - 0x59, 0xda, 0xf7, 0x1b, 0x0d, 0x55, 0xc6, 0xd5, 0xff, 0xb9, 0x0c, 0xd8, 0x3c, 0x54, 0x16, 0x75, - 0x7d, 0x97, 0xe1, 0x1f, 0x20, 0x98, 0x13, 0x59, 0x06, 0x9f, 0x1d, 0xc5, 0x21, 0xe9, 0x5c, 0x7b, - 0x7a, 0xef, 0x68, 0x61, 0x8d, 0x2c, 0xbd, 0xf7, 0xf7, 0x7f, 0xfd, 0x70, 0xe6, 0x34, 0x7e, 0x51, - 0xf6, 0xdd, 0xba, 0xd7, 0xcd, 0x1e, 0x58, 0x8c, 0xbf, 0x8b, 0x00, 0xab, 0xd4, 0x67, 0xb4, 0x5e, - 0xf0, 0x95, 0x51, 0x10, 0x87, 0xb4, 0x68, 0xec, 0xb3, 0x46, 0x18, 0xaa, 0xba, 0x3c, 0x62, 0x22, - 0xe8, 0xc8, 0x09, 0x12, 0xc0, 0x9a, 0x04, 0x70, 0x1e, 0x93, 0x61, 0x00, 0x6a, 0x8f, 0xc5, 0xa1, - 0x3f, 0xa9, 0xb1, 0xd4, 0xee, 0xcf, 0x11, 0xcc, 0x3f, 0x90, 0x8f, 0xaf, 0x31, 0x4e, 0xda, 0x9d, - 0x9a, 0x93, 0xa4, 0x39, 0x89, 0x96, 0x9c, 0x93, 0x48, 0xcf, 0xe2, 0x33, 0x1a, 0x69, 0x9c, 0x44, - 0x8c, 0xb6, 0x73, 0x80, 0xaf, 0x21, 0xfc, 0x21, 0x82, 0x85, 0xb4, 0x39, 0x83, 0x2f, 0x8c, 0x42, - 0x99, 0x6b, 0xde, 0xd8, 0xd3, 0xeb, 0x74, 0x90, 0xcb, 0x12, 0xe3, 0x39, 0x32, 0xf4, 0x38, 0x37, - 0x72, 0x7d, 0x90, 0xf7, 0x11, 0xcc, 0x6e, 0xb2, 0xb1, 0x7c, 0x9b, 0x22, 0xb8, 0x03, 0x0e, 0x1c, - 0x72, 0xd4, 0xf8, 0x03, 0x04, 0x2f, 0x6d, 0xb2, 0x64, 0x78, 0x2c, 0xc7, 0xab, 0xe3, 0x03, 0xac, - 0xa2, 0xdd, 0x95, 0x09, 0x66, 0x66, 0x41, 0xac, 0x26, 0x91, 0x5d, 0xc6, 0x97, 0x8a, 0x48, 0x28, - 0x0a, 0xe2, 0x47, 0x0a, 0xc7, 0x9f, 0x11, 0x9c, 0x18, 0x6c, 0x72, 0xe2, 0x7c, 0xf4, 0x1f, 0xda, - 0x03, 0xb5, 0x77, 0x0e, 0x1b, 0x50, 0xf2, 0x4a, 0xc9, 0x2d, 0x89, 0xfc, 0x55, 0xfc, 0x4a, 0x11, - 0x72, 0xdd, 0xf7, 0x89, 0x6b, 0x8f, 0xf5, 0xe7, 0x13, 0xd9, 0x2d, 0x97, 0xb0, 0xdf, 0x43, 0x70, - 0x64, 0x93, 0x25, 0xdb, 0x59, 0xdb, 0x63, 0x24, 0x6d, 0x73, 0x7d, 0x4d, 0x7b, 0xa9, 0x6a, 0x34, - 0xb5, 0xf5, 0x4f, 0x99, 0x4b, 0xd7, 0x25, 0xb0, 0x4b, 0xf8, 0x42, 0x11, 0xb0, 0x7e, 0xab, 0xe5, - 0xf7, 0x08, 0x16, 0xd2, 0xb6, 0xc2, 0x68, 0xf3, 0xb9, 0xa6, 0xe1, 0x34, 0x89, 0x79, 0x5b, 0x62, - 0xfd, 0xbc, 0x7d, 0x6d, 0x38, 0x56, 0x73, 0xbd, 0xf6, 0x5a, 0x55, 0x6e, 0x20, 0x7f, 0xa3, 0x3e, - 0x42, 0x00, 0xfd, 0xd6, 0x08, 0xbe, 0x5c, 0xbc, 0x0f, 0xa3, 0x7d, 0x62, 0x4f, 0xb7, 0x39, 0x42, - 0xaa, 0x72, 0x3f, 0xab, 0xf6, 0x4a, 0x21, 0x9d, 0x43, 0xe6, 0x6e, 0xa4, 0x6d, 0x94, 0x9f, 0x21, - 0x98, 0x97, 0xb5, 0x38, 0x3e, 0x3f, 0x0a, 0xb3, 0x59, 0xaa, 0x4f, 0xd3, 0xf5, 0x17, 0x25, 0xd4, - 0x95, 0x7a, 0x51, 0x4c, 0xd8, 0x40, 0x6b, 0xb8, 0x0b, 0x0b, 0x69, 0xed, 0x3c, 0x9a, 0x1e, 0xb9, - 0xda, 0xda, 0x5e, 0x29, 0xc8, 0x51, 0x29, 0x43, 0x55, 0x38, 0x5a, 0x1b, 0x17, 0x8e, 0xe6, 0x44, - 0xc4, 0xc0, 0xe7, 0x8a, 0xe2, 0xc9, 0xc7, 0xe0, 0x98, 0x2b, 0x12, 0xdd, 0x05, 0xb2, 0x32, 0x2e, - 0x24, 0x09, 0xef, 0xfc, 0x08, 0xc1, 0x89, 0xc1, 0x27, 0x0d, 0x3e, 0x33, 0x10, 0x8e, 0xcc, 0x37, - 0x9a, 0x9d, 0xf7, 0xe2, 0xa8, 0xe7, 0x10, 0xf9, 0x82, 0x44, 0xb1, 0x81, 0x6f, 0x8e, 0xbd, 0x19, - 0x3b, 0xfa, 0x42, 0x0b, 0x45, 0xeb, 0xfd, 0x0e, 0xeb, 0x6f, 0x10, 0x1c, 0xd1, 0x7a, 0xef, 0x45, - 0x8c, 0x15, 0xc3, 0x9a, 0xde, 0x45, 0x10, 0xb6, 0xc8, 0x67, 0x25, 0xfc, 0xcf, 0xe0, 0x1b, 0x13, - 0xc2, 0xd7, 0xb0, 0xd7, 0x13, 0x81, 0xf4, 0x0f, 0x08, 0x4e, 0x3e, 0x48, 0x79, 0xff, 0x09, 0xe1, - 0x7f, 0x5d, 0xe2, 0xff, 0x1c, 0x7e, 0xb5, 0xe0, 0xc9, 0x31, 0x6e, 0x1b, 0xd7, 0x10, 0xfe, 0x25, - 0x82, 0x92, 0xee, 0x4e, 0xe2, 0x4b, 0x23, 0x2f, 0x46, 0xbe, 0x7f, 0x39, 0x4d, 0x32, 0xab, 0xfc, - 0x4a, 0xce, 0x17, 0x66, 0x29, 0x65, 0x5f, 0x10, 0xfa, 0x7d, 0x04, 0x38, 0xab, 0x35, 0xb2, 0xea, - 0x03, 0x5f, 0xcc, 0x99, 0x1a, 0x59, 0x54, 0xda, 0x97, 0xc6, 0xce, 0xcb, 0x67, 0xa9, 0xb5, 0xc2, - 0x2c, 0xc5, 0x33, 0xfb, 0xdf, 0x43, 0x50, 0xd9, 0x64, 0xd9, 0x73, 0xb8, 0xc0, 0x97, 0xf9, 0xd6, - 0xac, 0xbd, 0x3a, 0x7e, 0xa2, 0x42, 0x74, 0x55, 0x22, 0xba, 0x88, 0x8b, 0x5d, 0xa5, 0x01, 0xfc, - 0x04, 0xc1, 0xd1, 0xbb, 0x26, 0x45, 0xf1, 0xd5, 0x71, 0x96, 0x72, 0x91, 0x7c, 0x72, 0x5c, 0x9f, - 0x96, 0xb8, 0xd6, 0xc9, 0x44, 0xb8, 0x36, 0x54, 0x8f, 0xf4, 0xa7, 0x08, 0x5e, 0x30, 0xeb, 0x07, - 0xd5, 0x4d, 0xfb, 0x7f, 0xfd, 0x56, 0xd0, 0x94, 0x23, 0x37, 0x24, 0xbe, 0x2a, 0xbe, 0x3a, 0x09, - 0xbe, 0x9a, 0x6a, 0xb1, 0xe1, 0x1f, 0x23, 0x38, 0x29, 0x7b, 0x95, 0xa6, 0xe2, 0x81, 0x14, 0x33, - 0xaa, 0xb3, 0x39, 0x41, 0x8a, 0x51, 0xf1, 0x87, 0x3c, 0x13, 0xa8, 0x0d, 0xd5, 0x87, 0xc4, 0xdf, - 0x47, 0x70, 0x4c, 0x27, 0x35, 0x75, 0xba, 0xeb, 0xe3, 0x1c, 0xf7, 0xac, 0x49, 0x50, 0xd1, 0x6d, - 0x6d, 0x32, 0xba, 0x7d, 0x88, 0x60, 0x51, 0xf5, 0x12, 0x0b, 0x9e, 0x0a, 0x46, 0xb3, 0xd1, 0x3e, - 0x95, 0x9b, 0xa5, 0x1b, 0x59, 0xe4, 0x6b, 0xd2, 0xec, 0x7d, 0x5c, 0x2b, 0x32, 0x1b, 0x72, 0x2f, - 0xae, 0x3d, 0x56, 0x5d, 0xa4, 0x27, 0xb5, 0x16, 0x6f, 0xc6, 0xef, 0x10, 0x5c, 0x98, 0x10, 0xc5, - 0x9c, 0x6b, 0xe8, 0xb5, 0x2f, 0xfe, 0xe9, 0xe9, 0x32, 0xfa, 0xeb, 0xd3, 0x65, 0xf4, 0x8f, 0xa7, - 0xcb, 0xe8, 0x9d, 0x9b, 0x93, 0xfd, 0xff, 0xc3, 0x6d, 0xf9, 0x2c, 0x48, 0x4c, 0xb5, 0xff, 0x0b, - 0x00, 0x00, 0xff, 0xff, 0xa3, 0x87, 0x84, 0x43, 0xe5, 0x22, 0x00, 0x00, + // 2305 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x8f, 0x1c, 0x47, + 0x15, 0x57, 0xcd, 0x7e, 0xcd, 0xbc, 0xf1, 0x47, 0x5c, 0x89, 0x4d, 0xa7, 0xbd, 0x5e, 0x56, 0xed, + 0xaf, 0xf5, 0xda, 0x3b, 0x63, 0x0f, 0x16, 0x72, 0x36, 0x20, 0x70, 0x1c, 0xe3, 0x18, 0x76, 0x37, + 0xa6, 0xd7, 0xc6, 0x28, 0x1c, 0xa0, 0xd2, 0x5d, 0x33, 0xdb, 0xec, 0x4c, 0x57, 0xbb, 0xbb, 0x67, + 0xac, 0x91, 0xf1, 0x25, 0x88, 0x1b, 0x0a, 0x52, 0x92, 0x03, 0x8a, 0x22, 0x84, 0x88, 0x72, 0xe6, + 0x86, 0x90, 0xb8, 0xc0, 0x05, 0x81, 0xc4, 0x01, 0xf1, 0x71, 0xc9, 0x09, 0x2c, 0x6e, 0x5c, 0xf8, + 0x13, 0x50, 0x55, 0x57, 0xf5, 0x54, 0xcf, 0xf4, 0xf4, 0xcc, 0xb2, 0x83, 0xe2, 0x5b, 0xbf, 0x9a, + 0xaa, 0xf7, 0x7e, 0xf5, 0xea, 0x7d, 0x54, 0xfd, 0x76, 0xe1, 0x5c, 0x44, 0xc3, 0x1e, 0x0d, 0xeb, + 0x24, 0x08, 0xda, 0x9e, 0x43, 0x62, 0x8f, 0xf9, 0xfa, 0x77, 0x2d, 0x08, 0x59, 0xcc, 0x70, 0x55, + 0x1b, 0x32, 0x97, 0x5b, 0x8c, 0xb5, 0xda, 0xb4, 0x4e, 0x02, 0xaf, 0x4e, 0x7c, 0x9f, 0xc5, 0x62, + 0x38, 0x4a, 0xa6, 0x9a, 0xd6, 0xfe, 0x8d, 0xa8, 0xe6, 0x31, 0xf1, 0xab, 0xc3, 0x42, 0x5a, 0xef, + 0x5d, 0xab, 0xb7, 0xa8, 0x4f, 0x43, 0x12, 0x53, 0x57, 0xce, 0xb9, 0x3e, 0x98, 0xd3, 0x21, 0xce, + 0x9e, 0xe7, 0xd3, 0xb0, 0x5f, 0x0f, 0xf6, 0x5b, 0x7c, 0x20, 0xaa, 0x77, 0x68, 0x4c, 0xf2, 0x56, + 0x6d, 0xb5, 0xbc, 0x78, 0xaf, 0xfb, 0x76, 0xcd, 0x61, 0x9d, 0x3a, 0x09, 0x5b, 0x2c, 0x08, 0xd9, + 0xf7, 0xc5, 0xc7, 0x86, 0xe3, 0xd6, 0x7b, 0x8d, 0x81, 0x02, 0x7d, 0x2f, 0xbd, 0x6b, 0xa4, 0x1d, + 0xec, 0x91, 0x51, 0x6d, 0xb7, 0x27, 0x68, 0x0b, 0x69, 0xc0, 0xa4, 0x6f, 0xc4, 0xa7, 0x17, 0xb3, + 0xb0, 0xaf, 0x7d, 0x26, 0x6a, 0xac, 0x4f, 0x11, 0xbc, 0x70, 0x73, 0x60, 0xef, 0x9b, 0x5d, 0x1a, + 0xf6, 0x31, 0x86, 0x79, 0x9f, 0x74, 0xa8, 0x81, 0x56, 0xd1, 0x5a, 0xc5, 0x16, 0xdf, 0xd8, 0x80, + 0xa5, 0x90, 0x36, 0x43, 0x1a, 0xed, 0x19, 0x25, 0x31, 0xac, 0x44, 0x6c, 0x42, 0x99, 0x1b, 0xa7, + 0x4e, 0x1c, 0x19, 0x73, 0xab, 0x73, 0x6b, 0x15, 0x3b, 0x95, 0xf1, 0x1a, 0x1c, 0x0f, 0x69, 0xc4, + 0xba, 0xa1, 0x43, 0xbf, 0x45, 0xc3, 0xc8, 0x63, 0xbe, 0x31, 0x2f, 0x56, 0x0f, 0x0f, 0x73, 0x2d, + 0x11, 0x6d, 0x53, 0x27, 0x66, 0xa1, 0xb1, 0x20, 0xa6, 0xa4, 0x32, 0xc7, 0xc3, 0x81, 0x1b, 0x8b, + 0x09, 0x1e, 0xfe, 0x8d, 0x2d, 0x38, 0x42, 0x82, 0x60, 0x87, 0x74, 0x68, 0x14, 0x10, 0x87, 0x1a, + 0x4b, 0xe2, 0xb7, 0xcc, 0x98, 0x75, 0x0b, 0x2a, 0x3b, 0xcc, 0xa5, 0xe3, 0x37, 0x35, 0xac, 0xa4, + 0x94, 0xa3, 0x64, 0x1f, 0x4e, 0xda, 0xb4, 0xe7, 0x71, 0x90, 0xdb, 0x34, 0x26, 0x2e, 0x89, 0xc9, + 0xb0, 0xc2, 0x52, 0xaa, 0xd0, 0x84, 0x72, 0x28, 0x27, 0x1b, 0x25, 0x31, 0x9e, 0xca, 0x23, 0xc6, + 0xe6, 0x72, 0x8c, 0xfd, 0x09, 0xc1, 0x8a, 0x76, 0x1c, 0xb6, 0x74, 0xd2, 0xed, 0x1e, 0xf5, 0xe3, + 0x68, 0xbc, 0xd9, 0x2b, 0x70, 0x42, 0xf9, 0x73, 0x78, 0x33, 0xa3, 0x3f, 0x70, 0x20, 0xfa, 0xa0, + 0x02, 0xa2, 0x8f, 0xe1, 0x55, 0xa8, 0x2a, 0xf9, 0xc1, 0xdd, 0xd7, 0xe5, 0xa1, 0xe9, 0x43, 0x23, + 0xdb, 0x59, 0xc8, 0xd9, 0x8e, 0x0f, 0x86, 0xb6, 0x9b, 0x6d, 0xe2, 0x7b, 0x4d, 0x1a, 0xc5, 0xd3, + 0xba, 0x0f, 0x1d, 0xd8, 0x7d, 0x27, 0xe1, 0xc5, 0xac, 0xf7, 0x02, 0xe6, 0x47, 0xd4, 0xfa, 0x0d, + 0xca, 0xe0, 0xb8, 0x15, 0x52, 0x12, 0x53, 0x9b, 0x3e, 0xea, 0xd2, 0x28, 0xc6, 0xfb, 0xa0, 0x57, + 0x07, 0x01, 0xa7, 0xda, 0xb8, 0x5b, 0x1b, 0xa4, 0x57, 0x4d, 0xa5, 0x97, 0xf8, 0xf8, 0xae, 0xe3, + 0xd6, 0x7a, 0x8d, 0x5a, 0xb0, 0xdf, 0xaa, 0xf1, 0x64, 0xad, 0xe9, 0xc5, 0x46, 0x25, 0x6b, 0x4d, + 0x07, 0xa1, 0x6b, 0xc7, 0xa7, 0x60, 0xb1, 0x1b, 0x44, 0x34, 0x8c, 0xc5, 0xf6, 0xca, 0xb6, 0x94, + 0xf8, 0xc6, 0x7b, 0xa4, 0xed, 0xb9, 0x24, 0x4e, 0x36, 0x56, 0xb6, 0x53, 0xd9, 0xfa, 0x38, 0x8b, + 0xfe, 0x41, 0xe0, 0x7e, 0x56, 0xe8, 0x75, 0x94, 0xa5, 0x21, 0x94, 0x1f, 0x66, 0x51, 0xbe, 0x4e, + 0xdb, 0x74, 0x80, 0x32, 0xef, 0xac, 0x0d, 0x58, 0x72, 0x48, 0xe4, 0x10, 0x57, 0xe9, 0x52, 0x22, + 0x8f, 0xe6, 0x20, 0x64, 0x01, 0x69, 0x09, 0x4d, 0xf7, 0x58, 0xdb, 0x73, 0xfa, 0xf2, 0xb8, 0x47, + 0x7f, 0x18, 0x89, 0x8b, 0xf9, 0x9c, 0xb8, 0x38, 0x0b, 0xd5, 0xdd, 0xbe, 0xef, 0xbc, 0x19, 0x88, + 0x4a, 0x8f, 0x5f, 0x82, 0x05, 0x2f, 0xa6, 0x9d, 0xc8, 0x40, 0xa2, 0x5c, 0x25, 0x82, 0xf5, 0xd1, + 0x02, 0x9c, 0xd2, 0x76, 0xc0, 0x17, 0x14, 0xe1, 0x2f, 0x8a, 0xd5, 0x53, 0xb0, 0xe8, 0x86, 0x7d, + 0xbb, 0xeb, 0xcb, 0xc3, 0x94, 0x12, 0x37, 0x1c, 0x84, 0x5d, 0x3f, 0x01, 0x59, 0xb6, 0x13, 0x01, + 0x37, 0xa1, 0x1c, 0xc5, 0xbc, 0xb6, 0xb7, 0xfa, 0x22, 0x8b, 0xaa, 0x8d, 0xaf, 0x1f, 0xee, 0x00, + 0x39, 0xf4, 0x5d, 0xa9, 0xd1, 0x4e, 0x75, 0xe3, 0x47, 0x50, 0x51, 0x09, 0x1c, 0x19, 0x4b, 0xab, + 0x73, 0x6b, 0xd5, 0xc6, 0xee, 0xe1, 0x0d, 0xbd, 0x19, 0xf0, 0xbe, 0xa4, 0x15, 0x2b, 0x7b, 0x60, + 0x05, 0x2f, 0x43, 0xa5, 0x23, 0xb3, 0x3e, 0x32, 0xca, 0xc2, 0xdb, 0x83, 0x01, 0xfc, 0x6d, 0x58, + 0xf0, 0xfc, 0x26, 0x8b, 0x8c, 0x8a, 0x00, 0xf3, 0xda, 0xe1, 0xc0, 0xdc, 0xf5, 0x9b, 0xcc, 0x4e, + 0x14, 0xe2, 0x47, 0x70, 0x34, 0xa4, 0x71, 0xd8, 0x57, 0x5e, 0x30, 0x40, 0xf8, 0xf5, 0x1b, 0x87, + 0xb3, 0x60, 0xeb, 0x2a, 0xed, 0xac, 0x05, 0xbc, 0x09, 0xd5, 0x68, 0x10, 0x63, 0x46, 0x55, 0x18, + 0x34, 0x32, 0x8a, 0xb4, 0x18, 0xb4, 0xf5, 0xc9, 0x23, 0x31, 0x7c, 0x24, 0x27, 0x86, 0xff, 0x8e, + 0x60, 0x79, 0xa4, 0x0c, 0xec, 0x06, 0xb4, 0x30, 0x48, 0x09, 0xcc, 0x47, 0x01, 0x75, 0x44, 0x2f, + 0xaa, 0x36, 0xb6, 0x67, 0x56, 0x17, 0x84, 0x5d, 0xa1, 0xba, 0xa8, 0x74, 0x4d, 0x95, 0x9b, 0x3f, + 0x42, 0xf0, 0x39, 0x4d, 0xf3, 0x3d, 0x12, 0x3b, 0x7b, 0x45, 0x5b, 0xe2, 0x39, 0xc4, 0xe7, 0xc8, + 0xfe, 0x9a, 0x08, 0x3c, 0xd0, 0xc4, 0xc7, 0xfd, 0x7e, 0xc0, 0x61, 0xf0, 0x5f, 0x06, 0x03, 0x53, + 0xf5, 0xaa, 0xf7, 0x10, 0x98, 0x7a, 0xe5, 0x63, 0xed, 0xf6, 0xdb, 0xc4, 0xd9, 0x2f, 0x82, 0x72, + 0x0c, 0x4a, 0x9e, 0x2b, 0x70, 0xcc, 0xd9, 0x25, 0xcf, 0x3d, 0x60, 0xda, 0x0f, 0x83, 0x5a, 0xcc, + 0x01, 0xf5, 0xe9, 0x10, 0x28, 0x95, 0x62, 0x05, 0xa0, 0x96, 0xa1, 0xe2, 0x0f, 0xdd, 0x01, 0x06, + 0x03, 0x39, 0xbd, 0xbf, 0x34, 0xd2, 0xfb, 0x0d, 0x58, 0xea, 0xa5, 0x97, 0x35, 0xfe, 0xb3, 0x12, + 0xf9, 0x46, 0x5a, 0x21, 0xeb, 0x06, 0xd2, 0x81, 0x89, 0xc0, 0x51, 0xec, 0x7b, 0xbe, 0x6b, 0x2c, + 0x26, 0x28, 0xf8, 0xf7, 0x54, 0xd7, 0xb3, 0xf7, 0x4b, 0xf0, 0xf9, 0x9c, 0xcd, 0x4d, 0x8c, 0x80, + 0xe7, 0x63, 0x87, 0x69, 0x1c, 0x2e, 0x8d, 0x8d, 0xc3, 0xf2, 0xa4, 0x38, 0xac, 0xe4, 0x78, 0xe5, + 0xdd, 0x12, 0xac, 0xe6, 0x78, 0x65, 0x72, 0x43, 0x7d, 0x6e, 0xdc, 0xd2, 0x64, 0xa1, 0x3c, 0xf1, + 0xb2, 0x9d, 0x08, 0x3c, 0x33, 0x58, 0x18, 0xec, 0x11, 0xdf, 0x28, 0x27, 0x99, 0x91, 0x48, 0x53, + 0x39, 0xe4, 0x3f, 0x08, 0x0c, 0xe5, 0x85, 0x9b, 0x8e, 0xf0, 0x49, 0xd7, 0x7f, 0xfe, 0x1d, 0x71, + 0x0a, 0x16, 0x89, 0x40, 0x2b, 0x03, 0x44, 0x4a, 0x23, 0x5b, 0x2e, 0xe7, 0xd7, 0xc4, 0xd3, 0xd9, + 0x2d, 0x47, 0x5b, 0x5e, 0x14, 0xab, 0x0b, 0x2d, 0x6e, 0xc2, 0x52, 0xa2, 0x2d, 0xb9, 0xc2, 0x54, + 0x1b, 0x5b, 0x87, 0x6d, 0x6c, 0x19, 0xf7, 0x2a, 0xe5, 0xd6, 0x2b, 0x70, 0x3a, 0xb7, 0xfa, 0x48, + 0x18, 0x26, 0x94, 0x55, 0x33, 0x97, 0x07, 0x90, 0xca, 0xd6, 0xbf, 0xe7, 0xb2, 0x65, 0x9d, 0xb9, + 0x5b, 0xac, 0x55, 0xf0, 0x84, 0x29, 0x3e, 0x34, 0x03, 0x96, 0x02, 0xe6, 0x6a, 0xaf, 0x15, 0x25, + 0xf2, 0x75, 0x0e, 0xf3, 0x63, 0xc2, 0x1f, 0xe1, 0xb2, 0xbf, 0x0c, 0x06, 0xb8, 0xb3, 0x23, 0xcf, + 0x77, 0xe8, 0x2e, 0x75, 0x98, 0xef, 0x46, 0xe2, 0xd4, 0xe6, 0xec, 0xcc, 0x18, 0x7e, 0x03, 0x2a, + 0x42, 0xbe, 0xef, 0x75, 0x92, 0x22, 0x5c, 0x6d, 0xac, 0xd7, 0x92, 0x17, 0x7e, 0x4d, 0x7f, 0xe1, + 0x0f, 0x7c, 0xc8, 0x5f, 0xf8, 0xb5, 0xde, 0xb5, 0x1a, 0x5f, 0x61, 0x0f, 0x16, 0x73, 0x2c, 0x31, + 0xf1, 0xda, 0x5b, 0x9e, 0x2f, 0x2e, 0x58, 0xdc, 0xd4, 0x60, 0x80, 0x07, 0x44, 0x93, 0xb5, 0xdb, + 0xec, 0xb1, 0xca, 0x81, 0x44, 0xe2, 0xab, 0xba, 0x7e, 0xec, 0xb5, 0x85, 0xfd, 0x24, 0x01, 0x06, + 0x03, 0x62, 0x95, 0xd7, 0x8e, 0x69, 0x28, 0xae, 0x30, 0x15, 0x5b, 0x4a, 0x69, 0xc8, 0x55, 0x93, + 0xe7, 0xac, 0xca, 0xbd, 0x24, 0x38, 0x8f, 0xe8, 0xc1, 0x39, 0x1c, 0xf0, 0x47, 0x73, 0x9e, 0x7b, + 0xe2, 0x0d, 0x4f, 0x7b, 0x1e, 0xeb, 0x46, 0xc6, 0xb1, 0xa4, 0x89, 0x2b, 0x79, 0x24, 0x60, 0x8f, + 0xe7, 0x04, 0xec, 0x6f, 0x11, 0x94, 0xb7, 0x58, 0xeb, 0xb6, 0x1f, 0x87, 0x7d, 0x71, 0xb3, 0x67, + 0x7e, 0x4c, 0x7d, 0x15, 0x15, 0x4a, 0xe4, 0xae, 0x8e, 0xbd, 0x0e, 0xdd, 0x8d, 0x49, 0x27, 0x90, + 0x77, 0x92, 0x03, 0xb9, 0x3a, 0x5d, 0xcc, 0xb7, 0xdf, 0x26, 0x51, 0x2c, 0xb2, 0xb7, 0x6c, 0x8b, + 0x6f, 0x0e, 0x34, 0x9d, 0xb0, 0x1b, 0x87, 0x32, 0x75, 0x33, 0x63, 0x7a, 0x20, 0x2d, 0x24, 0xd8, + 0xa4, 0x68, 0xed, 0xc2, 0xcb, 0xe9, 0x55, 0xf6, 0x3e, 0x0d, 0x3b, 0x9e, 0x4f, 0x8a, 0xeb, 0xed, + 0x34, 0xe4, 0xc1, 0x83, 0x4c, 0x02, 0xf1, 0xfb, 0xdf, 0x43, 0xcf, 0x77, 0xd9, 0xe3, 0x82, 0x44, + 0x98, 0x46, 0xed, 0x5f, 0xb2, 0x34, 0x81, 0xa6, 0x37, 0xcd, 0xcd, 0x37, 0xe0, 0x28, 0xcf, 0xe2, + 0x1e, 0x95, 0x3f, 0xc8, 0x42, 0x61, 0x65, 0x0a, 0x40, 0xae, 0x0e, 0x3b, 0xbb, 0x10, 0x6f, 0xc1, + 0x71, 0x12, 0x45, 0x5e, 0xcb, 0xa7, 0xae, 0xd2, 0x55, 0x9a, 0x5a, 0xd7, 0xf0, 0xd2, 0xe4, 0xd9, + 0x27, 0x66, 0xc8, 0xb3, 0x53, 0xa2, 0xf5, 0x43, 0x04, 0x27, 0x73, 0x95, 0xa4, 0xb1, 0x8e, 0xb4, + 0xf2, 0x6a, 0x42, 0x39, 0x72, 0xf6, 0xa8, 0xdb, 0x6d, 0x53, 0xc5, 0xb4, 0x28, 0x99, 0xff, 0xe6, + 0x76, 0x93, 0x93, 0x94, 0xe5, 0x3d, 0x95, 0xf1, 0x0a, 0x40, 0x87, 0xf8, 0x5d, 0xd2, 0x16, 0x10, + 0xe6, 0x05, 0x04, 0x6d, 0xc4, 0x5a, 0x06, 0x33, 0x2f, 0x0c, 0x24, 0x93, 0xf0, 0x37, 0x04, 0xc7, + 0x54, 0x19, 0x94, 0x67, 0xb8, 0x06, 0xc7, 0x35, 0x37, 0xec, 0x0c, 0x8e, 0x73, 0x78, 0x78, 0x42, + 0x89, 0x53, 0xb1, 0x30, 0x97, 0x25, 0xdd, 0x7a, 0x19, 0xda, 0x6c, 0xea, 0x3e, 0x84, 0x0e, 0x74, + 0x13, 0xfb, 0x01, 0x18, 0xdb, 0xc4, 0x27, 0x2d, 0xea, 0xa6, 0x9b, 0x4b, 0x03, 0xe9, 0x7b, 0xfa, + 0x63, 0xf9, 0xd0, 0x4f, 0xd3, 0xf4, 0x3a, 0xe3, 0x35, 0x9b, 0xf2, 0xe1, 0xdd, 0xf8, 0xe7, 0x0a, + 0x60, 0xfd, 0xe0, 0x69, 0xd8, 0xf3, 0x1c, 0x8a, 0xdf, 0x43, 0x30, 0xcf, 0xbb, 0x1e, 0x3e, 0x33, + 0x2e, 0xce, 0xc4, 0x01, 0x98, 0xb3, 0x7b, 0xd5, 0x70, 0x6b, 0xd6, 0xf2, 0x3b, 0x7f, 0xfd, 0xd7, + 0xfb, 0xa5, 0x53, 0xf8, 0x25, 0x41, 0x01, 0xf7, 0xae, 0xe9, 0x74, 0x6c, 0x84, 0x7f, 0x8c, 0x00, + 0xcb, 0x56, 0xac, 0x31, 0x73, 0xf8, 0xf2, 0x38, 0x88, 0x39, 0x0c, 0x9e, 0x79, 0x46, 0x2b, 0x79, + 0x35, 0x87, 0x85, 0x94, 0x17, 0x38, 0x31, 0x41, 0x00, 0x58, 0x17, 0x00, 0xce, 0x61, 0x2b, 0x0f, + 0x40, 0xfd, 0x09, 0x0f, 0x8c, 0xa7, 0x75, 0x9a, 0xd8, 0xfd, 0x05, 0x82, 0x85, 0x87, 0xe2, 0xe2, + 0x39, 0xc1, 0x49, 0xbb, 0x33, 0x73, 0x92, 0x30, 0x27, 0xd0, 0x5a, 0x67, 0x05, 0xd2, 0x33, 0xf8, + 0xb4, 0x42, 0x1a, 0xc5, 0x21, 0x25, 0x9d, 0x0c, 0xe0, 0xab, 0x08, 0x7f, 0x82, 0x60, 0x31, 0xe1, + 0xdc, 0xf0, 0xf9, 0x71, 0x28, 0x33, 0x9c, 0x9c, 0x39, 0x3b, 0x02, 0xcb, 0xba, 0x24, 0x30, 0x9e, + 0xb5, 0x72, 0x8f, 0x73, 0x33, 0x43, 0x6f, 0x7d, 0x80, 0x60, 0xee, 0x0e, 0x9d, 0x18, 0x6f, 0x33, + 0x04, 0x37, 0xe2, 0xc0, 0x9c, 0xa3, 0xc6, 0x1f, 0x23, 0x78, 0xf9, 0x0e, 0x8d, 0xf3, 0xeb, 0x3d, + 0x5e, 0x9b, 0x5c, 0x84, 0x65, 0xd8, 0x5d, 0x9e, 0x62, 0x66, 0x5a, 0xe8, 0xea, 0x02, 0xd9, 0x25, + 0x7c, 0xb1, 0x28, 0x08, 0xa3, 0xbe, 0xef, 0x3c, 0x96, 0x38, 0xfe, 0x88, 0xe0, 0x85, 0x61, 0x9e, + 0x1c, 0x67, 0x3b, 0x44, 0x2e, 0x8d, 0x6e, 0xee, 0x1c, 0xb6, 0xa0, 0x64, 0x95, 0x5a, 0x37, 0x05, + 0xf2, 0x57, 0xf1, 0x2b, 0x45, 0xc8, 0x15, 0x53, 0x17, 0xd5, 0x9f, 0xa8, 0xcf, 0xa7, 0xe2, 0x0f, + 0x37, 0x02, 0xf6, 0x3b, 0x08, 0x8e, 0xdc, 0xa1, 0xf1, 0x76, 0x4a, 0x54, 0x8d, 0x0d, 0xdb, 0x0c, + 0xa5, 0x6d, 0x2e, 0xd7, 0xb4, 0xbf, 0xaf, 0xa8, 0x9f, 0x52, 0x97, 0x6e, 0x08, 0x60, 0x17, 0xf1, + 0xf9, 0x22, 0x60, 0x03, 0x72, 0xec, 0x77, 0x08, 0x16, 0x13, 0x92, 0x67, 0xbc, 0xf9, 0x0c, 0x17, + 0x3c, 0xcb, 0xc0, 0xbc, 0x2d, 0xb0, 0x7e, 0xc5, 0xbc, 0x9a, 0x8f, 0x55, 0x5f, 0xaf, 0xbc, 0x56, + 0x13, 0x1b, 0xc8, 0x66, 0xd4, 0xaf, 0x10, 0xc0, 0x80, 0xa8, 0xc2, 0x97, 0x8a, 0xf7, 0xa1, 0x91, + 0x59, 0xe6, 0x6c, 0xa9, 0x2a, 0xab, 0x26, 0xf6, 0xb3, 0x66, 0xae, 0x16, 0x86, 0x73, 0x40, 0x9d, + 0xcd, 0x84, 0xd4, 0xfa, 0x39, 0x82, 0x05, 0xc1, 0x43, 0xe0, 0x73, 0xe3, 0x30, 0xeb, 0x34, 0xc5, + 0x2c, 0x5d, 0x7f, 0x41, 0x40, 0x5d, 0x6d, 0x14, 0xd5, 0x84, 0x4d, 0xb4, 0x8e, 0x7b, 0xb0, 0x98, + 0x70, 0x02, 0xe3, 0xc3, 0x23, 0xc3, 0x19, 0x98, 0xab, 0x05, 0x3d, 0x2a, 0x89, 0x50, 0x59, 0x8e, + 0xd6, 0x27, 0x95, 0xa3, 0x79, 0x5e, 0x31, 0xf0, 0xd9, 0xa2, 0x7a, 0xf2, 0x7f, 0x70, 0xcc, 0x65, + 0x81, 0xee, 0xbc, 0xb5, 0x3a, 0xa9, 0x24, 0x71, 0xef, 0xfc, 0x14, 0xc1, 0x0b, 0xc3, 0x57, 0x1a, + 0x7c, 0x7a, 0xa8, 0x1c, 0xe9, 0xf7, 0x38, 0x33, 0xeb, 0xc5, 0x71, 0xd7, 0x21, 0xeb, 0xab, 0x02, + 0xc5, 0x26, 0xbe, 0x31, 0x31, 0x33, 0x76, 0x54, 0x42, 0x73, 0x45, 0x1b, 0x03, 0x4e, 0xfc, 0xd7, + 0x08, 0x8e, 0x28, 0xbd, 0xf7, 0x43, 0x4a, 0x8b, 0x61, 0xcd, 0x2e, 0x11, 0xb8, 0x2d, 0xeb, 0x4b, + 0x02, 0xfe, 0x17, 0xf1, 0xf5, 0x29, 0xe1, 0x2b, 0xd8, 0x1b, 0x31, 0x47, 0xfa, 0x7b, 0x04, 0x27, + 0x1e, 0x26, 0x71, 0xff, 0x19, 0xe1, 0xbf, 0x25, 0xf0, 0x7f, 0x19, 0xbf, 0x5a, 0x70, 0xe5, 0x98, + 0xb4, 0x8d, 0xab, 0x08, 0xff, 0x12, 0x41, 0x59, 0x31, 0xbc, 0xf8, 0xe2, 0xd8, 0xc4, 0xc8, 0x72, + 0xc0, 0xb3, 0x0c, 0x66, 0xd9, 0x5f, 0xad, 0x73, 0x85, 0x5d, 0x4a, 0xda, 0xe7, 0x01, 0xfd, 0x01, + 0x02, 0x9c, 0xbe, 0x47, 0xd2, 0x17, 0x0a, 0xbe, 0x90, 0x31, 0x35, 0xf6, 0x01, 0x6b, 0x5e, 0x9c, + 0x38, 0x2f, 0xdb, 0xa5, 0xd6, 0x0b, 0xbb, 0x14, 0x4b, 0xed, 0xbf, 0x8b, 0xa0, 0x7a, 0x87, 0xa6, + 0xd7, 0xe1, 0x02, 0x5f, 0x66, 0xa9, 0x6b, 0x73, 0x6d, 0xf2, 0x44, 0x89, 0xe8, 0x8a, 0x40, 0x74, + 0x01, 0x17, 0xbb, 0x4a, 0x01, 0xf8, 0x08, 0xc1, 0xd1, 0x7b, 0x7a, 0x88, 0xe2, 0x2b, 0x93, 0x2c, + 0x65, 0x2a, 0xf9, 0xf4, 0xb8, 0xbe, 0x20, 0x70, 0x6d, 0x58, 0x53, 0xe1, 0xda, 0x94, 0xfc, 0xf0, + 0xcf, 0x10, 0xbc, 0xa8, 0xbf, 0x1f, 0x24, 0xbb, 0xf7, 0xbf, 0xfa, 0xad, 0x80, 0x24, 0xb4, 0xae, + 0x0b, 0x7c, 0x35, 0x7c, 0x65, 0x1a, 0x7c, 0x75, 0x49, 0xf9, 0xe1, 0x0f, 0x11, 0x9c, 0x10, 0xfc, + 0xaa, 0xae, 0x78, 0xa8, 0xc5, 0x8c, 0x63, 0x63, 0xa7, 0x68, 0x31, 0xb2, 0xfe, 0x58, 0x07, 0x02, + 0xb5, 0xa9, 0xb8, 0xd3, 0x9f, 0x20, 0x38, 0xa6, 0x9a, 0x9a, 0x3c, 0xdd, 0x8d, 0x49, 0x8e, 0x3b, + 0x68, 0x13, 0x94, 0xe1, 0xb6, 0x3e, 0x5d, 0xb8, 0x7d, 0x82, 0x60, 0x49, 0x72, 0x9b, 0x05, 0x57, + 0x05, 0x8d, 0xfc, 0x34, 0x4f, 0x66, 0x66, 0x29, 0xd2, 0xcc, 0xfa, 0x8e, 0x30, 0xfb, 0x00, 0xd7, + 0x8b, 0xcc, 0x06, 0xcc, 0x8d, 0xea, 0x4f, 0x24, 0x63, 0xf5, 0xb4, 0xde, 0x66, 0xad, 0xe8, 0x2d, + 0x0b, 0x17, 0x36, 0x44, 0x3e, 0xe7, 0x2a, 0x7a, 0xed, 0x6b, 0x7f, 0x78, 0xb6, 0x82, 0xfe, 0xfc, + 0x6c, 0x05, 0xfd, 0xe3, 0xd9, 0x0a, 0x7a, 0xeb, 0xc6, 0x74, 0xff, 0x8a, 0xe4, 0xb4, 0x3d, 0xea, + 0xc7, 0xba, 0xda, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x66, 0xdc, 0xb1, 0xfa, 0x70, 0x25, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -3305,6 +3456,13 @@ func (m *ApplicationQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x3a + } if m.Repo != nil { i -= len(*m.Repo) copy(dAtA[i:], *m.Repo) @@ -3376,6 +3534,13 @@ func (m *NodeQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x12 + } if m.Name != nil { i -= len(*m.Name) copy(dAtA[i:], *m.Name) @@ -3410,6 +3575,13 @@ func (m *RevisionMetadataQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x1a + } if m.Revision == nil { return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("revision") } else { @@ -3455,6 +3627,13 @@ func (m *ApplicationResourceEventsQuery) MarshalToSizedBuffer(dAtA []byte) (int, i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x2a + } if m.ResourceUID != nil { i -= len(*m.ResourceUID) copy(dAtA[i:], *m.ResourceUID) @@ -3512,6 +3691,13 @@ func (m *ApplicationManifestQuery) MarshalToSizedBuffer(dAtA []byte) (int, error i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x1a + } if m.Revision != nil { i -= len(*m.Revision) copy(dAtA[i:], *m.Revision) @@ -3694,6 +3880,13 @@ func (m *ApplicationDeleteRequest) MarshalToSizedBuffer(dAtA []byte) (int, error i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x22 + } if m.PropagationPolicy != nil { i -= len(*m.PropagationPolicy) copy(dAtA[i:], *m.PropagationPolicy) @@ -3783,6 +3976,13 @@ func (m *ApplicationSyncRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x62 + } if m.SyncOptions != nil { { size, err := m.SyncOptions.MarshalToSizedBuffer(dAtA[:i]) @@ -3919,6 +4119,13 @@ func (m *ApplicationUpdateSpecRequest) MarshalToSizedBuffer(dAtA []byte) (int, e i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x22 + } if m.Validate != nil { i-- if *m.Validate { @@ -3979,6 +4186,13 @@ func (m *ApplicationPatchRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x2a + } if m.PatchType == nil { return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("patchType") } else { @@ -4033,6 +4247,13 @@ func (m *ApplicationRollbackRequest) MarshalToSizedBuffer(dAtA []byte) (int, err i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x32 + } if m.Prune != nil { i-- if *m.Prune { @@ -4096,6 +4317,13 @@ func (m *ApplicationResourceRequest) MarshalToSizedBuffer(dAtA []byte) (int, err i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x3a + } if m.Kind == nil { return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("kind") } else { @@ -4173,6 +4401,13 @@ func (m *ApplicationResourcePatchRequest) MarshalToSizedBuffer(dAtA []byte) (int i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x4a + } if m.PatchType == nil { return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("patchType") } else { @@ -4268,6 +4503,13 @@ func (m *ApplicationResourceDeleteRequest) MarshalToSizedBuffer(dAtA []byte) (in i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x4a + } if m.Orphan != nil { i-- if *m.Orphan { @@ -4365,6 +4607,13 @@ func (m *ResourceActionRunRequest) MarshalToSizedBuffer(dAtA []byte) (int, error i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x42 + } if m.Action == nil { return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("action") } else { @@ -4528,6 +4777,13 @@ func (m *ApplicationPodLogsQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x7a + } if m.Previous != nil { i-- if *m.Previous { @@ -4742,6 +4998,13 @@ func (m *OperationTerminateRequest) MarshalToSizedBuffer(dAtA []byte) (int, erro i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x12 + } if m.Name == nil { return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("name") } else { @@ -4778,6 +5041,13 @@ func (m *ApplicationSyncWindowsQuery) MarshalToSizedBuffer(dAtA []byte) (int, er i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x12 + } if m.Name == nil { return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("name") } else { @@ -4974,6 +5244,13 @@ func (m *ResourcesQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.AppNamespace != nil { + i -= len(*m.AppNamespace) + copy(dAtA[i:], *m.AppNamespace) + i = encodeVarintApplication(dAtA, i, uint64(len(*m.AppNamespace))) + i-- + dAtA[i] = 0x3a + } if m.Kind != nil { i -= len(*m.Kind) copy(dAtA[i:], *m.Kind) @@ -5105,6 +5382,10 @@ func (m *ApplicationQuery) Size() (n int) { l = len(*m.Repo) n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5121,6 +5402,10 @@ func (m *NodeQuery) Size() (n int) { l = len(*m.Name) n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5141,6 +5426,10 @@ func (m *RevisionMetadataQuery) Size() (n int) { l = len(*m.Revision) n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5169,6 +5458,10 @@ func (m *ApplicationResourceEventsQuery) Size() (n int) { l = len(*m.ResourceUID) n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5189,6 +5482,10 @@ func (m *ApplicationManifestQuery) Size() (n int) { l = len(*m.Revision) n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5265,6 +5562,10 @@ func (m *ApplicationDeleteRequest) Size() (n int) { l = len(*m.PropagationPolicy) n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5339,6 +5640,10 @@ func (m *ApplicationSyncRequest) Size() (n int) { l = m.SyncOptions.Size() n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5362,6 +5667,10 @@ func (m *ApplicationUpdateSpecRequest) Size() (n int) { if m.Validate != nil { n += 2 } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5386,6 +5695,10 @@ func (m *ApplicationPatchRequest) Size() (n int) { l = len(*m.PatchType) n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5411,6 +5724,10 @@ func (m *ApplicationRollbackRequest) Size() (n int) { if m.Prune != nil { n += 2 } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5447,6 +5764,10 @@ func (m *ApplicationResourceRequest) Size() (n int) { l = len(*m.Kind) n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5491,6 +5812,10 @@ func (m *ApplicationResourcePatchRequest) Size() (n int) { l = len(*m.PatchType) n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5533,6 +5858,10 @@ func (m *ApplicationResourceDeleteRequest) Size() (n int) { if m.Orphan != nil { n += 2 } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5573,6 +5902,10 @@ func (m *ResourceActionRunRequest) Size() (n int) { l = len(*m.Action) n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5671,6 +6004,10 @@ func (m *ApplicationPodLogsQuery) Size() (n int) { if m.Previous != nil { n += 2 } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5718,6 +6055,10 @@ func (m *OperationTerminateRequest) Size() (n int) { l = len(*m.Name) n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5734,6 +6075,10 @@ func (m *ApplicationSyncWindowsQuery) Size() (n int) { l = len(*m.Name) n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5836,6 +6181,10 @@ func (m *ResourcesQuery) Size() (n int) { l = len(*m.Kind) n += 1 + l + sovApplication(uint64(l)) } + if m.AppNamespace != nil { + l = len(*m.AppNamespace) + n += 1 + l + sovApplication(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6092,6 +6441,39 @@ func (m *ApplicationQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.Repo = &s iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -6176,6 +6558,39 @@ func (m *NodeQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.Name = &s iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -6296,6 +6711,39 @@ func (m *RevisionMetadataQuery) Unmarshal(dAtA []byte) error { m.Revision = &s iNdEx = postIndex hasFields[0] |= uint64(0x00000002) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -6487,8 +6935,41 @@ func (m *ApplicationResourceEventsQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.ResourceUID = &s iNdEx = postIndex - default: - iNdEx = preIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex + default: + iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) if err != nil { return err @@ -6609,6 +7090,39 @@ func (m *ApplicationManifestQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.Revision = &s iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -7050,6 +7564,39 @@ func (m *ApplicationDeleteRequest) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.PropagationPolicy = &s iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -7505,6 +8052,39 @@ func (m *ApplicationSyncRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -7652,6 +8232,39 @@ func (m *ApplicationUpdateSpecRequest) Unmarshal(dAtA []byte) error { } b := bool(v != 0) m.Validate = &b + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -7812,6 +8425,39 @@ func (m *ApplicationPatchRequest) Unmarshal(dAtA []byte) error { m.PatchType = &s iNdEx = postIndex hasFields[0] |= uint64(0x00000004) + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -7970,6 +8616,39 @@ func (m *ApplicationRollbackRequest) Unmarshal(dAtA []byte) error { } b := bool(v != 0) m.Prune = &b + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -8230,6 +8909,39 @@ func (m *ApplicationResourceRequest) Unmarshal(dAtA []byte) error { m.Kind = &s iNdEx = postIndex hasFields[0] |= uint64(0x00000008) + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -8564,6 +9276,39 @@ func (m *ApplicationResourcePatchRequest) Unmarshal(dAtA []byte) error { m.PatchType = &s iNdEx = postIndex hasFields[0] |= uint64(0x00000020) + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -8878,6 +9623,39 @@ func (m *ApplicationResourceDeleteRequest) Unmarshal(dAtA []byte) error { } b := bool(v != 0) m.Orphan = &b + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -9178,6 +9956,39 @@ func (m *ResourceActionRunRequest) Unmarshal(dAtA []byte) error { m.Action = &s iNdEx = postIndex hasFields[0] |= uint64(0x00000010) + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -9835,6 +10646,39 @@ func (m *ApplicationPodLogsQuery) Unmarshal(dAtA []byte) error { } b := bool(v != 0) m.Previous = &b + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -10152,6 +10996,39 @@ func (m *OperationTerminateRequest) Unmarshal(dAtA []byte) error { m.Name = &s iNdEx = postIndex hasFields[0] |= uint64(0x00000001) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -10241,6 +11118,39 @@ func (m *ApplicationSyncWindowsQuery) Unmarshal(dAtA []byte) error { m.Name = &s iNdEx = postIndex hasFields[0] |= uint64(0x00000001) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) @@ -10879,6 +11789,39 @@ func (m *ResourcesQuery) Unmarshal(dAtA []byte) error { s := string(dAtA[iNdEx:postIndex]) m.Kind = &s iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AppNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApplication + } + 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 ErrInvalidLengthApplication + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApplication + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.AppNamespace = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipApplication(dAtA[iNdEx:]) diff --git a/pkg/apiclient/application/application.pb.gw.go b/pkg/apiclient/application/application.pb.gw.go index c67b638035cef..380438a80a5db 100644 --- a/pkg/apiclient/application/application.pb.gw.go +++ b/pkg/apiclient/application/application.pb.gw.go @@ -293,6 +293,10 @@ func local_request_ApplicationService_Get_0(ctx context.Context, marshaler runti } +var ( + filter_ApplicationService_GetApplicationSyncWindows_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + func request_ApplicationService_GetApplicationSyncWindows_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq ApplicationSyncWindowsQuery var metadata runtime.ServerMetadata @@ -315,6 +319,13 @@ func request_ApplicationService_GetApplicationSyncWindows_0(ctx context.Context, return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationService_GetApplicationSyncWindows_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.GetApplicationSyncWindows(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -342,11 +353,22 @@ func local_request_ApplicationService_GetApplicationSyncWindows_0(ctx context.Co return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationService_GetApplicationSyncWindows_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.GetApplicationSyncWindows(ctx, &protoReq) return msg, metadata, err } +var ( + filter_ApplicationService_RevisionMetadata_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0, "revision": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}} +) + func request_ApplicationService_RevisionMetadata_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq RevisionMetadataQuery var metadata runtime.ServerMetadata @@ -380,6 +402,13 @@ func request_ApplicationService_RevisionMetadata_0(ctx context.Context, marshale return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationService_RevisionMetadata_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.RevisionMetadata(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -418,6 +447,13 @@ func local_request_ApplicationService_RevisionMetadata_0(ctx context.Context, ma return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "revision", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationService_RevisionMetadata_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.RevisionMetadata(ctx, &protoReq) return msg, metadata, err @@ -1143,6 +1179,10 @@ func local_request_ApplicationService_Rollback_0(ctx context.Context, marshaler } +var ( + filter_ApplicationService_TerminateOperation_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + func request_ApplicationService_TerminateOperation_0(ctx context.Context, marshaler runtime.Marshaler, client ApplicationServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq OperationTerminateRequest var metadata runtime.ServerMetadata @@ -1165,6 +1205,13 @@ func request_ApplicationService_TerminateOperation_0(ctx context.Context, marsha return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationService_TerminateOperation_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := client.TerminateOperation(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err @@ -1192,6 +1239,13 @@ func local_request_ApplicationService_TerminateOperation_0(ctx context.Context, return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_ApplicationService_TerminateOperation_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + msg, err := server.TerminateOperation(ctx, &protoReq) return msg, metadata, err diff --git a/pkg/apiclient/settings/settings.pb.go b/pkg/apiclient/settings/settings.pb.go index af18096bf490c..15fee300d7960 100644 --- a/pkg/apiclient/settings/settings.pb.go +++ b/pkg/apiclient/settings/settings.pb.go @@ -98,6 +98,7 @@ type Settings struct { UiBannerPosition string `protobuf:"bytes,20,opt,name=uiBannerPosition,proto3" json:"uiBannerPosition,omitempty"` StatusBadgeRootUrl string `protobuf:"bytes,21,opt,name=statusBadgeRootUrl,proto3" json:"statusBadgeRootUrl,omitempty"` ExecEnabled bool `protobuf:"varint,22,opt,name=execEnabled,proto3" json:"execEnabled,omitempty"` + ControllerNamespace string `protobuf:"bytes,23,opt,name=controllerNamespace,proto3" json:"controllerNamespace,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -290,6 +291,13 @@ func (m *Settings) GetExecEnabled() bool { return false } +func (m *Settings) GetControllerNamespace() string { + if m != nil { + return m.ControllerNamespace + } + return "" +} + type GoogleAnalyticsConfig struct { TrackingID string `protobuf:"bytes,1,opt,name=trackingID,proto3" json:"trackingID,omitempty"` AnonymizeUsers bool `protobuf:"varint,2,opt,name=anonymizeUsers,proto3" json:"anonymizeUsers,omitempty"` @@ -667,77 +675,78 @@ func init() { func init() { proto.RegisterFile("server/settings/settings.proto", fileDescriptor_a480d494da040caa) } var fileDescriptor_a480d494da040caa = []byte{ - // 1106 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x5d, 0x6f, 0x23, 0x35, - 0x17, 0xd6, 0x34, 0xdd, 0x36, 0x39, 0xd9, 0x36, 0xad, 0xb7, 0xdb, 0x77, 0xde, 0x68, 0x49, 0x43, - 0x2e, 0x56, 0x01, 0xc1, 0x84, 0x66, 0x85, 0x40, 0x48, 0x2b, 0x20, 0xc9, 0x6a, 0x37, 0x6c, 0x4a, - 0xcb, 0x6c, 0xbb, 0x17, 0x48, 0xa8, 0x72, 0x67, 0xcc, 0xd4, 0x64, 0x62, 0x8f, 0x6c, 0x4f, 0x68, - 0xf6, 0x92, 0x3b, 0x6e, 0xb8, 0x81, 0x5f, 0xc3, 0x0f, 0x40, 0x5c, 0x22, 0x71, 0x5f, 0xa1, 0x88, - 0x1f, 0x82, 0xec, 0xf9, 0xe8, 0x34, 0x09, 0x1f, 0xd2, 0xde, 0xd9, 0xcf, 0x73, 0xbe, 0x7c, 0x7c, - 0x8e, 0x8f, 0xa1, 0x21, 0x89, 0x98, 0x12, 0xd1, 0x91, 0x44, 0x29, 0xca, 0x02, 0x99, 0x2f, 0x9c, - 0x48, 0x70, 0xc5, 0xd1, 0xa6, 0x17, 0xc6, 0x52, 0x11, 0x51, 0xdf, 0x0b, 0x78, 0xc0, 0x0d, 0xd6, - 0xd1, 0xab, 0x84, 0xae, 0x3f, 0x08, 0x38, 0x0f, 0x42, 0xd2, 0xc1, 0x11, 0xed, 0x60, 0xc6, 0xb8, - 0xc2, 0x8a, 0x72, 0x96, 0x2a, 0xd7, 0x47, 0x01, 0x55, 0x97, 0xf1, 0x85, 0xe3, 0xf1, 0x49, 0x07, - 0x0b, 0xa3, 0xfe, 0x8d, 0x59, 0xbc, 0xeb, 0xf9, 0x9d, 0x69, 0xb7, 0x13, 0x8d, 0x03, 0xad, 0x29, - 0x3b, 0x38, 0x8a, 0x42, 0xea, 0x19, 0xdd, 0xce, 0xf4, 0x10, 0x87, 0xd1, 0x25, 0x3e, 0xec, 0x04, - 0x84, 0x11, 0x81, 0x15, 0xf1, 0x53, 0x6b, 0x9f, 0xfc, 0x8b, 0xb5, 0xc5, 0x93, 0x70, 0xea, 0x7b, - 0x1d, 0x2f, 0xc4, 0x74, 0x92, 0xc6, 0xd3, 0xaa, 0xc1, 0xd6, 0x8b, 0x94, 0xfd, 0x22, 0x26, 0x62, - 0xd6, 0xfa, 0x05, 0xa0, 0x9c, 0x21, 0xe8, 0xff, 0x50, 0x8a, 0x45, 0x68, 0x5b, 0x4d, 0xab, 0x5d, - 0xe9, 0x6d, 0xce, 0xaf, 0x0f, 0x4a, 0x67, 0xee, 0xc8, 0xd5, 0x18, 0x7a, 0x0f, 0x2a, 0x3e, 0xb9, - 0xea, 0x73, 0xf6, 0x35, 0x0d, 0xec, 0xb5, 0xa6, 0xd5, 0xae, 0x76, 0x91, 0x93, 0x66, 0xc6, 0x19, - 0x64, 0x8c, 0x7b, 0x23, 0x84, 0xfa, 0x00, 0xda, 0x7f, 0xaa, 0x52, 0x32, 0x2a, 0xf7, 0x72, 0x95, - 0xe3, 0xe1, 0xa0, 0x9f, 0x50, 0xbd, 0xed, 0xf9, 0xf5, 0x01, 0xdc, 0xec, 0xdd, 0x82, 0x1a, 0x6a, - 0x42, 0x15, 0x47, 0xd1, 0x08, 0x5f, 0x90, 0xf0, 0x39, 0x99, 0xd9, 0xeb, 0x3a, 0x32, 0xb7, 0x08, - 0xa1, 0x97, 0xb0, 0x2b, 0x88, 0xe4, 0xb1, 0xf0, 0xc8, 0xf1, 0x94, 0x08, 0x41, 0x7d, 0x22, 0xed, - 0x3b, 0xcd, 0x52, 0xbb, 0xda, 0x6d, 0xe7, 0xde, 0xb2, 0x13, 0x3a, 0xee, 0xa2, 0xe8, 0x13, 0xa6, - 0xc4, 0xcc, 0x5d, 0x36, 0x81, 0x1c, 0x40, 0x52, 0x61, 0x15, 0xcb, 0x1e, 0xf6, 0x03, 0xf2, 0x84, - 0xe1, 0x8b, 0x90, 0xf8, 0xf6, 0x46, 0xd3, 0x6a, 0x97, 0xdd, 0x15, 0x0c, 0x7a, 0x06, 0xb5, 0xa4, - 0x12, 0x3e, 0x65, 0x38, 0x9c, 0x29, 0xea, 0x49, 0x7b, 0xd3, 0x9c, 0xb9, 0x91, 0x47, 0xf1, 0xf4, - 0x36, 0x9f, 0x1e, 0x77, 0x51, 0x0d, 0xbd, 0x82, 0x9d, 0x71, 0x2c, 0x15, 0x9f, 0xd0, 0x57, 0xe4, - 0x38, 0x32, 0xd5, 0x64, 0x97, 0x8d, 0xa9, 0xcf, 0x9d, 0x9b, 0x02, 0x70, 0xb2, 0x02, 0x30, 0x8b, - 0x73, 0xcf, 0x77, 0xa6, 0x5d, 0x27, 0x1a, 0x07, 0x8e, 0x2e, 0x27, 0xa7, 0x50, 0x4e, 0x4e, 0x56, - 0x4e, 0xce, 0xf3, 0x05, 0xab, 0xee, 0x92, 0x1f, 0xf4, 0x26, 0xac, 0x5f, 0x92, 0x30, 0xb2, 0x2b, - 0xc6, 0xdf, 0x56, 0x1e, 0xfa, 0x33, 0x12, 0x46, 0xae, 0xa1, 0xd0, 0x5b, 0xb0, 0x19, 0x85, 0x71, - 0x40, 0x99, 0xb4, 0xc1, 0xa4, 0xb9, 0x96, 0x4b, 0x9d, 0x18, 0xdc, 0xcd, 0x78, 0x9d, 0xc3, 0x58, - 0x12, 0x31, 0xe2, 0x7a, 0x37, 0xa0, 0x32, 0xc9, 0x61, 0x35, 0xc9, 0xe1, 0x32, 0x83, 0x7e, 0xb0, - 0xe0, 0x7f, 0x9e, 0xc9, 0xca, 0x11, 0x66, 0x38, 0x20, 0x13, 0xc2, 0xd4, 0x49, 0xea, 0xeb, 0xae, - 0xf1, 0x75, 0xfa, 0x7a, 0x19, 0xe8, 0xaf, 0x34, 0xee, 0xfe, 0x9d, 0x53, 0xf4, 0x0e, 0xec, 0xe6, - 0x29, 0x7a, 0x49, 0x84, 0x34, 0x77, 0xb1, 0xd5, 0x2c, 0xb5, 0x2b, 0xee, 0x32, 0x81, 0xea, 0x50, - 0x8e, 0x69, 0x5f, 0xca, 0x33, 0x77, 0x64, 0x6f, 0x9b, 0x4a, 0xcd, 0xf7, 0xa8, 0x0d, 0xb5, 0x98, - 0xf6, 0x30, 0x63, 0x44, 0xf4, 0x39, 0x53, 0x84, 0x29, 0xbb, 0x66, 0x44, 0x16, 0x61, 0x5d, 0xf2, - 0x19, 0xa4, 0x0d, 0xed, 0x24, 0x25, 0x5f, 0x80, 0xb4, 0xad, 0x08, 0x4b, 0xf9, 0x2d, 0x17, 0xfe, - 0x09, 0x56, 0x8a, 0x08, 0x66, 0xef, 0x26, 0xb6, 0x16, 0x60, 0xf4, 0x10, 0xb6, 0x95, 0xc0, 0xde, - 0x98, 0xb2, 0xe0, 0x88, 0xa8, 0x4b, 0xee, 0xdb, 0xc8, 0x08, 0x2e, 0xa0, 0xfa, 0x9c, 0x99, 0x83, - 0x13, 0x22, 0x26, 0x98, 0xe9, 0xf8, 0xee, 0x99, 0x7b, 0x5a, 0x26, 0xd0, 0xdb, 0xb0, 0x93, 0x83, - 0x5c, 0x52, 0x9d, 0x62, 0x7b, 0xcf, 0xd8, 0x5d, 0xc2, 0x17, 0xda, 0xc8, 0xe5, 0x5c, 0x9d, 0x89, - 0xd0, 0xbe, 0x6f, 0xa4, 0x57, 0x30, 0xfa, 0xf4, 0xe4, 0x8a, 0x78, 0x59, 0xbf, 0xed, 0x9b, 0x18, - 0x8a, 0x50, 0xfd, 0x27, 0x0b, 0xf6, 0x57, 0xb7, 0x31, 0xda, 0x81, 0xd2, 0x98, 0xcc, 0x92, 0xf7, - 0xcb, 0xd5, 0x4b, 0xe4, 0xc3, 0x9d, 0x29, 0x0e, 0x63, 0x92, 0x3e, 0x59, 0xaf, 0xd9, 0x40, 0x8b, - 0x6e, 0xdd, 0xc4, 0xf8, 0x47, 0x6b, 0x1f, 0x5a, 0xad, 0x73, 0xb8, 0xbf, 0xb2, 0xbf, 0x51, 0x03, - 0x20, 0xcb, 0xf6, 0x70, 0x90, 0xc6, 0x56, 0x40, 0xf4, 0x1d, 0x61, 0xc6, 0xd9, 0x4c, 0x97, 0xd2, - 0x99, 0x24, 0x42, 0x9a, 0x58, 0xcb, 0xee, 0x02, 0xda, 0xfa, 0xd9, 0x82, 0x75, 0xdd, 0x86, 0xc8, - 0x86, 0x4d, 0xef, 0x12, 0x9b, 0x3c, 0x26, 0xd6, 0xb2, 0xad, 0x2e, 0x40, 0xbd, 0x3c, 0x25, 0x57, - 0xca, 0x18, 0xa9, 0xb8, 0xf9, 0x1e, 0x3d, 0x06, 0xb8, 0xa0, 0x0c, 0x8b, 0xd9, 0x99, 0x08, 0xa5, - 0x5d, 0x32, 0xdd, 0xf4, 0xc6, 0xad, 0xfe, 0x76, 0x7a, 0x39, 0x9f, 0xbc, 0x8a, 0x05, 0x85, 0xfa, - 0x63, 0xa8, 0x2d, 0xd0, 0x2b, 0xb2, 0xbd, 0x57, 0xcc, 0x76, 0xa5, 0x98, 0x9d, 0x07, 0xb0, 0x91, - 0xf4, 0x14, 0x42, 0xb0, 0xce, 0xf0, 0x84, 0xa4, 0x6a, 0x66, 0xdd, 0xfa, 0x18, 0x2a, 0xf9, 0x08, - 0x41, 0x5d, 0x00, 0x8f, 0x33, 0x46, 0x3c, 0xc5, 0x85, 0xb4, 0x2d, 0x13, 0xe8, 0xcd, 0xa8, 0xe9, - 0x67, 0x94, 0x5b, 0x90, 0x6a, 0x3d, 0x82, 0x4a, 0x4e, 0xac, 0xf2, 0xa0, 0x31, 0x35, 0x8b, 0xb2, - 0xc0, 0xcc, 0xba, 0xf5, 0x7d, 0x09, 0x0a, 0x63, 0x67, 0xa5, 0xda, 0x3e, 0x6c, 0x50, 0x29, 0x63, - 0x22, 0x52, 0xc5, 0x74, 0x87, 0xda, 0x50, 0xf6, 0x42, 0x4a, 0x98, 0x1a, 0x0e, 0xcc, 0x64, 0xab, - 0xf4, 0xee, 0xce, 0xaf, 0x0f, 0xca, 0xfd, 0x14, 0x73, 0x73, 0x16, 0x1d, 0x42, 0xd5, 0x0b, 0x69, - 0x46, 0x24, 0x03, 0xac, 0x57, 0x9b, 0x5f, 0x1f, 0x54, 0xfb, 0xa3, 0x61, 0x2e, 0x5f, 0x94, 0xd1, - 0x4e, 0xa5, 0xc7, 0xa3, 0x74, 0x8c, 0x55, 0xdc, 0x74, 0x87, 0xce, 0x61, 0x8b, 0xfa, 0xa7, 0x7c, - 0x4c, 0x58, 0xdf, 0x8c, 0x74, 0x7b, 0xc3, 0xe4, 0xe6, 0xe1, 0x8a, 0x99, 0xea, 0x0c, 0x8b, 0x82, - 0xe6, 0xba, 0x7a, 0xbb, 0xf3, 0xeb, 0x83, 0xad, 0xe1, 0xa0, 0x80, 0xbb, 0xb7, 0xed, 0xd5, 0x67, - 0x80, 0x96, 0xf5, 0x56, 0x5c, 0xf3, 0xd1, 0xed, 0xa6, 0xfa, 0xe0, 0x1f, 0x9b, 0x2a, 0xf9, 0x93, - 0x38, 0xf9, 0xa7, 0x4a, 0x0f, 0x77, 0xc7, 0xd8, 0x2f, 0xd4, 0x47, 0xf7, 0x2b, 0xa8, 0x65, 0x33, - 0xfa, 0x05, 0x11, 0x53, 0xea, 0x11, 0xf4, 0x19, 0x94, 0x9e, 0x12, 0x85, 0xf6, 0x97, 0x86, 0xb8, - 0xf9, 0xb8, 0xd4, 0x77, 0x97, 0xf0, 0x96, 0xfd, 0xdd, 0xef, 0x7f, 0xfe, 0xb8, 0x86, 0xd0, 0x8e, - 0xf9, 0x8c, 0x4d, 0x0f, 0xf3, 0x8f, 0x50, 0xaf, 0xff, 0xeb, 0xbc, 0x61, 0xfd, 0x36, 0x6f, 0x58, - 0x7f, 0xcc, 0x1b, 0xd6, 0x97, 0xef, 0xff, 0xb7, 0x4f, 0x59, 0x72, 0x87, 0xb9, 0x91, 0x8b, 0x0d, - 0xf3, 0x85, 0x7a, 0xf4, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe2, 0xea, 0x37, 0xfe, 0x31, 0x0a, - 0x00, 0x00, + // 1129 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x4f, 0x6f, 0x1b, 0x45, + 0x14, 0xd7, 0xd6, 0x69, 0x62, 0xbf, 0x34, 0x75, 0x32, 0x6d, 0xd3, 0xc5, 0x2a, 0x8e, 0xf1, 0xa1, + 0x32, 0x08, 0xd6, 0x8d, 0x2b, 0x04, 0x42, 0xaa, 0x00, 0xdb, 0x55, 0x6b, 0xea, 0xb6, 0x61, 0xdb, + 0xf4, 0x80, 0x84, 0xaa, 0xc9, 0xee, 0x63, 0xb3, 0x78, 0x3d, 0xb3, 0x9a, 0x99, 0x35, 0x75, 0x8f, + 0xdc, 0xb8, 0x70, 0x81, 0x4f, 0xc3, 0x27, 0xe0, 0x88, 0xc4, 0x3d, 0x42, 0x16, 0x1f, 0x82, 0x23, + 0x9a, 0xd9, 0x3f, 0xd9, 0xd8, 0xe6, 0x8f, 0xd4, 0xdb, 0xcc, 0xef, 0xf7, 0xfe, 0xcd, 0x9b, 0x37, + 0xf3, 0x1e, 0x34, 0x25, 0x8a, 0x19, 0x8a, 0xae, 0x44, 0xa5, 0x42, 0x16, 0xc8, 0x62, 0xe1, 0xc4, + 0x82, 0x2b, 0x4e, 0xb6, 0xbc, 0x28, 0x91, 0x0a, 0x45, 0xe3, 0x7a, 0xc0, 0x03, 0x6e, 0xb0, 0xae, + 0x5e, 0xa5, 0x74, 0xe3, 0x56, 0xc0, 0x79, 0x10, 0x61, 0x97, 0xc6, 0x61, 0x97, 0x32, 0xc6, 0x15, + 0x55, 0x21, 0x67, 0x99, 0x72, 0x63, 0x1c, 0x84, 0xea, 0x34, 0x39, 0x71, 0x3c, 0x3e, 0xed, 0x52, + 0x61, 0xd4, 0xbf, 0x35, 0x8b, 0x0f, 0x3c, 0xbf, 0x3b, 0xeb, 0x75, 0xe3, 0x49, 0xa0, 0x35, 0x65, + 0x97, 0xc6, 0x71, 0x14, 0x7a, 0x46, 0xb7, 0x3b, 0x3b, 0xa4, 0x51, 0x7c, 0x4a, 0x0f, 0xbb, 0x01, + 0x32, 0x14, 0x54, 0xa1, 0x9f, 0x59, 0xfb, 0xec, 0x3f, 0xac, 0x2d, 0x9f, 0x84, 0x87, 0xbe, 0xd7, + 0xf5, 0x22, 0x1a, 0x4e, 0xb3, 0x78, 0xda, 0x75, 0xd8, 0x79, 0x96, 0xb1, 0x5f, 0x26, 0x28, 0xe6, + 0xed, 0xbf, 0x00, 0xaa, 0x39, 0x42, 0xde, 0x82, 0x4a, 0x22, 0x22, 0xdb, 0x6a, 0x59, 0x9d, 0x5a, + 0x7f, 0x6b, 0x71, 0x76, 0x50, 0x39, 0x76, 0xc7, 0xae, 0xc6, 0xc8, 0x1d, 0xa8, 0xf9, 0xf8, 0x6a, + 0xc0, 0xd9, 0x37, 0x61, 0x60, 0x5f, 0x6a, 0x59, 0x9d, 0xed, 0x1e, 0x71, 0xb2, 0xcc, 0x38, 0xc3, + 0x9c, 0x71, 0xcf, 0x85, 0xc8, 0x00, 0x40, 0xfb, 0xcf, 0x54, 0x2a, 0x46, 0xe5, 0x5a, 0xa1, 0xf2, + 0x74, 0x34, 0x1c, 0xa4, 0x54, 0xff, 0xea, 0xe2, 0xec, 0x00, 0xce, 0xf7, 0x6e, 0x49, 0x8d, 0xb4, + 0x60, 0x9b, 0xc6, 0xf1, 0x98, 0x9e, 0x60, 0xf4, 0x08, 0xe7, 0xf6, 0x86, 0x8e, 0xcc, 0x2d, 0x43, + 0xe4, 0x05, 0xec, 0x09, 0x94, 0x3c, 0x11, 0x1e, 0x3e, 0x9d, 0xa1, 0x10, 0xa1, 0x8f, 0xd2, 0xbe, + 0xdc, 0xaa, 0x74, 0xb6, 0x7b, 0x9d, 0xc2, 0x5b, 0x7e, 0x42, 0xc7, 0x5d, 0x16, 0xbd, 0xcf, 0x94, + 0x98, 0xbb, 0xab, 0x26, 0x88, 0x03, 0x44, 0x2a, 0xaa, 0x12, 0xd9, 0xa7, 0x7e, 0x80, 0xf7, 0x19, + 0x3d, 0x89, 0xd0, 0xb7, 0x37, 0x5b, 0x56, 0xa7, 0xea, 0xae, 0x61, 0xc8, 0x43, 0xa8, 0xa7, 0x95, + 0xf0, 0x39, 0xa3, 0xd1, 0x5c, 0x85, 0x9e, 0xb4, 0xb7, 0xcc, 0x99, 0x9b, 0x45, 0x14, 0x0f, 0x2e, + 0xf2, 0xd9, 0x71, 0x97, 0xd5, 0xc8, 0x6b, 0xd8, 0x9d, 0x24, 0x52, 0xf1, 0x69, 0xf8, 0x1a, 0x9f, + 0xc6, 0xa6, 0x9a, 0xec, 0xaa, 0x31, 0xf5, 0xc4, 0x39, 0x2f, 0x00, 0x27, 0x2f, 0x00, 0xb3, 0x78, + 0xe9, 0xf9, 0xce, 0xac, 0xe7, 0xc4, 0x93, 0xc0, 0xd1, 0xe5, 0xe4, 0x94, 0xca, 0xc9, 0xc9, 0xcb, + 0xc9, 0x79, 0xb4, 0x64, 0xd5, 0x5d, 0xf1, 0x43, 0xde, 0x81, 0x8d, 0x53, 0x8c, 0x62, 0xbb, 0x66, + 0xfc, 0xed, 0x14, 0xa1, 0x3f, 0xc4, 0x28, 0x76, 0x0d, 0x45, 0xde, 0x85, 0xad, 0x38, 0x4a, 0x82, + 0x90, 0x49, 0x1b, 0x4c, 0x9a, 0xeb, 0x85, 0xd4, 0x91, 0xc1, 0xdd, 0x9c, 0xd7, 0x39, 0x4c, 0x24, + 0x8a, 0x31, 0xd7, 0xbb, 0x61, 0x28, 0xd3, 0x1c, 0x6e, 0xa7, 0x39, 0x5c, 0x65, 0xc8, 0x8f, 0x16, + 0xdc, 0xf4, 0x4c, 0x56, 0x1e, 0x53, 0x46, 0x03, 0x9c, 0x22, 0x53, 0x47, 0x99, 0xaf, 0x2b, 0xc6, + 0xd7, 0xf3, 0x37, 0xcb, 0xc0, 0x60, 0xad, 0x71, 0xf7, 0x9f, 0x9c, 0x92, 0xf7, 0x61, 0xaf, 0x48, + 0xd1, 0x0b, 0x14, 0xd2, 0xdc, 0xc5, 0x4e, 0xab, 0xd2, 0xa9, 0xb9, 0xab, 0x04, 0x69, 0x40, 0x35, + 0x09, 0x07, 0x52, 0x1e, 0xbb, 0x63, 0xfb, 0xaa, 0xa9, 0xd4, 0x62, 0x4f, 0x3a, 0x50, 0x4f, 0xc2, + 0x3e, 0x65, 0x0c, 0xc5, 0x80, 0x33, 0x85, 0x4c, 0xd9, 0x75, 0x23, 0xb2, 0x0c, 0xeb, 0x92, 0xcf, + 0x21, 0x6d, 0x68, 0x37, 0x2d, 0xf9, 0x12, 0xa4, 0x6d, 0xc5, 0x54, 0xca, 0xef, 0xb8, 0xf0, 0x8f, + 0xa8, 0x52, 0x28, 0x98, 0xbd, 0x97, 0xda, 0x5a, 0x82, 0xc9, 0x6d, 0xb8, 0xaa, 0x04, 0xf5, 0x26, + 0x21, 0x0b, 0x1e, 0xa3, 0x3a, 0xe5, 0xbe, 0x4d, 0x8c, 0xe0, 0x12, 0xaa, 0xcf, 0x99, 0x3b, 0x38, + 0x42, 0x31, 0xa5, 0x4c, 0xc7, 0x77, 0xcd, 0xdc, 0xd3, 0x2a, 0x41, 0xde, 0x83, 0xdd, 0x02, 0xe4, + 0x32, 0xd4, 0x29, 0xb6, 0xaf, 0x1b, 0xbb, 0x2b, 0xf8, 0xd2, 0x33, 0x72, 0x39, 0x57, 0xc7, 0x22, + 0xb2, 0x6f, 0x18, 0xe9, 0x35, 0x8c, 0x3e, 0x3d, 0xbe, 0x42, 0x2f, 0x7f, 0x6f, 0xfb, 0x26, 0x86, + 0x32, 0x44, 0xee, 0xc0, 0x35, 0x8f, 0x33, 0x25, 0x78, 0x14, 0xa1, 0x78, 0x42, 0xa7, 0x28, 0x63, + 0xea, 0xa1, 0x7d, 0xd3, 0x98, 0x5c, 0x47, 0x35, 0x7e, 0xb6, 0x60, 0x7f, 0xfd, 0xc3, 0x27, 0xbb, + 0x50, 0x99, 0xe0, 0x3c, 0xfd, 0xf1, 0x5c, 0xbd, 0x24, 0x3e, 0x5c, 0x9e, 0xd1, 0x28, 0xc1, 0xec, + 0x93, 0x7b, 0xc3, 0x27, 0xb7, 0xec, 0xd6, 0x4d, 0x8d, 0x7f, 0x72, 0xe9, 0x63, 0xab, 0xfd, 0x12, + 0x6e, 0xac, 0xfd, 0x11, 0x48, 0x13, 0x20, 0xbf, 0x9f, 0xd1, 0x30, 0x8b, 0xad, 0x84, 0xe8, 0x5b, + 0xa5, 0x8c, 0xb3, 0xb9, 0x2e, 0xbe, 0x63, 0x89, 0x42, 0x9a, 0x58, 0xab, 0xee, 0x12, 0xda, 0xfe, + 0xc5, 0x82, 0x0d, 0xfd, 0x70, 0x89, 0x0d, 0x5b, 0xde, 0x29, 0x35, 0x99, 0x4f, 0xad, 0xe5, 0x5b, + 0x5d, 0xb2, 0x7a, 0xf9, 0x1c, 0x5f, 0x29, 0x63, 0xa4, 0xe6, 0x16, 0x7b, 0x72, 0x0f, 0xe0, 0x24, + 0x64, 0x54, 0xcc, 0x8f, 0x45, 0x24, 0xed, 0x8a, 0x79, 0x7f, 0x6f, 0x5f, 0xf8, 0x11, 0x9c, 0x7e, + 0xc1, 0xa7, 0xff, 0x68, 0x49, 0xa1, 0x71, 0x0f, 0xea, 0x4b, 0xf4, 0x9a, 0x6c, 0x5f, 0x2f, 0x67, + 0xbb, 0x56, 0xce, 0xce, 0x2d, 0xd8, 0x4c, 0x5f, 0x21, 0x21, 0xb0, 0xc1, 0xe8, 0x14, 0x33, 0x35, + 0xb3, 0x6e, 0x7f, 0x0a, 0xb5, 0xa2, 0xe9, 0x90, 0x1e, 0x80, 0xc7, 0x19, 0x43, 0x4f, 0x71, 0x21, + 0x6d, 0xcb, 0x04, 0x7a, 0xde, 0x9c, 0x06, 0x39, 0xe5, 0x96, 0xa4, 0xda, 0x77, 0xa1, 0x56, 0x10, + 0xeb, 0x3c, 0x68, 0x4c, 0xcd, 0xe3, 0x3c, 0x30, 0xb3, 0x6e, 0xff, 0x50, 0x81, 0x52, 0xa3, 0x5a, + 0xab, 0xb6, 0x0f, 0x9b, 0xa1, 0x94, 0x09, 0x8a, 0x4c, 0x31, 0xdb, 0x91, 0x0e, 0x54, 0xbd, 0x28, + 0x44, 0xa6, 0x46, 0x43, 0xd3, 0x0b, 0x6b, 0xfd, 0x2b, 0x8b, 0xb3, 0x83, 0xea, 0x20, 0xc3, 0xdc, + 0x82, 0x25, 0x87, 0xb0, 0xed, 0x45, 0x61, 0x4e, 0xa4, 0x2d, 0xaf, 0x5f, 0x5f, 0x9c, 0x1d, 0x6c, + 0x0f, 0xc6, 0xa3, 0x42, 0xbe, 0x2c, 0xa3, 0x9d, 0x4a, 0x8f, 0xc7, 0x59, 0xe3, 0xab, 0xb9, 0xd9, + 0x8e, 0xbc, 0x84, 0x9d, 0xd0, 0x7f, 0xce, 0x27, 0xc8, 0x06, 0x66, 0x08, 0xb0, 0x37, 0x4d, 0x6e, + 0x6e, 0xaf, 0xe9, 0xc2, 0xce, 0xa8, 0x2c, 0x68, 0xae, 0xab, 0xbf, 0xb7, 0x38, 0x3b, 0xd8, 0x19, + 0x0d, 0x4b, 0xb8, 0x7b, 0xd1, 0x5e, 0x63, 0x0e, 0x64, 0x55, 0x6f, 0xcd, 0x35, 0x3f, 0xbe, 0xf8, + 0xa8, 0x3e, 0xfa, 0xd7, 0x47, 0x95, 0x4e, 0x31, 0x4e, 0x31, 0x86, 0xe9, 0x71, 0xc0, 0x31, 0xf6, + 0x4b, 0xf5, 0xd1, 0xfb, 0x1a, 0xea, 0x79, 0x57, 0x7f, 0x86, 0x62, 0x16, 0x7a, 0x48, 0xbe, 0x80, + 0xca, 0x03, 0x54, 0x64, 0x7f, 0xa5, 0xed, 0x9b, 0x51, 0xa7, 0xb1, 0xb7, 0x82, 0xb7, 0xed, 0xef, + 0x7f, 0xff, 0xf3, 0xa7, 0x4b, 0x84, 0xec, 0x9a, 0xf1, 0x6d, 0x76, 0x58, 0x8c, 0x4e, 0xfd, 0xc1, + 0xaf, 0x8b, 0xa6, 0xf5, 0xdb, 0xa2, 0x69, 0xfd, 0xb1, 0x68, 0x5a, 0x5f, 0x7d, 0xf8, 0xff, 0xc6, + 0xb8, 0xf4, 0x0e, 0x0b, 0x23, 0x27, 0x9b, 0x66, 0xe8, 0xba, 0xfb, 0x77, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x2a, 0xfc, 0x97, 0xee, 0x63, 0x0a, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -873,6 +882,15 @@ func (m *Settings) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.ControllerNamespace) > 0 { + i -= len(m.ControllerNamespace) + copy(dAtA[i:], m.ControllerNamespace) + i = encodeVarintSettings(dAtA, i, uint64(len(m.ControllerNamespace))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xba + } if m.ExecEnabled { i-- if m.ExecEnabled { @@ -1554,6 +1572,10 @@ func (m *Settings) Size() (n int) { if m.ExecEnabled { n += 3 } + l = len(m.ControllerNamespace) + if l > 0 { + n += 2 + l + sovSettings(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -2571,6 +2593,38 @@ func (m *Settings) Unmarshal(dAtA []byte) error { } } m.ExecEnabled = bool(v != 0) + case 23: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ControllerNamespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSettings + } + 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 ErrInvalidLengthSettings + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSettings + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ControllerNamespace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSettings(dAtA[iNdEx:]) diff --git a/pkg/apis/api-rules/violation_exceptions.list b/pkg/apis/api-rules/violation_exceptions.list index 8fd1d8dcbfd31..a41bd9da2c3a7 100644 --- a/pkg/apis/api-rules/violation_exceptions.list +++ b/pkg/apis/api-rules/violation_exceptions.list @@ -5,6 +5,7 @@ API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/ap API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,NamespaceResourceWhitelist API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,Roles API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,SignatureKeys +API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,SourceNamespaces API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,AppProjectSpec,SourceRepos API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourceHelm,FileParameters API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourceHelm,Parameters diff --git a/pkg/apis/application/v1alpha1/app_project_types.go b/pkg/apis/application/v1alpha1/app_project_types.go index abb15c30bf6ce..b6985cad9d8ec 100644 --- a/pkg/apis/application/v1alpha1/app_project_types.go +++ b/pkg/apis/application/v1alpha1/app_project_types.go @@ -172,6 +172,15 @@ func (p *AppProject) ValidateProject() error { } destKeys[key] = true } + + srcNamespaces := make(map[string]bool) + for _, ns := range p.Spec.SourceNamespaces { + if _, ok := srcNamespaces[ns]; ok { + return status.Errorf(codes.InvalidArgument, "source namespaces '%s' already added", ns) + } + destKeys[ns] = true + } + srcRepos := make(map[string]bool) for _, src := range p.Spec.SourceRepos { if _, ok := srcRepos[src]; ok { @@ -482,3 +491,18 @@ func jwtTokensCombine(tokens1 []JWTToken, tokens2 []JWTToken) []JWTToken { }) return tokens } + +// IsAppNamespacePermitted checks whether an application that associates with +// this AppProject is allowed by comparing the Application's namespace with +// the list of allowed namespaces in the AppProject. +// +// Applications in the installation namespace are always permitted. Also, at +// application creation time, its namespace may yet be empty to indicate that +// the application will be created in the controller's namespace. +func (p AppProject) IsAppNamespacePermitted(app *Application, controllerNs string) bool { + if app.Namespace == "" || app.Namespace == controllerNs { + return true + } + + return glob.MatchStringInList(p.Spec.SourceNamespaces, app.Namespace, false) +} diff --git a/pkg/apis/application/v1alpha1/generated.pb.go b/pkg/apis/application/v1alpha1/generated.pb.go index cf8301fb3b190..79b6a6cb4ffe6 100644 --- a/pkg/apis/application/v1alpha1/generated.pb.go +++ b/pkg/apis/application/v1alpha1/generated.pb.go @@ -2604,433 +2604,434 @@ func init() { } var fileDescriptor_030104ce3b95bcac = []byte{ - // 6809 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x8c, 0x24, 0xc9, - 0x51, 0xf0, 0x55, 0xf7, 0xfc, 0x74, 0xc7, 0xfc, 0xec, 0x4e, 0xee, 0xcf, 0x8d, 0xe7, 0x3b, 0xef, - 0xac, 0xea, 0x64, 0xfb, 0xbe, 0xcf, 0xf6, 0xcc, 0x77, 0xcb, 0xd9, 0x1c, 0x3e, 0x73, 0x66, 0x7a, - 0x66, 0x7f, 0x66, 0x77, 0xfe, 0x36, 0x66, 0x76, 0x17, 0x9f, 0x8d, 0xb9, 0x9a, 0xea, 0xec, 0xee, - 0xda, 0xe9, 0xae, 0xea, 0xab, 0xaa, 0x9e, 0x9d, 0xb6, 0xf1, 0x9f, 0x64, 0xf0, 0x49, 0xfe, 0x95, - 0xcd, 0x83, 0x2d, 0x21, 0x30, 0x3f, 0x42, 0xe2, 0xc1, 0x02, 0x9e, 0x00, 0x21, 0x1e, 0x30, 0x2f, - 0x46, 0x3c, 0x60, 0x09, 0x84, 0x0d, 0x16, 0x83, 0xbd, 0x80, 0x0c, 0x48, 0x80, 0x00, 0xbf, 0xb0, - 0xe2, 0x01, 0xe5, 0x4f, 0x65, 0x66, 0x55, 0x77, 0xef, 0xcc, 0x6c, 0xd7, 0x2e, 0x96, 0xc5, 0xdb, - 0x74, 0x44, 0x64, 0x44, 0x64, 0x56, 0x66, 0x64, 0x44, 0x64, 0x64, 0x0e, 0xac, 0xd5, 0xbd, 0xb8, - 0xd1, 0xd9, 0x5d, 0x70, 0x83, 0xd6, 0xa2, 0x13, 0xd6, 0x83, 0x76, 0x18, 0xdc, 0xe5, 0x7f, 0xbc, - 0xdd, 0xad, 0x2e, 0xee, 0x5f, 0x5a, 0x6c, 0xef, 0xd5, 0x17, 0x9d, 0xb6, 0x17, 0x2d, 0x3a, 0xed, - 0x76, 0xd3, 0x73, 0x9d, 0xd8, 0x0b, 0xfc, 0xc5, 0xfd, 0xe7, 0x9d, 0x66, 0xbb, 0xe1, 0x3c, 0xbf, - 0x58, 0xa7, 0x3e, 0x0d, 0x9d, 0x98, 0x56, 0x17, 0xda, 0x61, 0x10, 0x07, 0xe4, 0xdd, 0x9a, 0xdb, - 0x42, 0xc2, 0x8d, 0xff, 0xf1, 0xd3, 0x6e, 0x75, 0x61, 0xff, 0xd2, 0x42, 0x7b, 0xaf, 0xbe, 0xc0, - 0xb8, 0x2d, 0x18, 0xdc, 0x16, 0x12, 0x6e, 0x73, 0x6f, 0x37, 0x74, 0xa9, 0x07, 0xf5, 0x60, 0x91, - 0x33, 0xdd, 0xed, 0xd4, 0xf8, 0x2f, 0xfe, 0x83, 0xff, 0x25, 0x84, 0xcd, 0xd9, 0x7b, 0x2f, 0x46, - 0x0b, 0x5e, 0xc0, 0xd4, 0x5b, 0x74, 0x83, 0x90, 0x2e, 0xee, 0xf7, 0x28, 0x34, 0xf7, 0x82, 0xa6, - 0x69, 0x39, 0x6e, 0xc3, 0xf3, 0x69, 0xd8, 0xd5, 0x7d, 0x6a, 0xd1, 0xd8, 0xe9, 0xd7, 0x6a, 0x71, - 0x50, 0xab, 0xb0, 0xe3, 0xc7, 0x5e, 0x8b, 0xf6, 0x34, 0x78, 0xe7, 0x51, 0x0d, 0x22, 0xb7, 0x41, - 0x5b, 0x4e, 0xb6, 0x9d, 0xfd, 0x1a, 0x4c, 0x2d, 0xdd, 0xd9, 0x5e, 0xea, 0xc4, 0x8d, 0xe5, 0xc0, - 0xaf, 0x79, 0x75, 0xf2, 0x0e, 0x98, 0x70, 0x9b, 0x9d, 0x28, 0xa6, 0xe1, 0x86, 0xd3, 0xa2, 0xb3, - 0xd6, 0x45, 0xeb, 0xb9, 0x72, 0xe5, 0xcc, 0xd7, 0x0f, 0xe7, 0x9f, 0xba, 0x7f, 0x38, 0x3f, 0xb1, - 0xac, 0x51, 0x68, 0xd2, 0x91, 0xff, 0x0b, 0xe3, 0x61, 0xd0, 0xa4, 0x4b, 0xb8, 0x31, 0x5b, 0xe0, - 0x4d, 0x4e, 0xc9, 0x26, 0xe3, 0x28, 0xc0, 0x98, 0xe0, 0xed, 0xbf, 0x28, 0x00, 0x2c, 0xb5, 0xdb, - 0x5b, 0x61, 0x70, 0x97, 0xba, 0x31, 0x79, 0x15, 0x4a, 0x6c, 0x14, 0xaa, 0x4e, 0xec, 0x70, 0x69, - 0x13, 0x97, 0xfe, 0xff, 0x82, 0xe8, 0xcc, 0x82, 0xd9, 0x19, 0xfd, 0xe5, 0x18, 0xf5, 0xc2, 0xfe, - 0xf3, 0x0b, 0x9b, 0xbb, 0xac, 0xfd, 0x3a, 0x8d, 0x9d, 0x0a, 0x91, 0xc2, 0x40, 0xc3, 0x50, 0x71, - 0x25, 0x3e, 0x8c, 0x44, 0x6d, 0xea, 0x72, 0xc5, 0x26, 0x2e, 0xad, 0x2d, 0x0c, 0x33, 0x45, 0x16, - 0xb4, 0xe6, 0xdb, 0x6d, 0xea, 0x56, 0x26, 0xa5, 0xe4, 0x11, 0xf6, 0x0b, 0xb9, 0x1c, 0xb2, 0x0f, - 0x63, 0x51, 0xec, 0xc4, 0x9d, 0x68, 0xb6, 0xc8, 0x25, 0x6e, 0xe4, 0x26, 0x91, 0x73, 0xad, 0x4c, - 0x4b, 0x99, 0x63, 0xe2, 0x37, 0x4a, 0x69, 0xf6, 0x5f, 0x5b, 0x30, 0xad, 0x89, 0xd7, 0xbc, 0x28, - 0x26, 0xef, 0xef, 0x19, 0xdc, 0x85, 0xe3, 0x0d, 0x2e, 0x6b, 0xcd, 0x87, 0xf6, 0xb4, 0x14, 0x56, - 0x4a, 0x20, 0xc6, 0xc0, 0xb6, 0x60, 0xd4, 0x8b, 0x69, 0x2b, 0x9a, 0x2d, 0x5c, 0x2c, 0x3e, 0x37, - 0x71, 0xe9, 0x5a, 0x5e, 0xfd, 0xac, 0x4c, 0x49, 0xa1, 0xa3, 0xab, 0x8c, 0x3d, 0x0a, 0x29, 0xf6, - 0xf7, 0xc1, 0xec, 0x1f, 0x1b, 0x70, 0xf2, 0x3c, 0x4c, 0x44, 0x41, 0x27, 0x74, 0x29, 0xd2, 0x76, - 0x10, 0xcd, 0x5a, 0x17, 0x8b, 0x6c, 0xea, 0xb1, 0x99, 0xba, 0xad, 0xc1, 0x68, 0xd2, 0x90, 0xcf, - 0x5a, 0x30, 0x59, 0xa5, 0x51, 0xec, 0xf9, 0x5c, 0x7e, 0xa2, 0xfc, 0xce, 0xd0, 0xca, 0x27, 0xc0, - 0x15, 0xcd, 0xbc, 0x72, 0x56, 0x76, 0x64, 0xd2, 0x00, 0x46, 0x98, 0x92, 0xcf, 0x56, 0x5c, 0x95, - 0x46, 0x6e, 0xe8, 0xb5, 0xd9, 0x6f, 0x3e, 0x67, 0x8c, 0x15, 0xb7, 0xa2, 0x51, 0x68, 0xd2, 0x11, - 0x1f, 0x46, 0xd9, 0x8a, 0x8a, 0x66, 0x47, 0xb8, 0xfe, 0xab, 0xc3, 0xe9, 0x2f, 0x07, 0x95, 0x2d, - 0x56, 0x3d, 0xfa, 0xec, 0x57, 0x84, 0x42, 0x0c, 0xf9, 0x8c, 0x05, 0xb3, 0x72, 0xc5, 0x23, 0x15, - 0x03, 0x7a, 0xa7, 0xe1, 0xc5, 0xb4, 0xe9, 0x45, 0xf1, 0xec, 0x28, 0xd7, 0x61, 0xf1, 0x78, 0x73, - 0xeb, 0x6a, 0x18, 0x74, 0xda, 0x37, 0x3c, 0xbf, 0x5a, 0xb9, 0x28, 0x25, 0xcd, 0x2e, 0x0f, 0x60, - 0x8c, 0x03, 0x45, 0x92, 0x2f, 0x5a, 0x30, 0xe7, 0x3b, 0x2d, 0x1a, 0xb5, 0x1d, 0xf6, 0x69, 0x05, - 0xba, 0xd2, 0x74, 0xdc, 0x3d, 0xae, 0xd1, 0xd8, 0xa3, 0x69, 0x64, 0x4b, 0x8d, 0xe6, 0x36, 0x06, - 0xb2, 0xc6, 0x87, 0x88, 0x25, 0xbf, 0x6a, 0xc1, 0x4c, 0x10, 0xb6, 0x1b, 0x8e, 0x4f, 0xab, 0x09, - 0x36, 0x9a, 0x1d, 0xe7, 0x4b, 0xef, 0x03, 0xc3, 0x7d, 0xa2, 0xcd, 0x2c, 0xdb, 0xf5, 0xc0, 0xf7, - 0xe2, 0x20, 0xdc, 0xa6, 0x71, 0xec, 0xf9, 0xf5, 0xa8, 0x72, 0xee, 0xfe, 0xe1, 0xfc, 0x4c, 0x0f, - 0x15, 0xf6, 0xea, 0x43, 0x3e, 0x04, 0x13, 0x51, 0xd7, 0x77, 0xef, 0x78, 0x7e, 0x35, 0xb8, 0x17, - 0xcd, 0x96, 0xf2, 0x58, 0xbe, 0xdb, 0x8a, 0xa1, 0x5c, 0x80, 0x5a, 0x00, 0x9a, 0xd2, 0xfa, 0x7f, - 0x38, 0x3d, 0x95, 0xca, 0x79, 0x7f, 0x38, 0x3d, 0x99, 0x1e, 0x22, 0x96, 0x7c, 0xd2, 0x82, 0xa9, - 0xc8, 0xab, 0xfb, 0x4e, 0xdc, 0x09, 0xe9, 0x0d, 0xda, 0x8d, 0x66, 0x81, 0x2b, 0x72, 0x7d, 0xc8, - 0x51, 0x31, 0x58, 0x56, 0xce, 0x49, 0x1d, 0xa7, 0x4c, 0x68, 0x84, 0x69, 0xb9, 0xfd, 0x16, 0x9a, - 0x9e, 0xd6, 0x13, 0xf9, 0x2e, 0x34, 0x3d, 0xa9, 0x07, 0x8a, 0xb4, 0xff, 0xb8, 0x00, 0xa7, 0xb3, - 0x7b, 0x10, 0xf9, 0x75, 0x0b, 0x4e, 0xdd, 0xbd, 0x17, 0xef, 0x04, 0x7b, 0xd4, 0x8f, 0x2a, 0x5d, - 0x66, 0x29, 0xb8, 0xf5, 0x9d, 0xb8, 0xe4, 0xe6, 0xbb, 0xdb, 0x2d, 0x5c, 0x4f, 0x4b, 0xb9, 0xec, - 0xc7, 0x61, 0xb7, 0xf2, 0xb4, 0xec, 0xcf, 0xa9, 0xeb, 0x77, 0x76, 0x4c, 0x2c, 0x66, 0x95, 0x9a, - 0xfb, 0x94, 0x05, 0x67, 0xfb, 0xb1, 0x20, 0xa7, 0xa1, 0xb8, 0x47, 0xbb, 0xc2, 0xc1, 0x41, 0xf6, - 0x27, 0xf9, 0x29, 0x18, 0xdd, 0x77, 0x9a, 0x1d, 0x2a, 0x1d, 0x85, 0xab, 0xc3, 0x75, 0x44, 0x69, - 0x86, 0x82, 0xeb, 0xbb, 0x0a, 0x2f, 0x5a, 0xf6, 0x9f, 0x16, 0x61, 0xc2, 0xd8, 0x2a, 0x9e, 0x80, - 0xf3, 0x13, 0xa4, 0x9c, 0x9f, 0xf5, 0xdc, 0x76, 0xb9, 0x81, 0xde, 0xcf, 0xbd, 0x8c, 0xf7, 0xb3, - 0x99, 0x9f, 0xc8, 0x87, 0xba, 0x3f, 0x24, 0x86, 0x72, 0xd0, 0x66, 0xce, 0x2d, 0xdb, 0x45, 0x47, - 0xf2, 0xf8, 0x84, 0x9b, 0x09, 0xbb, 0xca, 0xd4, 0xfd, 0xc3, 0xf9, 0xb2, 0xfa, 0x89, 0x5a, 0x90, - 0xfd, 0x4d, 0x0b, 0xce, 0x1a, 0x3a, 0x2e, 0x07, 0x7e, 0xd5, 0xe3, 0x9f, 0xf6, 0x22, 0x8c, 0xc4, - 0xdd, 0x76, 0xe2, 0x41, 0xab, 0x91, 0xda, 0xe9, 0xb6, 0x29, 0x72, 0x0c, 0xf3, 0x99, 0x5b, 0x34, - 0x8a, 0x9c, 0x3a, 0xcd, 0xfa, 0xcc, 0xeb, 0x02, 0x8c, 0x09, 0x9e, 0x84, 0x40, 0x9a, 0x4e, 0x14, - 0xef, 0x84, 0x8e, 0x1f, 0x71, 0xf6, 0x3b, 0x5e, 0x8b, 0xca, 0x01, 0xfe, 0x7f, 0xc7, 0x9b, 0x31, - 0xac, 0x45, 0xe5, 0xfc, 0xfd, 0xc3, 0x79, 0xb2, 0xd6, 0xc3, 0x09, 0xfb, 0x70, 0xb7, 0xbf, 0x68, - 0xc1, 0xf9, 0xfe, 0x6e, 0x0d, 0x79, 0x33, 0x8c, 0x45, 0x34, 0xdc, 0xa7, 0xa1, 0xec, 0x9d, 0xfe, - 0x24, 0x1c, 0x8a, 0x12, 0x4b, 0x16, 0xa1, 0xac, 0x4c, 0xae, 0xec, 0xe3, 0x8c, 0x24, 0x2d, 0x6b, - 0x3b, 0xad, 0x69, 0xd8, 0xa0, 0xb1, 0x1f, 0xd2, 0x09, 0x52, 0x83, 0xc6, 0xe3, 0x0d, 0x8e, 0xb1, - 0xff, 0xc6, 0x82, 0x53, 0x86, 0x56, 0x4f, 0xc0, 0xcb, 0xf5, 0xd3, 0x5e, 0xee, 0x6a, 0x6e, 0xf3, - 0x79, 0x80, 0x9b, 0xfb, 0x47, 0xa3, 0x30, 0x63, 0xce, 0x7a, 0x6e, 0x8e, 0x79, 0x80, 0x45, 0xdb, - 0xc1, 0x2d, 0x5c, 0x93, 0x63, 0xae, 0x03, 0x2c, 0x01, 0xc6, 0x04, 0xcf, 0x06, 0xb1, 0xed, 0xc4, - 0x0d, 0x39, 0xe0, 0x6a, 0x10, 0xb7, 0x9c, 0xb8, 0x81, 0x1c, 0x43, 0x5e, 0x86, 0xe9, 0xd8, 0x09, - 0xeb, 0x34, 0x46, 0xba, 0xef, 0x45, 0xc9, 0x7a, 0x29, 0x57, 0xce, 0x4b, 0xda, 0xe9, 0x9d, 0x14, - 0x16, 0x33, 0xd4, 0xe4, 0x35, 0x18, 0x69, 0xd0, 0x66, 0x4b, 0xfa, 0x35, 0xdb, 0xf9, 0xad, 0x70, - 0xde, 0xd7, 0x6b, 0xb4, 0xd9, 0xaa, 0x94, 0x98, 0xca, 0xec, 0x2f, 0xe4, 0xa2, 0xc8, 0xcf, 0x5a, - 0x50, 0xde, 0xeb, 0x44, 0x71, 0xd0, 0xf2, 0x3e, 0x48, 0x67, 0x4b, 0x5c, 0xf0, 0x4f, 0xe6, 0x2c, - 0xf8, 0x46, 0xc2, 0x5f, 0xac, 0x77, 0xf5, 0x13, 0xb5, 0x64, 0xae, 0x47, 0xd5, 0x0b, 0xa9, 0x1b, - 0x07, 0x61, 0x77, 0x16, 0x1e, 0x8b, 0x1e, 0x2b, 0x09, 0x7f, 0xa1, 0x87, 0xfa, 0x89, 0x5a, 0x32, - 0xe9, 0xc2, 0x58, 0xbb, 0xd9, 0xa9, 0x7b, 0xfe, 0xec, 0x04, 0xd7, 0xe1, 0x56, 0xce, 0x3a, 0x6c, - 0x71, 0xe6, 0x15, 0x60, 0xab, 0x5a, 0xfc, 0x8d, 0x52, 0x20, 0x79, 0x16, 0x46, 0xdd, 0x86, 0x13, - 0xc6, 0xb3, 0x93, 0x7c, 0xd2, 0xa8, 0x59, 0xbc, 0xcc, 0x80, 0x28, 0x70, 0xf6, 0x2f, 0x17, 0x60, - 0x6e, 0x70, 0xc7, 0xc4, 0x74, 0x76, 0x3b, 0x61, 0x24, 0x0c, 0x64, 0xc9, 0x9c, 0xce, 0x1c, 0x8c, - 0x09, 0x9e, 0x7c, 0xdc, 0x82, 0xf1, 0xbb, 0x51, 0xe0, 0xfb, 0x34, 0x96, 0xbb, 0xd8, 0xed, 0x9c, - 0xfb, 0x7a, 0x5d, 0x70, 0xd7, 0x3a, 0x48, 0x00, 0x26, 0x72, 0x99, 0xba, 0xf4, 0xc0, 0x6d, 0x76, - 0xaa, 0x89, 0x69, 0x52, 0xa4, 0x97, 0x05, 0x18, 0x13, 0x3c, 0x23, 0xf5, 0x7c, 0x41, 0x3a, 0x92, - 0x26, 0x5d, 0xf5, 0x25, 0xa9, 0xc4, 0xdb, 0xbf, 0x35, 0x0a, 0xe7, 0xfa, 0xce, 0x7e, 0xb2, 0x00, - 0xc0, 0x9d, 0x86, 0x2b, 0x1e, 0x8b, 0xf0, 0x44, 0x58, 0x3b, 0xcd, 0xf6, 0xf8, 0xdb, 0x0a, 0x8a, - 0x06, 0x05, 0xf9, 0x28, 0x40, 0xdb, 0x09, 0x9d, 0x16, 0x8d, 0x69, 0x98, 0x18, 0xaa, 0x1b, 0xc3, - 0x8d, 0x12, 0xd3, 0x63, 0x2b, 0xe1, 0xa9, 0x9d, 0x0c, 0x05, 0x8a, 0xd0, 0x10, 0xc9, 0x82, 0xd8, - 0x90, 0x36, 0xa9, 0x13, 0xd1, 0x0d, 0x6d, 0xbf, 0x55, 0x10, 0x8b, 0x1a, 0x85, 0x26, 0x1d, 0xdb, - 0x48, 0x78, 0x2f, 0x22, 0x39, 0x56, 0x6a, 0x23, 0xe1, 0xfd, 0x8c, 0x50, 0x62, 0xc9, 0xe7, 0x2c, - 0x98, 0xae, 0x79, 0x4d, 0xaa, 0xa5, 0xcb, 0x90, 0x73, 0x73, 0xf8, 0x4e, 0x5e, 0x31, 0xf9, 0x6a, - 0x13, 0x98, 0x02, 0x47, 0x98, 0x11, 0xcf, 0x3e, 0xf3, 0x3e, 0x0d, 0xb9, 0xed, 0x1c, 0x4b, 0x7f, - 0xe6, 0xdb, 0x02, 0x8c, 0x09, 0x9e, 0x2c, 0xc1, 0xa9, 0xb6, 0x13, 0x45, 0xcb, 0x21, 0xad, 0x52, - 0x3f, 0xf6, 0x9c, 0xa6, 0x08, 0x08, 0x4b, 0xda, 0x8b, 0xdd, 0x4a, 0xa3, 0x31, 0x4b, 0x4f, 0xde, - 0x0b, 0x4f, 0x7b, 0x75, 0x3f, 0x08, 0xe9, 0xba, 0x17, 0x45, 0x9e, 0x5f, 0xd7, 0xd3, 0x80, 0x9b, - 0xc2, 0x52, 0x65, 0x5e, 0xb2, 0x7a, 0x7a, 0xb5, 0x3f, 0x19, 0x0e, 0x6a, 0x4f, 0xde, 0x06, 0xa5, - 0x68, 0xcf, 0x6b, 0x2f, 0x87, 0xd5, 0x68, 0xb6, 0xcc, 0x79, 0xa9, 0xcd, 0x70, 0x5b, 0xc2, 0x51, - 0x51, 0xd8, 0x5f, 0x2e, 0xc0, 0xec, 0xa0, 0xf5, 0x43, 0x22, 0xb6, 0x4a, 0xe2, 0xdb, 0x4e, 0x18, - 0xc9, 0x58, 0x60, 0xc8, 0x90, 0x52, 0xf2, 0xbd, 0xed, 0x84, 0xe6, 0x7a, 0xe3, 0x02, 0x30, 0x91, - 0x44, 0xee, 0xc2, 0x48, 0xdc, 0x74, 0x72, 0xca, 0x41, 0x19, 0x12, 0xb5, 0xc7, 0xb6, 0xb6, 0x14, - 0x21, 0x97, 0x41, 0x9e, 0x81, 0x91, 0xa6, 0xb7, 0xcb, 0x3c, 0x5b, 0xb6, 0x20, 0xf9, 0x16, 0xb5, - 0xe6, 0xed, 0x46, 0xc8, 0xa1, 0xf6, 0xbf, 0x8e, 0xf5, 0x31, 0x79, 0x6a, 0x13, 0x21, 0x97, 0x00, - 0x98, 0x07, 0xb3, 0x15, 0xd2, 0x9a, 0x77, 0x20, 0x37, 0x71, 0xb5, 0xac, 0x36, 0x14, 0x06, 0x0d, - 0xaa, 0xa4, 0xcd, 0x76, 0xa7, 0xc6, 0xda, 0x14, 0x7a, 0xdb, 0x08, 0x0c, 0x1a, 0x54, 0xe4, 0x05, - 0x18, 0xf3, 0x5a, 0x4e, 0x9d, 0x26, 0x6a, 0x3e, 0xc3, 0xd6, 0xd3, 0x2a, 0x87, 0x3c, 0x38, 0x9c, - 0x9f, 0x56, 0x0a, 0x71, 0x10, 0x4a, 0x5a, 0xf2, 0x6b, 0x16, 0x4c, 0xba, 0x41, 0xab, 0x15, 0xf8, - 0x6b, 0xce, 0x2e, 0x6d, 0x26, 0x69, 0xa5, 0xbb, 0x8f, 0x6b, 0x8b, 0x5d, 0x58, 0x36, 0x84, 0x89, - 0xa0, 0x4e, 0x25, 0xcb, 0x4c, 0x14, 0xa6, 0xb4, 0x32, 0x97, 0xdd, 0xe8, 0x11, 0xcb, 0xee, 0x77, - 0x2d, 0x98, 0x11, 0x6d, 0x97, 0x7c, 0x3f, 0x88, 0x65, 0xb6, 0x4f, 0xe4, 0x85, 0x82, 0xc7, 0xdc, - 0x2d, 0x43, 0xa2, 0xe8, 0xdb, 0x1b, 0xa4, 0x9a, 0x33, 0x3d, 0x78, 0xec, 0x55, 0x92, 0x5c, 0x85, - 0x99, 0x5a, 0x10, 0xba, 0xd4, 0x1c, 0x08, 0x69, 0x33, 0x14, 0xa3, 0x2b, 0x59, 0x02, 0xec, 0x6d, - 0x43, 0x6e, 0xc3, 0x79, 0x03, 0x68, 0x8e, 0x83, 0x30, 0x1b, 0x17, 0x24, 0xb7, 0xf3, 0x57, 0xfa, - 0x52, 0xe1, 0x80, 0xd6, 0x73, 0xef, 0x81, 0x99, 0x9e, 0xef, 0xd7, 0x27, 0xa2, 0x3e, 0x6b, 0x46, - 0xd4, 0x65, 0x23, 0x10, 0x9e, 0x5b, 0x81, 0xf3, 0xfd, 0x47, 0xea, 0x24, 0x5c, 0xec, 0x5f, 0xb4, - 0xe0, 0xe9, 0x01, 0x9e, 0x8b, 0x0a, 0x25, 0xac, 0x41, 0xa1, 0x04, 0x71, 0xa0, 0x48, 0xfd, 0x7d, - 0x69, 0x38, 0xae, 0x0c, 0x37, 0x23, 0x2e, 0xfb, 0xfb, 0xe2, 0x43, 0x8f, 0xdf, 0x3f, 0x9c, 0x2f, - 0x5e, 0xf6, 0xf7, 0x91, 0xf1, 0xb6, 0x7f, 0x7e, 0x2c, 0x15, 0xad, 0x6c, 0x27, 0x01, 0x32, 0x57, - 0x54, 0xc6, 0x2a, 0x9b, 0x39, 0xcf, 0x45, 0x23, 0x1a, 0x13, 0x69, 0x6f, 0x29, 0x8e, 0x7c, 0xca, - 0xe2, 0x99, 0xe6, 0x24, 0x8a, 0x93, 0xce, 0xd4, 0xe3, 0x49, 0x7c, 0x9b, 0xf9, 0xeb, 0x04, 0x88, - 0xa6, 0x74, 0xb6, 0x92, 0xdb, 0x22, 0xd1, 0x93, 0x75, 0xa9, 0x92, 0x5c, 0x74, 0x82, 0x27, 0x07, - 0x00, 0x51, 0xd7, 0x77, 0xb7, 0x82, 0xa6, 0xe7, 0x76, 0x65, 0x68, 0x9f, 0x43, 0xb6, 0x52, 0xf0, - 0x13, 0x7e, 0x95, 0xfe, 0x8d, 0x86, 0x2c, 0xf2, 0x15, 0x0b, 0x66, 0xc4, 0xc6, 0xb9, 0xe2, 0xd5, - 0x6a, 0x34, 0xa4, 0xbe, 0x4b, 0x13, 0xd7, 0xe3, 0xce, 0x70, 0x1a, 0x24, 0x89, 0xb6, 0xd5, 0x2c, - 0x7b, 0xbd, 0xc4, 0x7b, 0x50, 0xd8, 0xab, 0x0c, 0xa9, 0xc2, 0x88, 0xe7, 0xd7, 0x02, 0x69, 0xd8, - 0x2a, 0xc3, 0x29, 0xb5, 0xea, 0xd7, 0x02, 0xbd, 0x56, 0xd8, 0x2f, 0xe4, 0xdc, 0xc9, 0x1a, 0x9c, - 0x0d, 0x65, 0xf4, 0x77, 0xcd, 0x8b, 0x98, 0x0b, 0xbf, 0xe6, 0xb5, 0xbc, 0x98, 0x1b, 0xa5, 0x62, - 0x65, 0xf6, 0xfe, 0xe1, 0xfc, 0x59, 0xec, 0x83, 0xc7, 0xbe, 0xad, 0xec, 0xd7, 0xcb, 0xe9, 0x10, - 0x57, 0x24, 0x70, 0x3e, 0x0c, 0xe5, 0x50, 0xa5, 0xcc, 0x85, 0x03, 0xb1, 0x96, 0xcf, 0x18, 0xcb, - 0xcc, 0x91, 0xca, 0x3d, 0xe8, 0xe4, 0xb8, 0x96, 0xc8, 0x1c, 0x09, 0xf6, 0xe5, 0xe5, 0xb2, 0xc8, - 0x61, 0x7e, 0x49, 0xa9, 0x3a, 0x49, 0xd6, 0xf5, 0x5d, 0xe4, 0x32, 0x48, 0x08, 0x63, 0x0d, 0xea, - 0x34, 0xe3, 0x86, 0xcc, 0xe1, 0x5c, 0x1f, 0xd6, 0x8d, 0x65, 0xbc, 0xb2, 0xf9, 0x31, 0x01, 0x45, - 0x29, 0x89, 0x1c, 0xc0, 0x78, 0x43, 0x7c, 0x04, 0xb9, 0xb7, 0xaf, 0x0f, 0x3b, 0xb8, 0xa9, 0x2f, - 0xab, 0xd7, 0xaf, 0x04, 0x60, 0x22, 0x8e, 0xfc, 0x9c, 0x05, 0xe0, 0x26, 0x89, 0xb1, 0x64, 0xf9, - 0x60, 0x6e, 0x76, 0x47, 0xe5, 0xdc, 0xb4, 0x6b, 0xa4, 0x40, 0x11, 0x1a, 0x92, 0xc9, 0xab, 0x30, - 0x19, 0x52, 0x37, 0xf0, 0x5d, 0xaf, 0x49, 0xab, 0x4b, 0x31, 0xf7, 0xdc, 0x4f, 0x96, 0x40, 0x3b, - 0xcd, 0xfc, 0x13, 0x34, 0x78, 0x60, 0x8a, 0x23, 0x79, 0xdd, 0x82, 0x69, 0x95, 0x1c, 0x64, 0x1f, - 0x84, 0xca, 0x24, 0xc9, 0x5a, 0x4e, 0xa9, 0x48, 0xce, 0xb3, 0x42, 0x58, 0x84, 0x92, 0x86, 0x61, - 0x46, 0x2e, 0x79, 0x05, 0x20, 0xd8, 0xe5, 0x89, 0x38, 0xd6, 0xd5, 0xd2, 0x89, 0xbb, 0x3a, 0x2d, - 0x72, 0xca, 0x09, 0x07, 0x34, 0xb8, 0x91, 0x1b, 0x00, 0x62, 0xd9, 0xec, 0x74, 0xdb, 0x94, 0x87, - 0x0d, 0xe5, 0xca, 0x5b, 0x93, 0xc1, 0xdf, 0x56, 0x98, 0x07, 0x87, 0xf3, 0xbd, 0x01, 0x2e, 0xcf, - 0x80, 0x1a, 0xcd, 0xc9, 0x87, 0x60, 0x3c, 0xea, 0xb4, 0x5a, 0x8e, 0xca, 0xa7, 0x6c, 0xe5, 0xb7, - 0x23, 0x0a, 0xbe, 0x7a, 0x6e, 0x4a, 0x00, 0x26, 0x12, 0x6d, 0x1f, 0x48, 0x2f, 0x3d, 0x79, 0x01, - 0x26, 0xe9, 0x41, 0x4c, 0x43, 0xdf, 0x69, 0xde, 0xc2, 0xb5, 0x24, 0x02, 0xe7, 0x1f, 0xff, 0xb2, - 0x01, 0xc7, 0x14, 0x15, 0xb1, 0x95, 0xe7, 0x5d, 0xe0, 0xf4, 0xa0, 0x3d, 0xef, 0xc4, 0xcf, 0xb6, - 0xff, 0xb3, 0x90, 0xf2, 0x08, 0x76, 0x42, 0x4a, 0x49, 0x00, 0xa3, 0x7e, 0x50, 0x55, 0x46, 0xef, - 0x7a, 0x3e, 0x46, 0x6f, 0x23, 0xa8, 0x1a, 0x67, 0xb9, 0xec, 0x57, 0x84, 0x42, 0x0e, 0x3f, 0xec, - 0x4a, 0x4e, 0x05, 0x39, 0x42, 0x3a, 0x41, 0x79, 0x4a, 0x56, 0x87, 0x5d, 0x9b, 0xa6, 0x20, 0x4c, - 0xcb, 0x25, 0x7b, 0x30, 0xda, 0x08, 0xa2, 0x58, 0xc4, 0x2a, 0x43, 0x7b, 0x61, 0xd7, 0x82, 0x28, - 0xe6, 0x5b, 0x98, 0xea, 0x36, 0x83, 0x44, 0x28, 0x64, 0xd8, 0xdf, 0xb3, 0x52, 0xf9, 0x96, 0x3b, - 0x4e, 0xec, 0x36, 0x2e, 0xef, 0x53, 0x9f, 0xcd, 0x67, 0x33, 0x59, 0xff, 0xa3, 0x66, 0xb2, 0xfe, - 0xc1, 0xe1, 0xfc, 0x5b, 0x06, 0x15, 0xd7, 0xdc, 0x63, 0x1c, 0x16, 0x38, 0x0b, 0x23, 0xaf, 0xff, - 0x31, 0x0b, 0x26, 0x0c, 0xf5, 0xe4, 0x86, 0x92, 0x63, 0xde, 0x58, 0x39, 0x57, 0x06, 0x10, 0x4d, - 0x91, 0xf6, 0x17, 0x2c, 0x18, 0xaf, 0x38, 0xee, 0x5e, 0x50, 0xab, 0xb1, 0x00, 0xbf, 0xda, 0x91, - 0xc7, 0x22, 0xa2, 0x7f, 0x2a, 0xc0, 0x5f, 0x91, 0x70, 0x54, 0x14, 0x6c, 0x0e, 0xd7, 0x1c, 0x37, - 0x0e, 0x42, 0xae, 0x76, 0x51, 0xcc, 0xe1, 0x2b, 0x1c, 0x82, 0x12, 0x43, 0xde, 0x01, 0x13, 0x2d, - 0xe7, 0x20, 0x69, 0x9c, 0x4d, 0xf6, 0xac, 0x6b, 0x14, 0x9a, 0x74, 0xf6, 0x1f, 0x96, 0x61, 0x5c, - 0x9e, 0x3f, 0x1e, 0xfb, 0x04, 0x21, 0xf1, 0xe2, 0x0b, 0x03, 0xbd, 0xf8, 0x08, 0xc6, 0x5c, 0x5e, - 0xba, 0x24, 0xb7, 0xd2, 0x21, 0xd3, 0x5e, 0x52, 0x41, 0x51, 0x0d, 0xa5, 0xd5, 0x12, 0xbf, 0x51, - 0x8a, 0x22, 0x9f, 0xb7, 0xe0, 0x94, 0x1b, 0xf8, 0x3e, 0x75, 0xb5, 0x9d, 0x1f, 0xc9, 0xe3, 0x84, - 0x6d, 0x39, 0xcd, 0x54, 0xa7, 0x88, 0x32, 0x08, 0xcc, 0x8a, 0x27, 0x2f, 0xc1, 0x94, 0x18, 0xb3, - 0xdb, 0xa9, 0xf8, 0x58, 0x9f, 0x39, 0x9b, 0x48, 0x4c, 0xd3, 0x92, 0x05, 0x91, 0x67, 0xe0, 0x87, - 0x30, 0x22, 0x46, 0x96, 0xf9, 0x46, 0x75, 0x4a, 0x13, 0xa1, 0x41, 0x41, 0x42, 0x20, 0x21, 0xad, - 0x85, 0x34, 0x6a, 0x20, 0x7d, 0xad, 0x43, 0xa3, 0x98, 0xef, 0x31, 0xe3, 0x8f, 0x76, 0x1e, 0x85, - 0x3d, 0x9c, 0xb0, 0x0f, 0x77, 0xb2, 0x27, 0x1d, 0xdd, 0x52, 0x1e, 0xcb, 0x49, 0x7e, 0xe6, 0x81, - 0xfe, 0xee, 0x3c, 0x8c, 0x46, 0x0d, 0x27, 0xac, 0xf2, 0xbd, 0xad, 0x58, 0x29, 0x33, 0x5b, 0xb2, - 0xcd, 0x00, 0x28, 0xe0, 0x64, 0x05, 0x4e, 0x67, 0x4e, 0xcc, 0x23, 0xbe, 0x7b, 0x95, 0x2a, 0xb3, - 0x92, 0xdd, 0xe9, 0xcc, 0x59, 0x7b, 0x84, 0x3d, 0x2d, 0xcc, 0x20, 0x68, 0xe2, 0x88, 0x20, 0xa8, - 0x0b, 0x63, 0x4d, 0x91, 0x08, 0x98, 0xe4, 0xa6, 0xf2, 0x66, 0x2e, 0x03, 0xb0, 0x60, 0x26, 0x60, - 0xd4, 0x6c, 0x97, 0x09, 0x05, 0x29, 0x90, 0x7c, 0x86, 0x19, 0x34, 0x23, 0x77, 0x30, 0xc5, 0x15, - 0xb8, 0x9d, 0x8f, 0x02, 0x3d, 0xa9, 0x12, 0x6d, 0xdd, 0x8c, 0x44, 0x84, 0x29, 0x7f, 0xee, 0xc7, - 0x60, 0xe2, 0x51, 0xf3, 0x0e, 0x2f, 0xc3, 0xe9, 0xa1, 0x32, 0x0e, 0xdf, 0xb7, 0x20, 0xf9, 0xae, - 0xcb, 0x8e, 0xdb, 0xa0, 0x6c, 0xca, 0x90, 0x97, 0x61, 0x5a, 0x85, 0x11, 0xcb, 0x41, 0xc7, 0x8f, - 0x39, 0xaf, 0xa2, 0xce, 0x25, 0x63, 0x0a, 0x8b, 0x19, 0x6a, 0xb2, 0x08, 0x65, 0x36, 0x4e, 0xa2, - 0xa9, 0x30, 0xbb, 0x2a, 0x54, 0x59, 0xda, 0x5a, 0x95, 0xad, 0x34, 0x0d, 0x09, 0x60, 0xa6, 0xe9, - 0x44, 0x31, 0xd7, 0x80, 0x45, 0x15, 0x8f, 0x78, 0x1a, 0xcc, 0x0b, 0x86, 0xd6, 0xb2, 0x8c, 0xb0, - 0x97, 0xb7, 0xfd, 0xcd, 0x11, 0x98, 0x4a, 0x59, 0x46, 0xb6, 0xab, 0x74, 0x22, 0xe6, 0xfa, 0xa8, - 0x14, 0x8b, 0xda, 0x55, 0x6e, 0x49, 0x38, 0x2a, 0x0a, 0x46, 0xdd, 0x76, 0xa2, 0xe8, 0x5e, 0x10, - 0x56, 0xa5, 0x29, 0x57, 0xd4, 0x5b, 0x12, 0x8e, 0x8a, 0x82, 0xed, 0x2f, 0xbb, 0xd4, 0x09, 0x69, - 0xc8, 0x0b, 0x28, 0xb2, 0xfb, 0x4b, 0x45, 0xa3, 0xd0, 0xa4, 0xe3, 0x46, 0x39, 0x6e, 0x46, 0xcb, - 0x4d, 0x8f, 0xfa, 0xb1, 0x50, 0x33, 0x1f, 0xa3, 0xbc, 0xb3, 0xb6, 0x6d, 0x32, 0xd5, 0x46, 0x39, - 0x83, 0xc0, 0xac, 0x78, 0xf2, 0x09, 0x0b, 0xa6, 0x9c, 0x7b, 0x91, 0xae, 0xaf, 0xe5, 0x56, 0x79, - 0xe8, 0x4d, 0x2a, 0x55, 0xb2, 0x5b, 0x99, 0x61, 0xe6, 0x3d, 0x05, 0xc2, 0xb4, 0x50, 0xf2, 0x25, - 0x0b, 0x08, 0x3d, 0xa0, 0xee, 0x56, 0x18, 0xec, 0x7b, 0xd5, 0xe4, 0x1b, 0xca, 0xf0, 0x67, 0x48, - 0x6f, 0xfb, 0x72, 0x0f, 0x5f, 0x61, 0xd5, 0x7b, 0xe1, 0xd8, 0x47, 0x07, 0xfb, 0xaf, 0x8a, 0x30, - 0x61, 0x18, 0xe3, 0xbe, 0x3b, 0xab, 0xf5, 0x03, 0xb6, 0xb3, 0x16, 0x4e, 0xb0, 0xb3, 0x7e, 0x14, - 0xca, 0x6e, 0x62, 0x28, 0xf2, 0xa9, 0x07, 0xce, 0x9a, 0x1f, 0x6d, 0x2b, 0x14, 0x08, 0xb5, 0x4c, - 0x72, 0x15, 0x66, 0x0c, 0x36, 0xd2, 0xc8, 0x8c, 0x70, 0x23, 0xa3, 0x12, 0x4d, 0x4b, 0x59, 0x02, - 0xec, 0x6d, 0x43, 0x9e, 0x67, 0x5e, 0xad, 0x27, 0xfb, 0x25, 0xa2, 0x78, 0x59, 0x6b, 0xbb, 0xb4, - 0xb5, 0x9a, 0x80, 0xd1, 0xa4, 0xb1, 0xbf, 0x69, 0xa9, 0x8f, 0xfb, 0x04, 0x0a, 0x35, 0xee, 0xa6, - 0x0b, 0x35, 0x2e, 0xe7, 0x32, 0xcc, 0x03, 0x8a, 0x34, 0x36, 0x60, 0x7c, 0x39, 0x68, 0xb5, 0x1c, - 0xbf, 0x4a, 0xde, 0x04, 0xe3, 0xae, 0xf8, 0x53, 0x86, 0x89, 0x13, 0x6c, 0xff, 0x96, 0x58, 0x4c, - 0x70, 0xe4, 0x19, 0x18, 0x71, 0xc2, 0x7a, 0x12, 0x1a, 0xf2, 0xb3, 0xa3, 0xa5, 0xb0, 0x1e, 0x21, - 0x87, 0xda, 0x5f, 0x2c, 0x00, 0x2c, 0x07, 0xad, 0xb6, 0x13, 0xd2, 0xea, 0x4e, 0xf0, 0xbf, 0x39, - 0x62, 0x11, 0x31, 0x7c, 0xda, 0x02, 0xc2, 0x46, 0x25, 0xf0, 0xa9, 0x1f, 0xab, 0xc3, 0x57, 0xb6, - 0x5f, 0xba, 0x09, 0x54, 0x6e, 0x3e, 0x7a, 0x0d, 0x24, 0x08, 0xd4, 0x34, 0xc7, 0x88, 0x22, 0x9e, - 0x4d, 0x76, 0xfc, 0x62, 0xba, 0xa6, 0x81, 0x1f, 0x94, 0x4a, 0x07, 0xc0, 0xfe, 0x5a, 0x01, 0xce, - 0x0b, 0xb3, 0xb5, 0xee, 0xf8, 0x4e, 0x9d, 0xb6, 0x98, 0x56, 0xc7, 0x3d, 0x6d, 0x70, 0x99, 0xfb, - 0xea, 0x25, 0x25, 0x0c, 0xc3, 0x4e, 0x4e, 0x31, 0xa9, 0xc4, 0x34, 0x5a, 0xf5, 0xbd, 0x18, 0x39, - 0x73, 0x12, 0x41, 0x29, 0xb9, 0xe1, 0x21, 0x8d, 0x4d, 0x4e, 0x82, 0xd4, 0xba, 0xbb, 0x2a, 0xd9, - 0xa3, 0x12, 0xc4, 0x36, 0xf7, 0x66, 0xe0, 0xee, 0x21, 0x6d, 0x07, 0xdc, 0xb0, 0x18, 0x27, 0xc8, - 0x6b, 0x12, 0x8e, 0x8a, 0xc2, 0xfe, 0x9a, 0x05, 0x59, 0x93, 0xcb, 0xa3, 0x41, 0x51, 0x33, 0x98, - 0x8d, 0x06, 0xd3, 0x25, 0x7e, 0x27, 0xa8, 0x98, 0x7b, 0x3f, 0x4c, 0x38, 0x71, 0x4c, 0x5b, 0x6d, - 0x11, 0x9a, 0x14, 0x1f, 0x2d, 0xfd, 0xb5, 0x1e, 0x54, 0xbd, 0x9a, 0xc7, 0x43, 0x12, 0x93, 0x9d, - 0x7d, 0x13, 0x4a, 0xc9, 0x89, 0xcf, 0x31, 0x3e, 0xfd, 0xb3, 0x29, 0x77, 0x72, 0xc0, 0xe4, 0x7a, - 0x50, 0x80, 0x3e, 0x7b, 0x26, 0xeb, 0xb2, 0xb6, 0x2e, 0xa9, 0x2e, 0x9f, 0xcc, 0xc2, 0x90, 0x03, - 0x71, 0xda, 0x25, 0xf2, 0x2c, 0xef, 0xcd, 0x7b, 0xcf, 0xd7, 0x07, 0x60, 0x13, 0x52, 0x3f, 0x75, - 0x08, 0x46, 0x2e, 0x01, 0xe8, 0x4d, 0x41, 0x16, 0x7a, 0xa8, 0x4c, 0xad, 0xde, 0x3b, 0xd0, 0xa0, - 0x62, 0x2e, 0xa0, 0xe7, 0x47, 0xb1, 0xd3, 0x6c, 0x5e, 0xf3, 0xfc, 0x58, 0xc6, 0xb2, 0xca, 0x60, - 0xac, 0x6a, 0x14, 0x9a, 0x74, 0x73, 0xef, 0x34, 0xbe, 0xcb, 0x49, 0xdc, 0xfa, 0x4f, 0x17, 0x60, - 0xfa, 0xaa, 0xdf, 0xd9, 0xba, 0xba, 0xd5, 0xd9, 0x6d, 0x7a, 0xee, 0x0d, 0xda, 0x65, 0x1f, 0x6d, - 0x8f, 0x76, 0x57, 0x57, 0xe4, 0xb0, 0xab, 0x8f, 0x76, 0x83, 0x01, 0x51, 0xe0, 0x98, 0x9a, 0x35, - 0xcf, 0xaf, 0xd3, 0xb0, 0x1d, 0x7a, 0xd2, 0x77, 0x37, 0xd4, 0xbc, 0xa2, 0x51, 0x68, 0xd2, 0x31, - 0xde, 0xc1, 0x3d, 0x9f, 0x86, 0x59, 0x6b, 0xb3, 0xc9, 0x80, 0x28, 0x70, 0x8c, 0x28, 0x0e, 0x3b, - 0x51, 0x2c, 0x47, 0x4c, 0x11, 0xed, 0x30, 0x20, 0x0a, 0x1c, 0x9b, 0x1e, 0x51, 0x67, 0x97, 0x67, - 0x61, 0x33, 0xe7, 0xe1, 0xdb, 0x02, 0x8c, 0x09, 0x9e, 0x91, 0xee, 0xd1, 0xee, 0x0a, 0xdb, 0x7b, - 0x33, 0x15, 0x2b, 0x37, 0x04, 0x18, 0x13, 0xbc, 0xfd, 0xf7, 0x16, 0x90, 0xf4, 0x70, 0x3c, 0x81, - 0xed, 0xfb, 0xb5, 0xf4, 0xf6, 0x3d, 0x64, 0xc2, 0x3c, 0xad, 0xfe, 0x80, 0x5d, 0xfc, 0x57, 0x2c, - 0x98, 0x34, 0xcf, 0x4e, 0x48, 0x3d, 0x63, 0x88, 0x36, 0xd3, 0x86, 0xe8, 0xc1, 0xe1, 0xfc, 0x8f, - 0xf7, 0xbb, 0xae, 0x58, 0xf7, 0xe2, 0xa0, 0x1d, 0xbd, 0x9d, 0xfa, 0x75, 0xcf, 0xa7, 0x3c, 0x33, - 0x28, 0xce, 0x5c, 0x52, 0x07, 0x33, 0xcb, 0x41, 0x95, 0x3e, 0x82, 0x25, 0xb3, 0xef, 0xc0, 0x4c, - 0x4f, 0x99, 0xd2, 0x31, 0x8c, 0xce, 0x91, 0x55, 0xa0, 0x36, 0xc2, 0x04, 0x63, 0xbc, 0xd9, 0x16, - 0x87, 0x23, 0xcb, 0x30, 0x23, 0xaa, 0xad, 0x98, 0xa4, 0x6d, 0xb7, 0x41, 0x5b, 0xaa, 0xf4, 0x8c, - 0x07, 0x8a, 0xb7, 0xb3, 0x48, 0xec, 0xa5, 0xb7, 0x3f, 0x63, 0xc1, 0x54, 0xaa, 0x72, 0x2c, 0x27, - 0xf3, 0xc8, 0x57, 0x5a, 0xc0, 0x8f, 0xf2, 0x42, 0xcf, 0x17, 0xb9, 0xbe, 0x92, 0xb1, 0xd2, 0x34, - 0x0a, 0x4d, 0x3a, 0xfb, 0x0b, 0x05, 0x28, 0x25, 0x59, 0xe1, 0x63, 0xa8, 0xf2, 0x29, 0x0b, 0xa6, - 0x54, 0x70, 0xce, 0x5d, 0x76, 0x31, 0x19, 0x37, 0x86, 0xcf, 0x4b, 0xab, 0xf3, 0x5e, 0xe6, 0xb2, - 0xab, 0xd8, 0x01, 0x4d, 0x61, 0x98, 0x96, 0x4d, 0x6e, 0x03, 0x44, 0xdd, 0x28, 0xa6, 0x2d, 0x23, - 0x78, 0xb0, 0x8d, 0x15, 0xb7, 0xe0, 0x06, 0x21, 0x65, 0xeb, 0x6b, 0x23, 0xa8, 0xd2, 0x6d, 0x45, - 0xa9, 0x8d, 0xab, 0x86, 0xa1, 0xc1, 0xc9, 0xfe, 0xcd, 0x02, 0x9c, 0xce, 0xaa, 0x44, 0xde, 0x07, - 0x93, 0x89, 0x74, 0xe3, 0xe6, 0x67, 0x92, 0x0a, 0x9f, 0x44, 0x03, 0xf7, 0xe0, 0x70, 0x7e, 0xbe, - 0xf7, 0xea, 0xeb, 0x82, 0x49, 0x82, 0x29, 0x66, 0x22, 0x43, 0x22, 0x53, 0x79, 0x95, 0xee, 0x52, - 0xbb, 0x2d, 0xd3, 0x1c, 0x46, 0x86, 0xc4, 0xc4, 0x62, 0x86, 0x9a, 0x6c, 0xc1, 0x59, 0x03, 0xb2, - 0x41, 0xbd, 0x7a, 0x63, 0x37, 0x08, 0xc5, 0x15, 0x83, 0x62, 0xe5, 0x19, 0xc9, 0xe5, 0x2c, 0xf6, - 0xa1, 0xc1, 0xbe, 0x2d, 0x99, 0xd3, 0xe2, 0x3a, 0x6d, 0xc7, 0xf5, 0xe2, 0xae, 0x8c, 0x86, 0x94, - 0x6d, 0x5a, 0x96, 0x70, 0x54, 0x14, 0xf6, 0x3a, 0x8c, 0x1c, 0x73, 0x06, 0x1d, 0x6b, 0xaf, 0xbf, - 0x09, 0x25, 0xc6, 0x8e, 0xd9, 0xa2, 0xbc, 0x58, 0x06, 0x50, 0x4a, 0x6e, 0x9c, 0x10, 0x1b, 0x8a, - 0x9e, 0x93, 0x24, 0xa1, 0x54, 0xb7, 0x56, 0xa3, 0xa8, 0xc3, 0x3d, 0x19, 0x86, 0x24, 0xcf, 0x42, - 0x91, 0x1e, 0xb4, 0xb3, 0xd9, 0xa6, 0xcb, 0x07, 0x6d, 0x2f, 0xa4, 0x11, 0x23, 0xa2, 0x07, 0x6d, - 0x32, 0x07, 0x05, 0xaf, 0x2a, 0x37, 0x29, 0x90, 0x34, 0x85, 0xd5, 0x15, 0x2c, 0x78, 0x55, 0xfb, - 0x00, 0xca, 0xea, 0x8a, 0x0b, 0xd9, 0x4b, 0x6c, 0xb7, 0x95, 0xc7, 0x31, 0x4e, 0xc2, 0x77, 0x80, - 0xd5, 0xee, 0x00, 0xe8, 0x3a, 0xbd, 0xbc, 0xec, 0xcb, 0x45, 0x18, 0x71, 0x03, 0x59, 0xde, 0x5b, - 0xd2, 0x6c, 0xb8, 0xd1, 0xe6, 0x18, 0xfb, 0x0e, 0x4c, 0xdf, 0xf0, 0x83, 0x7b, 0x3e, 0xdb, 0x4c, - 0xaf, 0x78, 0xb4, 0x59, 0x65, 0x8c, 0x6b, 0xec, 0x8f, 0xac, 0x8b, 0xc0, 0xb1, 0x28, 0x70, 0xea, - 0x1e, 0x48, 0x61, 0xd0, 0x3d, 0x10, 0xfb, 0x63, 0x16, 0x9c, 0x56, 0x05, 0x64, 0x89, 0x35, 0x7e, - 0x11, 0x26, 0x77, 0x3b, 0x5e, 0xb3, 0x2a, 0x7f, 0x4b, 0x11, 0xaa, 0x44, 0xae, 0x62, 0xe0, 0x30, - 0x45, 0xc9, 0xdc, 0xad, 0x5d, 0xcf, 0x77, 0xc2, 0xee, 0x96, 0x36, 0xff, 0xca, 0x22, 0x54, 0x14, - 0x06, 0x0d, 0x2a, 0xfb, 0xcf, 0x8b, 0xa0, 0xaf, 0xb7, 0x10, 0x4f, 0x56, 0x42, 0x58, 0x79, 0xe4, - 0xaa, 0xb6, 0xbb, 0xbe, 0xab, 0x2f, 0xd2, 0x94, 0x32, 0x85, 0x10, 0x9f, 0xb4, 0x98, 0xa3, 0xe7, - 0xc5, 0x9e, 0xc3, 0xd7, 0xa7, 0x8c, 0x8e, 0xb6, 0x72, 0x3a, 0x2c, 0x5f, 0x15, 0x9c, 0x83, 0xd0, - 0x74, 0x1d, 0x95, 0x30, 0x34, 0x25, 0x93, 0x57, 0xe5, 0xf1, 0x42, 0x31, 0xb7, 0x3a, 0x9a, 0x52, - 0xe6, 0x4c, 0xa1, 0x0d, 0xa3, 0x21, 0x8d, 0xc3, 0xa4, 0x82, 0xe9, 0xc6, 0xb0, 0x87, 0xad, 0x71, - 0xd8, 0xdd, 0x8e, 0x59, 0x04, 0x56, 0x37, 0xfc, 0x1b, 0x0e, 0x46, 0x21, 0xc8, 0x8e, 0x80, 0xf4, - 0x8e, 0xc5, 0x09, 0x53, 0xb7, 0x8b, 0x50, 0x76, 0x3a, 0x71, 0xd0, 0x62, 0xc3, 0xc4, 0x3f, 0x4f, - 0xc9, 0x48, 0x4e, 0x27, 0x08, 0xd4, 0x34, 0xf6, 0xe7, 0x46, 0x21, 0x53, 0x9a, 0x40, 0x0e, 0xcc, - 0xab, 0x59, 0x56, 0xbe, 0x57, 0xb3, 0x94, 0x32, 0xfd, 0xae, 0x67, 0x91, 0x3a, 0x8c, 0xb6, 0x1b, - 0x4e, 0x94, 0x2c, 0xbf, 0x9b, 0xc9, 0x30, 0x6d, 0x31, 0xe0, 0x83, 0xc3, 0xf9, 0x9f, 0x38, 0x9e, - 0x3b, 0xc7, 0xe6, 0xea, 0xa2, 0xa8, 0xd3, 0xd4, 0xa2, 0x39, 0x0f, 0x14, 0xfc, 0x4d, 0x87, 0xae, - 0x78, 0x44, 0x68, 0xfa, 0x71, 0x4b, 0xd4, 0xb3, 0x21, 0x8d, 0x3a, 0xcd, 0x58, 0xce, 0x86, 0x9b, - 0x39, 0xae, 0x32, 0xc1, 0x58, 0x17, 0xb6, 0x89, 0xdf, 0x68, 0x08, 0x25, 0xef, 0x83, 0x72, 0x14, - 0x3b, 0x61, 0xfc, 0x88, 0x65, 0x30, 0x6a, 0xd0, 0xb7, 0x13, 0x26, 0xa8, 0xf9, 0x91, 0x57, 0x00, - 0x6a, 0x9e, 0xef, 0x45, 0x8d, 0x47, 0x3c, 0x15, 0xe4, 0x8a, 0x5f, 0x51, 0x1c, 0xd0, 0xe0, 0xc6, - 0xac, 0x1b, 0x9f, 0xdb, 0x22, 0x8f, 0x59, 0xe2, 0xdb, 0x97, 0xb2, 0x6e, 0xa8, 0x30, 0x68, 0x50, - 0xd9, 0x1f, 0x81, 0x33, 0xd9, 0x6b, 0xd1, 0x32, 0xc2, 0xab, 0x87, 0x41, 0xa7, 0x9d, 0x35, 0xdf, - 0xfc, 0xda, 0x2c, 0x0a, 0x1c, 0x33, 0xdf, 0x7b, 0x9e, 0x5f, 0xcd, 0x9a, 0xef, 0x1b, 0x9e, 0x5f, - 0x45, 0x8e, 0x39, 0xc6, 0x9d, 0xb5, 0xdf, 0xb7, 0xe0, 0xe2, 0x51, 0xb7, 0xb7, 0x59, 0xf4, 0x7e, - 0xcf, 0x09, 0x7d, 0x79, 0x1d, 0x86, 0xdb, 0x8e, 0x3b, 0x4e, 0xe8, 0x23, 0x87, 0x92, 0x2e, 0x8c, - 0x89, 0xd2, 0x3f, 0xe9, 0x90, 0xde, 0xcc, 0xf7, 0x2e, 0x39, 0x0b, 0x91, 0x54, 0xd2, 0x45, 0x94, - 0x1d, 0xa2, 0x14, 0x68, 0x7f, 0xc7, 0x02, 0xb2, 0xb9, 0x4f, 0xc3, 0xd0, 0xab, 0x1a, 0xc5, 0x8a, - 0xe4, 0x05, 0x98, 0xbc, 0xbb, 0xbd, 0xb9, 0xb1, 0x15, 0x78, 0x3e, 0xbf, 0x8f, 0x61, 0x94, 0xc8, - 0x5c, 0x37, 0xe0, 0x98, 0xa2, 0x62, 0x41, 0xc6, 0xdd, 0xd7, 0xd8, 0x96, 0x73, 0xf9, 0xa0, 0x1d, - 0xd2, 0x28, 0x52, 0x2f, 0x30, 0xc8, 0x20, 0xe3, 0xfa, 0xcd, 0x0c, 0x12, 0x7b, 0xe9, 0xc9, 0x26, - 0x9c, 0x6b, 0xf1, 0x04, 0x5c, 0x95, 0xef, 0xb4, 0x91, 0xc8, 0xc6, 0x85, 0x49, 0xc1, 0xfb, 0x1b, - 0xee, 0x1f, 0xce, 0x9f, 0x5b, 0xef, 0x47, 0x80, 0xfd, 0xdb, 0xd9, 0x5f, 0x2d, 0xc0, 0x84, 0xf1, - 0x02, 0xc2, 0x31, 0x7c, 0x8a, 0xcc, 0xa3, 0x0d, 0x85, 0x63, 0x3e, 0xda, 0xf0, 0x1c, 0x94, 0xda, - 0x41, 0xd3, 0x73, 0x3d, 0x55, 0x9d, 0x3f, 0xc9, 0xcf, 0xc0, 0x24, 0x0c, 0x15, 0x96, 0xdc, 0x83, - 0xb2, 0xba, 0xca, 0x2c, 0xeb, 0xf5, 0xf2, 0xf2, 0xaa, 0xd4, 0xe2, 0xd5, 0x57, 0x94, 0xb5, 0x2c, - 0x62, 0xc3, 0x18, 0x9f, 0xf9, 0x49, 0x86, 0x9f, 0x17, 0x80, 0xf0, 0x25, 0x11, 0xa1, 0xc4, 0xd8, - 0xff, 0x34, 0x0a, 0x65, 0xa4, 0xed, 0x60, 0x39, 0xa4, 0xd5, 0x88, 0xbc, 0x11, 0x8a, 0x9d, 0xb0, - 0x29, 0x07, 0x4b, 0xa5, 0x7f, 0x6e, 0xe1, 0x1a, 0x32, 0x78, 0x6a, 0xbb, 0x29, 0x9c, 0xe8, 0xa4, - 0xb0, 0x78, 0xe4, 0x49, 0xe1, 0x4b, 0x30, 0x15, 0x45, 0x8d, 0xad, 0xd0, 0xdb, 0x77, 0x62, 0x36, - 0x89, 0x65, 0xae, 0x44, 0x1f, 0xcd, 0x6c, 0x5f, 0xd3, 0x48, 0x4c, 0xd3, 0x92, 0xab, 0x30, 0xa3, - 0xcf, 0xeb, 0x68, 0x18, 0xf3, 0xd4, 0x88, 0xc8, 0xa2, 0xa8, 0x93, 0x11, 0x7d, 0xc2, 0x27, 0x09, - 0xb0, 0xb7, 0x0d, 0x59, 0x81, 0xd3, 0x29, 0x20, 0x53, 0x44, 0xa4, 0x58, 0x54, 0x2d, 0x40, 0x8a, - 0x0f, 0xd3, 0xa5, 0xa7, 0x05, 0x59, 0x87, 0x33, 0xe2, 0xfb, 0xf2, 0x2b, 0xf0, 0xaa, 0x47, 0xe3, - 0x9c, 0xd1, 0xff, 0x91, 0x8c, 0xce, 0x5c, 0xed, 0x25, 0xc1, 0x7e, 0xed, 0xd8, 0x0c, 0x55, 0xe0, - 0xd5, 0x15, 0x69, 0x29, 0xd5, 0x0c, 0x55, 0x6c, 0x56, 0xab, 0x68, 0xd2, 0x91, 0xf7, 0xc2, 0xd3, - 0xfa, 0xa7, 0xc8, 0xac, 0x09, 0xf7, 0x61, 0x45, 0x96, 0x42, 0xa8, 0x9b, 0x46, 0x57, 0xfb, 0x92, - 0x55, 0x71, 0x50, 0x7b, 0xb2, 0x0b, 0x73, 0x0a, 0x75, 0x99, 0x99, 0x83, 0x76, 0xe8, 0x45, 0xb4, - 0xe2, 0x44, 0xf4, 0x56, 0xd8, 0xe4, 0xc5, 0x13, 0x65, 0xfd, 0x8c, 0xc3, 0x55, 0x2f, 0xbe, 0xd6, - 0x8f, 0x12, 0xd7, 0xf0, 0x21, 0x5c, 0x98, 0xb7, 0x42, 0x7d, 0x67, 0xb7, 0x49, 0x37, 0x97, 0x57, - 0x79, 0x49, 0x85, 0xe1, 0xad, 0x5c, 0x4e, 0x10, 0xa8, 0x69, 0x94, 0x7b, 0x3e, 0x39, 0xd0, 0x3d, - 0xff, 0xb6, 0x05, 0x53, 0x6a, 0xb2, 0x3f, 0x81, 0x3c, 0x58, 0x33, 0x9d, 0x07, 0xbb, 0x3a, 0xac, - 0x9b, 0x28, 0x35, 0x1f, 0x10, 0x4c, 0x7d, 0xaf, 0x0c, 0xc0, 0x1f, 0xc6, 0xf1, 0x78, 0xa9, 0xee, - 0x45, 0x18, 0x09, 0x69, 0x3b, 0xc8, 0x5a, 0x3e, 0x9e, 0xc3, 0xe7, 0x98, 0x1f, 0xdc, 0xe5, 0xdc, - 0xef, 0xe4, 0x78, 0xf4, 0x7f, 0xf6, 0xe4, 0x78, 0x1b, 0xce, 0x79, 0x7e, 0x44, 0xdd, 0x4e, 0x28, - 0x77, 0xce, 0x6b, 0x41, 0xa4, 0xac, 0x43, 0xa9, 0xf2, 0x46, 0xc9, 0xe8, 0xdc, 0x6a, 0x3f, 0x22, - 0xec, 0xdf, 0x96, 0x0d, 0x69, 0x82, 0x90, 0x77, 0x82, 0x74, 0x88, 0x2f, 0xe1, 0xa8, 0x28, 0xf4, - 0x82, 0x58, 0xab, 0x25, 0x97, 0x7e, 0x32, 0x0b, 0x62, 0xed, 0xca, 0x36, 0x6a, 0x9a, 0xfe, 0x56, - 0xb1, 0x9c, 0x93, 0x55, 0x84, 0x13, 0x5b, 0xc5, 0x64, 0x7d, 0x4e, 0x0c, 0x7c, 0x46, 0x21, 0xd9, - 0xac, 0x27, 0x07, 0x6e, 0xd6, 0x2f, 0xc3, 0xb4, 0xe7, 0x37, 0x68, 0xe8, 0xc5, 0xb4, 0xca, 0xd7, - 0xc2, 0xec, 0x14, 0x1f, 0x08, 0x95, 0x7d, 0x5a, 0x4d, 0x61, 0x31, 0x43, 0x9d, 0x36, 0x2a, 0xd3, - 0xc7, 0x30, 0x2a, 0x03, 0x4c, 0xf9, 0xa9, 0x7c, 0x4c, 0xf9, 0xe9, 0xe1, 0x4d, 0xf9, 0xcc, 0x63, - 0x35, 0xe5, 0x24, 0x17, 0x53, 0xfe, 0x2c, 0x8c, 0xb6, 0xc3, 0xe0, 0xa0, 0x3b, 0x7b, 0x26, 0xed, - 0x9e, 0x6f, 0x31, 0x20, 0x0a, 0x9c, 0x59, 0x40, 0x77, 0xf6, 0xe1, 0x05, 0x74, 0xf6, 0xeb, 0x05, - 0x38, 0xa7, 0x2d, 0x1d, 0x9b, 0x5f, 0x5e, 0x8d, 0xad, 0x75, 0x7e, 0x33, 0x53, 0x14, 0x6d, 0x18, - 0x89, 0x4f, 0x9d, 0x43, 0x55, 0x18, 0x34, 0xa8, 0x78, 0xfe, 0x90, 0x86, 0xbc, 0xec, 0x37, 0x6b, - 0x06, 0x97, 0x25, 0x1c, 0x15, 0x05, 0x7f, 0x55, 0x8f, 0x86, 0xb1, 0x3c, 0x93, 0xc9, 0x56, 0x34, - 0x2d, 0x6b, 0x14, 0x9a, 0x74, 0xcc, 0x5d, 0x74, 0x93, 0x25, 0xc8, 0x4c, 0xe1, 0xa4, 0x70, 0x17, - 0xd5, 0xaa, 0x53, 0xd8, 0x44, 0x1d, 0x9e, 0x28, 0x1e, 0xed, 0x55, 0x87, 0x67, 0x21, 0x14, 0x85, - 0xfd, 0x1f, 0x16, 0xbc, 0xa1, 0xef, 0x50, 0x3c, 0x81, 0xed, 0xed, 0x20, 0xbd, 0xbd, 0x6d, 0x0f, - 0xbf, 0xbd, 0xf5, 0xf4, 0x62, 0xc0, 0x56, 0xf7, 0x97, 0x16, 0x4c, 0x6b, 0xfa, 0x27, 0xd0, 0x55, - 0x2f, 0xd7, 0xf7, 0xf1, 0xb4, 0xea, 0xa2, 0x1c, 0x35, 0xd5, 0xb7, 0x6f, 0xf3, 0xbe, 0x89, 0x60, - 0x6e, 0xc9, 0x4d, 0x1e, 0xa0, 0x39, 0x22, 0x88, 0xe9, 0xc2, 0x18, 0xbf, 0xc2, 0x1f, 0xe5, 0x13, - 0x54, 0xa6, 0xe5, 0xf3, 0x13, 0x20, 0x1d, 0x54, 0xf2, 0x9f, 0x11, 0x4a, 0x81, 0xbc, 0x28, 0xdd, - 0x8b, 0x98, 0xbd, 0xac, 0xca, 0x94, 0xab, 0x2e, 0x4a, 0x97, 0x70, 0x54, 0x14, 0x76, 0x0b, 0x66, - 0xd3, 0xcc, 0x57, 0x68, 0x8d, 0xe7, 0xee, 0x8e, 0xd5, 0xcd, 0x45, 0x28, 0x3b, 0xbc, 0xd5, 0x5a, - 0xc7, 0xc9, 0xbe, 0x42, 0xb3, 0x94, 0x20, 0x50, 0xd3, 0xd8, 0xbf, 0x61, 0xc1, 0x99, 0x3e, 0x9d, - 0xc9, 0x31, 0xd5, 0x1c, 0x6b, 0x2b, 0x30, 0xe0, 0x65, 0xa0, 0x2a, 0xad, 0x39, 0x49, 0x76, 0xc8, - 0xb0, 0x6a, 0x2b, 0x02, 0x8c, 0x09, 0xde, 0xfe, 0x67, 0x0b, 0x4e, 0xa5, 0x75, 0x8d, 0xc8, 0x75, - 0x20, 0xa2, 0x33, 0x2b, 0x5e, 0xe4, 0x06, 0xfb, 0x34, 0xec, 0xb2, 0x9e, 0x0b, 0xad, 0xe7, 0x24, - 0x27, 0xb2, 0xd4, 0x43, 0x81, 0x7d, 0x5a, 0xf1, 0xda, 0xdf, 0xaa, 0x1a, 0xed, 0x64, 0xa6, 0xdc, - 0xce, 0x73, 0xa6, 0xe8, 0x8f, 0x69, 0x46, 0xd0, 0x4a, 0x24, 0x9a, 0xf2, 0xed, 0xef, 0x8c, 0x80, - 0x3a, 0x8b, 0xe2, 0x79, 0x88, 0x9c, 0xb2, 0x38, 0xa9, 0xa7, 0x8a, 0x8a, 0x27, 0x78, 0xaa, 0x68, - 0xe4, 0x61, 0x39, 0x02, 0xf1, 0x6e, 0x8e, 0xf6, 0x45, 0x0d, 0xa3, 0xbf, 0xa3, 0x51, 0x68, 0xd2, - 0x31, 0x4d, 0x9a, 0xde, 0x3e, 0x15, 0x8d, 0xc6, 0xd2, 0x9a, 0xac, 0x25, 0x08, 0xd4, 0x34, 0x4c, - 0x93, 0xaa, 0x57, 0xab, 0xc9, 0x48, 0x51, 0x69, 0xc2, 0x46, 0x07, 0x39, 0x86, 0x51, 0x34, 0x82, - 0x60, 0x4f, 0xfa, 0x7f, 0x8a, 0xe2, 0x5a, 0x10, 0xec, 0x21, 0xc7, 0x30, 0x8f, 0xc5, 0x0f, 0xc2, - 0x96, 0xd3, 0xf4, 0x3e, 0x48, 0xab, 0x4a, 0x8a, 0xf4, 0xfb, 0x94, 0xc7, 0xb2, 0xd1, 0x4b, 0x82, - 0xfd, 0xda, 0xb1, 0x19, 0xd8, 0x0e, 0x69, 0xd5, 0x73, 0x63, 0x93, 0x1b, 0xa4, 0x67, 0xe0, 0x56, - 0x0f, 0x05, 0xf6, 0x69, 0x45, 0x96, 0xe0, 0x54, 0x72, 0x96, 0x98, 0xd4, 0x90, 0x08, 0x67, 0x50, - 0xf9, 0xe1, 0x98, 0x46, 0x63, 0x96, 0x9e, 0x59, 0x9b, 0x96, 0xac, 0xe4, 0xe1, 0x6e, 0xa2, 0x61, - 0x6d, 0x92, 0x0a, 0x1f, 0x54, 0x14, 0xf6, 0xc7, 0x8b, 0x6c, 0x77, 0x1c, 0x70, 0x3b, 0xf7, 0x89, - 0x65, 0x0d, 0xd3, 0x33, 0x72, 0xe4, 0x18, 0x33, 0xf2, 0x05, 0x98, 0xbc, 0x1b, 0x05, 0xbe, 0xca, - 0xc8, 0x8d, 0x0e, 0xcc, 0xc8, 0x19, 0x54, 0xfd, 0x33, 0x72, 0x63, 0x79, 0x65, 0xe4, 0xc6, 0x1f, - 0x31, 0x23, 0xf7, 0x27, 0xa3, 0x70, 0x5e, 0x9d, 0x27, 0xd3, 0xf8, 0x5e, 0x10, 0xee, 0x79, 0x7e, - 0x9d, 0x9f, 0xc1, 0x7e, 0xc5, 0x82, 0x49, 0xb1, 0x5e, 0xe4, 0xc3, 0x08, 0xe2, 0xcc, 0xb1, 0x96, - 0xd3, 0xdd, 0xb5, 0x94, 0xb0, 0x85, 0x1d, 0x43, 0x50, 0xe6, 0x95, 0x0a, 0x13, 0x85, 0x29, 0x8d, - 0xc8, 0x87, 0x01, 0x92, 0x17, 0xb3, 0x6a, 0x39, 0xbd, 0x1b, 0x96, 0xe8, 0x87, 0xb4, 0xa6, 0x7d, - 0xd3, 0x1d, 0x25, 0x04, 0x0d, 0x81, 0xe4, 0x75, 0x4b, 0xdd, 0x15, 0x11, 0xa7, 0x59, 0xaf, 0x3e, - 0x96, 0xb1, 0x39, 0xce, 0xd5, 0x11, 0x84, 0x71, 0xcf, 0xaf, 0xb3, 0x79, 0x22, 0x93, 0x98, 0x6f, - 0xe9, 0x57, 0xbf, 0xb0, 0x16, 0x38, 0xd5, 0x8a, 0xd3, 0x74, 0x7c, 0x97, 0x86, 0xab, 0x82, 0xdc, - 0x7c, 0x36, 0x89, 0x03, 0x30, 0x61, 0xd4, 0x73, 0x39, 0x73, 0xf4, 0x38, 0x97, 0x33, 0xe7, 0xde, - 0x03, 0x33, 0x3d, 0x1f, 0xf3, 0x44, 0x57, 0x47, 0x1e, 0xfd, 0xd6, 0x89, 0xfd, 0x07, 0x63, 0x7a, - 0xd3, 0xda, 0x08, 0xaa, 0xe2, 0x8a, 0x60, 0xa8, 0xbf, 0xa8, 0xf4, 0x3d, 0x73, 0x9c, 0x22, 0xc6, - 0xd3, 0x4b, 0x0a, 0x88, 0xa6, 0x48, 0x36, 0x47, 0xdb, 0x4e, 0x48, 0xfd, 0xc7, 0x3d, 0x47, 0xb7, - 0x94, 0x10, 0x34, 0x04, 0x92, 0x46, 0xea, 0xb8, 0xf5, 0xca, 0xf0, 0xc7, 0xad, 0xcc, 0x1d, 0xee, - 0x7b, 0x95, 0xeb, 0xf3, 0x16, 0x4c, 0xfb, 0xa9, 0x99, 0x2b, 0x8f, 0xdc, 0x76, 0x1e, 0xc7, 0xaa, - 0x10, 0x57, 0xb3, 0xd3, 0x30, 0xcc, 0xc8, 0xef, 0xb7, 0xa5, 0x8d, 0x9e, 0x70, 0x4b, 0xd3, 0x77, - 0x8d, 0xc7, 0x06, 0xdd, 0x35, 0x26, 0xbe, 0x7a, 0x65, 0x60, 0x3c, 0xf7, 0x57, 0x06, 0xa0, 0xcf, - 0x0b, 0x03, 0x77, 0xa0, 0xec, 0x86, 0xd4, 0x89, 0x1f, 0xf1, 0xc2, 0x39, 0x7f, 0xec, 0x6e, 0x39, - 0x61, 0x80, 0x9a, 0x97, 0xfd, 0x67, 0x45, 0x38, 0x9d, 0x8c, 0x48, 0x72, 0x14, 0xc5, 0xf6, 0x47, - 0x21, 0x57, 0x3b, 0xb7, 0x6a, 0x7f, 0xbc, 0x96, 0x20, 0x50, 0xd3, 0x30, 0x7f, 0xac, 0x13, 0xd1, - 0xcd, 0x36, 0xf5, 0xd7, 0xbc, 0xdd, 0x88, 0x8f, 0xb8, 0x51, 0x42, 0x76, 0x4b, 0xa3, 0xd0, 0xa4, - 0x63, 0xce, 0xb8, 0xf0, 0x8b, 0xa3, 0xec, 0xc9, 0xae, 0xf4, 0xb7, 0x31, 0xc1, 0x93, 0x2f, 0xf7, - 0x7d, 0x2e, 0x24, 0x9f, 0x9a, 0x86, 0x9e, 0x13, 0xb8, 0x13, 0xbe, 0x13, 0xf2, 0x39, 0x0b, 0x4e, - 0xed, 0xa5, 0xea, 0x57, 0x12, 0x93, 0x3c, 0x64, 0xa5, 0x65, 0xba, 0x28, 0x46, 0x4f, 0xe1, 0x34, - 0x3c, 0xc2, 0xac, 0x74, 0xfb, 0xdf, 0x2c, 0x30, 0xcd, 0xd3, 0xf1, 0x3c, 0x2b, 0xe3, 0x01, 0xa8, - 0xc2, 0x11, 0x0f, 0x40, 0x25, 0x4e, 0x58, 0xf1, 0x78, 0x4e, 0xff, 0xc8, 0x09, 0x9c, 0xfe, 0xd1, - 0x81, 0x5e, 0xdb, 0x1b, 0xa1, 0xd8, 0xf1, 0xaa, 0xd2, 0x6f, 0xd7, 0x87, 0x61, 0xab, 0x2b, 0xc8, - 0xe0, 0xf6, 0xef, 0x8d, 0xea, 0x38, 0x5d, 0x1e, 0xc5, 0xff, 0x50, 0x74, 0xbb, 0xa6, 0x0a, 0x67, - 0x45, 0xcf, 0x37, 0x7a, 0x0a, 0x67, 0xdf, 0x7d, 0xf2, 0x4a, 0x0b, 0x31, 0x40, 0x83, 0xea, 0x66, - 0xc7, 0x8f, 0x28, 0xb3, 0xb8, 0x0b, 0x25, 0x16, 0xda, 0xf0, 0x84, 0x5b, 0x29, 0xa5, 0x54, 0xe9, - 0x9a, 0x84, 0x3f, 0x38, 0x9c, 0x7f, 0xd7, 0xc9, 0xd5, 0x4a, 0x5a, 0xa3, 0xe2, 0x4f, 0x22, 0x28, - 0xb3, 0xbf, 0x79, 0x45, 0x88, 0x0c, 0x9a, 0x6e, 0x29, 0x5b, 0x94, 0x20, 0x72, 0x29, 0x37, 0xd1, - 0x72, 0x88, 0x0f, 0x65, 0xfe, 0x54, 0x11, 0x17, 0x2a, 0x62, 0xab, 0x2d, 0x55, 0x97, 0x91, 0x20, - 0x1e, 0x1c, 0xce, 0xbf, 0x74, 0x72, 0xa1, 0xaa, 0x39, 0x6a, 0x11, 0xf6, 0xdf, 0x15, 0xf5, 0xdc, - 0x95, 0xf5, 0xd2, 0x3f, 0x14, 0x73, 0xf7, 0xc5, 0xcc, 0xdc, 0xbd, 0xd8, 0x33, 0x77, 0xa7, 0xf5, - 0x73, 0x3e, 0xa9, 0xd9, 0xf8, 0xa4, 0x37, 0xd8, 0xa3, 0xe3, 0x78, 0xee, 0x59, 0xbc, 0xd6, 0xf1, - 0x42, 0x1a, 0x6d, 0x85, 0x1d, 0xdf, 0xf3, 0xeb, 0xf2, 0x51, 0x47, 0xc3, 0xb3, 0x48, 0xa1, 0x31, - 0x4b, 0x6f, 0x7f, 0x95, 0x9f, 0x77, 0x1a, 0xc5, 0x65, 0xec, 0x2b, 0x37, 0xf9, 0x6b, 0x4f, 0xa2, - 0xa2, 0x54, 0x7d, 0x65, 0xf1, 0xc4, 0x93, 0xc0, 0x91, 0x7b, 0x30, 0xbe, 0x2b, 0x5e, 0x9c, 0xc8, - 0xe7, 0x8a, 0x93, 0x7c, 0xbe, 0x82, 0x5f, 0x26, 0x4d, 0xde, 0xb2, 0x78, 0xa0, 0xff, 0xc4, 0x44, - 0x9a, 0xfd, 0x4b, 0x45, 0x38, 0x95, 0x79, 0x8b, 0x88, 0x05, 0xfc, 0xc9, 0xc3, 0x53, 0xd9, 0xec, - 0xbc, 0x7a, 0xd4, 0x58, 0x51, 0x90, 0x0f, 0x00, 0x54, 0x69, 0xbb, 0x19, 0x74, 0xb9, 0xe3, 0x32, - 0x72, 0x62, 0xc7, 0x45, 0xf9, 0xba, 0x2b, 0x8a, 0x0b, 0x1a, 0x1c, 0x65, 0x19, 0xed, 0xa8, 0x78, - 0x4f, 0x23, 0x5d, 0x46, 0x6b, 0xdc, 0xf4, 0x1b, 0x7b, 0xb2, 0x37, 0xfd, 0x3c, 0x38, 0x25, 0x54, - 0x54, 0x25, 0x5c, 0x8f, 0x50, 0xa9, 0x75, 0x86, 0xcd, 0xa8, 0x95, 0x34, 0x1b, 0xcc, 0xf2, 0xb5, - 0x3f, 0x5b, 0x60, 0xee, 0x9b, 0x18, 0xec, 0xf5, 0x24, 0x39, 0xfe, 0x66, 0x18, 0x73, 0x3a, 0x71, - 0x23, 0xe8, 0x79, 0x01, 0x64, 0x89, 0x43, 0x51, 0x62, 0xc9, 0x1a, 0x8c, 0x54, 0x9d, 0x38, 0x79, - 0x94, 0xff, 0x24, 0xca, 0xe9, 0x4c, 0x98, 0x13, 0x53, 0xe4, 0x5c, 0xc8, 0x33, 0x30, 0x12, 0x3b, - 0xf5, 0xd4, 0x0b, 0x9e, 0x3b, 0x4e, 0x3d, 0x42, 0x0e, 0x35, 0x77, 0x97, 0x91, 0x23, 0x76, 0x97, - 0x97, 0x8c, 0x7f, 0x17, 0x61, 0x9c, 0xba, 0xf4, 0xfe, 0x8b, 0x07, 0x51, 0xd8, 0x9f, 0xa2, 0xb5, - 0x7f, 0x04, 0x26, 0xcd, 0x7f, 0x01, 0x71, 0xac, 0xbb, 0x46, 0xf6, 0x3f, 0x8e, 0xc0, 0x54, 0xaa, - 0xcc, 0x2f, 0x35, 0xcb, 0xad, 0x23, 0x67, 0x39, 0x3f, 0x4f, 0xeb, 0xf8, 0x54, 0x16, 0x71, 0x1a, - 0xe7, 0x69, 0x1d, 0x9f, 0xa2, 0xc0, 0xb1, 0xaf, 0x52, 0x0d, 0xbb, 0xd8, 0xf1, 0x65, 0x56, 0x5e, - 0x7d, 0x95, 0x15, 0x0e, 0x45, 0x89, 0x65, 0x01, 0xec, 0x64, 0xc4, 0x8d, 0xa2, 0xb0, 0x11, 0x72, - 0xd5, 0x5c, 0xcf, 0xe3, 0xd5, 0x34, 0x59, 0xd2, 0xca, 0x03, 0x7a, 0x13, 0x82, 0x29, 0x89, 0xe4, - 0x13, 0x96, 0xf9, 0x5e, 0xdc, 0x58, 0x1e, 0xa7, 0x49, 0xd9, 0x2a, 0x4a, 0xb1, 0x82, 0x1e, 0xfe, - 0x6c, 0x5c, 0xa4, 0x16, 0xf0, 0xf8, 0xe3, 0x59, 0xc0, 0xd0, 0x67, 0xf1, 0xbe, 0x15, 0xca, 0x2d, - 0xc7, 0xf7, 0x6a, 0x34, 0x8a, 0xc5, 0xbf, 0x6f, 0x29, 0x8b, 0xe8, 0x69, 0x3d, 0x01, 0xa2, 0xc6, - 0xf3, 0x7f, 0x92, 0xc4, 0x3b, 0x26, 0x82, 0x98, 0xb2, 0xf1, 0x4f, 0x92, 0x34, 0x18, 0x4d, 0x1a, - 0xfb, 0xb7, 0x2d, 0x38, 0xd7, 0x77, 0x30, 0x7e, 0x70, 0xd3, 0x9f, 0xf6, 0xef, 0x14, 0xe0, 0x4c, - 0x9f, 0x32, 0x58, 0xd2, 0x7d, 0x6c, 0xcf, 0x0a, 0xca, 0x3a, 0xdb, 0xa9, 0x81, 0x73, 0xe3, 0x64, - 0xdb, 0x90, 0xde, 0x0a, 0x8a, 0x4f, 0x74, 0x2b, 0xb0, 0xbf, 0x5a, 0x00, 0xe3, 0x01, 0x4c, 0xf2, - 0x11, 0xb3, 0xe2, 0xdb, 0xca, 0xab, 0x3a, 0x59, 0x30, 0x57, 0x15, 0xe3, 0x62, 0xd4, 0xfa, 0x15, - 0x90, 0x67, 0xe7, 0x6b, 0xe1, 0xe8, 0xf9, 0x4a, 0x9a, 0x49, 0x69, 0x7d, 0x31, 0xff, 0xd2, 0xfa, - 0x72, 0x4f, 0x59, 0xfd, 0x2f, 0x58, 0x62, 0xa6, 0x65, 0xba, 0xa4, 0x2d, 0xac, 0xf5, 0x10, 0x0b, - 0xfb, 0x36, 0x28, 0x45, 0xb4, 0x59, 0x63, 0x9e, 0x9d, 0xb4, 0xc4, 0xfa, 0xbd, 0x6d, 0x09, 0x47, - 0x45, 0xc1, 0xef, 0xce, 0x36, 0x9b, 0xc1, 0xbd, 0xcb, 0xad, 0x76, 0xdc, 0x95, 0x36, 0x59, 0xdf, - 0x9d, 0x55, 0x18, 0x34, 0xa8, 0xec, 0x7f, 0xb7, 0xc4, 0xe7, 0x94, 0x3e, 0xfa, 0x8b, 0x99, 0x3b, - 0x8d, 0xc7, 0x77, 0x6f, 0x7f, 0x06, 0xc0, 0x55, 0x6f, 0x12, 0xe4, 0xf3, 0x2e, 0xa6, 0x7e, 0xe3, - 0xc0, 0x7c, 0xac, 0x31, 0x81, 0xa1, 0x21, 0x2f, 0xb5, 0x78, 0x8a, 0x47, 0x2d, 0x1e, 0xfb, 0x5f, - 0x2c, 0x48, 0x6d, 0x16, 0xa4, 0x0d, 0xa3, 0x4c, 0x83, 0x6e, 0x3e, 0x2f, 0x28, 0x98, 0xac, 0xd9, - 0xc2, 0x92, 0xd3, 0x82, 0xff, 0x89, 0x42, 0x10, 0x69, 0x4a, 0xef, 0xbc, 0x90, 0xc7, 0x2b, 0x1f, - 0xa6, 0x40, 0xe6, 0xdf, 0xcb, 0x7f, 0x88, 0xa1, 0x3c, 0x7d, 0xfb, 0x45, 0x98, 0xe9, 0x51, 0x8a, - 0xdf, 0x48, 0x0a, 0x92, 0x67, 0x23, 0x8c, 0x19, 0xc8, 0xef, 0x47, 0xa2, 0xc0, 0x31, 0x07, 0xff, - 0x74, 0x96, 0x3d, 0xf9, 0x92, 0x05, 0x33, 0x51, 0x96, 0xdf, 0xe3, 0x1a, 0x3b, 0x95, 0xb9, 0xea, - 0x41, 0x61, 0xaf, 0x12, 0xf6, 0x7f, 0x49, 0xf3, 0x24, 0xfe, 0x81, 0x98, 0xda, 0x5c, 0xac, 0x81, - 0x9b, 0x0b, 0x5b, 0x62, 0x6e, 0x83, 0x56, 0x3b, 0xcd, 0x9e, 0xda, 0x9c, 0x6d, 0x09, 0x47, 0x45, - 0x91, 0x7a, 0x1f, 0xaf, 0x78, 0xe4, 0xfb, 0x78, 0x2f, 0xc0, 0xa4, 0xf9, 0x34, 0x0a, 0x4f, 0xa1, - 0xc9, 0xc3, 0x07, 0xf3, 0x15, 0x15, 0x4c, 0x51, 0x65, 0xde, 0x57, 0x1b, 0x3d, 0xf2, 0x7d, 0xb5, - 0xe7, 0xa0, 0x24, 0xdf, 0x0a, 0x4b, 0xf2, 0xbb, 0xa2, 0xf0, 0x47, 0xc2, 0x50, 0x61, 0x99, 0x81, - 0x68, 0x39, 0x7e, 0xc7, 0x69, 0xb2, 0x11, 0x92, 0xf5, 0x80, 0x6a, 0x65, 0xad, 0x2b, 0x0c, 0x1a, - 0x54, 0xac, 0xc7, 0xb1, 0xd7, 0xa2, 0xaf, 0x04, 0x7e, 0x92, 0x19, 0x51, 0x3d, 0xde, 0x91, 0x70, - 0x54, 0x14, 0xf6, 0x3f, 0x58, 0x90, 0x7d, 0xe8, 0x28, 0x55, 0x83, 0x68, 0x1d, 0x59, 0x83, 0x98, - 0xae, 0xaf, 0x2a, 0x1c, 0xab, 0xbe, 0xca, 0x2c, 0x7d, 0x2a, 0x3e, 0xb4, 0xf4, 0xe9, 0x4d, 0xfa, - 0x5e, 0xbb, 0xa8, 0x91, 0x9a, 0xe8, 0x77, 0xa7, 0x9d, 0xd8, 0x30, 0xe6, 0x3a, 0xaa, 0xc4, 0x7b, - 0x52, 0xb8, 0x55, 0xcb, 0x4b, 0x9c, 0x48, 0x62, 0x2a, 0xbb, 0x5f, 0xff, 0xee, 0x85, 0xa7, 0xbe, - 0xf1, 0xdd, 0x0b, 0x4f, 0x7d, 0xeb, 0xbb, 0x17, 0x9e, 0xfa, 0xd8, 0xfd, 0x0b, 0xd6, 0xd7, 0xef, - 0x5f, 0xb0, 0xbe, 0x71, 0xff, 0x82, 0xf5, 0xad, 0xfb, 0x17, 0xac, 0xef, 0xdc, 0xbf, 0x60, 0x7d, - 0xfe, 0x6f, 0x2f, 0x3c, 0xf5, 0xca, 0xbb, 0x87, 0xf9, 0x8f, 0xb5, 0xff, 0x1d, 0x00, 0x00, 0xff, - 0xff, 0xd1, 0x9f, 0xce, 0xf7, 0xf0, 0x76, 0x00, 0x00, + // 6831 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x59, 0x6c, 0x24, 0xc9, + 0x71, 0xe8, 0x56, 0x37, 0x8f, 0xee, 0xe0, 0x31, 0x64, 0xce, 0xb1, 0x14, 0xdf, 0x6a, 0x38, 0xa8, + 0x85, 0xa4, 0x7d, 0x4f, 0x12, 0xf9, 0x76, 0xde, 0x4a, 0x6f, 0xad, 0x95, 0x57, 0x62, 0x93, 0x73, + 0x70, 0x86, 0x1c, 0x72, 0x82, 0x9c, 0x19, 0x6b, 0x25, 0xcb, 0x5b, 0xac, 0xce, 0xee, 0xae, 0x61, + 0x77, 0x55, 0x6f, 0x55, 0x35, 0x87, 0x2d, 0x59, 0x17, 0x20, 0x5b, 0x0b, 0xe8, 0x84, 0xe4, 0x0f, + 0x09, 0x30, 0x6c, 0xf9, 0x80, 0x01, 0x7f, 0x08, 0xb6, 0x7f, 0x7c, 0xc0, 0xf0, 0x87, 0xe5, 0x1f, + 0x19, 0xfe, 0xb0, 0x00, 0x1b, 0x96, 0x6c, 0xc1, 0xb4, 0x34, 0xb6, 0x21, 0xdb, 0x80, 0x6d, 0xf8, + 0xf8, 0xf1, 0xc0, 0x1f, 0x46, 0x1e, 0x95, 0x99, 0x55, 0xdd, 0x3d, 0x24, 0xa7, 0x6b, 0xc6, 0x82, + 0xe0, 0x3f, 0x76, 0x44, 0x64, 0x44, 0x64, 0x56, 0x66, 0x64, 0x44, 0x64, 0x64, 0x12, 0xd6, 0xeb, + 0x5e, 0xdc, 0xe8, 0xec, 0x2e, 0xba, 0x41, 0x6b, 0xc9, 0x09, 0xeb, 0x41, 0x3b, 0x0c, 0xee, 0xf2, + 0x3f, 0xde, 0xee, 0x56, 0x97, 0xf6, 0x2f, 0x2e, 0xb5, 0xf7, 0xea, 0x4b, 0x4e, 0xdb, 0x8b, 0x96, + 0x9c, 0x76, 0xbb, 0xe9, 0xb9, 0x4e, 0xec, 0x05, 0xfe, 0xd2, 0xfe, 0xf3, 0x4e, 0xb3, 0xdd, 0x70, + 0x9e, 0x5f, 0xaa, 0x53, 0x9f, 0x86, 0x4e, 0x4c, 0xab, 0x8b, 0xed, 0x30, 0x88, 0x03, 0xf2, 0x6e, + 0xcd, 0x6d, 0x31, 0xe1, 0xc6, 0xff, 0xf8, 0x09, 0xb7, 0xba, 0xb8, 0x7f, 0x71, 0xb1, 0xbd, 0x57, + 0x5f, 0x64, 0xdc, 0x16, 0x0d, 0x6e, 0x8b, 0x09, 0xb7, 0xf9, 0xb7, 0x1b, 0xba, 0xd4, 0x83, 0x7a, + 0xb0, 0xc4, 0x99, 0xee, 0x76, 0x6a, 0xfc, 0x17, 0xff, 0xc1, 0xff, 0x12, 0xc2, 0xe6, 0xed, 0xbd, + 0x17, 0xa3, 0x45, 0x2f, 0x60, 0xea, 0x2d, 0xb9, 0x41, 0x48, 0x97, 0xf6, 0x7b, 0x14, 0x9a, 0x7f, + 0x41, 0xd3, 0xb4, 0x1c, 0xb7, 0xe1, 0xf9, 0x34, 0xec, 0xea, 0x3e, 0xb5, 0x68, 0xec, 0xf4, 0x6b, + 0xb5, 0x34, 0xa8, 0x55, 0xd8, 0xf1, 0x63, 0xaf, 0x45, 0x7b, 0x1a, 0xbc, 0xf3, 0xa8, 0x06, 0x91, + 0xdb, 0xa0, 0x2d, 0x27, 0xdb, 0xce, 0x7e, 0x0d, 0xa6, 0x96, 0xef, 0x6c, 0x2f, 0x77, 0xe2, 0xc6, + 0x4a, 0xe0, 0xd7, 0xbc, 0x3a, 0x79, 0x07, 0x4c, 0xb8, 0xcd, 0x4e, 0x14, 0xd3, 0xf0, 0x86, 0xd3, + 0xa2, 0x73, 0xd6, 0x05, 0xeb, 0xb9, 0x72, 0xe5, 0xf4, 0x37, 0x0e, 0x17, 0x9e, 0xba, 0x7f, 0xb8, + 0x30, 0xb1, 0xa2, 0x51, 0x68, 0xd2, 0x91, 0xff, 0x0d, 0xe3, 0x61, 0xd0, 0xa4, 0xcb, 0x78, 0x63, + 0xae, 0xc0, 0x9b, 0x9c, 0x92, 0x4d, 0xc6, 0x51, 0x80, 0x31, 0xc1, 0xdb, 0x7f, 0x56, 0x00, 0x58, + 0x6e, 0xb7, 0xb7, 0xc2, 0xe0, 0x2e, 0x75, 0x63, 0xf2, 0x2a, 0x94, 0xd8, 0x28, 0x54, 0x9d, 0xd8, + 0xe1, 0xd2, 0x26, 0x2e, 0xfe, 0xdf, 0x45, 0xd1, 0x99, 0x45, 0xb3, 0x33, 0xfa, 0xcb, 0x31, 0xea, + 0xc5, 0xfd, 0xe7, 0x17, 0x37, 0x77, 0x59, 0xfb, 0x0d, 0x1a, 0x3b, 0x15, 0x22, 0x85, 0x81, 0x86, + 0xa1, 0xe2, 0x4a, 0x7c, 0x18, 0x89, 0xda, 0xd4, 0xe5, 0x8a, 0x4d, 0x5c, 0x5c, 0x5f, 0x1c, 0x66, + 0x8a, 0x2c, 0x6a, 0xcd, 0xb7, 0xdb, 0xd4, 0xad, 0x4c, 0x4a, 0xc9, 0x23, 0xec, 0x17, 0x72, 0x39, + 0x64, 0x1f, 0xc6, 0xa2, 0xd8, 0x89, 0x3b, 0xd1, 0x5c, 0x91, 0x4b, 0xbc, 0x91, 0x9b, 0x44, 0xce, + 0xb5, 0x32, 0x2d, 0x65, 0x8e, 0x89, 0xdf, 0x28, 0xa5, 0xd9, 0x7f, 0x69, 0xc1, 0xb4, 0x26, 0x5e, + 0xf7, 0xa2, 0x98, 0x7c, 0xa0, 0x67, 0x70, 0x17, 0x8f, 0x37, 0xb8, 0xac, 0x35, 0x1f, 0xda, 0x19, + 0x29, 0xac, 0x94, 0x40, 0x8c, 0x81, 0x6d, 0xc1, 0xa8, 0x17, 0xd3, 0x56, 0x34, 0x57, 0xb8, 0x50, + 0x7c, 0x6e, 0xe2, 0xe2, 0xd5, 0xbc, 0xfa, 0x59, 0x99, 0x92, 0x42, 0x47, 0xd7, 0x18, 0x7b, 0x14, + 0x52, 0xec, 0xdf, 0x9c, 0x30, 0xfb, 0xc7, 0x06, 0x9c, 0x3c, 0x0f, 0x13, 0x51, 0xd0, 0x09, 0x5d, + 0x8a, 0xb4, 0x1d, 0x44, 0x73, 0xd6, 0x85, 0x22, 0x9b, 0x7a, 0x6c, 0xa6, 0x6e, 0x6b, 0x30, 0x9a, + 0x34, 0xe4, 0x73, 0x16, 0x4c, 0x56, 0x69, 0x14, 0x7b, 0x3e, 0x97, 0x9f, 0x28, 0xbf, 0x33, 0xb4, + 0xf2, 0x09, 0x70, 0x55, 0x33, 0xaf, 0x9c, 0x91, 0x1d, 0x99, 0x34, 0x80, 0x11, 0xa6, 0xe4, 0xb3, + 0x15, 0x57, 0xa5, 0x91, 0x1b, 0x7a, 0x6d, 0xf6, 0x9b, 0xcf, 0x19, 0x63, 0xc5, 0xad, 0x6a, 0x14, + 0x9a, 0x74, 0xc4, 0x87, 0x51, 0xb6, 0xa2, 0xa2, 0xb9, 0x11, 0xae, 0xff, 0xda, 0x70, 0xfa, 0xcb, + 0x41, 0x65, 0x8b, 0x55, 0x8f, 0x3e, 0xfb, 0x15, 0xa1, 0x10, 0x43, 0x3e, 0x6b, 0xc1, 0x9c, 0x5c, + 0xf1, 0x48, 0xc5, 0x80, 0xde, 0x69, 0x78, 0x31, 0x6d, 0x7a, 0x51, 0x3c, 0x37, 0xca, 0x75, 0x58, + 0x3a, 0xde, 0xdc, 0xba, 0x12, 0x06, 0x9d, 0xf6, 0x75, 0xcf, 0xaf, 0x56, 0x2e, 0x48, 0x49, 0x73, + 0x2b, 0x03, 0x18, 0xe3, 0x40, 0x91, 0xe4, 0x4b, 0x16, 0xcc, 0xfb, 0x4e, 0x8b, 0x46, 0x6d, 0x87, + 0x7d, 0x5a, 0x81, 0xae, 0x34, 0x1d, 0x77, 0x8f, 0x6b, 0x34, 0xf6, 0x68, 0x1a, 0xd9, 0x52, 0xa3, + 0xf9, 0x1b, 0x03, 0x59, 0xe3, 0x43, 0xc4, 0x92, 0x5f, 0xb2, 0x60, 0x36, 0x08, 0xdb, 0x0d, 0xc7, + 0xa7, 0xd5, 0x04, 0x1b, 0xcd, 0x8d, 0xf3, 0xa5, 0xf7, 0xc1, 0xe1, 0x3e, 0xd1, 0x66, 0x96, 0xed, + 0x46, 0xe0, 0x7b, 0x71, 0x10, 0x6e, 0xd3, 0x38, 0xf6, 0xfc, 0x7a, 0x54, 0x39, 0x7b, 0xff, 0x70, + 0x61, 0xb6, 0x87, 0x0a, 0x7b, 0xf5, 0x21, 0x1f, 0x86, 0x89, 0xa8, 0xeb, 0xbb, 0x77, 0x3c, 0xbf, + 0x1a, 0xdc, 0x8b, 0xe6, 0x4a, 0x79, 0x2c, 0xdf, 0x6d, 0xc5, 0x50, 0x2e, 0x40, 0x2d, 0x00, 0x4d, + 0x69, 0xfd, 0x3f, 0x9c, 0x9e, 0x4a, 0xe5, 0xbc, 0x3f, 0x9c, 0x9e, 0x4c, 0x0f, 0x11, 0x4b, 0x3e, + 0x65, 0xc1, 0x54, 0xe4, 0xd5, 0x7d, 0x27, 0xee, 0x84, 0xf4, 0x3a, 0xed, 0x46, 0x73, 0xc0, 0x15, + 0xb9, 0x36, 0xe4, 0xa8, 0x18, 0x2c, 0x2b, 0x67, 0xa5, 0x8e, 0x53, 0x26, 0x34, 0xc2, 0xb4, 0xdc, + 0x7e, 0x0b, 0x4d, 0x4f, 0xeb, 0x89, 0x7c, 0x17, 0x9a, 0x9e, 0xd4, 0x03, 0x45, 0x92, 0xf7, 0xc2, + 0x8c, 0x00, 0xa9, 0x91, 0x8d, 0xe6, 0x26, 0xb9, 0xa1, 0x3d, 0x73, 0xff, 0x70, 0x61, 0x66, 0x3b, + 0x83, 0xc3, 0x1e, 0x6a, 0xfb, 0x0f, 0x0b, 0x30, 0x93, 0xdd, 0xc5, 0xc8, 0xaf, 0x58, 0x70, 0xea, + 0xee, 0xbd, 0x78, 0x27, 0xd8, 0xa3, 0x7e, 0x54, 0xe9, 0x32, 0x5b, 0xc3, 0xed, 0xf7, 0xc4, 0x45, + 0x37, 0xdf, 0xfd, 0x72, 0xf1, 0x5a, 0x5a, 0xca, 0x25, 0x3f, 0x0e, 0xbb, 0x95, 0xa7, 0xe5, 0x88, + 0x9c, 0xba, 0x76, 0x67, 0xc7, 0xc4, 0x62, 0x56, 0xa9, 0xf9, 0x4f, 0x5b, 0x70, 0xa6, 0x1f, 0x0b, + 0x32, 0x03, 0xc5, 0x3d, 0xda, 0x15, 0x2e, 0x12, 0xb2, 0x3f, 0xc9, 0x8f, 0xc3, 0xe8, 0xbe, 0xd3, + 0xec, 0x50, 0xe9, 0x6a, 0x5c, 0x19, 0xae, 0x23, 0x4a, 0x33, 0x14, 0x5c, 0xdf, 0x55, 0x78, 0xd1, + 0xb2, 0xff, 0xb8, 0x08, 0x13, 0xc6, 0x66, 0xf3, 0x04, 0xdc, 0xa7, 0x20, 0xe5, 0x3e, 0x6d, 0xe4, + 0xb6, 0x4f, 0x0e, 0xf4, 0x9f, 0xee, 0x65, 0xfc, 0xa7, 0xcd, 0xfc, 0x44, 0x3e, 0xd4, 0x81, 0x22, + 0x31, 0x94, 0x83, 0x36, 0x73, 0x8f, 0xd9, 0x3e, 0x3c, 0x92, 0xc7, 0x27, 0xdc, 0x4c, 0xd8, 0x55, + 0xa6, 0xee, 0x1f, 0x2e, 0x94, 0xd5, 0x4f, 0xd4, 0x82, 0xec, 0x6f, 0x59, 0x70, 0xc6, 0xd0, 0x71, + 0x25, 0xf0, 0xab, 0x1e, 0xff, 0xb4, 0x17, 0x60, 0x24, 0xee, 0xb6, 0x13, 0x1f, 0x5c, 0x8d, 0xd4, + 0x4e, 0xb7, 0x4d, 0x91, 0x63, 0x98, 0xd7, 0xdd, 0xa2, 0x51, 0xe4, 0xd4, 0x69, 0xd6, 0xeb, 0xde, + 0x10, 0x60, 0x4c, 0xf0, 0x24, 0x04, 0xd2, 0x74, 0xa2, 0x78, 0x27, 0x74, 0xfc, 0x88, 0xb3, 0xdf, + 0xf1, 0x5a, 0x54, 0x0e, 0xf0, 0xff, 0x39, 0xde, 0x8c, 0x61, 0x2d, 0x2a, 0xe7, 0xee, 0x1f, 0x2e, + 0x90, 0xf5, 0x1e, 0x4e, 0xd8, 0x87, 0xbb, 0xfd, 0x25, 0x0b, 0xce, 0xf5, 0x77, 0x8c, 0xc8, 0x9b, + 0x61, 0x2c, 0xa2, 0xe1, 0x3e, 0x0d, 0x65, 0xef, 0xf4, 0x27, 0xe1, 0x50, 0x94, 0x58, 0xb2, 0x04, + 0x65, 0x65, 0xb4, 0x65, 0x1f, 0x67, 0x25, 0x69, 0x59, 0x5b, 0x7a, 0x4d, 0xc3, 0x06, 0x8d, 0xfd, + 0x90, 0x6e, 0x94, 0x1a, 0x34, 0x1e, 0xb1, 0x70, 0x8c, 0xfd, 0x57, 0x16, 0x9c, 0x32, 0xb4, 0x7a, + 0x02, 0x7e, 0xb2, 0x9f, 0xf6, 0x93, 0xd7, 0x72, 0x9b, 0xcf, 0x03, 0x1c, 0xe5, 0x3f, 0x18, 0x85, + 0x59, 0x73, 0xd6, 0x73, 0x7b, 0xcc, 0x43, 0x34, 0xda, 0x0e, 0x6e, 0xe1, 0xba, 0x1c, 0x73, 0x1d, + 0xa2, 0x09, 0x30, 0x26, 0x78, 0x36, 0x88, 0x6d, 0x27, 0x6e, 0xc8, 0x01, 0x57, 0x83, 0xb8, 0xe5, + 0xc4, 0x0d, 0xe4, 0x18, 0xf2, 0x32, 0x4c, 0xc7, 0x4e, 0x58, 0xa7, 0x31, 0xd2, 0x7d, 0x2f, 0x4a, + 0xd6, 0x4b, 0xb9, 0x72, 0x4e, 0xd2, 0x4e, 0xef, 0xa4, 0xb0, 0x98, 0xa1, 0x26, 0xaf, 0xc1, 0x48, + 0x83, 0x36, 0x5b, 0xd2, 0x33, 0xda, 0xce, 0x6f, 0x85, 0xf3, 0xbe, 0x5e, 0xa5, 0xcd, 0x56, 0xa5, + 0xc4, 0x54, 0x66, 0x7f, 0x21, 0x17, 0x45, 0x7e, 0xca, 0x82, 0xf2, 0x5e, 0x27, 0x8a, 0x83, 0x96, + 0xf7, 0x21, 0x3a, 0x57, 0xe2, 0x82, 0x7f, 0x2c, 0x67, 0xc1, 0xd7, 0x13, 0xfe, 0x62, 0xbd, 0xab, + 0x9f, 0xa8, 0x25, 0x73, 0x3d, 0xaa, 0x5e, 0x48, 0xdd, 0x38, 0x08, 0xbb, 0x73, 0xf0, 0x58, 0xf4, + 0x58, 0x4d, 0xf8, 0x0b, 0x3d, 0xd4, 0x4f, 0xd4, 0x92, 0x49, 0x17, 0xc6, 0xda, 0xcd, 0x4e, 0xdd, + 0xf3, 0xe7, 0x26, 0xb8, 0x0e, 0xb7, 0x72, 0xd6, 0x61, 0x8b, 0x33, 0xaf, 0x00, 0x5b, 0xd5, 0xe2, + 0x6f, 0x94, 0x02, 0xc9, 0xb3, 0x30, 0xea, 0x36, 0x9c, 0x30, 0x9e, 0x9b, 0xe4, 0x93, 0x46, 0xcd, + 0xe2, 0x15, 0x06, 0x44, 0x81, 0xb3, 0x7f, 0xa1, 0x00, 0xf3, 0x83, 0x3b, 0x26, 0xa6, 0xb3, 0xdb, + 0x09, 0x23, 0x61, 0x20, 0x4b, 0xe6, 0x74, 0xe6, 0x60, 0x4c, 0xf0, 0xe4, 0x13, 0x16, 0x8c, 0xdf, + 0x8d, 0x02, 0xdf, 0xa7, 0xb1, 0xdc, 0xc5, 0x6e, 0xe7, 0xdc, 0xd7, 0x6b, 0x82, 0xbb, 0xd6, 0x41, + 0x02, 0x30, 0x91, 0xcb, 0xd4, 0xa5, 0x07, 0x6e, 0xb3, 0x53, 0x4d, 0x4c, 0x93, 0x22, 0xbd, 0x24, + 0xc0, 0x98, 0xe0, 0x19, 0xa9, 0xe7, 0x0b, 0xd2, 0x91, 0x34, 0xe9, 0x9a, 0x2f, 0x49, 0x25, 0xde, + 0xfe, 0xf5, 0x51, 0x38, 0xdb, 0x77, 0xf6, 0x93, 0x45, 0x00, 0xee, 0x34, 0x5c, 0xf6, 0x58, 0x8c, + 0x28, 0x02, 0xe3, 0x69, 0xb6, 0xc7, 0xdf, 0x56, 0x50, 0x34, 0x28, 0xc8, 0xc7, 0x00, 0xda, 0x4e, + 0xe8, 0xb4, 0x68, 0x4c, 0xc3, 0xc4, 0x50, 0x5d, 0x1f, 0x6e, 0x94, 0x98, 0x1e, 0x5b, 0x09, 0x4f, + 0xed, 0x64, 0x28, 0x50, 0x84, 0x86, 0x48, 0x16, 0x06, 0x87, 0xb4, 0x49, 0x9d, 0x88, 0x7b, 0x8e, + 0xd9, 0x30, 0x18, 0x35, 0x0a, 0x4d, 0x3a, 0xb6, 0x91, 0xf0, 0x5e, 0x44, 0x72, 0xac, 0xd4, 0x46, + 0xc2, 0xfb, 0x19, 0xa1, 0xc4, 0x92, 0xcf, 0x5b, 0x30, 0x5d, 0xf3, 0x9a, 0x54, 0x4b, 0x97, 0x41, + 0xeb, 0xe6, 0xf0, 0x9d, 0xbc, 0x6c, 0xf2, 0xd5, 0x26, 0x30, 0x05, 0x8e, 0x30, 0x23, 0x9e, 0x7d, + 0xe6, 0x7d, 0x1a, 0x72, 0xdb, 0x39, 0x96, 0xfe, 0xcc, 0xb7, 0x05, 0x18, 0x13, 0x3c, 0x59, 0x86, + 0x53, 0x6d, 0x27, 0x8a, 0x56, 0x42, 0x5a, 0xa5, 0x7e, 0xec, 0x39, 0x4d, 0x11, 0x52, 0x96, 0xb4, + 0x17, 0xbb, 0x95, 0x46, 0x63, 0x96, 0x9e, 0xbc, 0x0f, 0x9e, 0xf6, 0xea, 0x7e, 0x10, 0xd2, 0x0d, + 0x2f, 0x8a, 0x3c, 0xbf, 0xae, 0xa7, 0x01, 0x37, 0x85, 0xa5, 0xca, 0x82, 0x64, 0xf5, 0xf4, 0x5a, + 0x7f, 0x32, 0x1c, 0xd4, 0x9e, 0xbc, 0x0d, 0x4a, 0xd1, 0x9e, 0xd7, 0x5e, 0x09, 0xab, 0xd1, 0x5c, + 0x99, 0xf3, 0x52, 0x9b, 0xe1, 0xb6, 0x84, 0xa3, 0xa2, 0xb0, 0xbf, 0x52, 0x80, 0xb9, 0x41, 0xeb, + 0x87, 0x44, 0x6c, 0x95, 0xc4, 0xb7, 0x9d, 0x30, 0x92, 0xb1, 0xc0, 0x90, 0x41, 0xa9, 0xe4, 0x7b, + 0xdb, 0x09, 0xcd, 0xf5, 0xc6, 0x05, 0x60, 0x22, 0x89, 0xdc, 0x85, 0x91, 0xb8, 0xe9, 0xe4, 0x94, + 0xc5, 0x32, 0x24, 0x6a, 0x8f, 0x6d, 0x7d, 0x39, 0x42, 0x2e, 0x83, 0x3c, 0x03, 0x23, 0x4d, 0x6f, + 0x97, 0x79, 0xb6, 0x6c, 0x41, 0xf2, 0x2d, 0x6a, 0xdd, 0xdb, 0x8d, 0x90, 0x43, 0xed, 0x7f, 0x1e, + 0xeb, 0x63, 0xf2, 0xd4, 0x26, 0x42, 0x2e, 0x02, 0x30, 0x0f, 0x66, 0x2b, 0xa4, 0x35, 0xef, 0x40, + 0x6e, 0xe2, 0x6a, 0x59, 0xdd, 0x50, 0x18, 0x34, 0xa8, 0x92, 0x36, 0xdb, 0x9d, 0x1a, 0x6b, 0x53, + 0xe8, 0x6d, 0x23, 0x30, 0x68, 0x50, 0x91, 0x17, 0x60, 0xcc, 0x6b, 0x39, 0x75, 0x9a, 0xa8, 0xf9, + 0x0c, 0x5b, 0x4f, 0x6b, 0x1c, 0xf2, 0xe0, 0x70, 0x61, 0x5a, 0x29, 0xc4, 0x41, 0x28, 0x69, 0xc9, + 0x2f, 0x5b, 0x30, 0xe9, 0x06, 0xad, 0x56, 0xe0, 0xaf, 0x3b, 0xbb, 0xb4, 0x99, 0x24, 0xa6, 0xee, + 0x3e, 0xae, 0x2d, 0x76, 0x71, 0xc5, 0x10, 0x26, 0x82, 0x3a, 0x95, 0x6e, 0x33, 0x51, 0x98, 0xd2, + 0xca, 0x5c, 0x76, 0xa3, 0x47, 0x2c, 0xbb, 0xdf, 0xb6, 0x60, 0x56, 0xb4, 0x5d, 0xf6, 0xfd, 0x20, + 0x96, 0xf9, 0x42, 0x91, 0x59, 0x0a, 0x1e, 0x73, 0xb7, 0x0c, 0x89, 0xa2, 0x6f, 0x6f, 0x90, 0x6a, + 0xce, 0xf6, 0xe0, 0xb1, 0x57, 0x49, 0x72, 0x05, 0x66, 0x6b, 0x41, 0xe8, 0x52, 0x73, 0x20, 0xa4, + 0xcd, 0x50, 0x8c, 0x2e, 0x67, 0x09, 0xb0, 0xb7, 0x0d, 0xb9, 0x0d, 0xe7, 0x0c, 0xa0, 0x39, 0x0e, + 0xc2, 0x6c, 0x9c, 0x97, 0xdc, 0xce, 0x5d, 0xee, 0x4b, 0x85, 0x03, 0x5a, 0xcf, 0xbf, 0x07, 0x66, + 0x7b, 0xbe, 0x5f, 0x9f, 0x88, 0xfa, 0x8c, 0x19, 0x51, 0x97, 0x8d, 0x40, 0x78, 0x7e, 0x15, 0xce, + 0xf5, 0x1f, 0xa9, 0x93, 0x70, 0xb1, 0x7f, 0xce, 0x82, 0xa7, 0x07, 0x78, 0x2e, 0x2a, 0x94, 0xb0, + 0x06, 0x85, 0x12, 0xc4, 0x81, 0x22, 0xf5, 0xf7, 0xa5, 0xe1, 0xb8, 0x3c, 0xdc, 0x8c, 0xb8, 0xe4, + 0xef, 0x8b, 0x0f, 0x3d, 0x7e, 0xff, 0x70, 0xa1, 0x78, 0xc9, 0xdf, 0x47, 0xc6, 0xdb, 0xfe, 0x99, + 0xb1, 0x54, 0xb4, 0xb2, 0x9d, 0x04, 0xc8, 0x5c, 0x51, 0x19, 0xab, 0x6c, 0xe6, 0x3c, 0x17, 0x8d, + 0x68, 0x4c, 0x24, 0xce, 0xa5, 0x38, 0xf2, 0x69, 0x8b, 0xe7, 0xaa, 0x93, 0x28, 0x4e, 0x3a, 0x53, + 0x8f, 0x27, 0x75, 0x6e, 0x66, 0xc0, 0x13, 0x20, 0x9a, 0xd2, 0xd9, 0x4a, 0x6e, 0x8b, 0x44, 0x4f, + 0xd6, 0xa5, 0x4a, 0xb2, 0xd9, 0x09, 0x9e, 0x1c, 0x00, 0x44, 0x5d, 0xdf, 0xdd, 0x0a, 0x9a, 0x9e, + 0xdb, 0x95, 0xa1, 0x7d, 0x0e, 0xf9, 0x4e, 0xc1, 0x4f, 0xf8, 0x55, 0xfa, 0x37, 0x1a, 0xb2, 0xc8, + 0x57, 0x2d, 0x98, 0x15, 0x1b, 0xe7, 0xaa, 0x57, 0xab, 0xd1, 0x90, 0xfa, 0x2e, 0x4d, 0x5c, 0x8f, + 0x3b, 0xc3, 0x69, 0x90, 0xa4, 0xea, 0xd6, 0xb2, 0xec, 0xf5, 0x12, 0xef, 0x41, 0x61, 0xaf, 0x32, + 0xa4, 0x0a, 0x23, 0x9e, 0x5f, 0x0b, 0xa4, 0x61, 0xab, 0x0c, 0xa7, 0xd4, 0x9a, 0x5f, 0x0b, 0xf4, + 0x5a, 0x61, 0xbf, 0x90, 0x73, 0x27, 0xeb, 0x70, 0x26, 0x94, 0xd1, 0xdf, 0x55, 0x2f, 0x62, 0x2e, + 0xfc, 0xba, 0xd7, 0xf2, 0x62, 0x6e, 0x94, 0x8a, 0x95, 0xb9, 0xfb, 0x87, 0x0b, 0x67, 0xb0, 0x0f, + 0x1e, 0xfb, 0xb6, 0xb2, 0x5f, 0x2f, 0xa7, 0x43, 0x5c, 0x91, 0xc0, 0xf9, 0x08, 0x94, 0x43, 0x95, + 0x74, 0x17, 0x0e, 0xc4, 0x7a, 0x3e, 0x63, 0x2c, 0x33, 0x47, 0x2a, 0xf7, 0xa0, 0xd3, 0xeb, 0x5a, + 0x22, 0x73, 0x24, 0xd8, 0x97, 0x97, 0xcb, 0x22, 0x87, 0xf9, 0x25, 0xa5, 0xea, 0x24, 0x59, 0xd7, + 0x77, 0x91, 0xcb, 0x20, 0x21, 0x8c, 0x35, 0xa8, 0xd3, 0x8c, 0x1b, 0x32, 0x87, 0x73, 0x6d, 0x58, + 0x37, 0x96, 0xf1, 0xca, 0xe6, 0xc7, 0x04, 0x14, 0xa5, 0x24, 0x72, 0x00, 0xe3, 0x0d, 0xf1, 0x11, + 0xe4, 0xde, 0xbe, 0x31, 0xec, 0xe0, 0xa6, 0xbe, 0xac, 0x5e, 0xbf, 0x12, 0x80, 0x89, 0x38, 0xf2, + 0xd3, 0x16, 0x80, 0x9b, 0x24, 0xc6, 0x92, 0xe5, 0x83, 0xb9, 0xd9, 0x1d, 0x95, 0x73, 0xd3, 0xae, + 0x91, 0x02, 0x45, 0x68, 0x48, 0x26, 0xaf, 0xc2, 0x64, 0x48, 0xdd, 0xc0, 0x77, 0xbd, 0x26, 0xad, + 0x2e, 0xc7, 0xdc, 0x73, 0x3f, 0x59, 0x02, 0x6d, 0x86, 0xf9, 0x27, 0x68, 0xf0, 0xc0, 0x14, 0x47, + 0xf2, 0xba, 0x05, 0xd3, 0x2a, 0x39, 0xc8, 0x3e, 0x08, 0x95, 0x49, 0x92, 0xf5, 0x9c, 0x52, 0x91, + 0x9c, 0x67, 0x85, 0xb0, 0x08, 0x25, 0x0d, 0xc3, 0x8c, 0x5c, 0xf2, 0x0a, 0x40, 0xb0, 0xcb, 0x13, + 0x71, 0xac, 0xab, 0xa5, 0x13, 0x77, 0x75, 0x5a, 0xe4, 0x94, 0x13, 0x0e, 0x68, 0x70, 0x23, 0xd7, + 0x01, 0xc4, 0xb2, 0xd9, 0xe9, 0xb6, 0x29, 0x0f, 0x1b, 0xca, 0x95, 0xb7, 0x26, 0x83, 0xbf, 0xad, + 0x30, 0x0f, 0x0e, 0x17, 0x7a, 0x03, 0x5c, 0x9e, 0x01, 0x35, 0x9a, 0x93, 0x0f, 0xc3, 0x78, 0xd4, + 0x69, 0xb5, 0x1c, 0x95, 0x4f, 0xd9, 0xca, 0x6f, 0x47, 0x14, 0x7c, 0xf5, 0xdc, 0x94, 0x00, 0x4c, + 0x24, 0xda, 0x3e, 0x90, 0x5e, 0x7a, 0xf2, 0x02, 0x4c, 0xd2, 0x83, 0x98, 0x86, 0xbe, 0xd3, 0xbc, + 0x85, 0xeb, 0x49, 0x04, 0xce, 0x3f, 0xfe, 0x25, 0x03, 0x8e, 0x29, 0x2a, 0x62, 0x2b, 0xcf, 0xbb, + 0xc0, 0xe9, 0x41, 0x7b, 0xde, 0x89, 0x9f, 0x6d, 0xff, 0x47, 0x21, 0xe5, 0x11, 0xec, 0x84, 0x94, + 0x92, 0x00, 0x46, 0xfd, 0xa0, 0xaa, 0x8c, 0xde, 0xb5, 0x7c, 0x8c, 0xde, 0x8d, 0xa0, 0x6a, 0x9c, + 0x06, 0xb3, 0x5f, 0x11, 0x0a, 0x39, 0xfc, 0xb8, 0x2c, 0x39, 0x57, 0xe4, 0x08, 0xe9, 0x04, 0xe5, + 0x29, 0x59, 0x1d, 0x97, 0x6d, 0x9a, 0x82, 0x30, 0x2d, 0x97, 0xec, 0xc1, 0x68, 0x23, 0x88, 0x62, + 0x11, 0xab, 0x0c, 0xed, 0x85, 0x5d, 0x0d, 0xa2, 0x98, 0x6f, 0x61, 0xaa, 0xdb, 0x0c, 0x12, 0xa1, + 0x90, 0x61, 0x7f, 0xdf, 0x4a, 0xe5, 0x5b, 0xee, 0x38, 0xb1, 0xdb, 0xb8, 0xb4, 0x4f, 0x7d, 0x36, + 0x9f, 0xcd, 0x64, 0xfd, 0xff, 0x37, 0x93, 0xf5, 0x0f, 0x0e, 0x17, 0xde, 0x32, 0xa8, 0x3c, 0xe7, + 0x1e, 0xe3, 0xb0, 0xc8, 0x59, 0x18, 0x79, 0xfd, 0x8f, 0x5b, 0x30, 0x61, 0xa8, 0x27, 0x37, 0x94, + 0x1c, 0xf3, 0xc6, 0xca, 0xb9, 0x32, 0x80, 0x68, 0x8a, 0xb4, 0xbf, 0x68, 0xc1, 0x78, 0xc5, 0x71, + 0xf7, 0x82, 0x5a, 0x8d, 0x05, 0xf8, 0xd5, 0x8e, 0x3c, 0x16, 0x11, 0xfd, 0x53, 0x01, 0xfe, 0xaa, + 0x84, 0xa3, 0xa2, 0x60, 0x73, 0xb8, 0xe6, 0xb8, 0x71, 0x10, 0x72, 0xb5, 0x8b, 0x62, 0x0e, 0x5f, + 0xe6, 0x10, 0x94, 0x18, 0xf2, 0x0e, 0x98, 0x68, 0x39, 0x07, 0x49, 0xe3, 0x6c, 0xb2, 0x67, 0x43, + 0xa3, 0xd0, 0xa4, 0xb3, 0x7f, 0xbf, 0x0c, 0xe3, 0xf2, 0x04, 0xf3, 0xd8, 0x27, 0x08, 0x89, 0x17, + 0x5f, 0x18, 0xe8, 0xc5, 0x47, 0x30, 0xe6, 0xf2, 0xe2, 0x27, 0xb9, 0x95, 0x0e, 0x99, 0xf6, 0x92, + 0x0a, 0x8a, 0x7a, 0x2a, 0xad, 0x96, 0xf8, 0x8d, 0x52, 0x14, 0xf9, 0x82, 0x05, 0xa7, 0xdc, 0xc0, + 0xf7, 0xa9, 0xab, 0xed, 0xfc, 0x48, 0x1e, 0x27, 0x6c, 0x2b, 0x69, 0xa6, 0x3a, 0x45, 0x94, 0x41, + 0x60, 0x56, 0x3c, 0x79, 0x09, 0xa6, 0xc4, 0x98, 0xdd, 0x4e, 0xc5, 0xc7, 0xfa, 0xd4, 0xda, 0x44, + 0x62, 0x9a, 0x96, 0x2c, 0x8a, 0x3c, 0x83, 0x3c, 0x1f, 0x1e, 0xd3, 0xf9, 0x46, 0xe3, 0x64, 0xd8, + 0xa0, 0x20, 0x21, 0x90, 0x90, 0xd6, 0x42, 0x1a, 0x35, 0x90, 0xbe, 0xd6, 0xa1, 0x51, 0xcc, 0xf7, + 0x98, 0xf1, 0x47, 0x3b, 0x8f, 0xc2, 0x1e, 0x4e, 0xd8, 0x87, 0x3b, 0xd9, 0x93, 0x8e, 0x6e, 0x29, + 0x8f, 0xe5, 0x24, 0x3f, 0xf3, 0x40, 0x7f, 0x77, 0x01, 0x46, 0xa3, 0x86, 0x13, 0x56, 0xf9, 0xde, + 0x56, 0xac, 0x94, 0x99, 0x2d, 0xd9, 0x66, 0x00, 0x14, 0x70, 0xb2, 0x0a, 0x33, 0x99, 0x33, 0xf7, + 0x88, 0xef, 0x5e, 0xa5, 0xca, 0x9c, 0x64, 0x37, 0x93, 0x39, 0xad, 0x8f, 0xb0, 0xa7, 0x85, 0x19, + 0x04, 0x4d, 0x1c, 0x11, 0x04, 0x75, 0x61, 0xac, 0x29, 0x12, 0x01, 0x93, 0xdc, 0x54, 0xde, 0xcc, + 0x65, 0x00, 0x16, 0xcd, 0x04, 0x8c, 0x9a, 0xed, 0x32, 0xa1, 0x20, 0x05, 0x92, 0xcf, 0x32, 0x83, + 0x66, 0xe4, 0x0e, 0xa6, 0xb8, 0x02, 0xb7, 0xf3, 0x51, 0xa0, 0x27, 0x55, 0xa2, 0xad, 0x9b, 0x91, + 0x88, 0x30, 0xe5, 0xcf, 0xff, 0x08, 0x4c, 0x3c, 0x6a, 0xde, 0xe1, 0x65, 0x98, 0x19, 0x2a, 0xe3, + 0xf0, 0xef, 0x16, 0x24, 0xdf, 0x75, 0xc5, 0x71, 0x1b, 0x94, 0x4d, 0x19, 0xf2, 0x32, 0x4c, 0xab, + 0x30, 0x62, 0x25, 0xe8, 0xf8, 0x31, 0xe7, 0x55, 0xd4, 0xb9, 0x64, 0x4c, 0x61, 0x31, 0x43, 0x4d, + 0x96, 0xa0, 0xcc, 0xc6, 0x49, 0x34, 0x15, 0x66, 0x57, 0x85, 0x2a, 0xcb, 0x5b, 0x6b, 0xb2, 0x95, + 0xa6, 0x21, 0x01, 0xcc, 0x36, 0x9d, 0x28, 0xe6, 0x1a, 0xb0, 0xa8, 0xe2, 0x11, 0x4f, 0x83, 0x79, + 0xc9, 0xd1, 0x7a, 0x96, 0x11, 0xf6, 0xf2, 0xb6, 0xbf, 0x35, 0x02, 0x53, 0x29, 0xcb, 0xc8, 0x76, + 0x95, 0x4e, 0xc4, 0x5c, 0x1f, 0x95, 0x62, 0x51, 0xbb, 0xca, 0x2d, 0x09, 0x47, 0x45, 0xc1, 0xa8, + 0xdb, 0x4e, 0x14, 0xdd, 0x0b, 0xc2, 0xaa, 0x34, 0xe5, 0x8a, 0x7a, 0x4b, 0xc2, 0x51, 0x51, 0xb0, + 0xfd, 0x65, 0x97, 0x3a, 0x21, 0x0d, 0x79, 0x01, 0x45, 0x76, 0x7f, 0xa9, 0x68, 0x14, 0x9a, 0x74, + 0xdc, 0x28, 0xc7, 0xcd, 0x68, 0xa5, 0xe9, 0x51, 0x3f, 0x16, 0x6a, 0xe6, 0x63, 0x94, 0x77, 0xd6, + 0xb7, 0x4d, 0xa6, 0xda, 0x28, 0x67, 0x10, 0x98, 0x15, 0x4f, 0x3e, 0x69, 0xc1, 0x94, 0x73, 0x2f, + 0xd2, 0x15, 0xba, 0xdc, 0x2a, 0x0f, 0xbd, 0x49, 0xa5, 0x8a, 0x7e, 0x2b, 0xb3, 0xcc, 0xbc, 0xa7, + 0x40, 0x98, 0x16, 0x4a, 0xbe, 0x6c, 0x01, 0xa1, 0x07, 0xd4, 0xdd, 0x0a, 0x83, 0x7d, 0xaf, 0x9a, + 0x7c, 0x43, 0x19, 0xfe, 0x0c, 0xe9, 0x6d, 0x5f, 0xea, 0xe1, 0x2b, 0xac, 0x7a, 0x2f, 0x1c, 0xfb, + 0xe8, 0x60, 0xff, 0x45, 0x11, 0x26, 0x0c, 0x63, 0xdc, 0x77, 0x67, 0xb5, 0x7e, 0xc0, 0x76, 0xd6, + 0xc2, 0x09, 0x76, 0xd6, 0x8f, 0x41, 0xd9, 0x4d, 0x0c, 0x45, 0x3e, 0x15, 0xc5, 0x59, 0xf3, 0xa3, + 0x6d, 0x85, 0x02, 0xa1, 0x96, 0x49, 0xae, 0xc0, 0xac, 0xc1, 0x46, 0x1a, 0x99, 0x11, 0x6e, 0x64, + 0x54, 0xa2, 0x69, 0x39, 0x4b, 0x80, 0xbd, 0x6d, 0xc8, 0xf3, 0xcc, 0xab, 0xf5, 0x64, 0xbf, 0x44, + 0x14, 0x2f, 0xab, 0x75, 0x97, 0xb7, 0xd6, 0x12, 0x30, 0x9a, 0x34, 0xf6, 0xb7, 0x2c, 0xf5, 0x71, + 0x9f, 0x40, 0xa1, 0xc6, 0xdd, 0x74, 0xa1, 0xc6, 0xa5, 0x5c, 0x86, 0x79, 0x40, 0x91, 0xc6, 0x0d, + 0x18, 0x5f, 0x09, 0x5a, 0x2d, 0xc7, 0xaf, 0x92, 0x37, 0xc1, 0xb8, 0x2b, 0xfe, 0x94, 0x61, 0xe2, + 0x04, 0xdb, 0xbf, 0x25, 0x16, 0x13, 0x1c, 0x79, 0x06, 0x46, 0x9c, 0xb0, 0x9e, 0x84, 0x86, 0xfc, + 0xec, 0x68, 0x39, 0xac, 0x47, 0xc8, 0xa1, 0xf6, 0x97, 0x0a, 0x00, 0x2b, 0x41, 0xab, 0xed, 0x84, + 0xb4, 0xba, 0x13, 0xfc, 0x4f, 0x8e, 0x58, 0x44, 0x0c, 0x9f, 0xb1, 0x80, 0xb0, 0x51, 0x09, 0x7c, + 0xea, 0xc7, 0xea, 0xf0, 0x95, 0xed, 0x97, 0x6e, 0x02, 0x95, 0x9b, 0x8f, 0x5e, 0x03, 0x09, 0x02, + 0x35, 0xcd, 0x31, 0xa2, 0x88, 0x67, 0x93, 0x1d, 0xbf, 0x98, 0xae, 0x69, 0xe0, 0x07, 0xa5, 0xd2, + 0x01, 0xb0, 0xbf, 0x5e, 0x80, 0x73, 0xc2, 0x6c, 0x6d, 0x38, 0xbe, 0x53, 0xa7, 0x2d, 0xa6, 0xd5, + 0x71, 0x4f, 0x1b, 0x5c, 0xe6, 0xbe, 0x7a, 0x49, 0x09, 0xc3, 0xb0, 0x93, 0x53, 0x4c, 0x2a, 0x31, + 0x8d, 0xd6, 0x7c, 0x2f, 0x46, 0xce, 0x9c, 0x44, 0x50, 0x4a, 0xee, 0x88, 0x48, 0x63, 0x93, 0x93, + 0x20, 0xb5, 0xee, 0xae, 0x48, 0xf6, 0xa8, 0x04, 0xb1, 0xcd, 0xbd, 0x19, 0xb8, 0x7b, 0x48, 0xdb, + 0x01, 0x37, 0x2c, 0xc6, 0x09, 0xf2, 0xba, 0x84, 0xa3, 0xa2, 0xb0, 0xbf, 0x6e, 0x41, 0xd6, 0xe4, + 0xf2, 0x68, 0x50, 0xd4, 0x0c, 0x66, 0xa3, 0xc1, 0x74, 0x89, 0xdf, 0x09, 0x2a, 0xe6, 0x3e, 0x00, + 0x13, 0x4e, 0x1c, 0xd3, 0x56, 0x5b, 0x84, 0x26, 0xc5, 0x47, 0x4b, 0x7f, 0x6d, 0x04, 0x55, 0xaf, + 0xe6, 0xf1, 0x90, 0xc4, 0x64, 0x67, 0xdf, 0x84, 0x52, 0x72, 0xe2, 0x73, 0x8c, 0x4f, 0xff, 0x6c, + 0xca, 0x9d, 0x1c, 0x30, 0xb9, 0x1e, 0x14, 0xa0, 0xcf, 0x9e, 0xc9, 0xba, 0xac, 0xad, 0x4b, 0xaa, + 0xcb, 0x27, 0xb3, 0x30, 0xe4, 0x40, 0x9c, 0x76, 0x89, 0x3c, 0xcb, 0xfb, 0xf2, 0xde, 0xf3, 0xf5, + 0x01, 0xd8, 0x84, 0xd4, 0x4f, 0x1d, 0x82, 0x91, 0x8b, 0x00, 0x7a, 0x53, 0x90, 0x85, 0x1e, 0x2a, + 0x53, 0xab, 0xf7, 0x0e, 0x34, 0xa8, 0x98, 0x0b, 0xe8, 0xf9, 0x51, 0xec, 0x34, 0x9b, 0x57, 0x3d, + 0x3f, 0x96, 0xb1, 0xac, 0x32, 0x18, 0x6b, 0x1a, 0x85, 0x26, 0xdd, 0xfc, 0x3b, 0x8d, 0xef, 0x72, + 0x12, 0xb7, 0xfe, 0x33, 0x05, 0x98, 0xbe, 0xe2, 0x77, 0xb6, 0xae, 0x6c, 0x75, 0x76, 0x9b, 0x9e, + 0x7b, 0x9d, 0x76, 0xd9, 0x47, 0xdb, 0xa3, 0xdd, 0xb5, 0x55, 0x39, 0xec, 0xea, 0xa3, 0x5d, 0x67, + 0x40, 0x14, 0x38, 0xa6, 0x66, 0xcd, 0xf3, 0xeb, 0x34, 0x6c, 0x87, 0x9e, 0xf4, 0xdd, 0x0d, 0x35, + 0x2f, 0x6b, 0x14, 0x9a, 0x74, 0x8c, 0x77, 0x70, 0xcf, 0xa7, 0x61, 0xd6, 0xda, 0x6c, 0x32, 0x20, + 0x0a, 0x1c, 0x23, 0x8a, 0xc3, 0x4e, 0x14, 0xcb, 0x11, 0x53, 0x44, 0x3b, 0x0c, 0x88, 0x02, 0xc7, + 0xa6, 0x47, 0xd4, 0xd9, 0xe5, 0x59, 0xd8, 0xcc, 0x79, 0xf8, 0xb6, 0x00, 0x63, 0x82, 0x67, 0xa4, + 0x7b, 0xb4, 0xbb, 0xca, 0xf6, 0xde, 0x4c, 0xc5, 0xca, 0x75, 0x01, 0xc6, 0x04, 0x6f, 0xff, 0xad, + 0x05, 0x24, 0x3d, 0x1c, 0x4f, 0x60, 0xfb, 0x7e, 0x2d, 0xbd, 0x7d, 0x0f, 0x99, 0x30, 0x4f, 0xab, + 0x3f, 0x60, 0x17, 0xff, 0x45, 0x0b, 0x26, 0xcd, 0xb3, 0x13, 0x52, 0xcf, 0x18, 0xa2, 0xcd, 0xb4, + 0x21, 0x7a, 0x70, 0xb8, 0xf0, 0xa3, 0xfd, 0x2e, 0x3c, 0xd6, 0xbd, 0x38, 0x68, 0x47, 0x6f, 0xa7, + 0x7e, 0xdd, 0xf3, 0x29, 0xcf, 0x0c, 0x8a, 0x33, 0x97, 0xd4, 0xc1, 0xcc, 0x4a, 0x50, 0xa5, 0x8f, + 0x60, 0xc9, 0xec, 0x3b, 0x30, 0xdb, 0x53, 0xa6, 0x74, 0x0c, 0xa3, 0x73, 0x64, 0x15, 0xa8, 0x8d, + 0x30, 0xc1, 0x18, 0x6f, 0xb6, 0xc5, 0xe1, 0xc8, 0x0a, 0xcc, 0x8a, 0x6a, 0x2b, 0x26, 0x69, 0xdb, + 0x6d, 0xd0, 0x96, 0x2a, 0x3d, 0xe3, 0x81, 0xe2, 0xed, 0x2c, 0x12, 0x7b, 0xe9, 0xed, 0xcf, 0x5a, + 0x30, 0x95, 0xaa, 0x1c, 0xcb, 0xc9, 0x3c, 0xf2, 0x95, 0x16, 0xf0, 0xa3, 0xbc, 0xd0, 0xf3, 0x45, + 0xae, 0xaf, 0x64, 0xac, 0x34, 0x8d, 0x42, 0x93, 0xce, 0xfe, 0x62, 0x01, 0x4a, 0x49, 0x56, 0xf8, + 0x18, 0xaa, 0x7c, 0xda, 0x82, 0x29, 0x15, 0x9c, 0x73, 0x97, 0x5d, 0x4c, 0xc6, 0x1b, 0xc3, 0xe7, + 0xa5, 0xd5, 0x79, 0x2f, 0x73, 0xd9, 0x55, 0xec, 0x80, 0xa6, 0x30, 0x4c, 0xcb, 0x26, 0xb7, 0x01, + 0xa2, 0x6e, 0x14, 0xd3, 0x96, 0x11, 0x3c, 0xd8, 0xc6, 0x8a, 0x5b, 0x74, 0x83, 0x90, 0xb2, 0xf5, + 0x75, 0x23, 0xa8, 0xd2, 0x6d, 0x45, 0xa9, 0x8d, 0xab, 0x86, 0xa1, 0xc1, 0xc9, 0xfe, 0xb5, 0x02, + 0xcc, 0x64, 0x55, 0x22, 0xef, 0x87, 0xc9, 0x44, 0xba, 0x71, 0x77, 0x34, 0x49, 0x85, 0x4f, 0xa2, + 0x81, 0x7b, 0x70, 0xb8, 0xb0, 0xd0, 0x7b, 0x79, 0x76, 0xd1, 0x24, 0xc1, 0x14, 0x33, 0x91, 0x21, + 0x91, 0xa9, 0xbc, 0x4a, 0x77, 0xb9, 0xdd, 0x96, 0x69, 0x0e, 0x23, 0x43, 0x62, 0x62, 0x31, 0x43, + 0x4d, 0xb6, 0xe0, 0x8c, 0x01, 0xb9, 0x41, 0xbd, 0x7a, 0x63, 0x37, 0x08, 0xc5, 0x15, 0x83, 0x62, + 0xe5, 0x19, 0xc9, 0xe5, 0x0c, 0xf6, 0xa1, 0xc1, 0xbe, 0x2d, 0x99, 0xd3, 0xe2, 0x3a, 0x6d, 0xc7, + 0xf5, 0xe2, 0xae, 0x8c, 0x86, 0x94, 0x6d, 0x5a, 0x91, 0x70, 0x54, 0x14, 0xf6, 0x06, 0x8c, 0x1c, + 0x73, 0x06, 0x1d, 0x6b, 0xaf, 0xbf, 0x09, 0x25, 0xc6, 0x8e, 0xd9, 0xa2, 0xbc, 0x58, 0x06, 0x50, + 0x4a, 0x6e, 0x9c, 0x10, 0x1b, 0x8a, 0x9e, 0x93, 0x24, 0xa1, 0x54, 0xb7, 0xd6, 0xa2, 0xa8, 0xc3, + 0x3d, 0x19, 0x86, 0x24, 0xcf, 0x42, 0x91, 0x1e, 0xb4, 0xb3, 0xd9, 0xa6, 0x4b, 0x07, 0x6d, 0x2f, + 0xa4, 0x11, 0x23, 0xa2, 0x07, 0x6d, 0x32, 0x0f, 0x05, 0xaf, 0x2a, 0x37, 0x29, 0x90, 0x34, 0x85, + 0xb5, 0x55, 0x2c, 0x78, 0x55, 0xfb, 0x00, 0xca, 0xea, 0x8a, 0x0b, 0xd9, 0x4b, 0x6c, 0xb7, 0x95, + 0xc7, 0x31, 0x4e, 0xc2, 0x77, 0x80, 0xd5, 0xee, 0x00, 0xe8, 0x3a, 0xbd, 0xbc, 0xec, 0xcb, 0x05, + 0x18, 0x71, 0x03, 0x59, 0xde, 0x5b, 0xd2, 0x6c, 0xb8, 0xd1, 0xe6, 0x18, 0xfb, 0x0e, 0x4c, 0x5f, + 0xf7, 0x83, 0x7b, 0x3e, 0xdb, 0x4c, 0x2f, 0x7b, 0xb4, 0x59, 0x65, 0x8c, 0x6b, 0xec, 0x8f, 0xac, + 0x8b, 0xc0, 0xb1, 0x28, 0x70, 0xea, 0x1e, 0x48, 0x61, 0xd0, 0x3d, 0x10, 0xfb, 0xe3, 0x16, 0xcc, + 0xa8, 0x02, 0xb2, 0xc4, 0x1a, 0xbf, 0x08, 0x93, 0xbb, 0x1d, 0xaf, 0x59, 0x95, 0xbf, 0xa5, 0x08, + 0x55, 0x22, 0x57, 0x31, 0x70, 0x98, 0xa2, 0x64, 0xee, 0xd6, 0xae, 0xe7, 0x3b, 0x61, 0x77, 0x4b, + 0x9b, 0x7f, 0x65, 0x11, 0x2a, 0x0a, 0x83, 0x06, 0x95, 0xfd, 0xa7, 0x45, 0xd0, 0xd7, 0x5b, 0x88, + 0x27, 0x2b, 0x21, 0xac, 0x3c, 0x72, 0x55, 0xdb, 0x5d, 0xdf, 0xd5, 0x17, 0x69, 0x4a, 0x99, 0x42, + 0x88, 0x4f, 0x59, 0xcc, 0xd1, 0xf3, 0x62, 0xcf, 0xe1, 0xeb, 0x53, 0x46, 0x47, 0x5b, 0x39, 0x1d, + 0x96, 0xaf, 0x09, 0xce, 0x41, 0x68, 0xba, 0x8e, 0x4a, 0x18, 0x9a, 0x92, 0xc9, 0xab, 0xf2, 0x78, + 0xa1, 0x98, 0x5b, 0x1d, 0x4d, 0x29, 0x73, 0xa6, 0xd0, 0x86, 0xd1, 0x90, 0xc6, 0x61, 0x52, 0xc1, + 0x74, 0x7d, 0xd8, 0xc3, 0xd6, 0x38, 0xec, 0x6e, 0xc7, 0x2c, 0x02, 0xab, 0x1b, 0xfe, 0x0d, 0x07, + 0xa3, 0x10, 0x64, 0x47, 0x40, 0x7a, 0xc7, 0xe2, 0x84, 0xa9, 0xdb, 0x25, 0x28, 0x3b, 0x9d, 0x38, + 0x68, 0xb1, 0x61, 0xe2, 0x9f, 0xa7, 0x64, 0x24, 0xa7, 0x13, 0x04, 0x6a, 0x1a, 0xfb, 0xf3, 0xa3, + 0x90, 0x29, 0x4d, 0x20, 0x07, 0xe6, 0xd5, 0x2c, 0x2b, 0xdf, 0xab, 0x59, 0x4a, 0x99, 0x7e, 0xd7, + 0xb3, 0x48, 0x1d, 0x46, 0xdb, 0x0d, 0x27, 0x4a, 0x96, 0xdf, 0xcd, 0x64, 0x98, 0xb6, 0x18, 0xf0, + 0xc1, 0xe1, 0xc2, 0x7b, 0x8f, 0xe7, 0xce, 0xb1, 0xb9, 0xba, 0x24, 0xea, 0x34, 0xb5, 0x68, 0xce, + 0x03, 0x05, 0x7f, 0xd3, 0xa1, 0x2b, 0x1e, 0x11, 0x9a, 0x7e, 0xc2, 0x12, 0xf5, 0x6c, 0x48, 0xa3, + 0x4e, 0x33, 0x96, 0xb3, 0xe1, 0x66, 0x8e, 0xab, 0x4c, 0x30, 0xd6, 0x85, 0x6d, 0xe2, 0x37, 0x1a, + 0x42, 0xc9, 0xfb, 0xa1, 0x1c, 0xc5, 0x4e, 0x18, 0x3f, 0x62, 0x19, 0x8c, 0x1a, 0xf4, 0xed, 0x84, + 0x09, 0x6a, 0x7e, 0xe4, 0x15, 0x80, 0x9a, 0xe7, 0x7b, 0x51, 0xe3, 0x11, 0x4f, 0x05, 0xb9, 0xe2, + 0x97, 0x15, 0x07, 0x34, 0xb8, 0x31, 0xeb, 0xc6, 0xe7, 0xb6, 0xc8, 0x63, 0x96, 0xf8, 0xf6, 0xa5, + 0xac, 0x1b, 0x2a, 0x0c, 0x1a, 0x54, 0xf6, 0x47, 0xe1, 0x74, 0xf6, 0x62, 0xb5, 0x8c, 0xf0, 0xea, + 0x61, 0xd0, 0x69, 0x67, 0xcd, 0x37, 0xbf, 0x78, 0x8b, 0x02, 0xc7, 0xcc, 0xf7, 0x9e, 0xe7, 0x57, + 0xb3, 0xe6, 0xfb, 0xba, 0xe7, 0x57, 0x91, 0x63, 0x8e, 0x71, 0x67, 0xed, 0x77, 0x2d, 0xb8, 0x70, + 0xd4, 0xfd, 0x6f, 0x16, 0xbd, 0xdf, 0x73, 0x42, 0x5f, 0x5e, 0x87, 0xe1, 0xb6, 0xe3, 0x8e, 0x13, + 0xfa, 0xc8, 0xa1, 0xa4, 0x0b, 0x63, 0xa2, 0xf4, 0x4f, 0x3a, 0xa4, 0x37, 0xf3, 0xbd, 0x8d, 0xce, + 0x42, 0x24, 0x95, 0x74, 0x11, 0x65, 0x87, 0x28, 0x05, 0xda, 0xdf, 0xb5, 0x80, 0x6c, 0xee, 0xd3, + 0x30, 0xf4, 0xaa, 0x46, 0xb1, 0x22, 0x79, 0x01, 0x26, 0xef, 0x6e, 0x6f, 0xde, 0xd8, 0x0a, 0x3c, + 0x9f, 0xdf, 0xc7, 0x30, 0x4a, 0x64, 0xae, 0x19, 0x70, 0x4c, 0x51, 0xb1, 0x20, 0xe3, 0xee, 0x6b, + 0x6c, 0xcb, 0xb9, 0x74, 0xd0, 0x0e, 0x69, 0x14, 0xa9, 0x37, 0x1c, 0x64, 0x90, 0x71, 0xed, 0x66, + 0x06, 0x89, 0xbd, 0xf4, 0x64, 0x13, 0xce, 0xb6, 0x78, 0x02, 0xae, 0xca, 0x77, 0xda, 0x48, 0x64, + 0xe3, 0xc2, 0xa4, 0xe0, 0xfd, 0x0d, 0xf7, 0x0f, 0x17, 0xce, 0x6e, 0xf4, 0x23, 0xc0, 0xfe, 0xed, + 0xec, 0xaf, 0x15, 0x60, 0xc2, 0x78, 0x43, 0xe1, 0x18, 0x3e, 0x45, 0xe6, 0xd9, 0x87, 0xc2, 0x31, + 0x9f, 0x7d, 0x78, 0x0e, 0x4a, 0xed, 0xa0, 0xe9, 0xb9, 0x9e, 0xaa, 0xce, 0x9f, 0xe4, 0x67, 0x60, + 0x12, 0x86, 0x0a, 0x4b, 0xee, 0x41, 0x59, 0x5d, 0x65, 0x96, 0xf5, 0x7a, 0x79, 0x79, 0x55, 0x6a, + 0xf1, 0xea, 0x2b, 0xca, 0x5a, 0x16, 0xb1, 0x61, 0x8c, 0xcf, 0xfc, 0x24, 0xc3, 0xcf, 0x0b, 0x40, + 0xf8, 0x92, 0x88, 0x50, 0x62, 0xec, 0x7f, 0x18, 0x85, 0x32, 0xd2, 0x76, 0xb0, 0x12, 0xd2, 0x6a, + 0x44, 0xde, 0x08, 0xc5, 0x4e, 0xd8, 0x94, 0x83, 0xa5, 0xd2, 0x3f, 0xb7, 0x70, 0x1d, 0x19, 0x3c, + 0xb5, 0xdd, 0x14, 0x4e, 0x74, 0x52, 0x58, 0x3c, 0xf2, 0xa4, 0xf0, 0x25, 0x98, 0x8a, 0xa2, 0xc6, + 0x56, 0xe8, 0xed, 0x3b, 0x31, 0x9b, 0xc4, 0x32, 0x57, 0xa2, 0x8f, 0x66, 0xb6, 0xaf, 0x6a, 0x24, + 0xa6, 0x69, 0xc9, 0x15, 0x98, 0xd5, 0xe7, 0x75, 0x34, 0x8c, 0x79, 0x6a, 0x44, 0x64, 0x51, 0xd4, + 0xc9, 0x88, 0x3e, 0xe1, 0x93, 0x04, 0xd8, 0xdb, 0x86, 0xac, 0xc2, 0x4c, 0x0a, 0xc8, 0x14, 0x11, + 0x29, 0x16, 0x55, 0x0b, 0x90, 0xe2, 0xc3, 0x74, 0xe9, 0x69, 0x41, 0x36, 0xe0, 0xb4, 0xf8, 0xbe, + 0xfc, 0x0a, 0xbc, 0xea, 0xd1, 0x38, 0x67, 0xf4, 0xbf, 0x24, 0xa3, 0xd3, 0x57, 0x7a, 0x49, 0xb0, + 0x5f, 0x3b, 0x36, 0x43, 0x15, 0x78, 0x6d, 0x55, 0x5a, 0x4a, 0x35, 0x43, 0x15, 0x9b, 0xb5, 0x2a, + 0x9a, 0x74, 0xe4, 0x7d, 0xf0, 0xb4, 0xfe, 0x29, 0x32, 0x6b, 0xc2, 0x7d, 0x58, 0x95, 0xa5, 0x10, + 0xea, 0xa6, 0xd1, 0x95, 0xbe, 0x64, 0x55, 0x1c, 0xd4, 0x9e, 0xec, 0xc2, 0xbc, 0x42, 0x5d, 0x62, + 0xe6, 0xa0, 0x1d, 0x7a, 0x11, 0xad, 0x38, 0x11, 0xbd, 0x15, 0x36, 0x79, 0xf1, 0x44, 0x59, 0x3f, + 0x04, 0x71, 0xc5, 0x8b, 0xaf, 0xf6, 0xa3, 0xc4, 0x75, 0x7c, 0x08, 0x17, 0xe6, 0xad, 0x50, 0xdf, + 0xd9, 0x6d, 0xd2, 0xcd, 0x95, 0x35, 0x5e, 0x52, 0x61, 0x78, 0x2b, 0x97, 0x12, 0x04, 0x6a, 0x1a, + 0xe5, 0x9e, 0x4f, 0x0e, 0x74, 0xcf, 0xbf, 0x63, 0xc1, 0x94, 0x9a, 0xec, 0x4f, 0x20, 0x0f, 0xd6, + 0x4c, 0xe7, 0xc1, 0xae, 0x0c, 0xeb, 0x26, 0x4a, 0xcd, 0x07, 0x04, 0x53, 0xdf, 0x2f, 0x03, 0xf0, + 0xa7, 0x75, 0x3c, 0x5e, 0xaa, 0x7b, 0x01, 0x46, 0x42, 0xda, 0x0e, 0xb2, 0x96, 0x8f, 0xe7, 0xf0, + 0x39, 0xe6, 0x07, 0x77, 0x39, 0xf7, 0x3b, 0x39, 0x1e, 0xfd, 0xef, 0x3d, 0x39, 0xde, 0x86, 0xb3, + 0x9e, 0x1f, 0x51, 0xb7, 0x13, 0xca, 0x9d, 0xf3, 0x6a, 0x10, 0x29, 0xeb, 0x50, 0xaa, 0xbc, 0x51, + 0x32, 0x3a, 0xbb, 0xd6, 0x8f, 0x08, 0xfb, 0xb7, 0x65, 0x43, 0x9a, 0x20, 0xe4, 0x9d, 0x20, 0x1d, + 0xe2, 0x4b, 0x38, 0x2a, 0x0a, 0xbd, 0x20, 0xd6, 0x6b, 0xc9, 0xa5, 0x9f, 0xcc, 0x82, 0x58, 0xbf, + 0xbc, 0x8d, 0x9a, 0xa6, 0xbf, 0x55, 0x2c, 0xe7, 0x64, 0x15, 0xe1, 0xc4, 0x56, 0x31, 0x59, 0x9f, + 0x13, 0x03, 0x9f, 0x51, 0x48, 0x36, 0xeb, 0xc9, 0x81, 0x9b, 0xf5, 0xcb, 0x30, 0xed, 0xf9, 0x0d, + 0x1a, 0x7a, 0x31, 0xad, 0xf2, 0xb5, 0x30, 0x37, 0xc5, 0x07, 0x42, 0x65, 0x9f, 0xd6, 0x52, 0x58, + 0xcc, 0x50, 0xa7, 0x8d, 0xca, 0xf4, 0x31, 0x8c, 0xca, 0x00, 0x53, 0x7e, 0x2a, 0x1f, 0x53, 0x3e, + 0x33, 0xbc, 0x29, 0x9f, 0x7d, 0xac, 0xa6, 0x9c, 0xe4, 0x62, 0xca, 0x9f, 0x85, 0xd1, 0x76, 0x18, + 0x1c, 0x74, 0xe7, 0x4e, 0xa7, 0xdd, 0xf3, 0x2d, 0x06, 0x44, 0x81, 0x33, 0x0b, 0xe8, 0xce, 0x3c, + 0xbc, 0x80, 0xce, 0x7e, 0xbd, 0x00, 0x67, 0xb5, 0xa5, 0x63, 0xf3, 0xcb, 0xab, 0xb1, 0xb5, 0xce, + 0x6f, 0x66, 0x8a, 0xa2, 0x0d, 0x23, 0xf1, 0xa9, 0x73, 0xa8, 0x0a, 0x83, 0x06, 0x15, 0xcf, 0x1f, + 0xd2, 0x90, 0x97, 0xfd, 0x66, 0xcd, 0xe0, 0x8a, 0x84, 0xa3, 0xa2, 0xe0, 0xef, 0xf2, 0xd1, 0x30, + 0x96, 0x67, 0x32, 0xd9, 0x8a, 0xa6, 0x15, 0x8d, 0x42, 0x93, 0x8e, 0xb9, 0x8b, 0x6e, 0xb2, 0x04, + 0x99, 0x29, 0x9c, 0x14, 0xee, 0xa2, 0x5a, 0x75, 0x0a, 0x9b, 0xa8, 0xc3, 0x13, 0xc5, 0xa3, 0xbd, + 0xea, 0xf0, 0x2c, 0x84, 0xa2, 0xb0, 0xff, 0xcd, 0x82, 0x37, 0xf4, 0x1d, 0x8a, 0x27, 0xb0, 0xbd, + 0x1d, 0xa4, 0xb7, 0xb7, 0xed, 0xe1, 0xb7, 0xb7, 0x9e, 0x5e, 0x0c, 0xd8, 0xea, 0xfe, 0xdc, 0x82, + 0x69, 0x4d, 0xff, 0x04, 0xba, 0xea, 0xe5, 0xfa, 0xc2, 0x9e, 0x56, 0x5d, 0x94, 0xa3, 0xa6, 0xfa, + 0xf6, 0x1d, 0xde, 0x37, 0x11, 0xcc, 0x2d, 0xbb, 0xc9, 0x03, 0x34, 0x47, 0x04, 0x31, 0x5d, 0x18, + 0xe3, 0x57, 0xf8, 0xa3, 0x7c, 0x82, 0xca, 0xb4, 0x7c, 0x7e, 0x02, 0xa4, 0x83, 0x4a, 0xfe, 0x33, + 0x42, 0x29, 0x90, 0x17, 0xa5, 0x7b, 0x11, 0xb3, 0x97, 0x55, 0x99, 0x72, 0xd5, 0x45, 0xe9, 0x12, + 0x8e, 0x8a, 0xc2, 0x6e, 0xc1, 0x5c, 0x9a, 0xf9, 0x2a, 0xad, 0xf1, 0xdc, 0xdd, 0xb1, 0xba, 0xb9, + 0x04, 0x65, 0x87, 0xb7, 0x5a, 0xef, 0x38, 0xd9, 0x57, 0x68, 0x96, 0x13, 0x04, 0x6a, 0x1a, 0xfb, + 0x57, 0x2d, 0x38, 0xdd, 0xa7, 0x33, 0x39, 0xa6, 0x9a, 0x63, 0x6d, 0x05, 0x06, 0xbc, 0x0c, 0x54, + 0xa5, 0x35, 0x27, 0xc9, 0x0e, 0x19, 0x56, 0x6d, 0x55, 0x80, 0x31, 0xc1, 0xdb, 0xff, 0x68, 0xc1, + 0xa9, 0xb4, 0xae, 0x11, 0xb9, 0x06, 0x44, 0x74, 0x66, 0xd5, 0x8b, 0xdc, 0x60, 0x9f, 0x86, 0x5d, + 0xd6, 0x73, 0xa1, 0xf5, 0xbc, 0xe4, 0x44, 0x96, 0x7b, 0x28, 0xb0, 0x4f, 0x2b, 0x5e, 0xfb, 0x5b, + 0x55, 0xa3, 0x9d, 0xcc, 0x94, 0xdb, 0x79, 0xce, 0x14, 0xfd, 0x31, 0xcd, 0x08, 0x5a, 0x89, 0x44, + 0x53, 0xbe, 0xfd, 0xdd, 0x11, 0x50, 0x67, 0x51, 0x3c, 0x0f, 0x91, 0x53, 0x16, 0x27, 0xf5, 0x54, + 0x51, 0xf1, 0x04, 0x4f, 0x15, 0x8d, 0x3c, 0x2c, 0x47, 0x20, 0xde, 0xcd, 0xd1, 0xbe, 0xa8, 0x61, + 0xf4, 0x77, 0x34, 0x0a, 0x4d, 0x3a, 0xa6, 0x49, 0xd3, 0xdb, 0xa7, 0xa2, 0xd1, 0x58, 0x5a, 0x93, + 0xf5, 0x04, 0x81, 0x9a, 0x86, 0x69, 0x52, 0xf5, 0x6a, 0x35, 0x19, 0x29, 0x2a, 0x4d, 0xd8, 0xe8, + 0x20, 0xc7, 0x30, 0x8a, 0x46, 0x10, 0xec, 0x49, 0xff, 0x4f, 0x51, 0x5c, 0x0d, 0x82, 0x3d, 0xe4, + 0x18, 0xe6, 0xb1, 0xf8, 0x41, 0xd8, 0x72, 0x9a, 0xde, 0x87, 0x68, 0x55, 0x49, 0x91, 0x7e, 0x9f, + 0xf2, 0x58, 0x6e, 0xf4, 0x92, 0x60, 0xbf, 0x76, 0x6c, 0x06, 0xb6, 0x43, 0x5a, 0xf5, 0xdc, 0xd8, + 0xe4, 0x06, 0xe9, 0x19, 0xb8, 0xd5, 0x43, 0x81, 0x7d, 0x5a, 0x91, 0x65, 0x38, 0x95, 0x9c, 0x25, + 0x26, 0x35, 0x24, 0xc2, 0x19, 0x54, 0x7e, 0x38, 0xa6, 0xd1, 0x98, 0xa5, 0x67, 0xd6, 0xa6, 0x25, + 0x2b, 0x79, 0xb8, 0x9b, 0x68, 0x58, 0x9b, 0xa4, 0xc2, 0x07, 0x15, 0x85, 0xfd, 0x89, 0x22, 0xdb, + 0x1d, 0x07, 0xdc, 0xce, 0x7d, 0x62, 0x59, 0xc3, 0xf4, 0x8c, 0x1c, 0x39, 0xc6, 0x8c, 0x7c, 0x01, + 0x26, 0xef, 0x46, 0x81, 0xaf, 0x32, 0x72, 0xa3, 0x03, 0x33, 0x72, 0x06, 0x55, 0xff, 0x8c, 0xdc, + 0x58, 0x5e, 0x19, 0xb9, 0xf1, 0x47, 0xcc, 0xc8, 0xfd, 0xd1, 0x28, 0x9c, 0x53, 0xe7, 0xc9, 0x34, + 0xbe, 0x17, 0x84, 0x7b, 0x9e, 0x5f, 0xe7, 0x67, 0xb0, 0x5f, 0xb5, 0x60, 0x52, 0xac, 0x17, 0xf9, + 0x30, 0x82, 0x38, 0x73, 0xac, 0xe5, 0x74, 0x77, 0x2d, 0x25, 0x6c, 0x71, 0xc7, 0x10, 0x94, 0x79, + 0xa5, 0xc2, 0x44, 0x61, 0x4a, 0x23, 0xf2, 0x11, 0x80, 0xe4, 0xc5, 0xac, 0x5a, 0x4e, 0xef, 0x86, + 0x25, 0xfa, 0x21, 0xad, 0x69, 0xdf, 0x74, 0x47, 0x09, 0x41, 0x43, 0x20, 0x79, 0xdd, 0x52, 0x77, + 0x45, 0xc4, 0x69, 0xd6, 0xab, 0x8f, 0x65, 0x6c, 0x8e, 0x73, 0x75, 0x04, 0x61, 0xdc, 0xf3, 0xeb, + 0x6c, 0x9e, 0xc8, 0x24, 0xe6, 0x5b, 0xfa, 0xd5, 0x2f, 0xac, 0x07, 0x4e, 0xb5, 0xe2, 0x34, 0x1d, + 0xdf, 0xa5, 0xe1, 0x9a, 0x20, 0x37, 0x9f, 0x4d, 0xe2, 0x00, 0x4c, 0x18, 0xf5, 0x5c, 0xce, 0x1c, + 0x3d, 0xce, 0xe5, 0xcc, 0xf9, 0xf7, 0xc0, 0x6c, 0xcf, 0xc7, 0x3c, 0xd1, 0xd5, 0x91, 0x47, 0xbf, + 0x75, 0x62, 0xff, 0xde, 0x98, 0xde, 0xb4, 0x6e, 0x04, 0x55, 0x71, 0x45, 0x30, 0xd4, 0x5f, 0x54, + 0xfa, 0x9e, 0x39, 0x4e, 0x11, 0xe3, 0xe9, 0x25, 0x05, 0x44, 0x53, 0x24, 0x9b, 0xa3, 0x6d, 0x27, + 0xa4, 0xfe, 0xe3, 0x9e, 0xa3, 0x5b, 0x4a, 0x08, 0x1a, 0x02, 0x49, 0x23, 0x75, 0xdc, 0x7a, 0x79, + 0xf8, 0xe3, 0x56, 0xe6, 0x0e, 0xf7, 0xbd, 0xca, 0xf5, 0x05, 0x0b, 0xa6, 0xfd, 0xd4, 0xcc, 0x95, + 0x47, 0x6e, 0x3b, 0x8f, 0x63, 0x55, 0x88, 0xab, 0xd9, 0x69, 0x18, 0x66, 0xe4, 0xf7, 0xdb, 0xd2, + 0x46, 0x4f, 0xb8, 0xa5, 0xe9, 0xbb, 0xc6, 0x63, 0x83, 0xee, 0x1a, 0x13, 0x5f, 0xbd, 0x32, 0x30, + 0x9e, 0xfb, 0x2b, 0x03, 0xd0, 0xe7, 0x85, 0x81, 0x3b, 0x50, 0x76, 0x43, 0xea, 0xc4, 0x8f, 0x78, + 0xe1, 0x9c, 0x3f, 0x76, 0xb7, 0x92, 0x30, 0x40, 0xcd, 0xcb, 0xfe, 0x93, 0x22, 0xcc, 0x24, 0x23, + 0x92, 0x1c, 0x45, 0xb1, 0xfd, 0x51, 0xc8, 0xd5, 0xce, 0xad, 0xda, 0x1f, 0xaf, 0x26, 0x08, 0xd4, + 0x34, 0xcc, 0x1f, 0xeb, 0x44, 0x74, 0xb3, 0x4d, 0xfd, 0x75, 0x6f, 0x37, 0xe2, 0x23, 0x6e, 0x94, + 0x90, 0xdd, 0xd2, 0x28, 0x34, 0xe9, 0x98, 0x33, 0x2e, 0xfc, 0xe2, 0x28, 0x7b, 0xb2, 0x2b, 0xfd, + 0x6d, 0x4c, 0xf0, 0xe4, 0x2b, 0x7d, 0x9f, 0x0b, 0xc9, 0xa7, 0xa6, 0xa1, 0xe7, 0x04, 0xee, 0x84, + 0xef, 0x84, 0x7c, 0xde, 0x82, 0x53, 0x7b, 0xa9, 0xfa, 0x95, 0xc4, 0x24, 0x0f, 0x59, 0x69, 0x99, + 0x2e, 0x8a, 0xd1, 0x53, 0x38, 0x0d, 0x8f, 0x30, 0x2b, 0xdd, 0xfe, 0x17, 0x0b, 0x4c, 0xf3, 0x74, + 0x3c, 0xcf, 0xca, 0x78, 0x00, 0xaa, 0x70, 0xc4, 0x03, 0x50, 0x89, 0x13, 0x56, 0x3c, 0x9e, 0xd3, + 0x3f, 0x72, 0x02, 0xa7, 0x7f, 0x74, 0xa0, 0xd7, 0xf6, 0x46, 0x28, 0x76, 0xbc, 0xaa, 0xf4, 0xdb, + 0xf5, 0x61, 0xd8, 0xda, 0x2a, 0x32, 0xb8, 0xfd, 0x3b, 0xa3, 0x3a, 0x4e, 0x97, 0x47, 0xf1, 0x3f, + 0x14, 0xdd, 0xae, 0xa9, 0xc2, 0x59, 0xd1, 0xf3, 0x1b, 0x3d, 0x85, 0xb3, 0xef, 0x3e, 0x79, 0xa5, + 0x85, 0x18, 0xa0, 0x41, 0x75, 0xb3, 0xe3, 0x47, 0x94, 0x59, 0xdc, 0x85, 0x12, 0x0b, 0x6d, 0x78, + 0xc2, 0xad, 0x94, 0x52, 0xaa, 0x74, 0x55, 0xc2, 0x1f, 0x1c, 0x2e, 0xbc, 0xeb, 0xe4, 0x6a, 0x25, + 0xad, 0x51, 0xf1, 0x27, 0x11, 0x94, 0xd9, 0xdf, 0xbc, 0x22, 0x44, 0x06, 0x4d, 0xb7, 0x94, 0x2d, + 0x4a, 0x10, 0xb9, 0x94, 0x9b, 0x68, 0x39, 0xc4, 0x87, 0x32, 0x7f, 0xaa, 0x88, 0x0b, 0x15, 0xb1, + 0xd5, 0x96, 0xaa, 0xcb, 0x48, 0x10, 0x0f, 0x0e, 0x17, 0x5e, 0x3a, 0xb9, 0x50, 0xd5, 0x1c, 0xb5, + 0x08, 0xfb, 0x6f, 0x8a, 0x7a, 0xee, 0xca, 0x7a, 0xe9, 0x1f, 0x8a, 0xb9, 0xfb, 0x62, 0x66, 0xee, + 0x5e, 0xe8, 0x99, 0xbb, 0xd3, 0xfa, 0x39, 0x9f, 0xd4, 0x6c, 0x7c, 0xd2, 0x1b, 0xec, 0xd1, 0x71, + 0x3c, 0xf7, 0x2c, 0x5e, 0xeb, 0x78, 0x21, 0x8d, 0xb6, 0xc2, 0x8e, 0xef, 0xf9, 0x75, 0xf9, 0xa8, + 0xa3, 0xe1, 0x59, 0xa4, 0xd0, 0x98, 0xa5, 0xb7, 0xbf, 0xc6, 0xcf, 0x3b, 0x8d, 0xe2, 0x32, 0xf6, + 0x95, 0x9b, 0xfc, 0xb5, 0x27, 0x51, 0x51, 0xaa, 0xbe, 0xb2, 0x78, 0xe2, 0x49, 0xe0, 0xc8, 0x3d, + 0x18, 0xdf, 0x15, 0x2f, 0x4e, 0xe4, 0x73, 0xc5, 0x49, 0x3e, 0x5f, 0xc1, 0x2f, 0x93, 0x26, 0x6f, + 0x59, 0x3c, 0xd0, 0x7f, 0x62, 0x22, 0xcd, 0xfe, 0xf9, 0x22, 0x9c, 0xca, 0xbc, 0x45, 0xc4, 0x02, + 0xfe, 0xe4, 0xe1, 0xa9, 0x6c, 0x76, 0x5e, 0x3d, 0x6a, 0xac, 0x28, 0xc8, 0x07, 0x01, 0xaa, 0xb4, + 0xdd, 0x0c, 0xba, 0xdc, 0x71, 0x19, 0x39, 0xb1, 0xe3, 0xa2, 0x7c, 0xdd, 0x55, 0xc5, 0x05, 0x0d, + 0x8e, 0xb2, 0x8c, 0x76, 0x54, 0xbc, 0xa7, 0x91, 0x2e, 0xa3, 0x35, 0x6e, 0xfa, 0x8d, 0x3d, 0xd9, + 0x9b, 0x7e, 0x1e, 0x9c, 0x12, 0x2a, 0xaa, 0x12, 0xae, 0x47, 0xa8, 0xd4, 0x3a, 0xcd, 0x66, 0xd4, + 0x6a, 0x9a, 0x0d, 0x66, 0xf9, 0xda, 0x9f, 0x2b, 0x30, 0xf7, 0x4d, 0x0c, 0xf6, 0x46, 0x92, 0x1c, + 0x7f, 0x33, 0x8c, 0x39, 0x9d, 0xb8, 0x11, 0xf4, 0xbc, 0x00, 0xb2, 0xcc, 0xa1, 0x28, 0xb1, 0x64, + 0x1d, 0x46, 0xaa, 0x4e, 0x9c, 0x3c, 0xca, 0x7f, 0x12, 0xe5, 0x74, 0x26, 0xcc, 0x89, 0x29, 0x72, + 0x2e, 0xe4, 0x19, 0x18, 0x89, 0x9d, 0x7a, 0xea, 0x05, 0xcf, 0x1d, 0xa7, 0x1e, 0x21, 0x87, 0x9a, + 0xbb, 0xcb, 0xc8, 0x11, 0xbb, 0xcb, 0x4b, 0xc6, 0x3f, 0x9c, 0x30, 0x4e, 0x5d, 0x7a, 0xff, 0x49, + 0x84, 0x28, 0xec, 0x4f, 0xd1, 0xda, 0xff, 0x0f, 0x26, 0xcd, 0x7f, 0x22, 0x71, 0xac, 0xbb, 0x46, + 0xf6, 0xdf, 0x8f, 0xc0, 0x54, 0xaa, 0xcc, 0x2f, 0x35, 0xcb, 0xad, 0x23, 0x67, 0x39, 0x3f, 0x4f, + 0xeb, 0xf8, 0x54, 0x16, 0x71, 0x1a, 0xe7, 0x69, 0x1d, 0x9f, 0xa2, 0xc0, 0xb1, 0xaf, 0x52, 0x0d, + 0xbb, 0xd8, 0xf1, 0x65, 0x56, 0x5e, 0x7d, 0x95, 0x55, 0x0e, 0x45, 0x89, 0x65, 0x01, 0xec, 0x64, + 0xc4, 0x8d, 0xa2, 0xb0, 0x11, 0x72, 0xd5, 0x5c, 0xcb, 0xe3, 0xd5, 0x34, 0x59, 0xd2, 0xca, 0x03, + 0x7a, 0x13, 0x82, 0x29, 0x89, 0xe4, 0x93, 0x96, 0xf9, 0x5e, 0xdc, 0x58, 0x1e, 0xa7, 0x49, 0xd9, + 0x2a, 0x4a, 0xb1, 0x82, 0x1e, 0xfe, 0x6c, 0x5c, 0xa4, 0x16, 0xf0, 0xf8, 0xe3, 0x59, 0xc0, 0xd0, + 0x67, 0xf1, 0xbe, 0x15, 0xca, 0x2d, 0xc7, 0xf7, 0x6a, 0x34, 0x8a, 0xc5, 0x3f, 0x80, 0x29, 0x8b, + 0xe8, 0x69, 0x23, 0x01, 0xa2, 0xc6, 0xf3, 0x7f, 0xb3, 0xc4, 0x3b, 0x26, 0x82, 0x98, 0xb2, 0xf1, + 0x6f, 0x96, 0x34, 0x18, 0x4d, 0x1a, 0xfb, 0x37, 0x2c, 0x38, 0xdb, 0x77, 0x30, 0x7e, 0x70, 0xd3, + 0x9f, 0xf6, 0x6f, 0x15, 0xe0, 0x74, 0x9f, 0x32, 0x58, 0xd2, 0x7d, 0x6c, 0xcf, 0x0a, 0xca, 0x3a, + 0xdb, 0xa9, 0x81, 0x73, 0xe3, 0x64, 0xdb, 0x90, 0xde, 0x0a, 0x8a, 0x4f, 0x74, 0x2b, 0xb0, 0xbf, + 0x56, 0x00, 0xe3, 0x01, 0x4c, 0xf2, 0x51, 0xb3, 0xe2, 0xdb, 0xca, 0xab, 0x3a, 0x59, 0x30, 0x57, + 0x15, 0xe3, 0x62, 0xd4, 0xfa, 0x15, 0x90, 0x67, 0xe7, 0x6b, 0xe1, 0xe8, 0xf9, 0x4a, 0x9a, 0x49, + 0x69, 0x7d, 0x31, 0xff, 0xd2, 0xfa, 0x72, 0x4f, 0x59, 0xfd, 0xcf, 0x5a, 0x62, 0xa6, 0x65, 0xba, + 0xa4, 0x2d, 0xac, 0xf5, 0x10, 0x0b, 0xfb, 0x36, 0x28, 0x45, 0xb4, 0x59, 0x63, 0x9e, 0x9d, 0xb4, + 0xc4, 0xfa, 0xbd, 0x6d, 0x09, 0x47, 0x45, 0xc1, 0xef, 0xce, 0x36, 0x9b, 0xc1, 0xbd, 0x4b, 0xad, + 0x76, 0xdc, 0x95, 0x36, 0x59, 0xdf, 0x9d, 0x55, 0x18, 0x34, 0xa8, 0xec, 0x7f, 0xb5, 0xc4, 0xe7, + 0x94, 0x3e, 0xfa, 0x8b, 0x99, 0x3b, 0x8d, 0xc7, 0x77, 0x6f, 0x7f, 0x12, 0xc0, 0x55, 0x6f, 0x12, + 0xe4, 0xf3, 0x2e, 0xa6, 0x7e, 0xe3, 0xc0, 0x7c, 0xac, 0x31, 0x81, 0xa1, 0x21, 0x2f, 0xb5, 0x78, + 0x8a, 0x47, 0x2d, 0x1e, 0xfb, 0x9f, 0x2c, 0x48, 0x6d, 0x16, 0xa4, 0x0d, 0xa3, 0x4c, 0x83, 0x6e, + 0x3e, 0x2f, 0x28, 0x98, 0xac, 0xd9, 0xc2, 0x92, 0xd3, 0x82, 0xff, 0x89, 0x42, 0x10, 0x69, 0x4a, + 0xef, 0xbc, 0x90, 0xc7, 0x2b, 0x1f, 0xa6, 0x40, 0xe6, 0xdf, 0xcb, 0x7f, 0x88, 0xa1, 0x3c, 0x7d, + 0xfb, 0x45, 0x98, 0xed, 0x51, 0x8a, 0xdf, 0x48, 0x0a, 0x92, 0x67, 0x23, 0x8c, 0x19, 0xc8, 0xef, + 0x47, 0xa2, 0xc0, 0x31, 0x07, 0x7f, 0x26, 0xcb, 0x9e, 0x7c, 0xd9, 0x82, 0xd9, 0x28, 0xcb, 0xef, + 0x71, 0x8d, 0x9d, 0xca, 0x5c, 0xf5, 0xa0, 0xb0, 0x57, 0x09, 0xfb, 0x3f, 0xa5, 0x79, 0x12, 0xff, + 0x82, 0x4c, 0x6d, 0x2e, 0xd6, 0xc0, 0xcd, 0x85, 0x2d, 0x31, 0xb7, 0x41, 0xab, 0x9d, 0x66, 0x4f, + 0x6d, 0xce, 0xb6, 0x84, 0xa3, 0xa2, 0x48, 0xbd, 0x8f, 0x57, 0x3c, 0xf2, 0x7d, 0xbc, 0x17, 0x60, + 0xd2, 0x7c, 0x1a, 0x85, 0xa7, 0xd0, 0xe4, 0xe1, 0x83, 0xf9, 0x8a, 0x0a, 0xa6, 0xa8, 0x32, 0xef, + 0xab, 0x8d, 0x1e, 0xf9, 0xbe, 0xda, 0x73, 0x50, 0x92, 0x6f, 0x85, 0x25, 0xf9, 0x5d, 0x51, 0xf8, + 0x23, 0x61, 0xa8, 0xb0, 0xcc, 0x40, 0xb4, 0x1c, 0xbf, 0xe3, 0x34, 0xd9, 0x08, 0xc9, 0x7a, 0x40, + 0xb5, 0xb2, 0x36, 0x14, 0x06, 0x0d, 0x2a, 0xd6, 0xe3, 0xd8, 0x6b, 0xd1, 0x57, 0x02, 0x3f, 0xc9, + 0x8c, 0xa8, 0x1e, 0xef, 0x48, 0x38, 0x2a, 0x0a, 0xfb, 0xef, 0x2c, 0xc8, 0x3e, 0x74, 0x94, 0xaa, + 0x41, 0xb4, 0x8e, 0xac, 0x41, 0x4c, 0xd7, 0x57, 0x15, 0x8e, 0x55, 0x5f, 0x65, 0x96, 0x3e, 0x15, + 0x1f, 0x5a, 0xfa, 0xf4, 0x26, 0x7d, 0xaf, 0x5d, 0xd4, 0x48, 0x4d, 0xf4, 0xbb, 0xd3, 0x4e, 0x6c, + 0x18, 0x73, 0x1d, 0x55, 0xe2, 0x3d, 0x29, 0xdc, 0xaa, 0x95, 0x65, 0x4e, 0x24, 0x31, 0x95, 0xdd, + 0x6f, 0x7c, 0xef, 0xfc, 0x53, 0xdf, 0xfc, 0xde, 0xf9, 0xa7, 0xbe, 0xfd, 0xbd, 0xf3, 0x4f, 0x7d, + 0xfc, 0xfe, 0x79, 0xeb, 0x1b, 0xf7, 0xcf, 0x5b, 0xdf, 0xbc, 0x7f, 0xde, 0xfa, 0xf6, 0xfd, 0xf3, + 0xd6, 0x77, 0xef, 0x9f, 0xb7, 0xbe, 0xf0, 0xd7, 0xe7, 0x9f, 0x7a, 0xe5, 0xdd, 0xc3, 0xfc, 0xcf, + 0xdb, 0xff, 0x0a, 0x00, 0x00, 0xff, 0xff, 0xb9, 0x8e, 0x56, 0xe9, 0x32, 0x77, 0x00, 0x00, } func (m *AWSAuthConfig) Marshal() (dAtA []byte, err error) { @@ -3186,6 +3187,15 @@ func (m *AppProjectSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.SourceNamespaces) > 0 { + for iNdEx := len(m.SourceNamespaces) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.SourceNamespaces[iNdEx]) + copy(dAtA[i:], m.SourceNamespaces[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.SourceNamespaces[iNdEx]))) + i-- + dAtA[i] = 0x62 + } + } if len(m.ClusterResourceBlacklist) > 0 { for iNdEx := len(m.ClusterResourceBlacklist) - 1; iNdEx >= 0; iNdEx-- { { @@ -8235,6 +8245,12 @@ func (m *AppProjectSpec) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) } } + if len(m.SourceNamespaces) > 0 { + for _, s := range m.SourceNamespaces { + l = len(s) + n += 1 + l + sovGenerated(uint64(l)) + } + } return n } @@ -10132,6 +10148,7 @@ func (this *AppProjectSpec) String() string { `NamespaceResourceWhitelist:` + repeatedStringForNamespaceResourceWhitelist + `,`, `SignatureKeys:` + repeatedStringForSignatureKeys + `,`, `ClusterResourceBlacklist:` + repeatedStringForClusterResourceBlacklist + `,`, + `SourceNamespaces:` + fmt.Sprintf("%v", this.SourceNamespaces) + `,`, `}`, }, "") return s @@ -12284,6 +12301,38 @@ func (m *AppProjectSpec) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourceNamespaces", 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 + } + m.SourceNamespaces = append(m.SourceNamespaces, string(dAtA[iNdEx:postIndex])) + 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 1f28b70a8ce9e..ed4f358da3e3a 100644 --- a/pkg/apis/application/v1alpha1/generated.proto +++ b/pkg/apis/application/v1alpha1/generated.proto @@ -82,6 +82,9 @@ message AppProjectSpec { // ClusterResourceBlacklist contains list of blacklisted cluster level resources repeated k8s.io.apimachinery.pkg.apis.meta.v1.GroupKind clusterResourceBlacklist = 11; + + // SourceNamespaces defines the namespaces application resources are allowed to be created in + repeated string sourceNamespaces = 12; } // AppProjectStatus contains status information for AppProject CRs diff --git a/pkg/apis/application/v1alpha1/openapi_generated.go b/pkg/apis/application/v1alpha1/openapi_generated.go index ec0e000c591c2..25fd3a7ccc43a 100644 --- a/pkg/apis/application/v1alpha1/openapi_generated.go +++ b/pkg/apis/application/v1alpha1/openapi_generated.go @@ -377,6 +377,21 @@ func schema_pkg_apis_application_v1alpha1_AppProjectSpec(ref common.ReferenceCal }, }, }, + "sourceNamespaces": { + SchemaProps: spec.SchemaProps{ + Description: "SourceNamespaces defines the namespaces application resources are allowed to be created in", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, }, }, }, diff --git a/pkg/apis/application/v1alpha1/types.go b/pkg/apis/application/v1alpha1/types.go index 92f264d05e678..2b33d7a9e6fba 100644 --- a/pkg/apis/application/v1alpha1/types.go +++ b/pkg/apis/application/v1alpha1/types.go @@ -1669,6 +1669,8 @@ type AppProjectSpec struct { SignatureKeys []SignatureKey `json:"signatureKeys,omitempty" protobuf:"bytes,10,opt,name=signatureKeys"` // ClusterResourceBlacklist contains list of blacklisted cluster level resources ClusterResourceBlacklist []metav1.GroupKind `json:"clusterResourceBlacklist,omitempty" protobuf:"bytes,11,opt,name=clusterResourceBlacklist"` + // SourceNamespaces defines the namespaces application resources are allowed to be created in + SourceNamespaces []string `json:"sourceNamespaces,omitempty" protobuf:"bytes,12,opt,name=sourceNamespaces"` } // SyncWindows is a collection of sync windows in this project @@ -2520,3 +2522,37 @@ func (d *ApplicationDestination) MarshalJSON() ([]byte, error) { } return json.Marshal(&struct{ *Alias }{Alias: (*Alias)(dest)}) } + +// InstanceName returns the name of the application as used in the instance +// tracking values, i.e. in the format _. When the namespace +// of the application is similar to the value of defaultNs, only the name of +// the application is returned to keep backwards compatibility. +func (a *Application) InstanceName(defaultNs string) string { + // When app has no namespace set, or the namespace is the default ns, we + // return just the application name + if a.Namespace == "" || a.Namespace == defaultNs { + return a.Name + } + return a.Namespace + "_" + a.Name +} + +// QualifiedName returns the full qualified name of the application, including +// the name of the namespace it is created in delimited by a forward slash, +// i.e. / +func (a *Application) QualifiedName() string { + if a.Namespace == "" { + return a.Name + } else { + return a.Namespace + "/" + a.Name + } +} + +// RBACName returns the full qualified RBAC resource name for the application +// in a backwards-compatible way. +func (a *Application) RBACName(defaultNS string) string { + if defaultNS != "" && a.Namespace != defaultNS && a.Namespace != "" { + return fmt.Sprintf("%s/%s/%s", a.Spec.GetProject(), a.Namespace, a.Name) + } else { + return fmt.Sprintf("%s/%s", a.Spec.GetProject(), a.Name) + } +} diff --git a/pkg/apis/application/v1alpha1/types_test.go b/pkg/apis/application/v1alpha1/types_test.go index 427b0b72c8eeb..e3eb17214d036 100644 --- a/pkg/apis/application/v1alpha1/types_test.go +++ b/pkg/apis/application/v1alpha1/types_test.go @@ -2883,3 +2883,160 @@ func TestGetCAPath(t *testing.T) { assert.Empty(t, path) } } + +func TestAppProjectIsSourceNamespacePermitted(t *testing.T) { + app1 := &Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "argocd", + }, + Spec: ApplicationSpec{}, + } + app2 := &Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app2", + Namespace: "some-ns", + }, + Spec: ApplicationSpec{}, + } + app3 := &Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app2", + Namespace: "", + }, + Spec: ApplicationSpec{}, + } + app4 := &Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app2", + Namespace: "other-ns", + }, + Spec: ApplicationSpec{}, + } + app5 := &Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app2", + Namespace: "some-ns1", + }, + Spec: ApplicationSpec{}, + } + app6 := &Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app2", + Namespace: "some-ns2", + }, + Spec: ApplicationSpec{}, + } + app7 := &Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app2", + Namespace: "someotherns", + }, + Spec: ApplicationSpec{}, + } + t.Run("App in same namespace as controller", func(t *testing.T) { + proj := &AppProject{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + Namespace: "argocd", + }, + Spec: AppProjectSpec{ + SourceNamespaces: []string{"other-ns"}, + }, + } + // app1 is installed to argocd namespace, controller as well + assert.True(t, proj.IsAppNamespacePermitted(app1, "argocd")) + // app2 is installed to some-ns namespace, controller as well + assert.True(t, proj.IsAppNamespacePermitted(app2, "some-ns")) + // app3 has no namespace set, so will be implicitly created in controller's namespace + assert.True(t, proj.IsAppNamespacePermitted(app3, "argocd")) + }) + t.Run("App not permitted when sourceNamespaces is empty", func(t *testing.T) { + proj := &AppProject{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + Namespace: "argocd", + }, + Spec: AppProjectSpec{ + SourceNamespaces: []string{}, + }, + } + // app1 is installed to argocd namespace + assert.True(t, proj.IsAppNamespacePermitted(app1, "argocd")) + // app2 is installed to some-ns, controller running in argocd + assert.False(t, proj.IsAppNamespacePermitted(app2, "argocd")) + }) + + t.Run("App permitted when sourceNamespaces has app namespace", func(t *testing.T) { + proj := &AppProject{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + Namespace: "argocd", + }, + Spec: AppProjectSpec{ + SourceNamespaces: []string{"some-ns"}, + }, + } + // app2 is installed to some-ns, controller running in argocd + assert.True(t, proj.IsAppNamespacePermitted(app2, "argocd")) + // app4 is installed to other-ns, controller running in argocd + assert.False(t, proj.IsAppNamespacePermitted(app4, "argocd")) + }) + + t.Run("App permitted by glob pattern", func(t *testing.T) { + proj := &AppProject{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + Namespace: "argocd", + }, + Spec: AppProjectSpec{ + SourceNamespaces: []string{"some-*"}, + }, + } + // app5 is installed to some-ns1, controller running in argocd + assert.True(t, proj.IsAppNamespacePermitted(app5, "argocd")) + // app6 is installed to some-ns2, controller running in argocd + assert.True(t, proj.IsAppNamespacePermitted(app6, "argocd")) + // app7 is installed to someotherns, controller running in argocd + assert.False(t, proj.IsAppNamespacePermitted(app7, "argocd")) + }) + +} + +func Test_RBACName(t *testing.T) { + testApp := func(namespace, project string) *Application { + return &Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-app", + Namespace: namespace, + }, + Spec: ApplicationSpec{ + Project: project, + }, + } + } + t.Run("App in same namespace as controller when ns is argocd", func(t *testing.T) { + a := testApp("argocd", "default") + assert.Equal(t, "default/test-app", a.RBACName("argocd")) + }) + t.Run("App in same namespace as controller when ns is not argocd", func(t *testing.T) { + a := testApp("some-ns", "default") + assert.Equal(t, "default/test-app", a.RBACName("some-ns")) + }) + t.Run("App in different namespace as controller when ns is argocd", func(t *testing.T) { + a := testApp("some-ns", "default") + assert.Equal(t, "default/some-ns/test-app", a.RBACName("argocd")) + }) + t.Run("App in different namespace as controller when ns is not argocd", func(t *testing.T) { + a := testApp("some-ns", "default") + assert.Equal(t, "default/some-ns/test-app", a.RBACName("other-ns")) + }) + t.Run("App in same namespace as controller when project is not yet set", func(t *testing.T) { + a := testApp("argocd", "") + assert.Equal(t, "default/test-app", a.RBACName("argocd")) + }) + t.Run("App in same namespace as controller when ns is not yet set", func(t *testing.T) { + a := testApp("", "") + assert.Equal(t, "default/test-app", a.RBACName("argocd")) + }) +} diff --git a/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go index c3a2dfddfb197..24d02c0caf087 100644 --- a/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/application/v1alpha1/zz_generated.deepcopy.go @@ -149,6 +149,11 @@ func (in *AppProjectSpec) DeepCopyInto(out *AppProjectSpec) { *out = make([]v1.GroupKind, len(*in)) copy(*out, *in) } + if in.SourceNamespaces != nil { + in, out := &in.SourceNamespaces, &out.SourceNamespaces + *out = make([]string, len(*in)) + copy(*out, *in) + } return } diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go index b3d31adcaa493..c3d43e6635d25 100644 --- a/reposerver/repository/repository.go +++ b/reposerver/repository/repository.go @@ -752,8 +752,14 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie defer manifestGenerateLock.Unlock(appPath) } + // We use the app name as Helm's release name property, which must not + // contain any underscore characters and must not exceed 53 characters. + // We are not interested in the fully qualified application name while + // templating, thus, we just use the name part of the identifier. + appName, _ := argo.ParseAppInstanceName(q.AppName, "") + templateOpts := &helm.TemplateOpts{ - Name: q.AppName, + Name: appName, Namespace: q.Namespace, KubeVersion: text.SemVer(q.KubeVersion), APIVersions: q.ApiVersions, diff --git a/server/application/application.go b/server/application/application.go index bdcae34346f5a..8e705b2ffce3a 100644 --- a/server/application/application.go +++ b/server/application/application.go @@ -44,12 +44,12 @@ import ( "github.com/argoproj/argo-cd/v2/reposerver/apiclient" servercache "github.com/argoproj/argo-cd/v2/server/cache" "github.com/argoproj/argo-cd/v2/server/rbacpolicy" - apputil "github.com/argoproj/argo-cd/v2/util/app" "github.com/argoproj/argo-cd/v2/util/argo" argoutil "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/db" "github.com/argoproj/argo-cd/v2/util/env" "github.com/argoproj/argo-cd/v2/util/git" + "github.com/argoproj/argo-cd/v2/util/glob" "github.com/argoproj/argo-cd/v2/util/io" ioutil "github.com/argoproj/argo-cd/v2/util/io" "github.com/argoproj/argo-cd/v2/util/lua" @@ -72,21 +72,22 @@ var ( // Server provides an Application service type Server struct { - ns string - kubeclientset kubernetes.Interface - appclientset appclientset.Interface - appLister applisters.ApplicationNamespaceLister - appInformer cache.SharedIndexInformer - appBroadcaster *broadcasterHandler - repoClientset apiclient.Clientset - kubectl kube.Kubectl - db db.ArgoDB - enf *rbac.Enforcer - projectLock sync.KeyLock - auditLogger *argo.AuditLogger - settingsMgr *settings.SettingsManager - cache *servercache.Cache - projInformer cache.SharedIndexInformer + ns string + kubeclientset kubernetes.Interface + appclientset appclientset.Interface + appLister applisters.ApplicationLister + appInformer cache.SharedIndexInformer + appBroadcaster *broadcasterHandler + repoClientset apiclient.Clientset + kubectl kube.Kubectl + db db.ArgoDB + enf *rbac.Enforcer + projectLock sync.KeyLock + auditLogger *argo.AuditLogger + settingsMgr *settings.SettingsManager + cache *servercache.Cache + projInformer cache.SharedIndexInformer + enabledNamespaces []string } // NewServer returns a new instance of the Application service @@ -94,7 +95,7 @@ func NewServer( namespace string, kubeclientset kubernetes.Interface, appclientset appclientset.Interface, - appLister applisters.ApplicationNamespaceLister, + appLister applisters.ApplicationLister, appInformer cache.SharedIndexInformer, repoClientset apiclient.Clientset, cache *servercache.Cache, @@ -104,25 +105,27 @@ func NewServer( projectLock sync.KeyLock, settingsMgr *settings.SettingsManager, projInformer cache.SharedIndexInformer, + enabledNamespaces []string, ) (application.ApplicationServiceServer, AppResourceTreeFn) { appBroadcaster := &broadcasterHandler{} appInformer.AddEventHandler(appBroadcaster) s := &Server{ - ns: namespace, - appclientset: appclientset, - appLister: appLister, - appInformer: appInformer, - appBroadcaster: appBroadcaster, - kubeclientset: kubeclientset, - cache: cache, - db: db, - repoClientset: repoClientset, - kubectl: kubectl, - enf: enf, - projectLock: projectLock, - auditLogger: argo.NewAuditLogger(namespace, kubeclientset, "argocd-server"), - settingsMgr: settingsMgr, - projInformer: projInformer, + ns: namespace, + appclientset: appclientset, + appLister: appLister, + appInformer: appInformer, + appBroadcaster: appBroadcaster, + kubeclientset: kubeclientset, + cache: cache, + db: db, + repoClientset: repoClientset, + kubectl: kubectl, + enf: enf, + projectLock: projectLock, + auditLogger: argo.NewAuditLogger(namespace, kubeclientset, "argocd-server"), + settingsMgr: settingsMgr, + projInformer: projInformer, + enabledNamespaces: enabledNamespaces, } return s, s.GetAppResources } @@ -133,16 +136,27 @@ func (s *Server) List(ctx context.Context, q *application.ApplicationQuery) (*ap if err != nil { return nil, fmt.Errorf("error converting selector to labels map: %w", err) } - apps, err := s.appLister.List(labelsMap.AsSelector()) + var apps []*appv1.Application + if q.GetAppNamespace() == "" { + apps, err = s.appLister.List(labelsMap.AsSelector()) + } else { + apps, err = s.appLister.Applications(q.GetAppNamespace()).List(labelsMap.AsSelector()) + } if err != nil { return nil, fmt.Errorf("error listing apps with selectors: %w", err) } newItems := make([]appv1.Application, 0) for _, a := range apps { - if s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)) { + // Skip any application that is neither in the conrol plane's namespace + // nor in the list of enabled namespaces. + if a.Namespace != s.ns && !glob.MatchStringInList(s.enabledNamespaces, a.Namespace, false) { + continue + } + if s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)) { newItems = append(newItems, *a) } } + if q.Name != nil { newItems, err = argoutil.FilterByName(newItems, *q.Name) if err != nil { @@ -175,14 +189,15 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq if q.GetApplication() == nil { return nil, fmt.Errorf("error creating application: application is nil in request") } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionCreate, apputil.AppRBACName(*q.Application)); err != nil { + a := q.GetApplication() + + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionCreate, a.RBACName(s.ns)); err != nil { return nil, err } - s.projectLock.RLock(q.Application.Spec.Project) - defer s.projectLock.RUnlock(q.Application.Spec.Project) + s.projectLock.RLock(a.Spec.GetProject()) + defer s.projectLock.RUnlock(a.Spec.GetProject()) - a := q.GetApplication() validate := true if q.Validate != nil { validate = *q.Validate @@ -191,7 +206,14 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq if err != nil { return nil, fmt.Errorf("error while validating and normalizing app: %w", err) } - created, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Create(ctx, a, metav1.CreateOptions{}) + + appNs := s.appNamespaceOrDefault(a.Namespace) + + if !s.isNamespaceEnabled(appNs) { + return nil, namespaceNotPermittedError(appNs) + } + + created, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Create(ctx, a, metav1.CreateOptions{}) if err == nil { s.logAppEvent(created, ctx, argo.EventReasonResourceCreated, "created application") s.waitSync(created) @@ -200,10 +222,11 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq if !apierr.IsAlreadyExists(err) { return nil, fmt.Errorf("error creating application: %w", err) } + // act idempotent if existing spec matches new spec - existing, err := s.appLister.Get(a.Name) + existing, err := s.appLister.Applications(appNs).Get(a.Name) if err != nil { - return nil, status.Errorf(codes.Internal, "unable to check existing application details: %v", err) + return nil, status.Errorf(codes.Internal, "unable to check existing application details (%s): %v", appNs, err) } equalSpecs := reflect.DeepEqual(existing.Spec, a.Spec) && reflect.DeepEqual(existing.Labels, a.Labels) && @@ -216,7 +239,7 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq if q.Upsert == nil || !*q.Upsert { return nil, status.Errorf(codes.InvalidArgument, "existing application spec is different, use upsert flag to force update") } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, a.RBACName(s.ns)); err != nil { return nil, err } updated, err := s.updateApp(existing, a, ctx, true) @@ -253,7 +276,7 @@ func (s *Server) queryRepoServer(ctx context.Context, a *v1alpha1.Application, a if err != nil { return fmt.Errorf("error getting kustomize settings options: %w", err) } - proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) + proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) if err != nil { if apierr.IsNotFound(err) { return status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", a.Spec.Project) @@ -291,14 +314,23 @@ func (s *Server) queryRepoServer(ctx context.Context, a *v1alpha1.Application, a // GetManifests returns application manifests func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationManifestQuery) (*apiclient.ManifestResponse, error) { - a, err := s.appLister.Get(*q.Name) + if q.Name == nil || *q.Name == "" { + return nil, fmt.Errorf("invalid request: application name is missing") + } + appName := q.GetName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + a, err := s.appLister.Applications(appNs).Get(appName) if err != nil { return nil, fmt.Errorf("error getting application: %w", err) } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return nil, err } + if !s.isNamespaceEnabled(a.Namespace) { + return nil, namespaceNotPermittedError(a.Namespace) + } + var manifestInfo *apiclient.ManifestResponse err = s.queryRepoServer(ctx, a, func( client apiclient.RepoServerServiceClient, repo *appv1.Repository, helmRepos []*appv1.Repository, helmCreds []*appv1.RepoCreds, helmOptions *appv1.HelmOptions, kustomizeOptions *appv1.KustomizeOptions, enableGenerateManifests map[string]bool) error { @@ -334,7 +366,7 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan Repo: repo, Revision: revision, AppLabelKey: appInstanceLabelKey, - AppName: a.Name, + AppName: a.InstanceName(s.ns), Namespace: a.Spec.Destination.Namespace, ApplicationSource: &a.Spec.Source, Repos: helmRepos, @@ -381,17 +413,20 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan // Get returns an application by name func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*appv1.Application, error) { + appName := q.GetName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + // We must use a client Get instead of an informer Get, because it's common to call Get immediately // following a Watch (which is not yet powered by an informer), and the Get must reflect what was // previously seen by the client. - a, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, q.GetName(), metav1.GetOptions{ + a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{ ResourceVersion: q.GetResourceVersion(), }) if err != nil { return nil, fmt.Errorf("error getting application: %w", err) } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return nil, err } if q.Refresh == nil { @@ -402,16 +437,16 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app if *q.Refresh == string(appv1.RefreshTypeHard) { refreshType = appv1.RefreshTypeHard } - appIf := s.appclientset.ArgoprojV1alpha1().Applications(s.ns) + appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs) // subscribe early with buffered channel to ensure we don't miss events events := make(chan *appv1.ApplicationWatchEvent, watchAPIBufferSize) unsubscribe := s.appBroadcaster.Subscribe(events, func(event *appv1.ApplicationWatchEvent) bool { - return event.Application.Name == q.GetName() + return event.Application.Name == appName && event.Application.Namespace == appNs }) defer unsubscribe() - app, err := argoutil.RefreshApp(appIf, *q.Name, refreshType) + app, err := argoutil.RefreshApp(appIf, appName, refreshType) if err != nil { return nil, fmt.Errorf("error refreshing the app: %w", err) } @@ -430,7 +465,7 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app _, err := client.GetAppDetails(ctx, &apiclient.RepoServerAppDetailsQuery{ Repo: repo, Source: &app.Spec.Source, - AppName: app.Name, + AppName: appName, KustomizeOptions: kustomizeOptions, Repos: helmRepos, NoCache: true, @@ -469,11 +504,13 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app // ListResourceEvents returns a list of event resources func (s *Server) ListResourceEvents(ctx context.Context, q *application.ApplicationResourceEventsQuery) (*v1.EventList, error) { - a, err := s.appLister.Get(*q.Name) + appName := q.GetName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + a, err := s.appLister.Applications(appNs).Get(appName) if err != nil { return nil, fmt.Errorf("error getting application: %w", err) } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return nil, err } var ( @@ -537,7 +574,7 @@ func (s *Server) validateAndUpdateApp(ctx context.Context, newApp *appv1.Applica s.projectLock.RLock(newApp.Spec.GetProject()) defer s.projectLock.RUnlock(newApp.Spec.GetProject()) - app, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, newApp.Name, metav1.GetOptions{}) + app, err := s.appclientset.ArgoprojV1alpha1().Applications(newApp.Namespace).Get(ctx, newApp.Name, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("error getting application: %w", err) } @@ -586,7 +623,7 @@ func (s *Server) waitSync(app *appv1.Application) { return } for { - if currApp, err := s.appLister.Get(app.Name); err == nil { + if currApp, err := s.appLister.Applications(app.Namespace).Get(app.Name); err == nil { currVersion, err := strconv.Atoi(currApp.ResourceVersion) if err == nil && currVersion >= minVersion { return @@ -613,7 +650,7 @@ func (s *Server) updateApp(app *appv1.Application, newApp *appv1.Application, ct app.Finalizers = newApp.Finalizers - res, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Update(ctx, app, metav1.UpdateOptions{}) + res, err := s.appclientset.ArgoprojV1alpha1().Applications(app.Namespace).Update(ctx, app, metav1.UpdateOptions{}) if err == nil { s.logAppEvent(app, ctx, argo.EventReasonResourceUpdated, "updated application spec") s.waitSync(res) @@ -623,7 +660,7 @@ func (s *Server) updateApp(app *appv1.Application, newApp *appv1.Application, ct return nil, err } - app, err = s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, newApp.Name, metav1.GetOptions{}) + app, err = s.appclientset.ArgoprojV1alpha1().Applications(app.Namespace).Get(ctx, newApp.Name, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("error getting application: %w", err) } @@ -633,7 +670,11 @@ func (s *Server) updateApp(app *appv1.Application, newApp *appv1.Application, ct // Update updates an application func (s *Server) Update(ctx context.Context, q *application.ApplicationUpdateRequest) (*appv1.Application, error) { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, apputil.AppRBACName(*q.Application)); err != nil { + if q.GetApplication() == nil { + return nil, fmt.Errorf("error creating application: application is nil in request") + } + a := q.GetApplication() + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, a.RBACName(s.ns)); err != nil { return nil, err } @@ -649,11 +690,13 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat if q.GetSpec() == nil { return nil, fmt.Errorf("error updating application spec: spec is nil in request") } - a, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, *q.Name, metav1.GetOptions{}) + appName := q.GetName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("error getting application: %w", err) } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, a.RBACName(s.ns)); err != nil { return nil, err } a.Spec = *q.GetSpec() @@ -670,13 +713,14 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat // Patch patches an application func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchRequest) (*appv1.Application, error) { - - app, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, *q.Name, metav1.GetOptions{}) + appName := q.GetName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + app, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("error getting application: %w", err) } - if err = s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, apputil.AppRBACName(*app)); err != nil { + if err = s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, app.RBACName(s.ns)); err != nil { return nil, err } @@ -716,7 +760,9 @@ func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchReque // Delete removes an application and all associated resources func (s *Server) Delete(ctx context.Context, q *application.ApplicationDeleteRequest) (*application.ApplicationResponse, error) { - a, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, *q.Name, metav1.GetOptions{}) + appName := q.GetName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("error getting application: %w", err) } @@ -724,7 +770,7 @@ func (s *Server) Delete(ctx context.Context, q *application.ApplicationDeleteReq s.projectLock.RLock(a.Spec.Project) defer s.projectLock.RUnlock(a.Spec.Project) - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionDelete, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionDelete, a.RBACName(s.ns)); err != nil { return nil, err } @@ -768,7 +814,7 @@ func (s *Server) Delete(ctx context.Context, q *application.ApplicationDeleteReq } } - err = s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Delete(ctx, *q.Name, metav1.DeleteOptions{}) + err = s.appclientset.ArgoprojV1alpha1().Applications(appNs).Delete(ctx, appName, metav1.DeleteOptions{}) if err != nil { return nil, fmt.Errorf("error deleting application: %w", err) } @@ -777,6 +823,8 @@ func (s *Server) Delete(ctx context.Context, q *application.ApplicationDeleteReq } func (s *Server) Watch(q *application.ApplicationQuery, ws application.ApplicationService_WatchServer) error { + appName := q.GetName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) logCtx := log.NewEntry(log.New()) if q.Name != nil { logCtx = logCtx.WithField("application", *q.Name) @@ -807,12 +855,12 @@ func (s *Server) Watch(q *application.ApplicationQuery, ws application.Applicati if appVersion, err := strconv.Atoi(a.ResourceVersion); err == nil && appVersion < minVersion { return } - matchedEvent := (q.GetName() == "" || a.Name == q.GetName()) && selector.Matches(labels.Set(a.Labels)) + matchedEvent := (appName == "" || (a.Name == appName && a.Namespace == appNs)) && selector.Matches(labels.Set(a.Labels)) if !matchedEvent { return } - if !s.enf.Enforce(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(a)) { + if !s.enf.Enforce(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)) { // do not emit apps user does not have accessing return } @@ -837,7 +885,7 @@ func (s *Server) Watch(q *application.ApplicationQuery, ws application.Applicati return fmt.Errorf("error listing apps with selector: %w", err) } sort.Slice(apps, func(i, j int) bool { - return apps[i].Name < apps[j].Name + return apps[i].QualifiedName() < apps[j].QualifiedName() }) for i := range apps { sendIfPermitted(*apps[i], watch.Added) @@ -856,14 +904,18 @@ func (s *Server) Watch(q *application.ApplicationQuery, ws application.Applicati } func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Application, validate bool) error { - proj, err := argo.GetAppProject(&app.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) + proj, err := argo.GetAppProject(app, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) if err != nil { if apierr.IsNotFound(err) { return status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", app.Spec.Project) } return fmt.Errorf("error getting application's project: %w", err) } - currApp, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, app.Name, metav1.GetOptions{}) + if app.GetName() == "" { + return fmt.Errorf("resource name may not be empty") + } + appNs := s.appNamespaceOrDefault(app.Namespace) + currApp, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, app.Name, metav1.GetOptions{}) if err != nil { if !apierr.IsNotFound(err) { return fmt.Errorf("error getting application by name: %w", err) @@ -875,11 +927,11 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica if currApp != nil && currApp.Spec.GetProject() != app.Spec.GetProject() { // When changing projects, caller must have application create & update privileges in new project // NOTE: the update check was already verified in the caller to this function - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionCreate, apputil.AppRBACName(*app)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionCreate, app.RBACName(s.ns)); err != nil { return err } // They also need 'update' privileges in the old project - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, apputil.AppRBACName(*currApp)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, currApp.RBACName(s.ns)); err != nil { return err } } @@ -949,8 +1001,9 @@ func (s *Server) getCachedAppState(ctx context.Context, a *appv1.Application, ge return errors.New(argoutil.FormatAppConditions(conditions)) } _, err = s.Get(ctx, &application.ApplicationQuery{ - Name: pointer.StringPtr(a.Name), - Refresh: pointer.StringPtr(string(appv1.RefreshTypeNormal)), + Name: pointer.StringPtr(a.GetName()), + AppNamespace: pointer.StringPtr(a.GetNamespace()), + Refresh: pointer.StringPtr(string(appv1.RefreshTypeNormal)), }) if err != nil { return fmt.Errorf("error getting application by query: %w", err) @@ -963,7 +1016,7 @@ func (s *Server) getCachedAppState(ctx context.Context, a *appv1.Application, ge func (s *Server) GetAppResources(ctx context.Context, a *appv1.Application) (*appv1.ApplicationTree, error) { var tree appv1.ApplicationTree err := s.getCachedAppState(ctx, a, func() error { - return s.cache.GetAppResourcesTree(a.Name, &tree) + return s.cache.GetAppResourcesTree(a.InstanceName(s.ns), &tree) }) if err != nil { return &tree, fmt.Errorf("error getting cached app state: %w", err) @@ -972,11 +1025,13 @@ func (s *Server) GetAppResources(ctx context.Context, a *appv1.Application) (*ap } func (s *Server) getAppLiveResource(ctx context.Context, action string, q *application.ApplicationResourceRequest) (*appv1.ResourceNode, *rest.Config, *appv1.Application, error) { - a, err := s.appLister.Get(*q.Name) + appName := q.GetName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + a, err := s.appLister.Applications(appNs).Get(appName) if err != nil { return nil, nil, nil, fmt.Errorf("error getting app by name: %w", err) } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, action, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, action, a.RBACName(s.ns)); err != nil { return nil, nil, nil, err } @@ -1037,6 +1092,7 @@ func replaceSecretValues(obj *unstructured.Unstructured) (*unstructured.Unstruct func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationResourcePatchRequest) (*application.ApplicationResourceResponse, error) { resourceRequest := &application.ApplicationResourceRequest{ Name: q.Name, + AppNamespace: q.AppNamespace, Namespace: q.Namespace, ResourceName: q.ResourceName, Kind: q.Kind, @@ -1047,7 +1103,7 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe if err != nil { return nil, fmt.Errorf("error getting app live resource: %w", err) } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, a.RBACName(s.ns)); err != nil { return nil, err } @@ -1078,6 +1134,7 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationResourceDeleteRequest) (*application.ApplicationResponse, error) { resourceRequest := &application.ApplicationResourceRequest{ Name: q.Name, + AppNamespace: q.AppNamespace, Namespace: q.Namespace, ResourceName: q.ResourceName, Kind: q.Kind, @@ -1088,7 +1145,7 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR if err != nil { return nil, fmt.Errorf("error getting live resource for delete: %w", err) } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionDelete, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionDelete, a.RBACName(s.ns)); err != nil { return nil, err } var deleteOption metav1.DeleteOptions @@ -1112,23 +1169,27 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR } func (s *Server) ResourceTree(ctx context.Context, q *application.ResourcesQuery) (*appv1.ApplicationTree, error) { - a, err := s.appLister.Get(q.GetApplicationName()) + appName := q.GetApplicationName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + a, err := s.appLister.Applications(appNs).Get(appName) if err != nil { return nil, fmt.Errorf("error getting application by name: %w", err) } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return nil, err } return s.GetAppResources(ctx, a) } func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application.ApplicationService_WatchResourceTreeServer) error { - a, err := s.appLister.Get(q.GetApplicationName()) + appName := q.GetApplicationName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + a, err := s.appLister.Applications(appNs).Get(appName) if err != nil { return fmt.Errorf("error getting application by name: %w", err) } - if err := s.enf.EnforceErr(ws.Context().Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ws.Context().Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return err } @@ -1143,11 +1204,13 @@ func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application } func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMetadataQuery) (*v1alpha1.RevisionMetadata, error) { - a, err := s.appLister.Get(q.GetName()) + appName := q.GetName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + a, err := s.appLister.Applications(appNs).Get(appName) if err != nil { return nil, fmt.Errorf("error getting app by name: %w", err) } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return nil, err } repo, err := s.db.GetRepository(ctx, a.Spec.Source.RepoURL) @@ -1156,7 +1219,7 @@ func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMe } // We need to get some information with the project associated to the app, // so we'll know whether GPG signatures are enforced. - proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), a.Namespace, s.settingsMgr, s.db, ctx) + proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) if err != nil { return nil, fmt.Errorf("error getting app project: %w", err) } @@ -1180,16 +1243,18 @@ func isMatchingResource(q *application.ResourcesQuery, key kube.ResourceKey) boo } func (s *Server) ManagedResources(ctx context.Context, q *application.ResourcesQuery) (*application.ManagedResourcesResponse, error) { - a, err := s.appLister.Get(*q.ApplicationName) + appName := q.GetApplicationName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + a, err := s.appLister.Applications(appNs).Get(appName) if err != nil { return nil, fmt.Errorf("error getting application: %w", err) } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return nil, fmt.Errorf("error verifying rbac: %w", err) } items := make([]*appv1.ResourceDiff, 0) err = s.getCachedAppState(ctx, a, func() error { - return s.cache.GetAppManagedResources(a.Name, &items) + return s.cache.GetAppManagedResources(a.InstanceName(s.ns), &items) }) if err != nil { return nil, fmt.Errorf("error getting cached app state: %w", err) @@ -1239,12 +1304,14 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application. } } - a, err := s.appLister.Get(q.GetName()) + appName := q.GetName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + a, err := s.appLister.Applications(appNs).Get(appName) if err != nil { return fmt.Errorf("error getting application by name: %w", err) } - if err := s.enf.EnforceErr(ws.Context().Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ws.Context().Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return err } @@ -1259,7 +1326,7 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application. } if serverRBACLogEnforceEnable { - if err := s.enf.EnforceErr(ws.Context().Value("claims"), rbacpolicy.ResourceLogs, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ws.Context().Value("claims"), rbacpolicy.ResourceLogs, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return err } } @@ -1436,13 +1503,15 @@ func isTheSelectedOne(currentNode *appv1.ResourceNode, q *application.Applicatio // Sync syncs an application to its target state func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncRequest) (*appv1.Application, error) { - appIf := s.appclientset.ArgoprojV1alpha1().Applications(s.ns) - a, err := appIf.Get(ctx, *syncReq.Name, metav1.GetOptions{}) + appName := syncReq.GetName() + appNs := s.appNamespaceOrDefault(syncReq.GetAppNamespace()) + appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs) + a, err := appIf.Get(ctx, appName, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("error getting application by name: %w", err) } - proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), a.Namespace, s.settingsMgr, s.db, ctx) + proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) if err != nil { if apierr.IsNotFound(err) { return a, status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", a.Spec.Project) @@ -1454,11 +1523,11 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR return a, status.Errorf(codes.PermissionDenied, "cannot sync: blocked by sync window") } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionSync, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionSync, a.RBACName(s.ns)); err != nil { return nil, err } if syncReq.Manifests != nil { - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionOverride, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionOverride, a.RBACName(s.ns)); err != nil { return nil, err } if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.Automated != nil && !syncReq.GetDryRun() { @@ -1521,7 +1590,7 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR op.Retry = *retry } - a, err = argo.SetAppOperation(appIf, *syncReq.Name, &op) + a, err = argo.SetAppOperation(appIf, appName, &op) if err != nil { return nil, fmt.Errorf("error setting app operation: %w", err) } @@ -1538,12 +1607,14 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR } func (s *Server) Rollback(ctx context.Context, rollbackReq *application.ApplicationRollbackRequest) (*appv1.Application, error) { - appIf := s.appclientset.ArgoprojV1alpha1().Applications(s.ns) - a, err := appIf.Get(ctx, *rollbackReq.Name, metav1.GetOptions{}) + appName := rollbackReq.GetName() + appNs := s.appNamespaceOrDefault(rollbackReq.GetAppNamespace()) + appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs) + a, err := appIf.Get(ctx, appName, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("error getting application by name: %w", err) } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionSync, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionSync, a.RBACName(s.ns)); err != nil { return nil, err } if a.DeletionTimestamp != nil { @@ -1561,7 +1632,7 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat } } if deploymentInfo == nil { - return nil, status.Errorf(codes.InvalidArgument, "application %s does not have deployment with id %v", a.Name, rollbackReq.GetId()) + return nil, status.Errorf(codes.InvalidArgument, "application %s does not have deployment with id %v", a.QualifiedName(), rollbackReq.GetId()) } if deploymentInfo.Source.IsZero() { // Since source type was introduced to history starting with v0.12, and is now required for @@ -1586,7 +1657,7 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat }, InitiatedBy: appv1.OperationInitiator{Username: session.Username(ctx)}, } - a, err = argo.SetAppOperation(appIf, *rollbackReq.Name, &op) + a, err = argo.SetAppOperation(appIf, appName, &op) if err != nil { return nil, fmt.Errorf("error setting app operation: %w", err) } @@ -1633,11 +1704,13 @@ func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, sy } func (s *Server) TerminateOperation(ctx context.Context, termOpReq *application.OperationTerminateRequest) (*application.OperationTerminateResponse, error) { - a, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, *termOpReq.Name, metav1.GetOptions{}) + appName := termOpReq.GetName() + appNs := s.appNamespaceOrDefault(termOpReq.GetAppNamespace()) + a, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("error getting application by name: %w", err) } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionSync, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionSync, a.RBACName(s.ns)); err != nil { return nil, err } @@ -1646,7 +1719,7 @@ func (s *Server) TerminateOperation(ctx context.Context, termOpReq *application. return nil, status.Errorf(codes.InvalidArgument, "Unable to terminate operation. No operation is in progress") } a.Status.OperationState.Phase = common.OperationTerminating - updated, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Update(ctx, a, metav1.UpdateOptions{}) + updated, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Update(ctx, a, metav1.UpdateOptions{}) if err == nil { s.waitSync(updated) s.logAppEvent(a, ctx, argo.EventReasonResourceUpdated, "terminated running operation") @@ -1657,7 +1730,7 @@ func (s *Server) TerminateOperation(ctx context.Context, termOpReq *application. } log.Warnf("failed to set operation for app %q due to update conflict. retrying again...", *termOpReq.Name) time.Sleep(100 * time.Millisecond) - a, err = s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, *termOpReq.Name, metav1.GetOptions{}) + a, err = s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("error getting application by name: %w", err) } @@ -1734,6 +1807,7 @@ func (s *Server) getAvailableActions(resourceOverrides map[string]appv1.Resource func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceActionRunRequest) (*application.ApplicationResponse, error) { resourceRequest := &application.ApplicationResourceRequest{ Name: q.Name, + AppNamespace: q.AppNamespace, Namespace: q.Namespace, ResourceName: q.ResourceName, Kind: q.Kind, @@ -1870,17 +1944,19 @@ func (s *Server) plugins() ([]*v1alpha1.ConfigManagementPlugin, error) { } func (s *Server) GetApplicationSyncWindows(ctx context.Context, q *application.ApplicationSyncWindowsQuery) (*application.ApplicationSyncWindowsResponse, error) { - appIf := s.appclientset.ArgoprojV1alpha1().Applications(s.ns) - a, err := appIf.Get(ctx, *q.Name, metav1.GetOptions{}) + appName := q.GetName() + appNs := s.appNamespaceOrDefault(q.GetAppNamespace()) + appIf := s.appclientset.ArgoprojV1alpha1().Applications(appNs) + a, err := appIf.Get(ctx, appName, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("error getting application by name: %w", err) } - if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, apputil.AppRBACName(*a)); err != nil { + if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, a.RBACName(s.ns)); err != nil { return nil, err } - proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), a.Namespace, s.settingsMgr, s.db, ctx) + proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx) if err != nil { return nil, fmt.Errorf("error getting app project: %w", err) } @@ -1928,3 +2004,19 @@ func getPropagationPolicyFinalizer(policy string) string { return "" } } + +func (s *Server) appNamespaceOrDefault(appNs string) string { + if appNs == "" { + return s.ns + } else { + return appNs + } +} + +func (s *Server) isNamespaceEnabled(namespace string) bool { + return namespace == s.ns || glob.MatchStringInList(s.enabledNamespaces, namespace, false) +} + +func namespaceNotPermittedError(namespace string) error { + return fmt.Errorf("namespace '%s' is not permitted", namespace) +} diff --git a/server/application/application.proto b/server/application/application.proto index d2bcb8f87e571..187ed88cf05a0 100644 --- a/server/application/application.proto +++ b/server/application/application.proto @@ -27,11 +27,14 @@ message ApplicationQuery { optional string selector = 5; // the repoURL to restrict returned list applications optional string repo = 6; + // the application's namespace + optional string appNamespace = 7; } message NodeQuery { // the application's name optional string name = 1; + optional string appNamespace = 2; } message RevisionMetadataQuery{ @@ -39,6 +42,8 @@ message RevisionMetadataQuery{ required string name = 1; // the revision of the app required string revision = 2; + // the application's namespace + optional string appNamespace = 3; } // ApplicationEventsQuery is a query for application resource events @@ -47,12 +52,14 @@ message ApplicationResourceEventsQuery { optional string resourceNamespace = 2; optional string resourceName = 3; optional string resourceUID = 4; + optional string appNamespace = 5; } // ManifestQuery is a query for manifest resources message ApplicationManifestQuery { required string name = 1; optional string revision = 2; + optional string appNamespace = 3; } message ApplicationResponse {} @@ -72,6 +79,7 @@ message ApplicationDeleteRequest { required string name = 1; optional bool cascade = 2; optional string propagationPolicy = 3; + optional string appNamespace = 4; } message SyncOptions { @@ -90,6 +98,7 @@ message ApplicationSyncRequest { repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Info infos = 9; optional github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.RetryStrategy retryStrategy = 10; optional SyncOptions syncOptions = 11; + optional string appNamespace = 12; } // ApplicationUpdateSpecRequest is a request to update application spec @@ -97,6 +106,7 @@ message ApplicationUpdateSpecRequest { required string name = 1; required github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSpec spec = 2; optional bool validate = 3; + optional string appNamespace = 4; } // ApplicationPatchRequest is a request to patch an application @@ -104,6 +114,7 @@ message ApplicationPatchRequest { required string name = 1; required string patch = 2; required string patchType = 3; + optional string appNamespace = 5; } message ApplicationRollbackRequest { @@ -111,6 +122,7 @@ message ApplicationRollbackRequest { required int64 id = 2; optional bool dryRun = 3; optional bool prune = 4; + optional string appNamespace = 6; } message ApplicationResourceRequest { @@ -120,6 +132,7 @@ message ApplicationResourceRequest { required string version = 4; optional string group = 5; required string kind = 6; + optional string appNamespace = 7; } message ApplicationResourcePatchRequest { @@ -131,6 +144,7 @@ message ApplicationResourcePatchRequest { required string kind = 6; required string patch = 7; required string patchType = 8; + optional string appNamespace = 9; } message ApplicationResourceDeleteRequest { @@ -142,6 +156,7 @@ message ApplicationResourceDeleteRequest { required string kind = 6; optional bool force = 7; optional bool orphan = 8; + optional string appNamespace = 9; } message ResourceActionRunRequest { @@ -152,6 +167,7 @@ message ResourceActionRunRequest { optional string group = 5; required string kind = 6; required string action = 7; + optional string appNamespace = 8; } message ResourceActionsListResponse { @@ -177,6 +193,7 @@ message ApplicationPodLogsQuery { optional string group = 12; optional string resourceName = 13 ; optional bool previous = 14; + optional string appNamespace = 15; } message LogEntry { @@ -190,10 +207,12 @@ message LogEntry { message OperationTerminateRequest { required string name = 1; + optional string appNamespace = 2; } message ApplicationSyncWindowsQuery { required string name = 1; + optional string appNamespace = 2; } message ApplicationSyncWindowsResponse { @@ -221,6 +240,7 @@ message ResourcesQuery { optional string version = 4; optional string group = 5; optional string kind = 6; + optional string appNamespace = 7; } message ManagedResourcesResponse { diff --git a/server/application/application_test.go b/server/application/application_test.go index 3842452134015..e44bc1901b728 100644 --- a/server/application/application_test.go +++ b/server/application/application_test.go @@ -37,6 +37,7 @@ import ( "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks" "github.com/argoproj/argo-cd/v2/server/rbacpolicy" "github.com/argoproj/argo-cd/v2/test" + "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/assets" "github.com/argoproj/argo-cd/v2/util/cache" "github.com/argoproj/argo-cd/v2/util/db" @@ -201,7 +202,7 @@ func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), objects ...ru testNamespace, kubeclientset, fakeAppsClientset, - factory.Argoproj().V1alpha1().Applications().Lister().Applications(testNamespace), + factory.Argoproj().V1alpha1().Applications().Lister(), appInformer, mockRepoClient, nil, @@ -211,6 +212,7 @@ func newTestAppServerWithEnforcerConfigure(f func(*rbac.Enforcer), objects ...ru sync.NewKeyLock(), settingsMgr, projInformer, + []string{}, ) return server.(*Server) } @@ -749,8 +751,14 @@ func TestServer_GetApplicationSyncWindowsState(t *testing.T) { func TestGetCachedAppState(t *testing.T) { testApp := newTestApp() testApp.ObjectMeta.ResourceVersion = "1" - testApp.Spec.Project = "none" - appServer := newTestAppServer(testApp) + testApp.Spec.Project = "test-proj" + testProj := &appsv1.AppProject{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-proj", + Namespace: testNamespace, + }, + } + appServer := newTestAppServer(testApp, testProj) fakeClientSet := appServer.appclientset.(*apps.Clientset) t.Run("NoError", func(t *testing.T) { err := appServer.getCachedAppState(context.Background(), testApp, func() error { @@ -901,7 +909,8 @@ func TestLogsGetSelectedPod(t *testing.T) { // refreshAnnotationRemover runs an infinite loop until it detects and removes refresh annotation or given context is done func refreshAnnotationRemover(t *testing.T, ctx context.Context, patched *int32, appServer *Server, appName string, ch chan string) { for ctx.Err() == nil { - a, err := appServer.appLister.Get(appName) + aName, appNs := argo.ParseAppQualifiedName(appName, appServer.ns) + a, err := appServer.appLister.Applications(appNs).Get(aName) require.NoError(t, err) a = a.DeepCopy() if a.GetAnnotations() != nil && a.GetAnnotations()[appsv1.AnnotationKeyRefresh] != "" { diff --git a/server/application/terminal.go b/server/application/terminal.go index 94645b53feb6c..4644facee8880 100644 --- a/server/application/terminal.go +++ b/server/application/terminal.go @@ -28,16 +28,17 @@ import ( ) type terminalHandler struct { - appLister applisters.ApplicationNamespaceLister + appLister applisters.ApplicationLister db db.ArgoDB enf *rbac.Enforcer cache *servercache.Cache appResourceTreeFn func(ctx context.Context, app *appv1.Application) (*appv1.ApplicationTree, error) allowedShells []string + namespace string } // NewHandler returns a new terminal handler. -func NewHandler(appLister applisters.ApplicationNamespaceLister, db db.ArgoDB, enf *rbac.Enforcer, cache *servercache.Cache, +func NewHandler(appLister applisters.ApplicationLister, namespace string, db db.ArgoDB, enf *rbac.Enforcer, cache *servercache.Cache, appResourceTree AppResourceTreeFn, allowedShells []string) *terminalHandler { return &terminalHandler{ appLister: appLister, @@ -46,6 +47,7 @@ func NewHandler(appLister applisters.ApplicationNamespaceLister, db db.ArgoDB, e cache: cache, appResourceTreeFn: appResourceTree, allowedShells: allowedShells, + namespace: namespace, } } @@ -143,7 +145,7 @@ func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fieldLog := log.WithFields(log.Fields{"application": app, "userName": sessionmgr.Username(ctx), "container": container, "podName": podName, "namespace": namespace, "cluster": project}) - a, err := s.appLister.Get(app) + a, err := s.appLister.Applications(s.namespace).Get(app) if err != nil { if apierr.IsNotFound(err) { http.Error(w, "App not found", http.StatusNotFound) diff --git a/server/repository/repository.go b/server/repository/repository.go index d1a00116e96a9..f2738ed05e21d 100644 --- a/server/repository/repository.go +++ b/server/repository/repository.go @@ -35,7 +35,7 @@ type Server struct { repoClientset apiclient.Clientset enf *rbac.Enforcer cache *servercache.Cache - appLister applisters.ApplicationNamespaceLister + appLister applisters.ApplicationLister projLister applisters.AppProjectNamespaceLister settings *settings.SettingsManager } @@ -46,7 +46,7 @@ func NewServer( db db.ArgoDB, enf *rbac.Enforcer, cache *servercache.Cache, - appLister applisters.ApplicationNamespaceLister, + appLister applisters.ApplicationLister, projLister applisters.AppProjectNamespaceLister, settings *settings.SettingsManager, ) *Server { @@ -287,8 +287,8 @@ func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDeta if err := s.enf.EnforceErr(claims, rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil { return nil, err } - - app, err := s.appLister.Get(q.AppName) + appName, appNs := argo.ParseAppQualifiedName(q.AppName, s.settings.GetNamespace()) + app, err := s.appLister.Applications(appNs).Get(appName) appRBACObj := createRBACObject(q.AppProject, q.AppName) // ensure caller has read privileges to app if err := s.enf.EnforceErr(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, appRBACObj); err != nil { diff --git a/server/repository/repository_test.go b/server/repository/repository_test.go index 2b2770c6cfcf6..a76d373976f7b 100644 --- a/server/repository/repository_test.go +++ b/server/repository/repository_test.go @@ -123,7 +123,7 @@ var ( } ) -func newAppAndProjLister(objects ...runtime.Object) (applisters.ApplicationNamespaceLister, applisters.AppProjectNamespaceLister) { +func newAppAndProjLister(objects ...runtime.Object) (applisters.ApplicationLister, applisters.AppProjectNamespaceLister) { fakeAppsClientset := fakeapps.NewSimpleClientset(objects...) factory := appinformer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, appinformer.WithNamespace(""), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {})) projInformer := factory.Argoproj().V1alpha1().AppProjects() @@ -136,7 +136,7 @@ func newAppAndProjLister(objects ...runtime.Object) (applisters.ApplicationNames _ = appsInformer.Informer().GetStore().Add(obj) } } - appLister := appsInformer.Lister().Applications(testNamespace) + appLister := appsInformer.Lister() projLister := projInformer.Lister().AppProjects(testNamespace) return appLister, projLister } diff --git a/server/server.go b/server/server.go index 72367b0874498..9988ae6a4dfe6 100644 --- a/server/server.go +++ b/server/server.go @@ -167,7 +167,7 @@ type ArgoCDServer struct { projLister applisters.AppProjectNamespaceLister policyEnforcer *rbacpolicy.RBACPolicyEnforcer appInformer cache.SharedIndexInformer - appLister applisters.ApplicationNamespaceLister + appLister applisters.ApplicationLister db db.ArgoDB // stopCh is the channel which when closed, will shutdown the Argo CD server @@ -200,6 +200,7 @@ type ArgoCDServerOpts struct { XFrameOptions string ContentSecurityPolicy string ListenHost string + ApplicationNamespaces []string } // initializeDefaultProject creates the default project if it does not already exist @@ -231,12 +232,18 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer { err = initializeDefaultProject(opts) errors.CheckError(err) - factory := appinformer.NewSharedInformerFactoryWithOptions(opts.AppClientset, 0, appinformer.WithNamespace(opts.Namespace), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {})) - projInformer := factory.Argoproj().V1alpha1().AppProjects().Informer() - projLister := factory.Argoproj().V1alpha1().AppProjects().Lister().AppProjects(opts.Namespace) + appInformerNs := opts.Namespace + if len(opts.ApplicationNamespaces) > 0 { + appInformerNs = "" + } + projFactory := appinformer.NewSharedInformerFactoryWithOptions(opts.AppClientset, 0, appinformer.WithNamespace(opts.Namespace), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {})) + appFactory := appinformer.NewSharedInformerFactoryWithOptions(opts.AppClientset, 0, appinformer.WithNamespace(appInformerNs), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {})) + + projInformer := projFactory.Argoproj().V1alpha1().AppProjects().Informer() + projLister := projFactory.Argoproj().V1alpha1().AppProjects().Lister().AppProjects(opts.Namespace) - appInformer := factory.Argoproj().V1alpha1().Applications().Informer() - appLister := factory.Argoproj().V1alpha1().Applications().Lister().Applications(opts.Namespace) + appInformer := appFactory.Argoproj().V1alpha1().Applications().Informer() + appLister := appFactory.Argoproj().V1alpha1().Applications().Lister() userStateStorage := util_session.NewUserStateStorage(opts.RedisClient) sessionMgr := util_session.NewSessionManager(settingsMgr, projLister, opts.DexServerAddr, opts.DexTLSConfig, userStateStorage) @@ -440,6 +447,7 @@ func (a *ArgoCDServer) Run(ctx context.Context, listeners *Listeners) { // Start the muxed listeners for our servers log.Infof("argocd %s serving on port %d (url: %s, tls: %v, namespace: %s, sso: %v)", common.GetVersion(), a.ListenPort, a.settings.URL, a.useTLS(), a.Namespace, a.settings.IsSSOConfigured()) + log.Infof("Enabled application namespace patterns: %s", a.allowedApplicationNamespacesAsString()) go func() { a.checkServeErr("grpcS", grpcS.Serve(grpcL)) }() go func() { a.checkServeErr("httpS", httpS.Serve(httpL)) }() @@ -687,7 +695,8 @@ func (a *ArgoCDServer) newGRPCServer() (*grpc.Server, application.AppResourceTre a.enf, projectLock, a.settingsMgr, - a.projInformer) + a.projInformer, + a.ApplicationNamespaces) projectService := project.NewServer(a.Namespace, a.KubeClientset, a.AppClientset, a.enf, projectLock, a.sessionMgr, a.policyEnforcer, a.projInformer, a.settingsMgr, a.db) settingsService := settings.NewServer(a.settingsMgr, a, a.DisableAuth) accountService := account.NewServer(a.sessionMgr, a.settingsMgr, a.enf) @@ -812,7 +821,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl } mux.Handle("/api/", handler) - terminalHandler := application.NewHandler(a.appLister, a.db, a.enf, a.Cache, appResourceTreeFn, a.settings.ExecShells) + terminalHandler := application.NewHandler(a.appLister, a.Namespace, a.db, a.enf, a.Cache, appResourceTreeFn, a.settings.ExecShells) mux.HandleFunc("/terminal", func(writer http.ResponseWriter, request *http.Request) { argocdSettings, err := a.settingsMgr.GetSettings() if err != nil { @@ -1262,3 +1271,14 @@ func bug21955WorkaroundInterceptor(ctx context.Context, req interface{}, _ *grpc } return handler(ctx, req) } + +// allowedNamespacesAsString returns a string containing comma-separated list +// of allowed application namespaces +func (a *ArgoCDServer) allowedApplicationNamespacesAsString() string { + ns := a.Namespace + if len(a.ArgoCDServerOpts.ApplicationNamespaces) > 0 { + ns += ", " + ns += strings.Join(a.ArgoCDServerOpts.ApplicationNamespaces, ", ") + } + return ns +} diff --git a/server/settings/settings.go b/server/settings/settings.go index 148bf321c9cdb..ba16aa7c07ebc 100644 --- a/server/settings/settings.go +++ b/server/settings/settings.go @@ -125,6 +125,7 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin set.UiBannerURL = argoCDSettings.UiBannerURL set.UiBannerPermanent = argoCDSettings.UiBannerPermanent set.UiBannerPosition = argoCDSettings.UiBannerPosition + set.ControllerNamespace = s.mgr.GetNamespace() } if argoCDSettings.DexConfig != "" { var cfg settingspkg.DexConfig diff --git a/server/settings/settings.proto b/server/settings/settings.proto index be8ce88726738..6183632d0ae0d 100644 --- a/server/settings/settings.proto +++ b/server/settings/settings.proto @@ -39,6 +39,7 @@ message Settings { string uiBannerPosition = 20; string statusBadgeRootUrl = 21; bool execEnabled = 22; + string controllerNamespace = 23; } message GoogleAnalyticsConfig { diff --git a/test/container/Procfile b/test/container/Procfile index c0b30152a2190..bc03df97bd53a 100644 --- a/test/container/Procfile +++ b/test/container/Procfile @@ -1,5 +1,5 @@ -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}" -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_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} " +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} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}" +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_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} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} " dex: sh -c "test $ARGOCD_IN_CI = true && exit 0; ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/cmd gendexcfg -o `pwd`/dist/dex.yaml && 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:v2.30.0 serve /dex.yaml" redis: sh -c "/usr/local/bin/redis-server --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}" repo-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_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_BINARY_NAME=argocd-repo-server $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}" diff --git a/test/e2e/app_autosync_ns_test.go b/test/e2e/app_autosync_ns_test.go new file mode 100644 index 0000000000000..53e87db87d931 --- /dev/null +++ b/test/e2e/app_autosync_ns_test.go @@ -0,0 +1,102 @@ +package e2e + +import ( + "context" + "testing" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + . "github.com/argoproj/gitops-engine/pkg/sync/common" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/types" + + . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" + "github.com/argoproj/argo-cd/v2/util/errors" +) + +func TestNSAutoSyncSelfHealDisabled(t *testing.T) { + Given(t). + SetTrackingMethod("annotation"). + Path(guestbookPath). + SetAppNamespace(fixture.AppNamespace()). + // TODO: There is a bug with annotation tracking method that prevents + // controller from picking up changes in the cluster. + When(). + // app should be auto-synced once created + CreateFromFile(func(app *Application) { + app.Spec.SyncPolicy = &SyncPolicy{Automated: &SyncPolicyAutomated{SelfHeal: false}} + }). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + // app should be auto-synced if git change detected + When(). + PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": 1}]`). + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + // app should not be auto-synced if k8s change detected + When(). + And(func() { + errors.FailOnErr(fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Patch(context.Background(), + "guestbook-ui", types.MergePatchType, []byte(`{"spec": {"revisionHistoryLimit": 0}}`), v1.PatchOptions{})) + }). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)) +} + +func TestNSAutoSyncSelfHealEnabled(t *testing.T) { + Given(t). + SetTrackingMethod("annotation"). + Path(guestbookPath). + SetAppNamespace(fixture.AppNamespace()). + When(). + // app should be auto-synced once created + CreateFromFile(func(app *Application) { + app.Spec.SyncPolicy = &SyncPolicy{ + Automated: &SyncPolicyAutomated{SelfHeal: true}, + Retry: &RetryStrategy{Limit: 0}, + } + }). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + When(). + // app should be auto-synced once k8s change detected + And(func() { + errors.FailOnErr(fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Patch(context.Background(), + "guestbook-ui", types.MergePatchType, []byte(`{"spec": {"revisionHistoryLimit": 0}}`), v1.PatchOptions{})) + }). + Refresh(RefreshTypeNormal). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + When(). + // app should be attempted to auto-synced once and marked with error after failed attempt detected + PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": "badValue"}]`). + Refresh(RefreshTypeNormal). + Then(). + Expect(OperationPhaseIs(OperationFailed)). + When(). + // Trigger refresh again to make sure controller notices previously failed sync attempt before expectation timeout expires + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(Condition(ApplicationConditionSyncError, "Failed sync attempt")). + When(). + // SyncError condition should be removed after successful sync + PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": 1}]`). + Refresh(RefreshTypeNormal). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + When(). + // Trigger refresh twice to make sure controller notices successful attempt and removes condition + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + assert.Len(t, app.Status.Conditions, 0) + }) + +} diff --git a/test/e2e/app_autosync_test.go b/test/e2e/app_autosync_test.go index 4367fb72291bf..e9da1f659937e 100644 --- a/test/e2e/app_autosync_test.go +++ b/test/e2e/app_autosync_test.go @@ -38,6 +38,7 @@ func TestAutoSyncSelfHealDisabled(t *testing.T) { errors.FailOnErr(fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Patch(context.Background(), "guestbook-ui", types.MergePatchType, []byte(`{"spec": {"revisionHistoryLimit": 0}}`), v1.PatchOptions{})) }). + Refresh(RefreshTypeNormal). Then(). Expect(SyncStatusIs(SyncStatusCodeOutOfSync)) } diff --git a/test/e2e/app_management_ns_test.go b/test/e2e/app_management_ns_test.go new file mode 100644 index 0000000000000..8ca95a07da168 --- /dev/null +++ b/test/e2e/app_management_ns_test.go @@ -0,0 +1,2213 @@ +package e2e + +import ( + "context" + "fmt" + "math/rand" + "path" + "reflect" + "strings" + "testing" + "time" + + "github.com/argoproj/gitops-engine/pkg/diff" + "github.com/argoproj/gitops-engine/pkg/health" + . "github.com/argoproj/gitops-engine/pkg/sync/common" + "github.com/argoproj/gitops-engine/pkg/utils/kube" + "github.com/argoproj/pkg/errors" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/pointer" + + "github.com/argoproj/argo-cd/v2/common" + applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" + . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + . "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + accountFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/account" + . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" + projectFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/project" + repoFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/repos" + "github.com/argoproj/argo-cd/v2/test/e2e/testdata" + . "github.com/argoproj/argo-cd/v2/util/argo" + . "github.com/argoproj/argo-cd/v2/util/errors" + "github.com/argoproj/argo-cd/v2/util/io" + "github.com/argoproj/argo-cd/v2/util/settings" +) + +// This empty test is here only for clarity, to conform to logs rbac tests structure in account. This exact usecase is covered in the TestAppLogs test +func TestNamespacedGetLogsAllowNoSwitch(t *testing.T) { +} + +// There is some code duplication in the below GetLogs tests, the reason for that is to allow getting rid of most of those tests easily in the next release, +// when the temporary switch would die +func TestNamespacedGetLogsDenySwitchOn(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") + + accountFixture.Given(t). + Name("test"). + When(). + Create(). + Login(). + SetPermissions([]fixture.ACL{ + { + Resource: "applications", + Action: "create", + Scope: "*", + }, + { + Resource: "applications", + Action: "get", + Scope: "*", + }, + { + Resource: "applications", + Action: "sync", + Scope: "*", + }, + { + Resource: "projects", + Action: "get", + Scope: "*", + }, + }, "app-creator") + + ctx := GivenWithSameState(t) + ctx.SetAppNamespace(ArgoCDAppNamespace) + ctx. + Path("guestbook-logs"). + SetTrackingMethod("annotation"). + When(). + CreateApp(). + Sync(). + SetParamInSettingConfigMap("server.rbac.log.enforce.enable", "true"). + Then(). + Expect(HealthIs(health.HealthStatusHealthy)). + And(func(app *Application) { + _, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") + assert.Error(t, err) + assert.Contains(t, err.Error(), "permission denied") + }) +} + +func TestNamespacedGetLogsAllowSwitchOnNS(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") + + accountFixture.Given(t). + Name("test"). + When(). + Create(). + Login(). + SetPermissions([]fixture.ACL{ + { + Resource: "applications", + Action: "create", + Scope: "*", + }, + { + Resource: "applications", + Action: "get", + Scope: "*", + }, + { + Resource: "applications", + Action: "sync", + Scope: "*", + }, + { + Resource: "projects", + Action: "get", + Scope: "*", + }, + { + Resource: "logs", + Action: "get", + Scope: "*", + }, + }, "app-creator") + + ctx := GivenWithSameState(t) + ctx.SetAppNamespace(AppNamespace()) + ctx. + Path("guestbook-logs"). + SetTrackingMethod("annotation"). + When(). + CreateApp(). + Sync(). + SetParamInSettingConfigMap("server.rbac.log.enforce.enable", "true"). + Then(). + Expect(HealthIs(health.HealthStatusHealthy)). + And(func(app *Application) { + out, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") + assert.NoError(t, err) + assert.Contains(t, out, "Hi") + }). + And(func(app *Application) { + out, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Pod") + assert.NoError(t, err) + assert.Contains(t, out, "Hi") + }). + And(func(app *Application) { + out, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Service") + assert.NoError(t, err) + assert.NotContains(t, out, "Hi") + }) + +} + +func TestNamespacedGetLogsAllowSwitchOff(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") + + accountFixture.Given(t). + Name("test"). + When(). + Create(). + Login(). + SetPermissions([]fixture.ACL{ + { + Resource: "applications", + Action: "create", + Scope: "*", + }, + { + Resource: "applications", + Action: "get", + Scope: "*", + }, + { + Resource: "applications", + Action: "sync", + Scope: "*", + }, + { + Resource: "projects", + Action: "get", + Scope: "*", + }, + }, "app-creator") + ctx := GivenWithSameState(t) + ctx.SetAppNamespace(AppNamespace()) + ctx. + Path("guestbook-logs"). + SetTrackingMethod("annotation"). + When(). + CreateApp(). + Sync(). + SetParamInSettingConfigMap("server.rbac.log.enforce.enable", "false"). + Then(). + Expect(HealthIs(health.HealthStatusHealthy)). + And(func(app *Application) { + out, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") + assert.NoError(t, err) + assert.Contains(t, out, "Hi") + }). + And(func(app *Application) { + out, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Pod") + assert.NoError(t, err) + assert.Contains(t, out, "Hi") + }). + And(func(app *Application) { + out, err := RunCli("app", "logs", ctx.AppQualifiedName(), "--kind", "Service") + assert.NoError(t, err) + assert.NotContains(t, out, "Hi") + }) +} + +func TestNamespacedSyncToUnsignedCommit(t *testing.T) { + SkipOnEnv(t, "GPG") + GivenWithNamespace(t, AppNamespace()). + SetTrackingMethod("annotation"). + Project("gpg"). + Path(guestbookPath). + When(). + IgnoreErrors(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationError)). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(HealthIs(health.HealthStatusMissing)) +} + +func TestNamespacedSyncToSignedCommitWKK(t *testing.T) { + SkipOnEnv(t, "GPG") + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Project("gpg"). + Path(guestbookPath). + When(). + AddSignedFile("test.yaml", "null"). + IgnoreErrors(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationError)). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(HealthIs(health.HealthStatusMissing)) +} + +func TestNamespacedSyncToSignedCommitKWKK(t *testing.T) { + SkipOnEnv(t, "GPG") + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Project("gpg"). + Path(guestbookPath). + GPGPublicKeyAdded(). + Sleep(2). + When(). + AddSignedFile("test.yaml", "null"). + IgnoreErrors(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)) +} + +func TestNamespacedAppCreation(t *testing.T) { + ctx := Given(t) + ctx. + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + And(func(app *Application) { + assert.Equal(t, Name(), app.Name) + assert.Equal(t, AppNamespace(), app.Namespace) + assert.Equal(t, RepoURL(RepoURLTypeFile), app.Spec.Source.RepoURL) + assert.Equal(t, guestbookPath, app.Spec.Source.Path) + assert.Equal(t, DeploymentNamespace(), app.Spec.Destination.Namespace) + assert.Equal(t, KubernetesInternalAPIServerAddr, app.Spec.Destination.Server) + }). + Expect(NamespacedEvent(AppNamespace(), EventReasonResourceCreated, "create")). + And(func(app *Application) { + // app should be listed + output, err := RunCli("app", "list") + assert.NoError(t, err) + assert.Contains(t, output, ctx.AppQualifiedName()) + }). + When(). + // ensure that create is idempotent + CreateApp(). + Then(). + Given(). + Revision("master"). + When(). + // ensure that update replaces spec and merge labels and annotations + And(func() { + FailOnErr(AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Patch(context.Background(), + ctx.GetName(), types.MergePatchType, []byte(`{"metadata": {"labels": { "test": "label" }, "annotations": { "test": "annotation" }}}`), metav1.PatchOptions{})) + }). + CreateApp("--upsert"). + Then(). + And(func(app *Application) { + assert.Equal(t, "label", app.Labels["test"]) + assert.Equal(t, "annotation", app.Annotations["test"]) + assert.Equal(t, "master", app.Spec.Source.TargetRevision) + }) +} + +func TestNamespacedAppCreationWithoutForceUpdate(t *testing.T) { + ctx := Given(t) + + ctx. + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + DestName("in-cluster"). + When(). + CreateApp(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + And(func(app *Application) { + assert.Equal(t, ctx.AppName(), app.Name) + assert.Equal(t, AppNamespace(), app.Namespace) + assert.Equal(t, RepoURL(RepoURLTypeFile), app.Spec.Source.RepoURL) + assert.Equal(t, guestbookPath, app.Spec.Source.Path) + assert.Equal(t, DeploymentNamespace(), app.Spec.Destination.Namespace) + assert.Equal(t, "in-cluster", app.Spec.Destination.Name) + }). + Expect(NamespacedEvent(AppNamespace(), EventReasonResourceCreated, "create")). + And(func(_ *Application) { + // app should be listed + output, err := RunCli("app", "list") + assert.NoError(t, err) + assert.Contains(t, output, ctx.AppQualifiedName()) + }). + When(). + IgnoreErrors(). + CreateApp(). + Then(). + Expect(Error("", "existing application spec is different, use upsert flag to force update")) +} + +func TestNamespacedDeleteAppResource(t *testing.T) { + ctx := Given(t) + + ctx. + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(_ *Application) { + // app should be listed + if _, err := RunCli("app", "delete-resource", ctx.AppQualifiedName(), "--kind", "Service", "--resource-name", "guestbook-ui"); err != nil { + assert.NoError(t, err) + } + }). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(HealthIs(health.HealthStatusMissing)) +} + +// demonstrate that we cannot use a standard sync when an immutable field is changed, we must use "force" +func TestNamespacedImmutableChange(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") + text := FailOnErr(Run(".", "kubectl", "get", "service", "-n", "kube-system", "kube-dns", "-o", "jsonpath={.spec.clusterIP}")).(string) + parts := strings.Split(text, ".") + n := rand.Intn(254) + ip1 := fmt.Sprintf("%s.%s.%s.%d", parts[0], parts[1], parts[2], n) + ip2 := fmt.Sprintf("%s.%s.%s.%d", parts[0], parts[1], parts[2], n+1) + Given(t). + Path("service"). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + PatchFile("service.yaml", fmt.Sprintf(`[{"op": "add", "path": "/spec/clusterIP", "value": "%s"}]`, ip1)). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)). + When(). + PatchFile("service.yaml", fmt.Sprintf(`[{"op": "add", "path": "/spec/clusterIP", "value": "%s"}]`, ip2)). + IgnoreErrors(). + Sync(). + DoNotIgnoreErrors(). + Then(). + Expect(OperationPhaseIs(OperationFailed)). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(ResourceResultNumbering(1)). + Expect(ResourceResultMatches(ResourceResult{ + Kind: "Service", + Version: "v1", + Namespace: DeploymentNamespace(), + Name: "my-service", + SyncPhase: "Sync", + Status: "SyncFailed", + HookPhase: "Failed", + Message: `Service "my-service" is invalid`, + })). + // now we can do this will a force + Given(). + Force(). + When(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)) +} + +func TestNamespacedInvalidAppProject(t *testing.T) { + Given(t). + SetTrackingMethod("annotation"). + Path(guestbookPath). + SetAppNamespace(AppNamespace()). + Project("does-not-exist"). + When(). + IgnoreErrors(). + CreateApp(). + Then(). + Expect(Error("", "application references project does-not-exist which does not exist")) +} + +func TestNamespacedAppDeletion(t *testing.T) { + ctx := Given(t) + ctx. + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + When(). + Delete(true). + Then(). + Expect(DoesNotExist()). + Expect(NamespacedEvent(AppNamespace(), EventReasonResourceDeleted, "delete")) + + output, err := RunCli("app", "list") + assert.NoError(t, err) + assert.NotContains(t, output, ctx.AppQualifiedName()) +} + +func TestNamespacedAppLabels(t *testing.T) { + ctx := Given(t) + ctx. + Path("config-map"). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp("-l", "foo=bar"). + Then(). + And(func(app *Application) { + assert.Contains(t, FailOnErr(RunCli("app", "list")), ctx.AppQualifiedName()) + assert.Contains(t, FailOnErr(RunCli("app", "list", "-l", "foo=bar")), ctx.AppQualifiedName()) + assert.NotContains(t, FailOnErr(RunCli("app", "list", "-l", "foo=rubbish")), ctx.AppQualifiedName()) + }). + Given(). + // remove both name and replace labels means nothing will sync + Name(""). + When(). + IgnoreErrors(). + Sync("-l", "foo=rubbish"). + DoNotIgnoreErrors(). + Then(). + Expect(Error("", "No matching apps found for filter: selector foo=rubbish")). + // check we can update the app and it is then sync'd + Given(). + When(). + Sync("-l", "foo=bar") +} + +func TestNamespacedTrackAppStateAndSyncApp(t *testing.T) { + Given(t). + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)). + Expect(Success(fmt.Sprintf("Service %s guestbook-ui Synced ", DeploymentNamespace()))). + Expect(Success(fmt.Sprintf("apps Deployment %s guestbook-ui Synced", DeploymentNamespace()))). + Expect(NamespacedEvent(AppNamespace(), EventReasonResourceUpdated, "sync")). + And(func(app *Application) { + assert.NotNil(t, app.Status.OperationState.SyncResult) + }) +} + +func TestNamespacedAppRollbackSuccessful(t *testing.T) { + ctx := Given(t) + ctx. + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + assert.NotEmpty(t, app.Status.Sync.Revision) + }). + And(func(app *Application) { + appWithHistory := app.DeepCopy() + appWithHistory.Status.History = []RevisionHistory{{ + ID: 1, + Revision: app.Status.Sync.Revision, + DeployedAt: metav1.Time{Time: metav1.Now().UTC().Add(-1 * time.Minute)}, + Source: app.Spec.Source, + }, { + ID: 2, + Revision: "cdb", + DeployedAt: metav1.Time{Time: metav1.Now().UTC().Add(-2 * time.Minute)}, + Source: app.Spec.Source, + }} + patch, _, err := diff.CreateTwoWayMergePatch(app, appWithHistory, &Application{}) + require.NoError(t, err) + app, err = AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{}) + require.NoError(t, err) + + // sync app and make sure it reaches InSync state + _, err = RunCli("app", "rollback", app.QualifiedName(), "1") + require.NoError(t, err) + + }). + Expect(NamespacedEvent(AppNamespace(), EventReasonOperationStarted, "rollback")). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + assert.Equal(t, SyncStatusCodeSynced, app.Status.Sync.Status) + require.NotNil(t, app.Status.OperationState.SyncResult) + assert.Equal(t, 2, len(app.Status.OperationState.SyncResult.Resources)) + assert.Equal(t, OperationSucceeded, app.Status.OperationState.Phase) + assert.Equal(t, 3, len(app.Status.History)) + }) +} + +func TestNamespacedComparisonFailsIfClusterNotAdded(t *testing.T) { + Given(t). + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + DestServer("https://not-registered-cluster/api"). + When(). + IgnoreErrors(). + CreateApp(). + Then(). + Expect(DoesNotExist()) +} + +func TestNamespacedCannotSetInvalidPath(t *testing.T) { + Given(t). + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + IgnoreErrors(). + AppSet("--path", "garbage"). + Then(). + Expect(Error("", "app path does not exist")) +} + +func TestNamespacedManipulateApplicationResources(t *testing.T) { + ctx := Given(t) + ctx. + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + manifests, err := RunCli("app", "manifests", ctx.AppQualifiedName(), "--source", "live") + assert.NoError(t, err) + resources, err := kube.SplitYAML([]byte(manifests)) + assert.NoError(t, err) + + index := -1 + for i := range resources { + if resources[i].GetKind() == kube.DeploymentKind { + index = i + break + } + } + assert.True(t, index > -1) + + deployment := resources[index] + + closer, client, err := ArgoCDClientset.NewApplicationClient() + assert.NoError(t, err) + defer io.Close(closer) + + _, err = client.DeleteResource(context.Background(), &applicationpkg.ApplicationResourceDeleteRequest{ + Name: &app.Name, + AppNamespace: pointer.String(AppNamespace()), + Group: pointer.String(deployment.GroupVersionKind().Group), + Kind: pointer.String(deployment.GroupVersionKind().Kind), + Version: pointer.String(deployment.GroupVersionKind().Version), + Namespace: pointer.String(deployment.GetNamespace()), + ResourceName: pointer.String(deployment.GetName()), + }) + assert.NoError(t, err) + }). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)) +} + +func TestNamespacedAppWithSecrets(t *testing.T) { + closer, client, err := ArgoCDClientset.NewApplicationClient() + assert.NoError(t, err) + defer io.Close(closer) + + ctx := Given(t) + ctx. + Path("secrets"). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + When(). + CreateApp(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + res := FailOnErr(client.GetResource(context.Background(), &applicationpkg.ApplicationResourceRequest{ + Namespace: &app.Spec.Destination.Namespace, + AppNamespace: pointer.String(AppNamespace()), + Kind: pointer.String(kube.SecretKind), + Group: pointer.String(""), + Name: &app.Name, + Version: pointer.String("v1"), + ResourceName: pointer.String("test-secret"), + })).(*applicationpkg.ApplicationResourceResponse) + assetSecretDataHidden(t, res.GetManifest()) + + manifests, err := client.GetManifests(context.Background(), &applicationpkg.ApplicationManifestQuery{ + Name: &app.Name, + AppNamespace: pointer.String(AppNamespace()), + }) + errors.CheckError(err) + + for _, manifest := range manifests.Manifests { + assetSecretDataHidden(t, manifest) + } + + diffOutput := FailOnErr(RunCli("app", "diff", ctx.AppQualifiedName())).(string) + assert.Empty(t, diffOutput) + + // make sure resource update error does not print secret details + _, err = RunCli("app", "patch-resource", ctx.AppQualifiedName(), "--resource-name", "test-secret", + "--kind", "Secret", "--patch", `{"op": "add", "path": "/data", "value": "hello"}'`, + "--patch-type", "application/json-patch+json") + require.Error(t, err) + assert.Contains(t, err.Error(), fmt.Sprintf("failed to patch Secret %s/test-secret", DeploymentNamespace())) + assert.NotContains(t, err.Error(), "username") + assert.NotContains(t, err.Error(), "password") + + // patch secret and make sure app is out of sync and diff detects the change + FailOnErr(KubeClientset.CoreV1().Secrets(DeploymentNamespace()).Patch(context.Background(), + "test-secret", types.JSONPatchType, []byte(`[ + {"op": "remove", "path": "/data/username"}, + {"op": "add", "path": "/stringData", "value": {"password": "foo"}} +]`), metav1.PatchOptions{})) + }). + When(). + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + And(func(app *Application) { + diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName()) + assert.Error(t, err) + assert.Contains(t, diffOutput, "username: ++++++++") + assert.Contains(t, diffOutput, "password: ++++++++++++") + + // local diff should ignore secrets + diffOutput = FailOnErr(RunCli("app", "diff", ctx.AppQualifiedName(), "--local", "testdata/secrets")).(string) + assert.Empty(t, diffOutput) + + // ignore missing field and make sure diff shows no difference + app.Spec.IgnoreDifferences = []ResourceIgnoreDifferences{{ + Kind: kube.SecretKind, JSONPointers: []string{"/data"}, + }} + FailOnErr(client.UpdateSpec(context.Background(), &applicationpkg.ApplicationUpdateSpecRequest{Name: &app.Name, AppNamespace: pointer.String(AppNamespace()), Spec: &app.Spec})) + }). + When(). + Refresh(RefreshTypeNormal). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + diffOutput := FailOnErr(RunCli("app", "diff", ctx.AppQualifiedName())).(string) + assert.Empty(t, diffOutput) + }). + // verify not committed secret also ignore during diffing + When(). + WriteFile("secret3.yaml", ` +apiVersion: v1 +kind: Secret +metadata: + name: test-secret3 +stringData: + username: test-username`). + Then(). + And(func(app *Application) { + diffOutput := FailOnErr(RunCli("app", "diff", ctx.AppQualifiedName(), "--local", "testdata/secrets")).(string) + assert.Empty(t, diffOutput) + }) +} + +func TestNamespacedResourceDiffing(t *testing.T) { + ctx := Given(t) + ctx. + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + // Patch deployment + _, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Patch(context.Background(), + "guestbook-ui", types.JSONPatchType, []byte(`[{ "op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "test" }]`), metav1.PatchOptions{}) + assert.NoError(t, err) + }). + When(). + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + And(func(app *Application) { + diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local", "testdata/guestbook") + assert.Error(t, err) + assert.Contains(t, diffOutput, fmt.Sprintf("===== apps/Deployment %s/guestbook-ui ======", DeploymentNamespace())) + }). + Given(). + ResourceOverrides(map[string]ResourceOverride{"apps/Deployment": { + IgnoreDifferences: OverrideIgnoreDiff{JSONPointers: []string{"/spec/template/spec/containers/0/image"}}, + }}). + When(). + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local", "testdata/guestbook") + assert.NoError(t, err) + assert.Empty(t, diffOutput) + }). + Given(). + When(). + And(func() { + output, err := RunWithStdin(testdata.SSARevisionHistoryDeployment, "", "kubectl", "apply", "-n", DeploymentNamespace(), "--server-side=true", "--field-manager=revision-history-manager", "--validate=false", "--force-conflicts", "-f", "-") + assert.NoError(t, err) + assert.Contains(t, output, "serverside-applied") + }). + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Given(). + ResourceOverrides(map[string]ResourceOverride{"apps/Deployment": { + IgnoreDifferences: OverrideIgnoreDiff{ + ManagedFieldsManagers: []string{"revision-history-manager"}, + JSONPointers: []string{"/spec/template/spec/containers/0/image"}, + }, + }}). + When(). + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Given(). + When(). + Sync(). + PatchApp(`[{ + "op": "add", + "path": "/spec/syncPolicy", + "value": { "syncOptions": ["RespectIgnoreDifferences=true"] } + }]`). + And(func() { + deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}) + assert.NoError(t, err) + assert.Equal(t, int32(3), *deployment.Spec.RevisionHistoryLimit) + }). + And(func() { + output, err := RunWithStdin(testdata.SSARevisionHistoryDeployment, "", "kubectl", "apply", "-n", DeploymentNamespace(), "--server-side=true", "--field-manager=revision-history-manager", "--validate=false", "--force-conflicts", "-f", "-") + assert.NoError(t, err) + assert.Contains(t, output, "serverside-applied") + }). + Then(). + When().Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}) + assert.NoError(t, err) + assert.Equal(t, int32(1), *deployment.Spec.RevisionHistoryLimit) + }). + When().Sync().Then().Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}) + assert.NoError(t, err) + assert.Equal(t, int32(1), *deployment.Spec.RevisionHistoryLimit) + }) +} + +// func TestCRDs(t *testing.T) { +// testEdgeCasesApplicationResources(t, "crd-creation", health.HealthStatusHealthy) +// } + +func TestNamespacedKnownTypesInCRDDiffing(t *testing.T) { + dummiesGVR := schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "dummies"} + + ctx := Given(t) + ctx. + Path("crd-creation"). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When().CreateApp().Sync().Then(). + Expect(OperationPhaseIs(OperationSucceeded)).Expect(SyncStatusIs(SyncStatusCodeSynced)). + When(). + And(func() { + dummyResIf := DynamicClientset.Resource(dummiesGVR).Namespace(DeploymentNamespace()) + patchData := []byte(`{"spec":{"cpu": "2"}}`) + FailOnErr(dummyResIf.Patch(context.Background(), "dummy-crd-instance", types.MergePatchType, patchData, metav1.PatchOptions{})) + }).Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + When(). + And(func() { + SetResourceOverrides(map[string]ResourceOverride{ + "argoproj.io/Dummy": { + KnownTypeFields: []KnownTypeField{{ + Field: "spec", + Type: "core/v1/ResourceList", + }}, + }, + }) + }). + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)) +} + +// TODO(jannfis): This somehow doesn't work -- I suspect tracking method +// func TestNamespacedDuplicatedResources(t *testing.T) { +// testNSEdgeCasesApplicationResources(t, "duplicated-resources", health.HealthStatusHealthy) +// } + +func TestNamespacedConfigMap(t *testing.T) { + testNSEdgeCasesApplicationResources(t, "config-map", health.HealthStatusHealthy, "my-map Synced configmap/my-map created") +} + +func testNSEdgeCasesApplicationResources(t *testing.T, appPath string, statusCode health.HealthStatusCode, message ...string) { + ctx := Given(t) + expect := ctx. + Path(appPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)) + for i := range message { + expect = expect.Expect(Success(message[i])) + } + expect. + Expect(HealthIs(statusCode)). + And(func(app *Application) { + diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local", path.Join("testdata", appPath)) + assert.Empty(t, diffOutput) + assert.NoError(t, err) + }) +} + +// // We don't have tracking label in namespaced tests, thus we need a unique +// // resource action that modifies annotations instead of labels. +// const nsActionsConfig = `discovery.lua: return { sample = {} } +// definitions: +// - name: sample +// action.lua: | +// obj.metadata.annotations.sample = 'test' +// return obj` + +func TestNamespacedResourceAction(t *testing.T) { + ctx := Given(t) + ctx. + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + ResourceOverrides(map[string]ResourceOverride{"apps/Deployment": {Actions: actionsConfig}}). + When(). + CreateApp(). + Sync(). + Then(). + And(func(app *Application) { + + closer, client, err := ArgoCDClientset.NewApplicationClient() + assert.NoError(t, err) + defer io.Close(closer) + + actions, err := client.ListResourceActions(context.Background(), &applicationpkg.ApplicationResourceRequest{ + Name: &app.Name, + AppNamespace: pointer.String(AppNamespace()), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), + Version: pointer.String("v1"), + Namespace: pointer.String(DeploymentNamespace()), + ResourceName: pointer.String("guestbook-ui"), + }) + assert.NoError(t, err) + assert.Equal(t, []*ResourceAction{{Name: "sample", Disabled: false}}, actions.Actions) + + _, err = client.RunResourceAction(context.Background(), &applicationpkg.ResourceActionRunRequest{Name: &app.Name, + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), + Version: pointer.String("v1"), + Namespace: pointer.String(DeploymentNamespace()), + ResourceName: pointer.String("guestbook-ui"), + Action: pointer.String("sample"), + AppNamespace: pointer.String(AppNamespace()), + }) + assert.NoError(t, err) + + deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}) + assert.NoError(t, err) + + assert.Equal(t, "test", deployment.Labels["sample"]) + }) +} + +func TestNamespacedSyncResourceByLabel(t *testing.T) { + ctx := Given(t) + ctx. + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Sync(). + Then(). + And(func(app *Application) { + _, _ = RunCli("app", "sync", ctx.AppQualifiedName(), "--label", fmt.Sprintf("app.kubernetes.io/instance=%s", app.Name)) + }). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + _, err := RunCli("app", "sync", ctx.AppQualifiedName(), "--label", "this-label=does-not-exist") + assert.Error(t, err) + assert.Contains(t, err.Error(), "level=fatal") + }) +} + +func TestNamespacedLocalManifestSync(t *testing.T) { + ctx := Given(t) + ctx. + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Sync(). + Then(). + And(func(app *Application) { + res, _ := RunCli("app", "manifests", ctx.AppQualifiedName()) + assert.Contains(t, res, "containerPort: 80") + assert.Contains(t, res, "image: quay.io/argoprojlabs/argocd-e2e-container:0.2") + }). + Given(). + LocalPath(guestbookPathLocal). + When(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + res, _ := RunCli("app", "manifests", ctx.AppQualifiedName()) + assert.Contains(t, res, "containerPort: 81") + assert.Contains(t, res, "image: quay.io/argoprojlabs/argocd-e2e-container:0.3") + }). + Given(). + LocalPath(""). + When(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + res, _ := RunCli("app", "manifests", ctx.AppQualifiedName()) + assert.Contains(t, res, "containerPort: 80") + assert.Contains(t, res, "image: quay.io/argoprojlabs/argocd-e2e-container:0.2") + }) +} + +func TestNamespacedLocalSync(t *testing.T) { + Given(t). + // we've got to use Helm as this uses kubeVersion + Path("helm"). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Then(). + And(func(app *Application) { + FailOnErr(RunCli("app", "sync", app.QualifiedName(), "--local", "testdata/helm")) + }) +} + +func TestNamespacedNoLocalSyncWithAutosyncEnabled(t *testing.T) { + Given(t). + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Sync(). + Then(). + And(func(app *Application) { + _, err := RunCli("app", "set", app.QualifiedName(), "--sync-policy", "automated") + assert.NoError(t, err) + + _, err = RunCli("app", "sync", app.QualifiedName(), "--local", guestbookPathLocal) + assert.Error(t, err) + assert.Contains(t, err.Error(), "Cannot use local sync") + }) +} + +func TestNamespacedLocalSyncDryRunWithASEnabled(t *testing.T) { + Given(t). + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Sync(). + Then(). + And(func(app *Application) { + _, err := RunCli("app", "set", app.QualifiedName(), "--sync-policy", "automated") + assert.NoError(t, err) + + appBefore := app.DeepCopy() + _, err = RunCli("app", "sync", app.QualifiedName(), "--dry-run", "--local", guestbookPathLocal) + assert.NoError(t, err) + + appAfter := app.DeepCopy() + assert.True(t, reflect.DeepEqual(appBefore, appAfter)) + }) +} + +func TestNamespacedSyncAsync(t *testing.T) { + Given(t). + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + Async(true). + When(). + CreateApp(). + Sync(). + Then(). + Expect(Success("")). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)) +} + +// assertResourceActions verifies if view/modify resource actions are successful/failing for given application +func assertNSResourceActions(t *testing.T, appName string, successful bool) { + assertError := func(err error, message string) { + if successful { + assert.NoError(t, err) + } else { + if assert.Error(t, err) { + assert.Contains(t, err.Error(), message) + } + } + } + + closer, cdClient := ArgoCDClientset.NewApplicationClientOrDie() + defer io.Close(closer) + + deploymentResource, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}) + require.NoError(t, err) + + logs, err := cdClient.PodLogs(context.Background(), &applicationpkg.ApplicationPodLogsQuery{ + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), + Name: &appName, + AppNamespace: pointer.String(AppNamespace()), + Namespace: pointer.String(DeploymentNamespace()), + Container: pointer.String(""), + SinceSeconds: pointer.Int64(0), + TailLines: pointer.Int64(0), + Follow: pointer.Bool(false), + }) + require.NoError(t, err) + _, err = logs.Recv() + assertError(err, "EOF") + + expectedError := fmt.Sprintf("Deployment apps guestbook-ui not found as part of application %s", appName) + + _, err = cdClient.ListResourceEvents(context.Background(), &applicationpkg.ApplicationResourceEventsQuery{ + Name: &appName, + AppNamespace: pointer.String(AppNamespace()), + ResourceName: pointer.String("guestbook-ui"), + ResourceNamespace: pointer.String(DeploymentNamespace()), + ResourceUID: pointer.String(string(deploymentResource.UID)), + }) + assertError(err, fmt.Sprintf("%s not found as part of application %s", "guestbook-ui", appName)) + + _, err = cdClient.GetResource(context.Background(), &applicationpkg.ApplicationResourceRequest{ + Name: &appName, + AppNamespace: pointer.String(AppNamespace()), + ResourceName: pointer.String("guestbook-ui"), + Namespace: pointer.String(DeploymentNamespace()), + Version: pointer.String("v1"), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), + }) + assertError(err, expectedError) + + _, err = cdClient.RunResourceAction(context.Background(), &applicationpkg.ResourceActionRunRequest{ + Name: &appName, + AppNamespace: pointer.String(AppNamespace()), + ResourceName: pointer.String("guestbook-ui"), + Namespace: pointer.String(DeploymentNamespace()), + Version: pointer.String("v1"), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), + Action: pointer.String("restart"), + }) + assertError(err, expectedError) + + _, err = cdClient.DeleteResource(context.Background(), &applicationpkg.ApplicationResourceDeleteRequest{ + Name: &appName, + AppNamespace: pointer.String(AppNamespace()), + ResourceName: pointer.String("guestbook-ui"), + Namespace: pointer.String(DeploymentNamespace()), + Version: pointer.String("v1"), + Group: pointer.String("apps"), + Kind: pointer.String("Deployment"), + }) + assertError(err, expectedError) +} + +func TestNamespacedPermissions(t *testing.T) { + appCtx := Given(t) + projName := "argo-project" + projActions := projectFixture. + Given(t). + Name(projName). + SourceNamespaces([]string{AppNamespace()}). + When(). + Create() + + sourceError := fmt.Sprintf("application repo %s is not permitted in project 'argo-project'", RepoURL(RepoURLTypeFile)) + destinationError := fmt.Sprintf("application destination {%s %s} is not permitted in project 'argo-project'", KubernetesInternalAPIServerAddr, DeploymentNamespace()) + + appCtx. + Path("guestbook-logs"). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + Project(projName). + When(). + IgnoreErrors(). + // ensure app is not created if project permissions are missing + CreateApp(). + Then(). + Expect(Error("", sourceError)). + Expect(Error("", destinationError)). + When(). + DoNotIgnoreErrors(). + // add missing permissions, create and sync app + And(func() { + projActions.AddDestination("*", "*") + projActions.AddSource("*") + }). + CreateApp(). + Sync(). + Then(). + // make sure application resource actiions are successful + And(func(app *Application) { + assertNSResourceActions(t, app.Name, true) + }). + When(). + // remove projet permissions and "refresh" app + And(func() { + projActions.UpdateProject(func(proj *AppProject) { + proj.Spec.Destinations = nil + proj.Spec.SourceRepos = nil + }) + }). + Refresh(RefreshTypeNormal). + Then(). + // ensure app resource tree is empty when source/destination permissions are missing + Expect(Condition(ApplicationConditionInvalidSpecError, destinationError)). + Expect(Condition(ApplicationConditionInvalidSpecError, sourceError)). + And(func(app *Application) { + closer, cdClient := ArgoCDClientset.NewApplicationClientOrDie() + defer io.Close(closer) + tree, err := cdClient.ResourceTree(context.Background(), &applicationpkg.ResourcesQuery{ApplicationName: &app.Name, AppNamespace: &app.Namespace}) + require.NoError(t, err) + assert.Len(t, tree.Nodes, 0) + assert.Len(t, tree.OrphanedNodes, 0) + }). + When(). + // add missing permissions but deny management of Deployment kind + And(func() { + projActions. + AddDestination("*", "*"). + AddSource("*"). + UpdateProject(func(proj *AppProject) { + proj.Spec.NamespaceResourceBlacklist = []metav1.GroupKind{{Group: "*", Kind: "Deployment"}} + }) + }). + Refresh(RefreshTypeNormal). + Then(). + // make sure application resource actiions are failing + And(func(app *Application) { + assertNSResourceActions(t, app.Name, false) + }) +} + +func TestNamespacedPermissionWithScopedRepo(t *testing.T) { + projName := "argo-project" + fixture.EnsureCleanState(t) + projectFixture. + Given(t). + Name(projName). + SourceNamespaces([]string{AppNamespace()}). + Destination("*,*"). + When(). + Create() + + repoFixture.Given(t, true). + When(). + Path(RepoURL(RepoURLTypeFile)). + Project(projName). + Create() + + GivenWithSameState(t). + Project(projName). + RepoURLType(RepoURLTypeFile). + Path("two-nice-pods"). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Prune=false"}}]`). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + When(). + DeleteFile("pod-1.yaml"). + Refresh(RefreshTypeHard). + IgnoreErrors(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(ResourceSyncStatusIs("Pod", "pod-1", SyncStatusCodeOutOfSync)) +} + +func TestNamespacedPermissionDeniedWithScopedRepo(t *testing.T) { + projName := "argo-project" + projectFixture. + Given(t). + Name(projName). + Destination("*,*"). + SourceNamespaces([]string{AppNamespace()}). + When(). + Create() + + repoFixture.Given(t, true). + When(). + Path(RepoURL(RepoURLTypeFile)). + Create() + + GivenWithSameState(t). + Project(projName). + RepoURLType(RepoURLTypeFile). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + Path("two-nice-pods"). + When(). + PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Prune=false"}}]`). + IgnoreErrors(). + CreateApp(). + Then(). + Expect(Error("", "is not permitted in project")) + +} + +// make sure that if we deleted a resource from the app, it is not pruned if annotated with Prune=false +func TestNamespacedSyncOptionPruneFalse(t *testing.T) { + Given(t). + Path("two-nice-pods"). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Prune=false"}}]`). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + When(). + DeleteFile("pod-1.yaml"). + Refresh(RefreshTypeHard). + IgnoreErrors(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(ResourceSyncStatusIs("Pod", "pod-1", SyncStatusCodeOutOfSync)) +} + +// make sure that if we have an invalid manifest, we can add it if we disable validation, we get a server error rather than a client error +func TestNamespacedSyncOptionValidateFalse(t *testing.T) { + + Given(t). + Path("crd-validation"). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Then(). + Expect(Success("")). + When(). + IgnoreErrors(). + Sync(). + Then(). + // client error + Expect(Error("error validating data", "")). + When(). + PatchFile("deployment.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Validate=false"}}]`). + Sync(). + Then(). + // server error + Expect(Error("cannot be handled as a Deployment", "")) +} + +// make sure that, if we have a resource that needs pruning, but we're ignoring it, the app is in-sync +func TestNamespacedCompareOptionIgnoreExtraneous(t *testing.T) { + Given(t). + Prune(false). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + Path("two-nice-pods"). + When(). + PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/compare-options": "IgnoreExtraneous"}}]`). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + When(). + DeleteFile("pod-1.yaml"). + Refresh(RefreshTypeHard). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + assert.Len(t, app.Status.Resources, 2) + statusByName := map[string]SyncStatusCode{} + for _, r := range app.Status.Resources { + statusByName[r.Name] = r.Status + } + assert.Equal(t, SyncStatusCodeOutOfSync, statusByName["pod-1"]) + assert.Equal(t, SyncStatusCodeSynced, statusByName["pod-2"]) + }). + When(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)) +} + +func TestNamespacedSelfManagedApps(t *testing.T) { + + Given(t). + Path("self-managed-app"). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + PatchFile("resources.yaml", fmt.Sprintf(`[{"op": "replace", "path": "/spec/source/repoURL", "value": "%s"}]`, RepoURL(RepoURLTypeFile))). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(a *Application) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) + defer cancel() + + reconciledCount := 0 + var lastReconciledAt *metav1.Time + for event := range ArgoCDClientset.WatchApplicationWithRetry(ctx, a.QualifiedName(), a.ResourceVersion) { + reconciledAt := event.Application.Status.ReconciledAt + if reconciledAt == nil { + reconciledAt = &metav1.Time{} + } + if lastReconciledAt != nil && !lastReconciledAt.Equal(reconciledAt) { + reconciledCount = reconciledCount + 1 + } + lastReconciledAt = reconciledAt + } + + assert.True(t, reconciledCount < 3, "Application was reconciled too many times") + }) +} + +func TestNamespacedExcludedResource(t *testing.T) { + Given(t). + ResourceOverrides(map[string]ResourceOverride{"apps/Deployment": {Actions: actionsConfig}}). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + Path(guestbookPath). + ResourceFilter(settings.ResourcesFilter{ + ResourceExclusions: []settings.FilteredResource{{Kinds: []string{kube.DeploymentKind}}}, + }). + When(). + CreateApp(). + Sync(). + Refresh(RefreshTypeNormal). + Then(). + Expect(Condition(ApplicationConditionExcludedResourceWarning, "Resource apps/Deployment guestbook-ui is excluded in the settings")) +} + +func TestNamespacedRevisionHistoryLimit(t *testing.T) { + Given(t). + Path("config-map"). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + When(). + CreateApp(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + assert.Len(t, app.Status.History, 1) + }). + When(). + AppSet("--revision-history-limit", "1"). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + assert.Len(t, app.Status.History, 1) + }) +} + +func TestNamespacedOrphanedResource(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") + Given(t). + ProjectSpec(AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true)}, + SourceNamespaces: []string{AppNamespace()}, + }). + SetTrackingMethod("annotation"). + SetAppNamespace(AppNamespace()). + Path(guestbookPath). + When(). + CreateApp(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(NoConditions()). + When(). + And(func() { + FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "orphaned-configmap", + }, + }, metav1.CreateOptions{})) + }). + Refresh(RefreshTypeNormal). + Then(). + Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")). + And(func(app *Application) { + output, err := RunCli("app", "resources", app.QualifiedName()) + assert.NoError(t, err) + assert.Contains(t, output, "orphaned-configmap") + }). + Given(). + ProjectSpec(AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true), Ignore: []OrphanedResourceKey{{Group: "Test", Kind: "ConfigMap"}}}, + SourceNamespaces: []string{AppNamespace()}, + }). + When(). + Refresh(RefreshTypeNormal). + Then(). + Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")). + And(func(app *Application) { + output, err := RunCli("app", "resources", app.QualifiedName()) + assert.NoError(t, err) + assert.Contains(t, output, "orphaned-configmap") + }). + Given(). + ProjectSpec(AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap"}}}, + SourceNamespaces: []string{AppNamespace()}, + }). + When(). + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(NoConditions()). + And(func(app *Application) { + output, err := RunCli("app", "resources", app.QualifiedName()) + assert.NoError(t, err) + assert.NotContains(t, output, "orphaned-configmap") + }). + Given(). + ProjectSpec(AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap", Name: "orphaned-configmap"}}}, + SourceNamespaces: []string{AppNamespace()}, + }). + When(). + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(NoConditions()). + And(func(app *Application) { + output, err := RunCli("app", "resources", app.QualifiedName()) + assert.NoError(t, err) + assert.NotContains(t, output, "orphaned-configmap") + }). + Given(). + ProjectSpec(AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, + OrphanedResources: nil, + SourceNamespaces: []string{AppNamespace()}, + }). + When(). + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(NoConditions()) +} + +func TestNamespacedNotPermittedResources(t *testing.T) { + ctx := Given(t) + ctx.SetAppNamespace(AppNamespace()) + pathType := networkingv1.PathTypePrefix + ingress := &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample-ingress", + Annotations: map[string]string{ + common.AnnotationKeyAppInstance: fmt.Sprintf("%s_%s:networking/Ingress:%s/sample-ingress", AppNamespace(), ctx.AppName(), DeploymentNamespace()), + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{{ + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{{ + Path: "/", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "guestbook-ui", + Port: networkingv1.ServiceBackendPort{Number: 80}, + }, + }, + PathType: &pathType, + }}, + }, + }, + }}, + }, + } + defer func() { + log.Infof("Ingress 'sample-ingress' deleted from %s", ArgoCDNamespace) + CheckError(KubeClientset.NetworkingV1().Ingresses(ArgoCDNamespace).Delete(context.Background(), "sample-ingress", metav1.DeleteOptions{})) + }() + + svc := &v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "guestbook-ui", + Annotations: map[string]string{ + common.AnnotationKeyAppInstance: fmt.Sprintf("%s_%s:Service:%s/guesbook-ui", ArgoCDNamespace, ctx.AppQualifiedName(), DeploymentNamespace()), + }, + }, + Spec: v1.ServiceSpec{ + Ports: []v1.ServicePort{{ + Port: 80, + TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 80}, + }}, + Selector: map[string]string{ + "app": "guestbook-ui", + }, + }, + } + + ctx.ProjectSpec(AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []ApplicationDestination{{Namespace: DeploymentNamespace(), Server: "*"}}, + SourceNamespaces: []string{AppNamespace()}, + NamespaceResourceBlacklist: []metav1.GroupKind{ + {Group: "", Kind: "Service"}, + }}). + And(func() { + FailOnErr(KubeClientset.NetworkingV1().Ingresses(ArgoCDNamespace).Create(context.Background(), ingress, metav1.CreateOptions{})) + FailOnErr(KubeClientset.CoreV1().Services(DeploymentNamespace()).Create(context.Background(), svc, metav1.CreateOptions{})) + }). + Path(guestbookPath). + When(). + CreateApp(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + And(func(app *Application) { + statusByKind := make(map[string]ResourceStatus) + for _, res := range app.Status.Resources { + statusByKind[res.Kind] = res + } + _, hasIngress := statusByKind[kube.IngressKind] + assert.False(t, hasIngress, "Ingress is prohibited not managed object and should be even visible to user") + serviceStatus := statusByKind[kube.ServiceKind] + assert.Equal(t, serviceStatus.Status, SyncStatusCodeUnknown, "Service is prohibited managed resource so should be set to Unknown") + deploymentStatus := statusByKind[kube.DeploymentKind] + assert.Equal(t, deploymentStatus.Status, SyncStatusCodeOutOfSync) + }). + When(). + Delete(true). + Then(). + Expect(DoesNotExist()) + + // Make sure prohibited resources are not deleted during application deletion + FailOnErr(KubeClientset.NetworkingV1().Ingresses(ArgoCDNamespace).Get(context.Background(), "sample-ingress", metav1.GetOptions{})) + FailOnErr(KubeClientset.CoreV1().Services(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{})) +} + +func TestNamespacedSyncWithInfos(t *testing.T) { + expectedInfo := make([]*Info, 2) + expectedInfo[0] = &Info{Name: "name1", Value: "val1"} + expectedInfo[1] = &Info{Name: "name2", Value: "val2"} + + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Path(guestbookPath). + When(). + CreateApp(). + Then(). + And(func(app *Application) { + _, err := RunCli("app", "sync", app.QualifiedName(), + "--info", fmt.Sprintf("%s=%s", expectedInfo[0].Name, expectedInfo[0].Value), + "--info", fmt.Sprintf("%s=%s", expectedInfo[1].Name, expectedInfo[1].Value)) + assert.NoError(t, err) + }). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + assert.ElementsMatch(t, app.Status.OperationState.Operation.Info, expectedInfo) + }) +} + +// Given: argocd app create does not provide --dest-namespace +// +// Manifest contains resource console which does not require namespace +// +// Expect: no app.Status.Conditions +func TestNamespacedCreateAppWithNoNameSpaceForGlobalResource(t *testing.T) { + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Path(globalWithNoNameSpace). + When(). + CreateWithNoNameSpace(). + Then(). + And(func(app *Application) { + time.Sleep(500 * time.Millisecond) + app, err := AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Get(context.Background(), app.Name, metav1.GetOptions{}) + assert.NoError(t, err) + assert.Len(t, app.Status.Conditions, 0) + }) +} + +// Given: argocd app create does not provide --dest-namespace +// +// Manifest contains resource deployment, and service which requires namespace +// Deployment and service do not have namespace in manifest +// +// Expect: app.Status.Conditions for deployment ans service which does not have namespace in manifest +func TestNamespacedCreateAppWithNoNameSpaceWhenRequired(t *testing.T) { + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Path(guestbookPath). + When(). + CreateWithNoNameSpace(). + Refresh(RefreshTypeNormal). + Then(). + And(func(app *Application) { + updatedApp, err := AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Get(context.Background(), app.Name, metav1.GetOptions{}) + require.NoError(t, err) + + assert.Len(t, updatedApp.Status.Conditions, 2) + assert.Equal(t, updatedApp.Status.Conditions[0].Type, ApplicationConditionInvalidSpecError) + assert.Equal(t, updatedApp.Status.Conditions[1].Type, ApplicationConditionInvalidSpecError) + }) +} + +// Given: argocd app create does not provide --dest-namespace +// +// Manifest contains resource deployment, and service which requires namespace +// Some deployment and service has namespace in manifest +// Some deployment and service does not have namespace in manifest +// +// Expect: app.Status.Conditions for deployment and service which does not have namespace in manifest +func TestNamespacedCreateAppWithNoNameSpaceWhenRequired2(t *testing.T) { + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Path(guestbookWithNamespace). + When(). + CreateWithNoNameSpace(). + Refresh(RefreshTypeNormal). + Then(). + And(func(app *Application) { + updatedApp, err := AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Get(context.Background(), app.Name, metav1.GetOptions{}) + require.NoError(t, err) + + assert.Len(t, updatedApp.Status.Conditions, 2) + assert.Equal(t, updatedApp.Status.Conditions[0].Type, ApplicationConditionInvalidSpecError) + assert.Equal(t, updatedApp.Status.Conditions[1].Type, ApplicationConditionInvalidSpecError) + }) +} + +func TestNamespacedListResource(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + ProjectSpec(AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, + OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true)}, + SourceNamespaces: []string{AppNamespace()}, + }). + Path(guestbookPath). + When(). + CreateApp(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(NoConditions()). + When(). + And(func() { + FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "orphaned-configmap", + }, + }, metav1.CreateOptions{})) + }). + Refresh(RefreshTypeNormal). + Then(). + Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")). + And(func(app *Application) { + output, err := RunCli("app", "resources", app.QualifiedName()) + assert.NoError(t, err) + assert.Contains(t, output, "orphaned-configmap") + assert.Contains(t, output, "guestbook-ui") + }). + And(func(app *Application) { + output, err := RunCli("app", "resources", app.QualifiedName(), "--orphaned=true") + assert.NoError(t, err) + assert.Contains(t, output, "orphaned-configmap") + assert.NotContains(t, output, "guestbook-ui") + }). + And(func(app *Application) { + output, err := RunCli("app", "resources", app.QualifiedName(), "--orphaned=false") + assert.NoError(t, err) + assert.NotContains(t, output, "orphaned-configmap") + assert.Contains(t, output, "guestbook-ui") + }). + Given(). + ProjectSpec(AppProjectSpec{ + SourceRepos: []string{"*"}, + Destinations: []ApplicationDestination{{Namespace: "*", Server: "*"}}, + OrphanedResources: nil, + SourceNamespaces: []string{AppNamespace()}, + }). + When(). + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(NoConditions()) +} + +// Given application is set with --sync-option CreateNamespace=true +// +// application --dest-namespace does not exist +// +// Verity application --dest-namespace is created +// +// application sync successful +// when application is deleted, --dest-namespace is not deleted +func TestNamespacedNamespaceAutoCreation(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") + updatedNamespace := getNewNamespace(t) + defer func() { + if !t.Skipped() { + _, err := Run("", "kubectl", "delete", "namespace", updatedNamespace) + assert.NoError(t, err) + } + }() + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Timeout(30). + Path("guestbook"). + When(). + CreateApp("--sync-option", "CreateNamespace=true"). + Then(). + And(func(app *Application) { + //Make sure the namespace we are about to update to does not exist + _, err := Run("", "kubectl", "get", "namespace", updatedNamespace) + assert.Error(t, err) + assert.Contains(t, err.Error(), "not found") + }). + When(). + AppSet("--dest-namespace", updatedNamespace). + Sync(). + Then(). + Expect(Success("")). + Expect(OperationPhaseIs(OperationSucceeded)).Expect(ResourceHealthWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, health.HealthStatusHealthy)). + Expect(ResourceHealthWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, health.HealthStatusHealthy)). + Expect(ResourceSyncStatusWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, SyncStatusCodeSynced)). + Expect(ResourceSyncStatusWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, SyncStatusCodeSynced)). + When(). + Delete(true). + Then(). + Expect(Success("")). + And(func(app *Application) { + //Verify delete app does not delete the namespace auto created + output, err := Run("", "kubectl", "get", "namespace", updatedNamespace) + assert.NoError(t, err) + assert.Contains(t, output, updatedNamespace) + }) +} + +func TestNamespacedFailedSyncWithRetry(t *testing.T) { + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Path("hook"). + When(). + PatchFile("hook.yaml", `[{"op": "replace", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/hook": "PreSync"}}]`). + // make hook fail + PatchFile("hook.yaml", `[{"op": "replace", "path": "/spec/containers/0/command", "value": ["false"]}]`). + CreateApp(). + IgnoreErrors(). + Sync("--retry-limit=1", "--retry-backoff-duration=1s"). + Then(). + Expect(OperationPhaseIs(OperationFailed)). + Expect(OperationMessageContains("retried 1 times")) +} + +func TestNamespacedCreateDisableValidation(t *testing.T) { + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Path("baddir"). + When(). + CreateApp("--validate=false"). + Then(). + And(func(app *Application) { + _, err := RunCli("app", "create", app.QualifiedName(), "--upsert", "--validate=false", "--repo", RepoURL(RepoURLTypeFile), + "--path", "baddir2", "--project", app.Spec.Project, "--dest-server", KubernetesInternalAPIServerAddr, "--dest-namespace", DeploymentNamespace()) + assert.NoError(t, err) + }). + When(). + AppSet("--path", "baddir3", "--validate=false") + +} + +func TestNamespacedCreateFromPartialFile(t *testing.T) { + partialApp := + `metadata: + labels: + labels.local/from-file: file + labels.local/from-args: file + annotations: + annotations.local/from-file: file + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + syncPolicy: + automated: + prune: true +` + + path := "helm-values" + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + When(). + // app should be auto-synced once created + CreateFromPartialFile(partialApp, "--path", path, "-l", "labels.local/from-args=args", "--helm-set", "foo=foo"). + Then(). + Expect(Success("")). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(NoConditions()). + And(func(app *Application) { + assert.Equal(t, map[string]string{"labels.local/from-file": "file", "labels.local/from-args": "args"}, app.ObjectMeta.Labels) + assert.Equal(t, map[string]string{"annotations.local/from-file": "file"}, app.ObjectMeta.Annotations) + assert.Equal(t, []string{"resources-finalizer.argocd.argoproj.io"}, app.ObjectMeta.Finalizers) + assert.Equal(t, path, app.Spec.Source.Path) + assert.Equal(t, []HelmParameter{{Name: "foo", Value: "foo"}}, app.Spec.Source.Helm.Parameters) + }) +} + +// Ensure actions work when using a resource action that modifies status and/or spec +func TestNamespacedCRDStatusSubresourceAction(t *testing.T) { + actions := ` +discovery.lua: | + actions = {} + actions["update-spec"] = {["disabled"] = false} + actions["update-status"] = {["disabled"] = false} + actions["update-both"] = {["disabled"] = false} + return actions +definitions: +- name: update-both + action.lua: | + obj.spec = {} + obj.spec.foo = "update-both" + obj.status = {} + obj.status.bar = "update-both" + return obj +- name: update-spec + action.lua: | + obj.spec = {} + obj.spec.foo = "update-spec" + return obj +- name: update-status + action.lua: | + obj.status = {} + obj.status.bar = "update-status" + return obj +` + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Path("crd-subresource"). + And(func() { + SetResourceOverrides(map[string]ResourceOverride{ + "argoproj.io/StatusSubResource": { + Actions: actions, + }, + "argoproj.io/NonStatusSubResource": { + Actions: actions, + }, + }) + }). + When().CreateApp().Sync().Then(). + Expect(OperationPhaseIs(OperationSucceeded)).Expect(SyncStatusIs(SyncStatusCodeSynced)). + When(). + Refresh(RefreshTypeNormal). + Then(). + // tests resource actions on a CRD using status subresource + And(func(app *Application) { + _, err := RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "StatusSubResource", "update-both") + assert.NoError(t, err) + text := FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.spec.foo}")).(string) + assert.Equal(t, "update-both", text) + text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.status.bar}")).(string) + assert.Equal(t, "update-both", text) + + _, err = RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "StatusSubResource", "update-spec") + assert.NoError(t, err) + text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.spec.foo}")).(string) + assert.Equal(t, "update-spec", text) + + _, err = RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "StatusSubResource", "update-status") + assert.NoError(t, err) + text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.status.bar}")).(string) + assert.Equal(t, "update-status", text) + }). + // tests resource actions on a CRD *not* using status subresource + And(func(app *Application) { + _, err := RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "NonStatusSubResource", "update-both") + assert.NoError(t, err) + text := FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.spec.foo}")).(string) + assert.Equal(t, "update-both", text) + text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.status.bar}")).(string) + assert.Equal(t, "update-both", text) + + _, err = RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "NonStatusSubResource", "update-spec") + assert.NoError(t, err) + text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.spec.foo}")).(string) + assert.Equal(t, "update-spec", text) + + _, err = RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "NonStatusSubResource", "update-status") + assert.NoError(t, err) + text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.status.bar}")).(string) + assert.Equal(t, "update-status", text) + }) +} + +func TestNamespacedAppLogs(t *testing.T) { + SkipOnEnv(t, "OPENSHIFT") + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Path("guestbook-logs"). + When(). + CreateApp(). + Sync(). + Then(). + Expect(HealthIs(health.HealthStatusHealthy)). + And(func(app *Application) { + out, err := RunCli("app", "logs", app.QualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui") + assert.NoError(t, err) + assert.Contains(t, out, "Hi") + }). + And(func(app *Application) { + out, err := RunCli("app", "logs", app.QualifiedName(), "--kind", "Pod") + assert.NoError(t, err) + assert.Contains(t, out, "Hi") + }). + And(func(app *Application) { + out, err := RunCli("app", "logs", app.QualifiedName(), "--kind", "Service") + assert.NoError(t, err) + assert.NotContains(t, out, "Hi") + }) +} + +func TestNamespacedAppWaitOperationInProgress(t *testing.T) { + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + And(func() { + SetResourceOverrides(map[string]ResourceOverride{ + "batch/Job": { + HealthLua: `return { status = 'Running' }`, + }, + "apps/Deployment": { + HealthLua: `return { status = 'Suspended' }`, + }, + }) + }). + Async(true). + Path("hook-and-deployment"). + When(). + CreateApp(). + Sync(). + Then(). + // stuck in running state + Expect(OperationPhaseIs(OperationRunning)). + When(). + Then(). + And(func(app *Application) { + _, err := RunCli("app", "wait", app.QualifiedName(), "--suspended") + errors.CheckError(err) + }) +} + +func TestNamespacedSyncOptionReplace(t *testing.T) { + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Path("config-map"). + When(). + PatchFile("config-map.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Replace=true"}}]`). + CreateApp(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map created") + }). + When(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map replaced") + }) +} + +func TestNamespacedSyncOptionReplaceFromCLI(t *testing.T) { + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Path("config-map"). + Replace(). + When(). + CreateApp(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map created") + }). + When(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map replaced") + }) +} + +func TestNamespacedDiscoverNewCommit(t *testing.T) { + var sha string + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Path("config-map"). + When(). + CreateApp(). + Sync(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + And(func(app *Application) { + sha = app.Status.Sync.Revision + assert.NotEmpty(t, sha) + }). + When(). + PatchFile("config-map.yaml", `[{"op": "replace", "path": "/data/foo", "value": "hello"}]`). + Then(). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + // make sure new commit is not discovered immediately after push + And(func(app *Application) { + assert.Equal(t, sha, app.Status.Sync.Revision) + }). + When(). + // make sure new commit is not discovered after refresh is requested + Refresh(RefreshTypeNormal). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + And(func(app *Application) { + assert.NotEqual(t, sha, app.Status.Sync.Revision) + }) +} + +func TestNamespacedDisableManifestGeneration(t *testing.T) { + Given(t). + SetAppNamespace(AppNamespace()). + SetTrackingMethod("annotation"). + Path("guestbook"). + When(). + CreateApp(). + Refresh(RefreshTypeHard). + Then(). + And(func(app *Application) { + assert.Equal(t, app.Status.SourceType, ApplicationSourceTypeKustomize) + }). + When(). + And(func() { + SetEnableManifestGeneration(map[ApplicationSourceType]bool{ + ApplicationSourceTypeKustomize: false, + }) + }). + Refresh(RefreshTypeHard). + Then(). + And(func(app *Application) { + time.Sleep(1 * time.Second) + }). + And(func(app *Application) { + assert.Equal(t, app.Status.SourceType, ApplicationSourceTypeDirectory) + }) +} + +func TestCreateAppInNotAllowedNamespace(t *testing.T) { + ctx := Given(t) + ctx. + ProjectSpec(AppProjectSpec{ + SourceRepos: []string{"*"}, + SourceNamespaces: []string{"default"}, + Destinations: []ApplicationDestination{ + {Namespace: "*", Server: "*"}, + }, + }). + Path(guestbookPath). + SetTrackingMethod("annotation"). + SetAppNamespace("default"). + When(). + IgnoreErrors(). + CreateApp(). + Then(). + Expect(DoesNotExist()). + Expect(Error("", "namespace 'default' is not permitted")) +} diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index 1a85020b514b9..920133607a986 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -273,7 +273,6 @@ func TestSyncToSignedCommitKeyWithKnownKey(t *testing.T) { func TestAppCreation(t *testing.T) { ctx := Given(t) - ctx. Path(guestbookPath). When(). @@ -326,7 +325,7 @@ func TestAppCreationWithoutForceUpdate(t *testing.T) { Then(). Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). And(func(app *Application) { - assert.Equal(t, Name(), app.Name) + assert.Equal(t, ctx.AppName(), app.Name) assert.Equal(t, RepoURL(RepoURLTypeFile), app.Spec.Source.RepoURL) assert.Equal(t, guestbookPath, app.Spec.Source.Path) assert.Equal(t, DeploymentNamespace(), app.Spec.Destination.Namespace) @@ -512,21 +511,20 @@ func TestAppRollbackSuccessful(t *testing.T) { Source: app.Spec.Source, }} patch, _, err := diff.CreateTwoWayMergePatch(app, appWithHistory, &Application{}) - assert.NoError(t, err) - + require.NoError(t, err) app, err = AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{}) - assert.NoError(t, err) + require.NoError(t, err) // sync app and make sure it reaches InSync state _, err = RunCli("app", "rollback", app.Name, "1") - assert.NoError(t, err) + require.NoError(t, err) }). Expect(Event(EventReasonOperationStarted, "rollback")). Expect(SyncStatusIs(SyncStatusCodeSynced)). And(func(app *Application) { assert.Equal(t, SyncStatusCodeSynced, app.Status.Sync.Status) - assert.NotNil(t, app.Status.OperationState.SyncResult) + require.NotNil(t, app.Status.OperationState.SyncResult) assert.Equal(t, 2, len(app.Status.OperationState.SyncResult.Resources)) assert.Equal(t, OperationSucceeded, app.Status.OperationState.Phase) assert.Equal(t, 3, len(app.Status.History)) @@ -1179,7 +1177,9 @@ func TestPermissions(t *testing.T) { And(func(app *Application) { closer, cdClient := ArgoCDClientset.NewApplicationClientOrDie() defer io.Close(closer) - tree, err := cdClient.ResourceTree(context.Background(), &applicationpkg.ResourcesQuery{ApplicationName: &app.Name}) + appName, appNs := argo.ParseAppQualifiedName(app.Name, "") + fmt.Printf("APP NAME: %s\n", appName) + tree, err := cdClient.ResourceTree(context.Background(), &applicationpkg.ResourcesQuery{ApplicationName: &appName, AppNamespace: &appNs}) require.NoError(t, err) assert.Len(t, tree.Nodes, 0) assert.Len(t, tree.OrphanedNodes, 0) @@ -1204,6 +1204,7 @@ func TestPermissions(t *testing.T) { func TestPermissionWithScopedRepo(t *testing.T) { projName := "argo-project" + fixture.EnsureCleanState(t) projectFixture. Given(t). Name(projName). @@ -2012,7 +2013,8 @@ func TestAppLogs(t *testing.T) { } func TestAppWaitOperationInProgress(t *testing.T) { - Given(t). + ctx := Given(t) + ctx. And(func() { SetResourceOverrides(map[string]ResourceOverride{ "batch/Job": { @@ -2033,7 +2035,7 @@ func TestAppWaitOperationInProgress(t *testing.T) { Expect(OperationPhaseIs(OperationRunning)). When(). And(func() { - _, err := RunCli("app", "wait", Name(), "--suspended") + _, err := RunCli("app", "wait", ctx.AppName(), "--suspended") errors.CheckError(err) }) } diff --git a/test/e2e/app_namespaces_test.go b/test/e2e/app_namespaces_test.go new file mode 100644 index 0000000000000..8ee41aa0d7f2b --- /dev/null +++ b/test/e2e/app_namespaces_test.go @@ -0,0 +1,105 @@ +package e2e + +import ( + "context" + "testing" + + . "github.com/argoproj/gitops-engine/pkg/sync/common" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + + . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + . "github.com/argoproj/argo-cd/v2/test/e2e/fixture" + . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" + . "github.com/argoproj/argo-cd/v2/util/argo" + . "github.com/argoproj/argo-cd/v2/util/errors" +) + +func TestAppCreationInOtherNamespace(t *testing.T) { + ctx := Given(t) + ctx. + Path(guestbookPath). + SetAppNamespace(ArgoCDAppNamespace). + When(). + CreateApp(). + Then(). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + And(func(app *Application) { + assert.Equal(t, ctx.AppName(), app.Name) + assert.Equal(t, AppNamespace(), app.Namespace) + assert.Equal(t, RepoURL(RepoURLTypeFile), app.Spec.Source.RepoURL) + assert.Equal(t, guestbookPath, app.Spec.Source.Path) + assert.Equal(t, DeploymentNamespace(), app.Spec.Destination.Namespace) + assert.Equal(t, KubernetesInternalAPIServerAddr, app.Spec.Destination.Server) + }). + Expect(NamespacedEvent(ctx.AppNamespace(), EventReasonResourceCreated, "create")). + And(func(_ *Application) { + // app should be listed + output, err := RunCli("app", "list") + assert.NoError(t, err) + assert.Contains(t, output, ctx.AppName()) + }). + When(). + // ensure that create is idempotent + CreateApp(). + Then(). + Given(). + Revision("master"). + When(). + // ensure that update replaces spec and merge labels and annotations + And(func() { + FailOnErr(AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Patch(context.Background(), + ctx.AppName(), types.MergePatchType, []byte(`{"metadata": {"labels": { "test": "label" }, "annotations": { "test": "annotation" }}}`), metav1.PatchOptions{})) + }). + CreateApp("--upsert"). + Then(). + And(func(app *Application) { + assert.Equal(t, "label", app.Labels["test"]) + assert.Equal(t, "annotation", app.Annotations["test"]) + assert.Equal(t, "master", app.Spec.Source.TargetRevision) + }) +} + +func TestForbiddenNamespace(t *testing.T) { + ctx := Given(t) + ctx. + Path(guestbookPath). + SetAppNamespace("forbidden"). + When(). + IgnoreErrors(). + CreateApp(). + Then(). + Expect(DoesNotExist()) +} + +func TestDeletingNamespacedAppStuckInSync(t *testing.T) { + ctx := Given(t) + ctx.And(func() { + SetResourceOverrides(map[string]ResourceOverride{ + "ConfigMap": { + HealthLua: `return { status = obj.annotations and obj.annotations['health'] or 'Progressing' }`, + }, + }) + }). + Async(true). + SetAppNamespace(ArgoCDAppNamespace). + Path("hook-custom-health"). + When(). + CreateApp(). + Sync(). + Then(). + // stuck in running state + Expect(OperationPhaseIs(OperationRunning)). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + When(). + Delete(true). + Then(). + // delete is ignored, still stuck in running state + Expect(OperationPhaseIs(OperationRunning)). + When(). + TerminateOp(). + Then(). + // delete is successful + Expect(DoesNotExist()) +} diff --git a/test/e2e/custom_tool_test.go b/test/e2e/custom_tool_test.go index 2afc469fc6ab6..18b9d5ecfb3e1 100644 --- a/test/e2e/custom_tool_test.go +++ b/test/e2e/custom_tool_test.go @@ -19,7 +19,8 @@ import ( // make sure we can echo back the Git creds func TestCustomToolWithGitCreds(t *testing.T) { - Given(t). + ctx := Given(t) + ctx. // path does not matter, we ignore it ConfigManagementPlugin( ConfigManagementPlugin{ @@ -43,7 +44,7 @@ func TestCustomToolWithGitCreds(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { - output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.GitAskpass}") + output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.GitAskpass}") assert.NoError(t, err) assert.Equal(t, "argocd", output) }) @@ -51,7 +52,8 @@ func TestCustomToolWithGitCreds(t *testing.T) { // make sure we can echo back the Git creds func TestCustomToolWithGitCredsTemplate(t *testing.T) { - Given(t). + ctx := Given(t) + ctx. // path does not matter, we ignore it ConfigManagementPlugin( ConfigManagementPlugin{ @@ -77,17 +79,17 @@ func TestCustomToolWithGitCredsTemplate(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). Expect(HealthIs(health.HealthStatusHealthy)). And(func(app *Application) { - output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.GitAskpass}") + output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.GitAskpass}") assert.NoError(t, err) assert.Equal(t, "argocd", output) }). And(func(app *Application) { - output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.GitUsername}") + output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.GitUsername}") assert.NoError(t, err) assert.Empty(t, output) }). And(func(app *Application) { - output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.GitPassword}") + output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.GitPassword}") assert.NoError(t, err) assert.Empty(t, output) }) @@ -95,7 +97,8 @@ func TestCustomToolWithGitCredsTemplate(t *testing.T) { // make sure we can echo back the env func TestCustomToolWithEnv(t *testing.T) { - Given(t). + ctx := Given(t) + ctx. // path does not matter, we ignore it ConfigManagementPlugin( ConfigManagementPlugin{ @@ -124,18 +127,18 @@ func TestCustomToolWithEnv(t *testing.T) { time.Sleep(1 * time.Second) }). And(func(app *Application) { - output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.Bar}") + output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.Bar}") assert.NoError(t, err) assert.Equal(t, "baz", output) }). And(func(app *Application) { - output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.Foo}") + output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.Foo}") assert.NoError(t, err) assert.Equal(t, "bar", output) }). And(func(app *Application) { expectedKubeVersion := GetVersions().ServerVersion.Format("%s.%s") - output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.KubeVersion}") + output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.KubeVersion}") assert.NoError(t, err) assert.Equal(t, expectedKubeVersion, output) }). @@ -144,7 +147,7 @@ func TestCustomToolWithEnv(t *testing.T) { expectedApiVersionSlice := strings.Split(expectedApiVersion, ",") sort.Strings(expectedApiVersionSlice) - output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.KubeApiVersion}") + output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.KubeApiVersion}") assert.NoError(t, err) outputSlice := strings.Split(output, ",") sort.Strings(outputSlice) @@ -155,7 +158,8 @@ func TestCustomToolWithEnv(t *testing.T) { //make sure we can sync and diff with --local func TestCustomToolSyncAndDiffLocal(t *testing.T) { - Given(t). + ctx := Given(t) + ctx. // path does not matter, we ignore it ConfigManagementPlugin( ConfigManagementPlugin{ @@ -169,7 +173,7 @@ func TestCustomToolSyncAndDiffLocal(t *testing.T) { // does not matter what the path is Path("guestbook"). When(). - CreateApp("--config-management-plugin", Name()). + CreateApp("--config-management-plugin", ctx.AppName()). Sync("--local", "testdata/guestbook"). Then(). Expect(OperationPhaseIs(OperationSucceeded)). @@ -179,10 +183,10 @@ func TestCustomToolSyncAndDiffLocal(t *testing.T) { time.Sleep(1 * time.Second) }). And(func(app *Application) { - FailOnErr(RunCli("app", "sync", app.Name, "--local", "testdata/guestbook")) + FailOnErr(RunCli("app", "sync", ctx.AppName(), "--local", "testdata/guestbook")) }). And(func(app *Application) { - FailOnErr(RunCli("app", "diff", app.Name, "--local", "testdata/guestbook")) + FailOnErr(RunCli("app", "diff", ctx.AppName(), "--local", "testdata/guestbook")) }) } @@ -239,7 +243,8 @@ func TestCMPDiscoverWithFindGlob(t *testing.T) { //Discover by Find command func TestCMPDiscoverWithFindCommandWithEnv(t *testing.T) { pluginName := "cmp-find-command" - Given(t). + ctx := Given(t) + ctx. And(func() { go startCMPServer("./testdata/cmp-find-command") time.Sleep(1 * time.Second) @@ -257,13 +262,13 @@ func TestCMPDiscoverWithFindCommandWithEnv(t *testing.T) { time.Sleep(1 * time.Second) }). And(func(app *Application) { - output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.Bar}") + output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.Bar}") assert.NoError(t, err) assert.Equal(t, "baz", output) }). And(func(app *Application) { expectedKubeVersion := GetVersions().ServerVersion.Format("%s.%s") - output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.KubeVersion}") + output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.KubeVersion}") assert.NoError(t, err) assert.Equal(t, expectedKubeVersion, output) }). @@ -272,7 +277,7 @@ func TestCMPDiscoverWithFindCommandWithEnv(t *testing.T) { expectedApiVersionSlice := strings.Split(expectedApiVersion, ",") sort.Strings(expectedApiVersionSlice) - output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.KubeApiVersion}") + output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", ctx.AppName(), "-o", "jsonpath={.metadata.annotations.KubeApiVersion}") assert.NoError(t, err) outputSlice := strings.Split(output, ",") sort.Strings(outputSlice) diff --git a/test/e2e/deployment_test.go b/test/e2e/deployment_test.go index ebf1ff9f846f2..085ecee244ba2 100644 --- a/test/e2e/deployment_test.go +++ b/test/e2e/deployment_test.go @@ -54,11 +54,11 @@ func TestDeploymentWithAnnotationTrackingMode(t *testing.T) { When(). Then(). And(func(app *Application) { - out, err := RunCli("app", "manifests", app.Name) + out, err := RunCli("app", "manifests", ctx.AppName()) assert.NoError(t, err) assert.Contains(t, out, fmt.Sprintf(`annotations: argocd.argoproj.io/tracking-id: %s:apps/Deployment:%s/nginx-deployment -`, Name(), DeploymentNamespace())) +`, ctx.AppName(), DeploymentNamespace())) }) } @@ -77,17 +77,18 @@ func TestDeploymentWithLabelTrackingMode(t *testing.T) { When(). Then(). And(func(app *Application) { - out, err := RunCli("app", "manifests", app.Name) + out, err := RunCli("app", "manifests", ctx.AppName()) assert.NoError(t, err) assert.Contains(t, out, fmt.Sprintf(`labels: app: nginx app.kubernetes.io/instance: %s -`, Name())) +`, ctx.AppName())) }) } func TestDeploymentWithoutTrackingMode(t *testing.T) { - Given(t). + ctx := Given(t) + ctx. Path("deployment"). When(). CreateApp(). @@ -99,11 +100,11 @@ func TestDeploymentWithoutTrackingMode(t *testing.T) { When(). Then(). And(func(app *Application) { - out, err := RunCli("app", "manifests", app.Name) + out, err := RunCli("app", "manifests", ctx.AppName()) assert.NoError(t, err) assert.Contains(t, out, fmt.Sprintf(`labels: app: nginx app.kubernetes.io/instance: %s -`, Name())) +`, ctx.AppName())) }) } diff --git a/test/e2e/fixture/app/actions.go b/test/e2e/fixture/app/actions.go index fd4eb199c0c84..f73bea845d2c7 100644 --- a/test/e2e/fixture/app/actions.go +++ b/test/e2e/fixture/app/actions.go @@ -74,11 +74,14 @@ func (a *Actions) CreateFromPartialFile(data string, flags ...string) *Actions { args := append([]string{ "app", "create", "-f", tmpFile.Name(), - "--name", a.context.name, + "--name", a.context.AppName(), "--repo", fixture.RepoURL(a.context.repoURLType), "--dest-server", a.context.destServer, "--dest-namespace", fixture.DeploymentNamespace(), }, flags...) + if a.context.appNamespace != "" { + args = append(args, "--app-namespace", a.context.appNamespace) + } defer tmpFile.Close() a.runCli(args...) return a @@ -87,7 +90,8 @@ func (a *Actions) CreateFromFile(handler func(app *Application), flags ...string a.context.t.Helper() app := &Application{ ObjectMeta: v1.ObjectMeta{ - Name: a.context.name, + Name: a.context.AppName(), + Namespace: a.context.AppNamespace(), }, Spec: ApplicationSpec{ Project: a.context.project, @@ -157,7 +161,7 @@ func (a *Actions) CreateApp(args ...string) *Actions { func (a *Actions) prepareCreateAppArgs(args []string) []string { a.context.t.Helper() args = append([]string{ - "app", "create", a.context.name, + "app", "create", a.context.AppQualifiedName(), "--repo", fixture.RepoURL(a.context.repoURLType), }, args...) @@ -218,7 +222,7 @@ func (a *Actions) DeclarativeWithCustomRepo(filename string, repoURL string) *Ac values := map[string]interface{}{ "ArgoCDNamespace": fixture.ArgoCDNamespace, "DeploymentNamespace": fixture.DeploymentNamespace(), - "Name": a.context.name, + "Name": a.context.AppName(), "Path": a.context.path, "Project": a.context.project, "RepoURL": repoURL, @@ -230,13 +234,13 @@ func (a *Actions) DeclarativeWithCustomRepo(filename string, repoURL string) *Ac func (a *Actions) PatchApp(patch string) *Actions { a.context.t.Helper() - a.runCli("app", "patch", a.context.name, "--patch", patch) + a.runCli("app", "patch", a.context.AppQualifiedName(), "--patch", patch) return a } func (a *Actions) AppSet(flags ...string) *Actions { a.context.t.Helper() - args := []string{"app", "set", a.context.name} + args := []string{"app", "set", a.context.AppQualifiedName()} args = append(args, flags...) a.runCli(args...) return a @@ -244,7 +248,7 @@ func (a *Actions) AppSet(flags ...string) *Actions { func (a *Actions) AppUnSet(flags ...string) *Actions { a.context.t.Helper() - args := []string{"app", "unset", a.context.name} + args := []string{"app", "unset", a.context.AppQualifiedName()} args = append(args, flags...) a.runCli(args...) return a @@ -254,7 +258,7 @@ func (a *Actions) Sync(args ...string) *Actions { a.context.t.Helper() args = append([]string{"app", "sync"}, args...) if a.context.name != "" { - args = append(args, a.context.name) + args = append(args, a.context.AppQualifiedName()) } args = append(args, "--timeout", fmt.Sprintf("%v", a.context.timeout)) @@ -291,7 +295,7 @@ func (a *Actions) Sync(args ...string) *Actions { func (a *Actions) TerminateOp() *Actions { a.context.t.Helper() - a.runCli("app", "terminate-op", a.context.name) + a.runCli("app", "terminate-op", a.context.AppQualifiedName()) return a } @@ -302,14 +306,14 @@ func (a *Actions) Refresh(refreshType RefreshType) *Actions { RefreshTypeHard: "--hard-refresh", }[refreshType] - a.runCli("app", "get", a.context.name, flag) + a.runCli("app", "get", a.context.AppQualifiedName(), flag) return a } func (a *Actions) Delete(cascade bool) *Actions { a.context.t.Helper() - a.runCli("app", "delete", a.context.name, fmt.Sprintf("--cascade=%v", cascade), "--yes") + a.runCli("app", "delete", a.context.AppQualifiedName(), fmt.Sprintf("--cascade=%v", cascade), "--yes") return a } diff --git a/test/e2e/fixture/app/consequences.go b/test/e2e/fixture/app/consequences.go index 8d3c94fc71891..7a16aa6309831 100644 --- a/test/e2e/fixture/app/consequences.go +++ b/test/e2e/fixture/app/consequences.go @@ -69,7 +69,7 @@ func (c *Consequences) app() *Application { } func (c *Consequences) get() (*Application, error) { - return fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.ArgoCDNamespace).Get(context.Background(), c.context.name, v1.GetOptions{}) + return fixture.AppClientset.ArgoprojV1alpha1().Applications(c.context.AppNamespace()).Get(context.Background(), c.context.AppName(), v1.GetOptions{}) } func (c *Consequences) resource(kind, name, namespace string) ResourceStatus { diff --git a/test/e2e/fixture/app/context.go b/test/e2e/fixture/app/context.go index a527405ea6aee..923884cb69fa6 100644 --- a/test/e2e/fixture/app/context.go +++ b/test/e2e/fixture/app/context.go @@ -9,6 +9,7 @@ import ( "github.com/argoproj/argo-cd/v2/test/e2e/fixture/certs" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/gpgkeys" "github.com/argoproj/argo-cd/v2/test/e2e/fixture/repos" + "github.com/argoproj/argo-cd/v2/util/argo" "github.com/argoproj/argo-cd/v2/util/env" "github.com/argoproj/argo-cd/v2/util/settings" ) @@ -22,6 +23,7 @@ type Context struct { // seconds timeout int name string + appNamespace string destServer string destName string env string @@ -40,6 +42,11 @@ type Context struct { replace bool helmPassCredentials bool helmSkipCrds bool + trackingMethod v1alpha1.TrackingMethod +} + +type ContextArgs struct { + AppNamespace string } func Given(t *testing.T) *Context { @@ -47,11 +54,52 @@ func Given(t *testing.T) *Context { return GivenWithSameState(t) } +func GivenWithNamespace(t *testing.T, namespace string) *Context { + ctx := Given(t) + ctx.appNamespace = namespace + return ctx +} + func GivenWithSameState(t *testing.T) *Context { // ARGOCE_E2E_DEFAULT_TIMEOUT can be used to override the default timeout // for any context. timeout := env.ParseNumFromEnv("ARGOCD_E2E_DEFAULT_TIMEOUT", 10, 0, 180) - return &Context{t: t, destServer: v1alpha1.KubernetesInternalAPIServerAddr, repoURLType: fixture.RepoURLTypeFile, name: fixture.Name(), timeout: timeout, project: "default", prune: true} + return &Context{ + t: t, + destServer: v1alpha1.KubernetesInternalAPIServerAddr, + repoURLType: fixture.RepoURLTypeFile, + name: fixture.Name(), + timeout: timeout, + project: "default", + prune: true, + trackingMethod: argo.TrackingMethodLabel, + } +} + +func (c *Context) AppName() string { + return c.name +} + +func (c *Context) AppQualifiedName() string { + if c.appNamespace != "" { + return c.appNamespace + "/" + c.AppName() + } else { + return c.AppName() + } +} + +func (c *Context) AppNamespace() string { + if c.appNamespace != "" { + return c.appNamespace + } else { + return fixture.TestNamespace() + } +} + +func (c *Context) SetAppNamespace(namespace string) *Context { + c.appNamespace = namespace + //fixture.SetParamInSettingConfigMap("application.resourceTrackingMethod", "annotation") + return c } func (c *Context) GPGPublicKeyAdded() *Context { @@ -313,3 +361,7 @@ func (c *Context) SetTrackingMethod(trackingMethod string) *Context { fixture.SetTrackingMethod(trackingMethod) return c } + +func (c *Context) GetTrackingMethod() v1alpha1.TrackingMethod { + return c.trackingMethod +} diff --git a/test/e2e/fixture/app/expectation.go b/test/e2e/fixture/app/expectation.go index 0b53a3f85324d..e06d2462461e7 100644 --- a/test/e2e/fixture/app/expectation.go +++ b/test/e2e/fixture/app/expectation.go @@ -218,12 +218,12 @@ func pods() (*v1.PodList, error) { return pods, err } -func Event(reason string, message string) Expectation { +func event(namespace string, reason string, message string) Expectation { return func(c *Consequences) (state, string) { - list, err := fixture.KubeClientset.CoreV1().Events(fixture.ArgoCDNamespace).List(context.Background(), metav1.ListOptions{ + list, err := fixture.KubeClientset.CoreV1().Events(namespace).List(context.Background(), metav1.ListOptions{ FieldSelector: fields.SelectorFromSet(map[string]string{ - "involvedObject.name": c.context.name, - "involvedObject.namespace": fixture.ArgoCDNamespace, + "involvedObject.name": c.context.AppName(), + "involvedObject.namespace": namespace, }).String(), }) if err != nil { @@ -240,6 +240,14 @@ func Event(reason string, message string) Expectation { } } +func Event(reason string, message string) Expectation { + return event(fixture.ArgoCDNamespace, reason, message) +} + +func NamespacedEvent(namespace string, reason string, message string) Expectation { + return event(namespace, reason, message) +} + // asserts that the last command was successful func Success(message string) Expectation { return func(c *Consequences) (state, string) { diff --git a/test/e2e/fixture/fixture.go b/test/e2e/fixture/fixture.go index 1f22aefed36f1..7b376d436d79d 100644 --- a/test/e2e/fixture/fixture.go +++ b/test/e2e/fixture/fixture.go @@ -43,6 +43,7 @@ const ( DefaultTestUserPassword = "password" testingLabel = "e2e.argoproj.io" ArgoCDNamespace = "argocd-e2e" + ArgoCDAppNamespace = "argocd-e2e-external" // ensure all repos are in one directory tree, so we can easily clean them up TmpDir = "/tmp/argo-e2e" @@ -112,6 +113,10 @@ func TestNamespace() string { return GetEnvWithDefault("ARGOCD_E2E_NAMESPACE", ArgoCDNamespace) } +func AppNamespace() string { + return GetEnvWithDefault("ARGOCD_E2E_APP_NAMESPACE", ArgoCDAppNamespace) +} + // getKubeConfig creates new kubernetes client config using specified config path and config overrides variables func getKubeConfig(configPath string, overrides clientcmd.ConfigOverrides) *rest.Config { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() @@ -511,6 +516,7 @@ func EnsureCleanState(t *testing.T) { // delete resources // kubectl delete apps --all CheckError(AppClientset.ArgoprojV1alpha1().Applications(TestNamespace()).DeleteCollection(context.Background(), v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{})) + CheckError(AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).DeleteCollection(context.Background(), v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{})) // kubectl delete appprojects --field-selector metadata.name!=default CheckError(AppClientset.ArgoprojV1alpha1().AppProjects(TestNamespace()).DeleteCollection(context.Background(), v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{FieldSelector: "metadata.name!=default"})) @@ -556,6 +562,7 @@ func EnsureCleanState(t *testing.T) { SourceRepos: []string{"*"}, Destinations: []v1alpha1.ApplicationDestination{{Namespace: "*", Server: "*"}}, ClusterResourceWhitelist: []v1.GroupKind{{Group: "*", Kind: "*"}}, + SourceNamespaces: []string{AppNamespace()}, }) // Create separate project for testing gpg signature verification @@ -566,6 +573,7 @@ func EnsureCleanState(t *testing.T) { Destinations: []v1alpha1.ApplicationDestination{{Namespace: "*", Server: "*"}}, ClusterResourceWhitelist: []v1.GroupKind{{Group: "*", Kind: "*"}}, SignatureKeys: []v1alpha1.SignatureKey{{KeyID: GpgGoodKeyID}}, + SourceNamespaces: []string{AppNamespace()}, }) // Recreate temp dir diff --git a/test/e2e/fixture/project/actions.go b/test/e2e/fixture/project/actions.go index 3123da3f43768..81943c4fc2b6c 100644 --- a/test/e2e/fixture/project/actions.go +++ b/test/e2e/fixture/project/actions.go @@ -2,6 +2,7 @@ package project import ( "context" + "strings" "github.com/stretchr/testify/require" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -74,6 +75,9 @@ func (a *Actions) prepareCreateArgs(args []string) []string { args = append(args, "--dest", a.context.destination) } + if len(a.context.sourceNamespaces) > 0 { + args = append(args, "--source-namespaces", strings.Join(a.context.sourceNamespaces, ",")) + } return args } diff --git a/test/e2e/fixture/project/context.go b/test/e2e/fixture/project/context.go index 95f3749c7bfe3..e637acbba8cf4 100644 --- a/test/e2e/fixture/project/context.go +++ b/test/e2e/fixture/project/context.go @@ -12,10 +12,11 @@ import ( type Context struct { t *testing.T // seconds - timeout int - name string - destination string - repos []string + timeout int + name string + destination string + repos []string + sourceNamespaces []string } func Given(t *testing.T) *Context { @@ -49,6 +50,11 @@ func (c *Context) SourceRepositories(repos []string) *Context { return c } +func (c *Context) SourceNamespaces(namespaces []string) *Context { + c.sourceNamespaces = namespaces + return c +} + func (c *Context) And(block func()) *Context { block() return c diff --git a/test/e2e/fixture/repos/actions.go b/test/e2e/fixture/repos/actions.go index 800548413183d..5fa8c5d3bbada 100644 --- a/test/e2e/fixture/repos/actions.go +++ b/test/e2e/fixture/repos/actions.go @@ -1,6 +1,8 @@ package repos import ( + "log" + "github.com/argoproj/argo-cd/v2/test/e2e/fixture" ) @@ -81,4 +83,7 @@ func (a *Actions) Then() *Consequences { func (a *Actions) runCli(args ...string) { a.context.t.Helper() a.lastOutput, a.lastError = fixture.RunCli(args...) + if !a.ignoreErrors && a.lastError != nil { + log.Fatal(a.lastOutput) + } } diff --git a/test/e2e/scoped_repository_test.go b/test/e2e/scoped_repository_test.go index 2db45d4acbb4f..275deb0c20f63 100644 --- a/test/e2e/scoped_repository_test.go +++ b/test/e2e/scoped_repository_test.go @@ -53,6 +53,7 @@ func TestCreateRepositoryNonAdminUserPermissionDenied(t *testing.T) { When(). Path(path). Project("argo-project"). + IgnoreErrors(). Create(). Then(). AndCLIOutput(func(output string, err error) { @@ -79,6 +80,7 @@ func TestCreateRepositoryNonAdminUserWithWrongProject(t *testing.T) { When(). Path(path). Project("argo-project"). + IgnoreErrors(). Create(). Then(). AndCLIOutput(func(output string, err error) { @@ -165,6 +167,7 @@ func TestDeleteRepositoryRbacDenied(t *testing.T) { assert.Equal(t, r.Project, "argo-project") }). When(). + IgnoreErrors(). Delete(). Then(). AndCLIOutput(func(output string, err error) { diff --git a/test/e2e/testdata/duplicated-resources/duplicates-cluster.yaml b/test/e2e/testdata/duplicated-resources/duplicates-cluster.yaml index acd0f4025d80e..c8079cb9b3a37 100644 --- a/test/e2e/testdata/duplicated-resources/duplicates-cluster.yaml +++ b/test/e2e/testdata/duplicated-resources/duplicates-cluster.yaml @@ -3,6 +3,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: clusterdummies.argoproj.io + labels: + e2e.argoproj.io: "true" spec: conversion: strategy: None @@ -50,4 +52,4 @@ apiVersion: argoproj.io/v1alpha1 kind: ClusterDummy metadata: name: cluster-dummy-crd-instance - namespace: kube-system \ No newline at end of file + namespace: kube-system diff --git a/test/e2e/testdata/duplicated-resources/duplicates-namespaced.yaml b/test/e2e/testdata/duplicated-resources/duplicates-namespaced.yaml index 58458eadb3526..98ae85ec95965 100644 --- a/test/e2e/testdata/duplicated-resources/duplicates-namespaced.yaml +++ b/test/e2e/testdata/duplicated-resources/duplicates-namespaced.yaml @@ -3,6 +3,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: dummies.argoproj.io + labels: + e2e.argoproj.io: "true" spec: conversion: strategy: None diff --git a/test/e2e/testdata/guestbook/guestbook-ui-deployment.yaml b/test/e2e/testdata/guestbook/guestbook-ui-deployment.yaml index 039508bab8e24..bf3375672f70c 100644 --- a/test/e2e/testdata/guestbook/guestbook-ui-deployment.yaml +++ b/test/e2e/testdata/guestbook/guestbook-ui-deployment.yaml @@ -2,6 +2,8 @@ apiVersion: apps/v1 kind: Deployment metadata: name: guestbook-ui + labels: + test: "true" spec: replicas: 0 revisionHistoryLimit: 3 diff --git a/test/e2e/testdata/self-managed-app/resources.yaml b/test/e2e/testdata/self-managed-app/resources.yaml index 86048b6848351..baaf0d6887b3b 100644 --- a/test/e2e/testdata/self-managed-app/resources.yaml +++ b/test/e2e/testdata/self-managed-app/resources.yaml @@ -2,7 +2,6 @@ apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: test-self-managed-apps - namespace: argocd-e2e spec: project: default diff --git a/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx b/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx index 9f4a8e19eff31..296468556a047 100644 --- a/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx +++ b/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx @@ -202,6 +202,15 @@ export const ApplicationCreatePanel = (props: { component={Text} /> +
+ +
{ +export const RevisionMetadataRows = (props: {applicationName: string; applicationNamespace: string; source: ApplicationSource}) => { if (props.source.chart) { return (
@@ -20,7 +20,7 @@ export const RevisionMetadataRows = (props: {applicationName: string; source: Ap ); } return ( - services.applications.revisionMetadata(input.applicationName, input.source.targetRevision)}> + services.applications.revisionMetadata(input.applicationName, input.applicationNamespace, input.source.targetRevision)}> {(m: RevisionMetadata) => (
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 38e7878e5ca6a..690942e18354d 100644 --- a/ui/src/app/applications/components/application-details/application-details.tsx +++ b/ui/src/app/applications/components/application-details/application-details.tsx @@ -62,16 +62,22 @@ export const SelectNode = (fullName: string, containerIndex = 0, tab: string = n appContext.navigation.goto('.', {node, tab}, {replace: true}); }; -export class ApplicationDetails extends React.Component, ApplicationDetailsState> { +export class ApplicationDetails extends React.Component, ApplicationDetailsState> { public static contextTypes = { apis: PropTypes.object }; private appChanged = new BehaviorSubject(null); + private appNamespace: string; - constructor(props: RouteComponentProps<{name: string}>) { + constructor(props: RouteComponentProps<{appnamespace: string; name: string}>) { super(props); this.state = {page: 0, groupedResources: [], slidingPanelPage: 0, filteredGraph: [], truncateNameOnRight: false, collapsedNodes: []}; + if (typeof this.props.match.params.appnamespace === 'undefined') { + this.appNamespace = ''; + } else { + this.appNamespace = this.props.match.params.appnamespace; + } } private get showOperationState() { @@ -145,7 +151,7 @@ export class ApplicationDetails extends React.Component Loading...} input={this.props.match.params.name} load={name => - combineLatest([this.loadAppInfo(name), services.viewPreferences.getPreferences(), q]).pipe( + combineLatest([this.loadAppInfo(name, this.appNamespace), services.viewPreferences.getPreferences(), q]).pipe( map(items => { const pref = items[1].appDetails; const params = items[2]; @@ -432,7 +438,9 @@ export class ApplicationDetails extends React.Component null)); + (await services.applications + .getResource(application.metadata.name, application.metadata.namespace, resource) + .catch(() => null)); if (liveState?.metadata?.annotations?.[models.AnnotationHookKey]) { resourceRow.syncOrder = liveState?.metadata.annotations[models.AnnotationHookKey]; if (liveState?.metadata?.annotations?.[models.AnnotationSyncWaveKey]) { @@ -543,7 +551,10 @@ export class ApplicationDetails extends React.Component this.setState({revision: null})}> {this.state.revision && ( - services.applications.revisionMetadata(application.metadata.name, this.state.revision)}> + + services.applications.revisionMetadata(application.metadata.name, application.metadata.namespace, this.state.revision) + }> {metadata => (
@@ -646,7 +657,7 @@ export class ApplicationDetails extends React.Component !refreshing && services.applications.get(app.metadata.name, 'hard') + action: () => !refreshing && services.applications.get(app.metadata.name, app.metadata.namespace, 'hard') } ]} anchor={() => } @@ -656,7 +667,7 @@ export class ApplicationDetails extends React.Component { if (!refreshing) { - services.applications.get(app.metadata.name, 'normal'); + services.applications.get(app.metadata.name, app.metadata.namespace, 'normal'); AppUtils.setAppRefreshing(app); this.appChanged.next(app); } @@ -688,8 +699,8 @@ export class ApplicationDetails extends React.Component { - return from(services.applications.get(name)) + private loadAppInfo(name: string, appNamespace: string): Observable<{application: appModels.Application; tree: appModels.ApplicationTree}> { + return from(services.applications.get(name, appNamespace)) .pipe( mergeMap(app => { const fallbackTree = { @@ -703,7 +714,7 @@ export class ApplicationDetails extends React.Component !!item)), AppUtils.handlePageVisibility(() => services.applications - .watch({name}) + .watch({name, appNamespace}) .pipe( map(watchEvent => { if (watchEvent.type === 'DELETED') { @@ -718,10 +729,10 @@ export class ApplicationDetails extends React.Component fallbackTree), + services.applications.resourceTree(name, appNamespace).catch(() => fallbackTree), AppUtils.handlePageVisibility(() => services.applications - .watchResourceTree(name) + .watchResourceTree(name, appNamespace) .pipe(repeat()) .pipe(retryWhen(errors => errors.pipe(delay(500)))) ) @@ -739,7 +750,7 @@ export class ApplicationDetails extends React.Component) => { +export const ApplicationFullscreenLogs = (props: RouteComponentProps<{name: string; appnamespace: string; container: string; namespace: string}>) => { const appContext = React.useContext(Context); return ( @@ -25,6 +25,7 @@ export const ApplicationFullscreenLogs = (props: RouteComponentProps<{name: stri

{title}

( +export const ApplicationResourceEvents = (props: {applicationName: string; applicationNamespace: string; resource?: {namespace: string; name: string; uid: string}}) => (
(props.resource ? services.applications.resourceEvents(props.applicationName, props.resource) : services.applications.events(props.applicationName))} + load={() => + props.resource + ? services.applications.resourceEvents(props.applicationName, props.applicationNamespace, props.resource) + : services.applications.events(props.applicationName, props.applicationNamespace) + } loadingRenderer={() => }> {events => } 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 a22b72ba9be5a..c6b250356bb56 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 @@ -574,7 +574,7 @@ function renderPodGroup(props: ApplicationResourceTreeProps, id: string, node: R ), action: () => { - deletePodAction(pod, props.appContext, props.app.metadata.name); + deletePodAction(pod, props.appContext, props.app.metadata.name, props.app.metadata.namespace); } } ]} diff --git a/ui/src/app/applications/components/application-status-panel/application-status-panel.tsx b/ui/src/app/applications/components/application-status-panel/application-status-panel.tsx index 131d8cdd9e048..d2d5646b0c751 100644 --- a/ui/src/app/applications/components/application-status-panel/application-status-panel.tsx +++ b/ui/src/app/applications/components/application-status-panel/application-status-panel.tsx @@ -92,7 +92,12 @@ export const ApplicationStatusPanel = ({application, showOperation, showConditio
{application.status && application.status.sync && application.status.sync.revision && ( - + )}
@@ -127,6 +132,7 @@ export const ApplicationStatusPanel = ({application, showOperation, showConditio {(appOperationState.syncResult && appOperationState.syncResult.revision && ( @@ -158,9 +164,9 @@ export const ApplicationStatusPanel = ({application, showOperation, showConditio )} { - return await services.applications.getApplicationSyncWindowState(name); + input={application} + load={async app => { + return await services.applications.getApplicationSyncWindowState(app.metadata.name, app.metadata.namespace); }}> {(data: models.ApplicationSyncWindowState) => ( diff --git a/ui/src/app/applications/components/application-status-panel/revision-metadata-panel.tsx b/ui/src/app/applications/components/application-status-panel/revision-metadata-panel.tsx index 0415ac5b21137..fea9a0c8e2c4b 100644 --- a/ui/src/app/applications/components/application-status-panel/revision-metadata-panel.tsx +++ b/ui/src/app/applications/components/application-status-panel/revision-metadata-panel.tsx @@ -3,12 +3,12 @@ import * as React from 'react'; import {Timestamp} from '../../../shared/components/timestamp'; import {services} from '../../../shared/services'; -export const RevisionMetadataPanel = (props: {appName: string; type: string; revision: string}) => { +export const RevisionMetadataPanel = (props: {appName: string; appNamespace: string; type: string; revision: string}) => { if (props.type === 'helm') { return ; } return ( - services.applications.revisionMetadata(props.appName, props.revision)} errorRenderer={() =>
}> + services.applications.revisionMetadata(props.appName, props.appNamespace, props.revision)} errorRenderer={() =>
}> {m => ( ) => ( + + ); diff --git a/ui/src/app/applications/components/applications-list/applications-list.tsx b/ui/src/app/applications/components/applications-list/applications-list.tsx index b485881fea9c4..0c0ad3b1864b5 100644 --- a/ui/src/app/applications/components/applications-list/applications-list.tsx +++ b/ui/src/app/applications/components/applications-list/applications-list.tsx @@ -27,6 +27,7 @@ const EVENTS_BUFFER_TIMEOUT = 500; const WATCH_RETRY_TIMEOUT = 500; const APP_FIELDS = [ 'metadata.name', + 'metadata.namespace', 'metadata.annotations', 'metadata.labels', 'metadata.creationTimestamp', @@ -43,8 +44,8 @@ const APP_FIELDS = [ const APP_LIST_FIELDS = ['metadata.resourceVersion', ...APP_FIELDS.map(field => `items.${field}`)]; const APP_WATCH_FIELDS = ['result.type', ...APP_FIELDS.map(field => `result.application.${field}`)]; -function loadApplications(projects: string[]): Observable { - return from(services.applications.list(projects, {fields: APP_LIST_FIELDS})).pipe( +function loadApplications(projects: string[], appNamespace: string): Observable { + return from(services.applications.list(projects, {appNamespace, fields: APP_LIST_FIELDS})).pipe( mergeMap(applicationsList => { const applications = applicationsList.items; return merge( @@ -58,7 +59,7 @@ function loadApplications(projects: string[]): Observable .pipe( map(appChanges => { appChanges.forEach(appChange => { - const index = applications.findIndex(item => item.metadata.name === appChange.application.metadata.name); + const index = applications.findIndex(item => AppUtils.appInstanceName(item) === AppUtils.appInstanceName(appChange.application)); switch (appChange.type) { case 'DELETED': if (index > -1) { @@ -160,7 +161,9 @@ function filterApps(applications: models.Application[], pref: AppsListPreference const filterResults = getFilterResults(applications, pref); return { filterResults, - filteredApps: filterResults.filter(app => (search === '' || app.metadata.name.includes(search)) && Object.values(app.filterResult).every(val => val)) + filteredApps: filterResults.filter( + app => (search === '' || app.metadata.name.includes(search) || app.metadata.namespace.includes(search)) && Object.values(app.filterResult).every(val => val) + ) }; } @@ -250,7 +253,7 @@ const SearchBar = (props: {content: string; ctx: ContextApis; apps: models.Appli }} onChange={e => ctx.navigation.goto('.', {search: e.target.value}, {replace: true})} value={content || ''} - items={apps.map(app => app.metadata.name)} + items={apps.map(app => app.metadata.namespace + '/' + app.metadata.name)} /> ); }; @@ -361,7 +364,7 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { AppUtils.handlePageVisibility(() => loadApplications(pref.projectsFilter))} + load={() => AppUtils.handlePageVisibility(() => loadApplications(pref.projectsFilter, query.get('appNamespace')))} loadingRenderer={() => (
@@ -489,16 +492,24 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { (pref.view === 'tiles' && ( ctx.navigation.goto('.', {syncApp: appName}, {replace: true})} + syncApplication={(appName, appNamespace) => + ctx.navigation.goto('.', {syncApp: appName, appNamespace}, {replace: true}) + } refreshApplication={refreshApp} - deleteApplication={appName => AppUtils.deleteApplication(appName, ctx)} + deleteApplication={(appName, appNamespace) => + AppUtils.deleteApplication(appName, appNamespace, ctx) + } /> )) || ( ctx.navigation.goto('.', {syncApp: appName}, {replace: true})} + syncApplication={(appName, appNamespace) => + ctx.navigation.goto('.', {syncApp: appName, appNamespace}, {replace: true}) + } refreshApplication={refreshApp} - deleteApplication={appName => AppUtils.deleteApplication(appName, ctx)} + deleteApplication={(appName, appNamespace) => + AppUtils.deleteApplication(appName, appNamespace, ctx) + } /> ) } @@ -526,7 +537,8 @@ export const ApplicationsList = (props: RouteComponentProps<{}>) => { q.pipe( mergeMap(params => { const syncApp = params.get('syncApp'); - return (syncApp && from(services.applications.get(syncApp))) || from([null]); + const appNamespace = params.get('appNamespace'); + return (syncApp && from(services.applications.get(syncApp, appNamespace))) || from([null]); }) ) }> diff --git a/ui/src/app/applications/components/applications-list/applications-table.tsx b/ui/src/app/applications/components/applications-list/applications-table.tsx index f0af89a77c835..7b8a50b1954c7 100644 --- a/ui/src/app/applications/components/applications-list/applications-table.tsx +++ b/ui/src/app/applications/components/applications-list/applications-table.tsx @@ -14,9 +14,9 @@ require('./applications-table.scss'); export const ApplicationsTable = (props: { applications: models.Application[]; - syncApplication: (appName: string) => any; - refreshApplication: (appName: string) => any; - deleteApplication: (appName: string) => any; + syncApplication: (appName: string, appNamespace: string) => any; + refreshApplication: (appName: string, appNamespace: string) => any; + deleteApplication: (appName: string, appNamespace: string) => any; }) => { const [selectedApp, navApp, reset] = useNav(props.applications.length); const ctxh = React.useContext(Context); @@ -53,12 +53,12 @@ export const ApplicationsTable = (props: {
{props.applications.map((app, i) => (
ctx.navigation.goto(`/applications/${app.metadata.name}`, {}, {event: e})}> + onClick={e => ctx.navigation.goto(`/applications/${app.metadata.namespace}/${app.metadata.name}`, {}, {event: e})}>
@@ -125,9 +125,9 @@ export const ApplicationsTable = (props: { )} items={[ - {title: 'Sync', action: () => props.syncApplication(app.metadata.name)}, - {title: 'Refresh', action: () => props.refreshApplication(app.metadata.name)}, - {title: 'Delete', action: () => props.deleteApplication(app.metadata.name)} + {title: 'Sync', action: () => props.syncApplication(app.metadata.name, app.metadata.namespace)}, + {title: 'Refresh', action: () => props.refreshApplication(app.metadata.name, app.metadata.namespace)}, + {title: 'Delete', action: () => props.deleteApplication(app.metadata.name, app.metadata.namespace)} ]} />
diff --git a/ui/src/app/applications/components/applications-list/applications-tiles.tsx b/ui/src/app/applications/components/applications-list/applications-tiles.tsx index d307e05fbff50..491109b052dba 100644 --- a/ui/src/app/applications/components/applications-list/applications-tiles.tsx +++ b/ui/src/app/applications/components/applications-list/applications-tiles.tsx @@ -14,9 +14,9 @@ require('./applications-tiles.scss'); export interface ApplicationTilesProps { applications: models.Application[]; - syncApplication: (appName: string) => any; - refreshApplication: (appName: string) => any; - deleteApplication: (appName: string) => any; + syncApplication: (appName: string, appNamespace: string) => any; + refreshApplication: (appName: string, appNamespace: string) => any; + deleteApplication: (appName: string, appNamespace: string) => any; } const useItemsPerContainer = (itemRef: any, containerRef: any): number => { @@ -109,14 +109,18 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat className='applications-tiles argo-table-list argo-table-list--clickable row small-up-1 medium-up-2 large-up-3 xxxlarge-up-4' ref={appContainerRef}> {applications.map((app, i) => ( -
+
-
ctx.navigation.goto(`/applications/${app.metadata.name}`, {view: pref.appDetails.view}, {event: e})}> -
+
+ ctx.navigation.goto(`/applications/${app.metadata.namespace}/${app.metadata.name}`, {view: pref.appDetails.view}, {event: e}) + }> +
- {app.metadata.name.length > 30 ? ( - - {app.metadata.name} + {AppUtils.appQualifiedName(app).length > 30 ? ( + + {AppUtils.appQualifiedName(app)} ) : ( - {app.metadata.name} + {AppUtils.appQualifiedName(app)} )}
{ e.stopPropagation(); - syncApplication(app.metadata.name); + syncApplication(app.metadata.name, app.metadata.namespace); }}> Sync @@ -272,7 +276,7 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat {...AppUtils.refreshLinkAttrs(app)} onClick={e => { e.stopPropagation(); - refreshApplication(app.metadata.name); + refreshApplication(app.metadata.name, app.metadata.namespace); }}> {' '} Refresh @@ -283,7 +287,7 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat qe-id='applications-tiles-button-delete' onClick={e => { e.stopPropagation(); - deleteApplication(app.metadata.name); + deleteApplication(app.metadata.name, app.metadata.namespace); }}> Delete 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 27171d2a01ef8..9a5a662ce356b 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 @@ -15,6 +15,7 @@ import './pod-logs-viewer.scss'; const maxLines = 100; export interface PodLogsProps { namespace: string; + applicationNamespace: string; applicationName: string; podName?: string; containerName: string; @@ -112,7 +113,7 @@ export const PodsLogsViewer = (props: PodLogsProps & {fullscreen?: boolean}) => }; const fullscreenURL = - `/applications/${props.applicationName}/${props.namespace}/${props.containerName}/logs?` + + `/applications/${props.applicationNamespace}/${props.applicationName}/${props.namespace}/${props.containerName}/logs?` + `podName=${props.podName}&group=${props.group}&kind=${props.kind}&name=${props.name}`; return ( services.viewPreferences.getPreferences()}> @@ -165,6 +166,7 @@ export const PodsLogsViewer = (props: PodLogsProps & {fullscreen?: boolean}) => onClick={async () => { const downloadURL = services.applications.getDownloadLogsURL( props.applicationName, + props.applicationNamespace, props.namespace, props.podName, {group: props.group, kind: props.kind, name: props.name}, @@ -294,6 +296,7 @@ export const PodsLogsViewer = (props: PodLogsProps & {fullscreen?: boolean}) => let logsSource = services.applications .getContainerLogs( props.applicationName, + props.applicationNamespace, props.namespace, props.podName, {group: props.group, kind: props.kind, name: props.name}, 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 19c7a6b795909..699a783c7a08b 100644 --- a/ui/src/app/applications/components/resource-details/resource-details.tsx +++ b/ui/src/app/applications/components/resource-details/resource-details.tsx @@ -108,6 +108,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { name={node.name} namespace={podState.metadata.namespace} applicationName={application.metadata.name} + applicationNamespace={application.metadata.namespace} containerName={AppUtils.getContainerName(podState, activeContainer)} page={{number: page, untilTimes}} setPage={pageData => appContext.navigation.goto('.', {page: pageData.number, untilTimes: pageData.untilTimes.join(',')})} @@ -195,7 +196,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { input={application.spec} onSave={async patch => { const spec = JSON.parse(JSON.stringify(application.spec)); - return services.applications.updateSpec(application.metadata.name, jsonMergePatch.apply(spec, JSON.parse(patch))); + return services.applications.updateSpec(application.metadata.name, application.metadata.namespace, jsonMergePatch.apply(spec, JSON.parse(patch))); }} /> ) @@ -211,7 +212,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { - await services.applications.managedResources(application.metadata.name, { + await services.applications.managedResources(application.metadata.name, application.metadata.namespace, { fields: ['items.normalizedLiveState', 'items.predictedLiveState', 'items.group', 'items.kind', 'items.namespace', 'items.name'] }) }> @@ -224,7 +225,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { tabs.push({ title: 'EVENTS', key: 'event', - content: + content: }); const extensionTabs = services.extensions.getResourceTabs('argoproj.io', 'Application').map((ext, i) => ({ @@ -246,7 +247,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { noLoaderOnInputChange={true} input={selectedNode.resourceVersion} load={async () => { - const managedResources = await services.applications.managedResources(application.metadata.name, { + const managedResources = await services.applications.managedResources(application.metadata.name, application.metadata.namespace, { id: { name: selectedNode.name, namespace: selectedNode.namespace, @@ -261,10 +262,10 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { if (controlled && controlled.targetState) { resQuery.version = AppUtils.parseApiVersion(controlled.targetState.apiVersion).version; } - const liveState = await services.applications.getResource(application.metadata.name, resQuery).catch(() => null); + const liveState = await services.applications.getResource(application.metadata.name, application.metadata.namespace, resQuery).catch(() => null); const events = (liveState && - (await services.applications.resourceEvents(application.metadata.name, { + (await services.applications.resourceEvents(application.metadata.name, application.metadata.namespace, { name: liveState.metadata.name, namespace: liveState.metadata.namespace, uid: liveState.metadata.uid @@ -276,7 +277,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => { } else { const childPod = AppUtils.findChildPod(selectedNode, tree); if (childPod) { - podState = await services.applications.getResource(application.metadata.name, childPod).catch(() => null); + podState = await services.applications.getResource(application.metadata.name, application.metadata.namespace, childPod).catch(() => null); } } diff --git a/ui/src/app/applications/components/utils.tsx b/ui/src/app/applications/components/utils.tsx index c2526478bb475..40bfc310bf1da 100644 --- a/ui/src/app/applications/components/utils.tsx +++ b/ui/src/app/applications/components/utils.tsx @@ -45,7 +45,7 @@ export function helpTip(text: string) { ); } -export async function deleteApplication(appName: string, apis: ContextApis): Promise { +export async function deleteApplication(appName: string, appNamespace: string, apis: ContextApis): Promise { let confirmed = false; const propagationPolicies: {name: string; message: string}[] = [ { @@ -100,7 +100,7 @@ export async function deleteApplication(appName: string, apis: ContextApis): Pro }), submit: async (vals, _, close) => { try { - await services.applications.delete(appName, vals.propagationPolicy); + await services.applications.delete(appName, appNamespace, vals.propagationPolicy); confirmed = true; close(); } catch (e) { @@ -288,7 +288,7 @@ export function findChildPod(node: appModels.ResourceNode, tree: appModels.Appli }); } -export const deletePodAction = async (pod: appModels.Pod, appContext: AppContext, appName: string) => { +export const deletePodAction = async (pod: appModels.Pod, appContext: AppContext, appName: string, appNamespace: string) => { appContext.apis.popup.prompt( 'Delete pod', () => ( @@ -304,7 +304,7 @@ export const deletePodAction = async (pod: appModels.Pod, appContext: AppContext { submit: async (vals, _, close) => { try { - await services.applications.deleteResource(appName, pod, !!vals.force, false); + await services.applications.deleteResource(appName, appNamespace, pod, !!vals.force, false); close(); } catch (e) { appContext.apis.notifications.show({ @@ -370,9 +370,9 @@ export const deletePopup = async (ctx: ContextApis, resource: ResourceTreeNode, const force = deleteOptions.option === 'force'; const orphan = deleteOptions.option === 'orphan'; try { - await services.applications.deleteResource(application.metadata.name, resource, !!force, !!orphan); + await services.applications.deleteResource(application.metadata.name, application.metadata.namespace, resource, !!force, !!orphan); if (appChanged) { - appChanged.next(await services.applications.get(application.metadata.name)); + appChanged.next(await services.applications.get(application.metadata.name, application.metadata.namespace)); } close(); } catch (e) { @@ -453,7 +453,7 @@ function getActionItems( .catch(() => items); const resourceActions = services.applications - .getResourceActions(application.metadata.name, resource) + .getResourceActions(application.metadata.name, application.metadata.namespace, resource) .then(actions => { return items.concat( actions.map(action => ({ @@ -463,7 +463,7 @@ function getActionItems( try { const confirmed = await appContext.apis.popup.confirm(`Execute '${action.name}' action?`, `Are you sure you want to execute '${action.name}' action?`); if (confirmed) { - await services.applications.runResourceAction(application.metadata.name, resource, action.name); + await services.applications.runResourceAction(application.metadata.name, application.metadata.namespace, resource, action.name); } } catch (e) { appContext.apis.notifications.show({ @@ -1102,3 +1102,11 @@ export const urlPattern = new RegExp( 'gi' ) ); + +export function appQualifiedName(app: appModels.Application): string { + return app.metadata.namespace + '/' + app.metadata.name; +} + +export function appInstanceName(app: appModels.Application): string { + return app.metadata.namespace + '_' + app.metadata.name; +} diff --git a/ui/src/app/shared/components/application-selector/application-selector.tsx b/ui/src/app/shared/components/application-selector/application-selector.tsx index 32d948947a481..a5c4f50b27fca 100644 --- a/ui/src/app/shared/components/application-selector/application-selector.tsx +++ b/ui/src/app/shared/components/application-selector/application-selector.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import {FormFunctionProps} from 'react-form'; import {CheckboxField} from '..'; import * as models from '../../models'; -import {ComparisonStatusIcon, HealthStatusIcon, OperationPhaseIcon} from '../../../applications/components/utils'; +import {appInstanceName, appQualifiedName, ComparisonStatusIcon, HealthStatusIcon, OperationPhaseIcon} from '../../../applications/components/utils'; export const ApplicationSelector = ({apps, formApi}: {apps: models.Application[]; formApi: FormFunctionProps}) => { return ( @@ -15,10 +15,10 @@ export const ApplicationSelector = ({apps, formApi}: {apps: models.Application[]
{apps.map((app, i) => ( -