mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 17:48:47 +01:00
Compare commits
54 Commits
hydrator-c
...
v2.4.0-rc5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b84dd8bbfa | ||
|
|
e6f37d7245 | ||
|
|
57bac9ac75 | ||
|
|
3034183791 | ||
|
|
26c87b3f16 | ||
|
|
bd8d26d444 | ||
|
|
a6c58748cd | ||
|
|
46af59e258 | ||
|
|
867660a709 | ||
|
|
b1addf5bf1 | ||
|
|
0a9d1607e2 | ||
|
|
09fc32e6cb | ||
|
|
1be9296e6c | ||
|
|
76fad02f4a | ||
|
|
2f8eb04b84 | ||
|
|
66aa0e6e01 | ||
|
|
955270eb0d | ||
|
|
225a0af9f7 | ||
|
|
5191cd077c | ||
|
|
c6b928c830 | ||
|
|
4e73b3c7ee | ||
|
|
708c9e79b9 | ||
|
|
da2c249814 | ||
|
|
c27cf3f95e | ||
|
|
dbd3ce3133 | ||
|
|
58062c45de | ||
|
|
3c61070411 | ||
|
|
697fc77379 | ||
|
|
6655a22b0a | ||
|
|
0d109279a8 | ||
|
|
4c1e1e0ad6 | ||
|
|
078cfe130b | ||
|
|
cd098638f8 | ||
|
|
2826a9215d | ||
|
|
13bef3a831 | ||
|
|
c6f80377a8 | ||
|
|
a8a451a84b | ||
|
|
cd5b2af358 | ||
|
|
f6f9fa2cd6 | ||
|
|
c7749ca67e | ||
|
|
3399a81bed | ||
|
|
80e5c55ca0 | ||
|
|
c4182aedc7 | ||
|
|
e4404372af | ||
|
|
12140f8152 | ||
|
|
6cfd394445 | ||
|
|
1998b016c0 | ||
|
|
74bc1731f9 | ||
|
|
bb28b3c697 | ||
|
|
0ee9993369 | ||
|
|
d8f845a126 | ||
|
|
5901b46785 | ||
|
|
88542a616f | ||
|
|
cbe4f1b92e |
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -202,7 +202,7 @@ jobs:
|
||||
set -ue
|
||||
git clean -fd
|
||||
mkdir -p dist/
|
||||
docker buildx build --platform linux/amd64,linux/arm64 --push -t ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION} -t argoproj/argocd:v${TARGET_VERSION} .
|
||||
docker buildx build --platform linux/amd64,linux/arm64,linux/s390x,linux/ppc64le --push -t ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION} -t argoproj/argocd:v${TARGET_VERSION} .
|
||||
make release-cli
|
||||
make checksums
|
||||
chmod +x ./dist/argocd-linux-amd64
|
||||
|
||||
12
CHANGELOG.md
12
CHANGELOG.md
@@ -2,28 +2,32 @@
|
||||
|
||||
## v2.4.0 (Unreleased)
|
||||
|
||||
### Web Terminal In Argo CD UI
|
||||
### Web Terminal In Argo CD UI
|
||||
|
||||
Feature enables engineers to start a shell in the running application container without leaving the web interface. Just find the required Kubernetes
|
||||
Pod using the Application Details page, click on it and select the Terminal tab. The shell starts automatically and enables you to execute the required
|
||||
commands, and helps to troubleshoot the application state.
|
||||
|
||||
### Access Control For Pod Logs & Web Terminal
|
||||
### Access Control For Pod Logs & Web Terminal
|
||||
|
||||
Argo CD is used to manage the critical infrastructure of multiple organizations, which makes security the top priority of the project. We've listened to
|
||||
your feedback and introduced additional access control settings that control access to Kubernetes Pod logs and the new Web Terminal feature.
|
||||
|
||||
#### Known UI Issue for Pod Logs Access
|
||||
|
||||
Currently, upon pressing the "LOGS" tab in pod view by users who don't have an explicit allow get logs policy, the red "unable to load data: Internal error" is received in the bottom of the screen, and "Failed to load data, please try again" is displayed.
|
||||
|
||||
### OpenTelemetry Tracing Integration
|
||||
|
||||
The new feature allows emitting richer telemetry data that might make identifying performance bottlenecks easier. The new feature is available for argocd-server
|
||||
and argocd-repo-server components and can be enabled using the --otlp-address flag.
|
||||
|
||||
### Power PC and IBM Z Support
|
||||
### Power PC and IBM Z Support
|
||||
|
||||
The list of supported architectures has been expanded, and now includes IBM Z (s390x) and PowerPC (ppc64le). Starting with the v2.4 release the official quay.io
|
||||
repository is going to have images for amd64, arm64, ppc64le, and s390x architectures.
|
||||
|
||||
### Other Notable Changes
|
||||
### Other Notable Changes
|
||||
|
||||
Overall v2.4 release includes more than 300 hundred commits from nearly 90 contributors. Here is a short sample of the contributions:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG BASE_IMAGE=docker.io/library/ubuntu:21.10
|
||||
ARG BASE_IMAGE=docker.io/library/ubuntu:22.04
|
||||
####################################################################################################
|
||||
# Builder image
|
||||
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
|
||||
@@ -127,4 +127,4 @@ RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server && \
|
||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-applicationset-controller && \
|
||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-k8s-auth
|
||||
|
||||
USER 999
|
||||
USER 999
|
||||
|
||||
4
Makefile
4
Makefile
@@ -234,6 +234,8 @@ release-cli: clean-debug build-ui
|
||||
make BIN_NAME=argocd-darwin-arm64 GOOS=darwin GOARCH=arm64 argocd-all
|
||||
make BIN_NAME=argocd-linux-amd64 GOOS=linux argocd-all
|
||||
make BIN_NAME=argocd-linux-arm64 GOOS=linux GOARCH=arm64 argocd-all
|
||||
make BIN_NAME=argocd-linux-ppc64le GOOS=linux GOARCH=ppc64le argocd-all
|
||||
make BIN_NAME=argocd-linux-s390x GOOS=linux GOARCH=s390x argocd-all
|
||||
make BIN_NAME=argocd-windows-amd64.exe GOOS=windows argocd-all
|
||||
|
||||
.PHONY: test-tools-image
|
||||
@@ -566,4 +568,4 @@ applicationset-controller:
|
||||
|
||||
.PHONY: checksums
|
||||
checksums:
|
||||
for f in ./dist/$(BIN_NAME)-*; do openssl dgst -sha256 "$$f" | awk ' { print $$2 }' > "$$f".sha256 ; done
|
||||
for f in ./dist/$(BIN_NAME)-*; do openssl dgst -sha256 "$$f" | awk ' { print $$2 }' > "$$f".sha256 ; done
|
||||
|
||||
10
Procfile
10
Procfile
@@ -1,12 +1,12 @@
|
||||
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_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} "
|
||||
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}"
|
||||
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:v2.30.2 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:7.0.0-alpine --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}"
|
||||
cmp-server: [ "$ARGOCD_E2E_TEST" == 'true' ] && exit 0 || [ "$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-cmp-server ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} $COMMAND --config-dir-path ./test/cmp --loglevel debug"
|
||||
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}"
|
||||
cmp-server: [ "$ARGOCD_E2E_TEST" == 'true' ] && exit 0 || [ "$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-cmp-server ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} $COMMAND --config-dir-path ./test/cmp --loglevel debug --otlp-address=${ARGOCD_OTLP_ADDRESS}"
|
||||
ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start'
|
||||
git-server: test/fixture/testrepos/start-git.sh
|
||||
helm-registry: test/fixture/testrepos/start-helm-registry.sh
|
||||
dev-mounter: [[ "$ARGOCD_E2E_TEST" != "true" ]] && go run hack/dev-mounter/main.go --configmap argocd-ssh-known-hosts-cm=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} --configmap argocd-tls-certs-cm=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} --configmap argocd-gpg-keys-cm=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source}
|
||||
applicationset-controller: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_ASK_PASS_SOCK=/tmp/applicationset-ask-pass.sock ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
|
||||
notification: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug"
|
||||
notification: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug"
|
||||
|
||||
@@ -166,7 +166,7 @@ func (g *ClusterGenerator) getSecretsByClusterName(appSetGenerator *argoappsetv1
|
||||
|
||||
}
|
||||
|
||||
// santize the name in accordance with the below rules
|
||||
// sanitize the name in accordance with the below rules
|
||||
// 1. contain no more than 253 characters
|
||||
// 2. contain only lowercase alphanumeric characters, '-' or '.'
|
||||
// 3. start and end with an alphanumeric character
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Built-in policy which defines two roles: role:readonly and role:admin,
|
||||
# and additionally assigns the admin user to the role:admin role.
|
||||
# There are two policy formats:
|
||||
# 1. Applications (which belong to a project):
|
||||
# 1. Applications, logs, and exec (which belong to a project):
|
||||
# p, <user/group>, <resource>, <action>, <project>/<object>
|
||||
# 2. All other resources:
|
||||
# p, <user/group>, <resource>, <action>, <object>
|
||||
|
||||
|
@@ -28,6 +28,7 @@ import (
|
||||
kubeutil "github.com/argoproj/argo-cd/v2/util/kube"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
"github.com/argoproj/argo-cd/v2/util/tls"
|
||||
"github.com/argoproj/argo-cd/v2/util/trace"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -58,6 +59,7 @@ func NewCommand() *cobra.Command {
|
||||
redisClient *redis.Client
|
||||
repoServerPlaintext bool
|
||||
repoServerStrictTLS bool
|
||||
otlpAddress string
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -149,6 +151,14 @@ func NewCommand() *cobra.Command {
|
||||
stats.StartStatsTicker(10 * time.Minute)
|
||||
stats.RegisterHeapDumper("memprofile")
|
||||
|
||||
if otlpAddress != "" {
|
||||
closeTracer, err := trace.InitTracer(ctx, "argocd-controller", otlpAddress)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialize tracing: %v", err)
|
||||
}
|
||||
defer closeTracer()
|
||||
}
|
||||
|
||||
go appController.Run(ctx, statusProcessors, operationProcessors)
|
||||
|
||||
// Wait forever
|
||||
@@ -173,6 +183,7 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT", false), "Disable TLS on connections to repo server")
|
||||
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")
|
||||
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
redisClient = client
|
||||
})
|
||||
|
||||
@@ -88,6 +88,7 @@ func TestGetReconcileResults_Refresh(t *testing.T) {
|
||||
kubeClientset := kubefake.NewSimpleClientset(deployment, &cm)
|
||||
clusterCache := clustermocks.ClusterCache{}
|
||||
clusterCache.On("IsNamespaced", mock.Anything).Return(true, nil)
|
||||
clusterCache.On("GetGVKParser", mock.Anything).Return(nil)
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClient.On("GenerateManifest", mock.Anything, mock.Anything).Return(&argocdclient.ManifestResponse{
|
||||
Manifests: []string{test.DeploymentManifest},
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
@@ -46,6 +47,8 @@ func NewConnection(address string) (*grpc.ClientConn, error) {
|
||||
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)),
|
||||
grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(unaryInterceptors...)),
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(MaxGRPCMessageSize), grpc.MaxCallSendMsgSize(MaxGRPCMessageSize)),
|
||||
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()),
|
||||
grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()),
|
||||
}
|
||||
|
||||
dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
|
||||
@@ -233,12 +233,12 @@ func (s *Service) MatchRepository(stream apiclient.ConfigManagementPluginService
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = cmp.ReceiveRepoStream(bufferedCtx, stream, workDir)
|
||||
metadata, err := cmp.ReceiveRepoStream(bufferedCtx, stream, workDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("match repository error receiving stream: %s", err)
|
||||
}
|
||||
|
||||
isSupported, err := s.matchRepository(bufferedCtx, workDir)
|
||||
isSupported, err := s.matchRepository(bufferedCtx, workDir, metadata.GetEnv())
|
||||
if err != nil {
|
||||
return fmt.Errorf("match repository error: %s", err)
|
||||
}
|
||||
@@ -251,7 +251,7 @@ func (s *Service) MatchRepository(stream apiclient.ConfigManagementPluginService
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) matchRepository(ctx context.Context, workdir string) (bool, error) {
|
||||
func (s *Service) matchRepository(ctx context.Context, workdir string, envEntries []*apiclient.EnvEntry) (bool, error) {
|
||||
config := s.initConstants.PluginConfig
|
||||
if config.Spec.Discover.FileName != "" {
|
||||
log.Debugf("config.Spec.Discover.FileName is provided")
|
||||
@@ -284,7 +284,9 @@ func (s *Service) matchRepository(ctx context.Context, workdir string) (bool, er
|
||||
}
|
||||
|
||||
log.Debugf("Going to try runCommand.")
|
||||
find, err := runCommand(ctx, config.Spec.Discover.Find.Command, workdir, os.Environ())
|
||||
env := append(os.Environ(), environ(envEntries)...)
|
||||
|
||||
find, err := runCommand(ctx, config.Spec.Discover.Find.Command, workdir, env)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error running find command: %s", err)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmpserver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/test"
|
||||
)
|
||||
|
||||
@@ -62,6 +63,7 @@ func TestMatchRepository(t *testing.T) {
|
||||
type fixture struct {
|
||||
service *Service
|
||||
path string
|
||||
env []*apiclient.EnvEntry
|
||||
}
|
||||
setup := func(t *testing.T, opts ...pluginOpt) *fixture {
|
||||
t.Helper()
|
||||
@@ -71,6 +73,7 @@ func TestMatchRepository(t *testing.T) {
|
||||
return &fixture{
|
||||
service: s,
|
||||
path: path,
|
||||
env: []*apiclient.EnvEntry{{Name: "ENV_VAR", Value: "1"}},
|
||||
}
|
||||
}
|
||||
t.Run("will match plugin by filename", func(t *testing.T) {
|
||||
@@ -81,7 +84,7 @@ func TestMatchRepository(t *testing.T) {
|
||||
f := setup(t, withDiscover(d))
|
||||
|
||||
// when
|
||||
match, err := f.service.matchRepository(context.Background(), f.path)
|
||||
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
@@ -95,7 +98,7 @@ func TestMatchRepository(t *testing.T) {
|
||||
f := setup(t, withDiscover(d))
|
||||
|
||||
// when
|
||||
match, err := f.service.matchRepository(context.Background(), f.path)
|
||||
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
@@ -111,7 +114,7 @@ func TestMatchRepository(t *testing.T) {
|
||||
f := setup(t, withDiscover(d))
|
||||
|
||||
// when
|
||||
match, err := f.service.matchRepository(context.Background(), f.path)
|
||||
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
@@ -127,7 +130,7 @@ func TestMatchRepository(t *testing.T) {
|
||||
f := setup(t, withDiscover(d))
|
||||
|
||||
// when
|
||||
match, err := f.service.matchRepository(context.Background(), f.path)
|
||||
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
@@ -145,7 +148,7 @@ func TestMatchRepository(t *testing.T) {
|
||||
f := setup(t, withDiscover(d))
|
||||
|
||||
// when
|
||||
match, err := f.service.matchRepository(context.Background(), f.path)
|
||||
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
@@ -163,7 +166,43 @@ func TestMatchRepository(t *testing.T) {
|
||||
f := setup(t, withDiscover(d))
|
||||
|
||||
// when
|
||||
match, err := f.service.matchRepository(context.Background(), f.path)
|
||||
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, match)
|
||||
})
|
||||
t.Run("will match plugin because env var defined", func(t *testing.T) {
|
||||
// given
|
||||
d := Discover{
|
||||
Find: Find{
|
||||
Command: Command{
|
||||
Command: []string{"sh", "-c", "echo -n $ENV_VAR"},
|
||||
},
|
||||
},
|
||||
}
|
||||
f := setup(t, withDiscover(d))
|
||||
|
||||
// when
|
||||
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, match)
|
||||
})
|
||||
t.Run("will not match plugin because no env var defined", func(t *testing.T) {
|
||||
// given
|
||||
d := Discover{
|
||||
Find: Find{
|
||||
Command: Command{
|
||||
Command: []string{"sh", "-c", "echo -n $ENV_NO_VAR"},
|
||||
},
|
||||
},
|
||||
}
|
||||
f := setup(t, withDiscover(d))
|
||||
|
||||
// when
|
||||
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
@@ -181,7 +220,7 @@ func TestMatchRepository(t *testing.T) {
|
||||
f := setup(t, withDiscover(d))
|
||||
|
||||
// when
|
||||
match, err := f.service.matchRepository(context.Background(), f.path)
|
||||
match, err := f.service.matchRepository(context.Background(), f.path, f.env)
|
||||
|
||||
// then
|
||||
assert.Error(t, err)
|
||||
|
||||
@@ -605,11 +605,16 @@ func (ctrl *ApplicationController) hideSecretData(app *appv1.Application, compar
|
||||
return nil, fmt.Errorf("error getting tracking method: %s", err)
|
||||
}
|
||||
|
||||
clusterCache, err := ctrl.stateCache.GetClusterCache(app.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting cluster cache: %s", err)
|
||||
}
|
||||
diffConfig, err := argodiff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, resourceOverrides, compareOptions.IgnoreAggregatedRoles).
|
||||
WithTracking(appLabelKey, trackingMethod).
|
||||
WithNoCache().
|
||||
WithLogger(logutils.NewLogrusLogger(logutils.NewWithCurrentConfig())).
|
||||
WithGVKParser(clusterCache.GetGVKParser()).
|
||||
Build()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("appcontroller error building diff config: %s", err)
|
||||
|
||||
@@ -121,6 +121,7 @@ func newFakeController(data *fakeData) *ApplicationController {
|
||||
clusterCacheMock := mocks.ClusterCache{}
|
||||
clusterCacheMock.On("IsNamespaced", mock.Anything).Return(true, nil)
|
||||
clusterCacheMock.On("GetOpenAPISchema").Return(nil, nil)
|
||||
clusterCacheMock.On("GetGVKParser").Return(nil)
|
||||
|
||||
mockStateCache := mockstatecache.LiveStateCache{}
|
||||
ctrl.appStateManager.(*appStateManager).liveStateCache = &mockStateCache
|
||||
|
||||
@@ -461,7 +461,14 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
} else {
|
||||
diffConfigBuilder.WithCache(m.cache, app.GetName())
|
||||
}
|
||||
// it necessary to ignore the error at this point to avoid creating duplicated
|
||||
|
||||
gvkParser, err := m.getGVKParser(app.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionUnknownError, Message: err.Error(), LastTransitionTime: &now})
|
||||
}
|
||||
diffConfigBuilder.WithGVKParser(gvkParser)
|
||||
|
||||
// it is necessary to ignore the error at this point to avoid creating duplicated
|
||||
// application conditions as argo.StateDiffs will validate this diffConfig again.
|
||||
diffConfig, _ := diffConfigBuilder.Build()
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/managedfields"
|
||||
"k8s.io/kubectl/pkg/util/openapi"
|
||||
|
||||
cdcommon "github.com/argoproj/argo-cd/v2/common"
|
||||
@@ -46,6 +47,14 @@ func (m *appStateManager) getOpenAPISchema(server string) (openapi.Resources, er
|
||||
return cluster.GetOpenAPISchema(), nil
|
||||
}
|
||||
|
||||
func (m *appStateManager) getGVKParser(server string) (*managedfields.GvkParser, error) {
|
||||
cluster, err := m.liveStateCache.GetClusterCache(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cluster.GetGVKParser(), nil
|
||||
}
|
||||
|
||||
func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState) {
|
||||
// Sync requests might be requested with ambiguous revisions (e.g. master, HEAD, v1.2.3).
|
||||
// This can change meaning when resuming operations (e.g a hook sync). After calculating a
|
||||
|
||||
@@ -27,6 +27,8 @@ kubectl -n argocd scale deployment/argocd-dex-server --replicas 0
|
||||
kubectl -n argocd scale deployment/argocd-repo-server --replicas 0
|
||||
kubectl -n argocd scale deployment/argocd-server --replicas 0
|
||||
kubectl -n argocd scale deployment/argocd-redis --replicas 0
|
||||
kubectl -n argocd scale deployment/argocd-applicationset-controller --replicas 0
|
||||
kubectl -n argocd scale deployment/argocd-notifications-controller --replicas 0
|
||||
```
|
||||
|
||||
### Start local services
|
||||
|
||||
@@ -45,7 +45,7 @@ spec:
|
||||
metadata:
|
||||
name: '{{name}}-guestbook' # 'name' field of the Secret
|
||||
spec:
|
||||
project: "default"
|
||||
project: "my-project"
|
||||
source:
|
||||
repoURL: https://github.com/argoproj/argocd-example-apps/
|
||||
targetRevision: HEAD
|
||||
@@ -144,7 +144,7 @@ spec:
|
||||
metadata:
|
||||
name: '{{name}}-guestbook'
|
||||
spec:
|
||||
project: "default"
|
||||
project: "my-project"
|
||||
source:
|
||||
repoURL: https://github.com/argoproj/argocd-example-apps/
|
||||
# The cluster values field for each generator will be substituted here:
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
The Git generator contains two subtypes: the Git directory generator, and Git file generator.
|
||||
|
||||
!!! warning
|
||||
Git generators are often used to make it easier for (non-admin) developers to create Applications.
|
||||
If the `project` field in your ApplicationSet is templated, developers may be able to create Applications under Projects with excessive permissions.
|
||||
For ApplicationSets with a templated `project` field, [the source of truth _must_ be controlled by admins](./Security.md#templated-project-field)
|
||||
- in the case of git generators, PRs must require admin approval.
|
||||
|
||||
## Git Generator: Directories
|
||||
|
||||
The Git directory generator, one of two subtypes of the Git generator, generates parameters using the directory structure of a specified Git repository.
|
||||
@@ -41,7 +47,7 @@ spec:
|
||||
metadata:
|
||||
name: '{{path[0]}}'
|
||||
spec:
|
||||
project: default
|
||||
project: "my-project"
|
||||
source:
|
||||
repoURL: https://github.com/argoproj/argo-cd.git
|
||||
targetRevision: HEAD
|
||||
@@ -88,7 +94,7 @@ spec:
|
||||
metadata:
|
||||
name: '{{path.basename}}'
|
||||
spec:
|
||||
project: default
|
||||
project: "my-project"
|
||||
source:
|
||||
repoURL: https://github.com/argoproj/argo-cd.git
|
||||
targetRevision: HEAD
|
||||
|
||||
@@ -20,7 +20,7 @@ spec:
|
||||
metadata:
|
||||
name: '{{cluster}}-guestbook'
|
||||
spec:
|
||||
project: default
|
||||
project: "my-project"
|
||||
source:
|
||||
repoURL: https://github.com/argoproj/argo-cd.git
|
||||
targetRevision: HEAD
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Pull Request Generator
|
||||
|
||||
The Pull Request generator uses the API of an SCMaaS provider (eg GitHub/GitLab) to automatically discover open pull requests within an repository. This fits well with the style of building a test environment when you create a pull request.
|
||||
|
||||
The Pull Request generator uses the API of an SCMaaS provider (GitHub, Gitea, or Bitbucket Server) to automatically discover open pull requests within a repository. This fits well with the style of building a test environment when you create a pull request.
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
@@ -16,9 +15,15 @@ spec:
|
||||
# ...
|
||||
```
|
||||
|
||||
!!! note
|
||||
Know the security implications of PR generators in ApplicationSets.
|
||||
[Only admins may create ApplicationSets](./Security.md#only-admins-may-createupdatedelete-applicationsets) to avoid
|
||||
leaking Secrets, and [only admins may create PRs](./Security.md#templated-project-field) if the `project` field of
|
||||
an ApplicationSet with a PR generator is templated, to avoid granting management of out-of-bounds resources.
|
||||
|
||||
## GitHub
|
||||
|
||||
Specify the repository from which to fetch the Github Pull requests.
|
||||
Specify the repository from which to fetch the GitHub Pull requests.
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
@@ -48,7 +53,7 @@ spec:
|
||||
```
|
||||
|
||||
* `owner`: Required name of the GitHub organization or user.
|
||||
* `repo`: Required name of the Github repository.
|
||||
* `repo`: Required name of the GitHub repository.
|
||||
* `api`: If using GitHub Enterprise, the URL to access it. (Optional)
|
||||
* `tokenRef`: A `Secret` name and key containing the GitHub access token to use for requests. If not specified, will make anonymous requests which have a lower rate limit and can only see public repositories. (Optional)
|
||||
* `labels`: Labels is used to filter the PRs that you want to target. (Optional)
|
||||
@@ -182,7 +187,7 @@ spec:
|
||||
parameters:
|
||||
- name: "image.tag"
|
||||
value: "pull-{{head_sha}}"
|
||||
project: default
|
||||
project: "my-project"
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: default
|
||||
@@ -213,7 +218,7 @@ spec:
|
||||
app.kubernetes.io/instance: {{branch}}-{{number}}
|
||||
images:
|
||||
- ghcr.io/myorg/myrepo:{{head_sha}}
|
||||
project: default
|
||||
project: "my-project"
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: default
|
||||
@@ -230,6 +235,7 @@ When using a Pull Request generator, the ApplicationSet controller polls every `
|
||||
The configuration is almost the same as the one described [in the Git generator](Generators-Git.md), but there is one difference: if you want to use the Pull Request Generator as well, additionally configure the following settings.
|
||||
|
||||
In section 1, _"Create the webhook in the Git provider"_, add an event so that a webhook request will be sent when a pull request is created, closed, or label changed.
|
||||
|
||||
Select `Let me select individual events` and enable the checkbox for `Pull requests`.
|
||||
|
||||

|
||||
|
||||
@@ -19,6 +19,12 @@ spec:
|
||||
|
||||
* `cloneProtocol`: Which protocol to use for the SCM URL. Default is provider-specific but ssh if possible. Not all providers necessarily support all protocols, see provider documentation below for available options.
|
||||
|
||||
!!! note
|
||||
Know the security implications of using SCM generators. [Only admins may create ApplicationSets](./Security.md#only-admins-may-createupdatedelete-applicationsets)
|
||||
to avoid leaking Secrets, and [only admins may create repos/branches](./Security.md#templated-project-field) if the
|
||||
`project` field of an ApplicationSet with an SCM generator is templated, to avoid granting management of
|
||||
out-of-bounds resources.
|
||||
|
||||
## GitHub
|
||||
|
||||
The GitHub mode uses the GitHub API to scan and organization in either github.com or GitHub Enterprise.
|
||||
|
||||
@@ -22,9 +22,9 @@ Follow the [Argo CD Getting Started](../../getting_started.md) instructions for
|
||||
|
||||
### B) Install ApplicationSet into an existing Argo CD install (pre-Argo CD v2.3)
|
||||
|
||||
**Note**: These instruction only apply to versions of Argo CD before v2.3.0.
|
||||
**Note**: These instructions only apply to versions of Argo CD before v2.3.0.
|
||||
|
||||
The ApplicationSet controller *must* be installed into the same namespace as the Argo CD it is targetting.
|
||||
The ApplicationSet controller *must* be installed into the same namespace as the Argo CD it is targeting.
|
||||
|
||||
Presuming that Argo CD is installed into the `argocd` namespace, run the following command:
|
||||
|
||||
|
||||
38
docs/operator-manual/applicationset/Security.md
Normal file
38
docs/operator-manual/applicationset/Security.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# ApplicationSet Security
|
||||
|
||||
ApplicationSet is a powerful tool, and it is crucial to understand its security implications before using it.
|
||||
|
||||
## Only admins may create/update/delete ApplicationSets
|
||||
|
||||
ApplicationSets can create Applications under arbitrary [Projects](../../user-guide/projects.md). Argo CD setups often
|
||||
include Projects (such as the `default`) with high levels of permissions, often including the ability to manage the
|
||||
resources of Argo CD itself (like the RBAC ConfigMap).
|
||||
|
||||
ApplicationSets can also quickly create an arbitrary number of Applications and just as quickly delete them.
|
||||
|
||||
Finally, ApplicationSets can reveal privileged information. For example, the [git generator](./Generators-Git.md) can
|
||||
read Secrets in the Argo CD namespace and send them to arbitrary URLs as auth headers. (This functionality is intended
|
||||
for authorizing requests to SCM providers like GitHub, but it could be abused by a malicious user.)
|
||||
|
||||
For these reasons, **only admins** may be given permission (via Kubernetes RBAC or any other mechanism) to create,
|
||||
update, or delete ApplicationSets.
|
||||
|
||||
## Admins must apply appropriate controls for ApplicationSets' sources of truth
|
||||
|
||||
Even if non-admins can't create ApplicationSet resources, they may be able to affect the behavior of ApplicationSets.
|
||||
|
||||
For example, if an ApplicationSet uses a [git generator](./Generators-Git.md), a malicious user with push access to the
|
||||
source git repository could generate an excessively high number of Applications, putting strain on the ApplicationSet
|
||||
and Application controllers. They could also cause the SCM provider's rate limiting to kick in, degrading ApplicationSet
|
||||
service.
|
||||
|
||||
### Templated `project` field
|
||||
|
||||
It's important to pay special attention to ApplicationSets where the `project` field is templated. A malicious user with
|
||||
write access to the generator's source of truth (for example, someone with push access to the git repo for a git
|
||||
generator) could create Applications under Projects with insufficient restrictions. A malicious user with the ability to
|
||||
create an Application under an unrestricted Project (like the `default` Project) could take control of Argo CD itself
|
||||
by, for example, modifying its RBAC ConfigMap.
|
||||
|
||||
If the `project` field is not hard-coded in an ApplicationSet's template, then admins _must_ control all sources of
|
||||
truth for the ApplicationSet's generators.
|
||||
@@ -15,6 +15,9 @@ The ApplicationSet controller, supplements Argo CD by adding additional features
|
||||
- Improved support for monorepos: in the context of Argo CD, a monorepo is multiple Argo CD Application resources defined within a single Git repository
|
||||
- Within multitenant clusters, improves the ability of individual cluster tenants to deploy applications using Argo CD (without needing to involve privileged cluster administrators in enabling the destination clusters/namespaces)
|
||||
|
||||
!!! note
|
||||
Be aware of the [security implications](./Security.md) of ApplicationSets before using them.
|
||||
|
||||
## The ApplicationSet resource
|
||||
|
||||
This example defines a new `guestbook` resource of kind `ApplicationSet`:
|
||||
@@ -37,6 +40,7 @@ spec:
|
||||
metadata:
|
||||
name: '{{cluster}}-guestbook'
|
||||
spec:
|
||||
project: my-project
|
||||
source:
|
||||
repoURL: https://github.com/infra-team/cluster-deployments.git
|
||||
targetRevision: HEAD
|
||||
|
||||
@@ -17,6 +17,9 @@ data:
|
||||
# Redis database
|
||||
redis.db:
|
||||
|
||||
# Open-Telemetry collector address: (e.g. "otel-collector:4317")
|
||||
otlp.address:
|
||||
|
||||
## Controller Properties
|
||||
# Repo server RPC call timeout seconds.
|
||||
controller.repo.server.timeout.seconds: "60"
|
||||
@@ -103,4 +106,4 @@ data:
|
||||
reposerver.repo.cache.expiration: "24h0m0s"
|
||||
# Cache expiration default (default 24h0m0s)
|
||||
reposerver.default.cache.expiration: "24h0m0s"
|
||||
|
||||
|
||||
|
||||
66
docs/operator-manual/notifications/services/pagerduty.md
Executable file
66
docs/operator-manual/notifications/services/pagerduty.md
Executable file
@@ -0,0 +1,66 @@
|
||||
# Pagerduty
|
||||
|
||||
## Parameters
|
||||
|
||||
The Pagerduty notification service is used to create pagerduty incidents and requires specifying the following settings:
|
||||
|
||||
* `pagerdutyToken` - the pagerduty auth token
|
||||
* `from` - email address of a valid user associated with the account making the request.
|
||||
* `serviceID` - The ID of the resource.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
The following snippet contains sample Pagerduty service configuration:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: <secret-name>
|
||||
stringData:
|
||||
pagerdutyToken: <pd-api-token>
|
||||
```
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: <config-map-name>
|
||||
data:
|
||||
service.pagerduty: |
|
||||
token: $pagerdutyToken
|
||||
from: <emailid>
|
||||
```
|
||||
|
||||
## Template
|
||||
|
||||
Notification templates support specifying subject for pagerduty notifications:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: <config-map-name>
|
||||
data:
|
||||
template.rollout-aborted: |
|
||||
message: Rollout {{.rollout.metadata.name}} is aborted.
|
||||
pagerduty:
|
||||
title: "Rollout {{.rollout.metadata.name}}"
|
||||
urgency: "high"
|
||||
body: "Rollout {{.rollout.metadata.name}} aborted "
|
||||
priorityID: "<priorityID of incident>"
|
||||
```
|
||||
|
||||
NOTE: A Priority is a label representing the importance and impact of an incident. This is only available on Standard and Enterprise plans of pagerduty.
|
||||
|
||||
## Annotation
|
||||
|
||||
Annotation sample for pagerduty notifications:
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
annotations:
|
||||
notifications.argoproj.io/subscribe.on-rollout-aborted.pagerduty: "<serviceID for Pagerduty>"
|
||||
```
|
||||
@@ -54,6 +54,26 @@ The Slack notification service configuration includes following settings:
|
||||
annotations:
|
||||
notifications.argoproj.io/subscribe.on-sync-succeeded.slack: my_channel
|
||||
|
||||
1. Annotation with more than one trigger multiple of destinations and recipients
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
annotations:
|
||||
notifications.argoproj.io/subscriptions: |
|
||||
- trigger: [on-scaling-replica-set, on-rollout-updated, on-rollout-step-completed]
|
||||
destinations:
|
||||
- service: slack
|
||||
recipients: [my-channel-1, my-channel-2]
|
||||
- service: email
|
||||
recipients: [recipient-1, recipient-2, recipient-3 ]
|
||||
- trigger: [on-rollout-aborted, on-analysis-run-failed, on-analysis-run-error]
|
||||
destinations:
|
||||
- service: slack
|
||||
recipients: [my-channel-21, my-channel-22]
|
||||
```
|
||||
|
||||
## Templates
|
||||
|
||||
Notification templates can be customized to leverage slack message blocks and attachments
|
||||
|
||||
@@ -18,11 +18,11 @@ These default built-in role definitions can be seen in [builtin-policy.csv](http
|
||||
|
||||
Breaking down the permissions definition differs slightly between applications and every other resource type in Argo CD.
|
||||
|
||||
* All resources *except* applications permissions (see next bullet):
|
||||
* All resources *except* application-specific permissions (see next bullet):
|
||||
|
||||
`p, <role/user/group>, <resource>, <action>, <object>`
|
||||
|
||||
* Applications (which belong to an AppProject):
|
||||
* Applications, logs, and exec (which belong to an AppProject):
|
||||
|
||||
`p, <role/user/group>, <resource>, <action>, <appproject>/<object>`
|
||||
|
||||
@@ -30,7 +30,29 @@ Breaking down the permissions definition differs slightly between applications a
|
||||
|
||||
Resources: `clusters`, `projects`, `applications`, `repositories`, `certificates`, `accounts`, `gpgkeys`, `logs`, `exec`
|
||||
|
||||
Actions: `get`, `create`, `update`, `delete`, `sync`, `override`, `action`
|
||||
Actions: `get`, `create`, `update`, `delete`, `sync`, `override`,
|
||||
`action/<group/kind/action-name>`
|
||||
|
||||
#### Application resources
|
||||
|
||||
The resource path for application objects is of the form
|
||||
`<project-name>/<application-name>`.
|
||||
|
||||
Delete access to sub-resources of a project, such as a rollout or a pod, cannot
|
||||
be managed granularly. `<project-name>/<application-name>` grants access to all
|
||||
subresources of an application.
|
||||
|
||||
#### The `action` action
|
||||
|
||||
The `action` action corresponds to either built-in resource customizations defined
|
||||
[in the Argo CD repository](https://github.com/argoproj/argo-cd/search?q=filename%3Aaction.lua+path%3Aresource_customizations),
|
||||
or to [custom resource actions](resource_actions.md#custom-resource-actions) defined by you.
|
||||
The `action` path is of the form `action/<api-group>/<Kind>/<action-name>`. For
|
||||
example, a resource customization path
|
||||
`resource_customizations/extensions/DaemonSet/actions/restart/action.lua`
|
||||
corresponds to the `action` path `action/extensions/DaemonSet/restart`. You can
|
||||
also use glob patterns in the action path: `action/*` (or regex patterns if you have
|
||||
[enabled the `regex` match mode](https://github.com/argoproj/argo-cd/blob/master/docs/operator-manual/argocd-rbac-cm.yaml)).
|
||||
|
||||
#### `exec` resource
|
||||
|
||||
@@ -42,7 +64,8 @@ they have `create` privileges. If the Pod mounts a ServiceAccount token (which i
|
||||
then the user effectively has the same privileges as that ServiceAccount.
|
||||
|
||||
The exec feature is disabled entirely by default. To enable it, set the `exec.enabled` key to "true" on the argocd-cm
|
||||
ConfigMap. You will also need to add the following to the argocd-api-server Role or ClusterRole.
|
||||
ConfigMap. You will also need to add the following to the argocd-api-server Role (if you're using Argo CD in namespaced
|
||||
mode) or ClusterRole (if you're using Argo CD in cluster mode).
|
||||
|
||||
```yaml
|
||||
- apiGroups:
|
||||
@@ -78,7 +101,7 @@ data:
|
||||
p, role:org-admin, repositories, update, *, allow
|
||||
p, role:org-admin, repositories, delete, *, allow
|
||||
p, role:org-admin, logs, get, *, allow
|
||||
p, role:org-admin, exec, get, *, allow
|
||||
p, role:org-admin, exec, create, */*, allow
|
||||
|
||||
g, your-github-org:your-team, role:org-admin
|
||||
```
|
||||
@@ -94,12 +117,12 @@ p, role:staging-db-admins, applications, override, staging-db-admins/*, allow
|
||||
p, role:staging-db-admins, applications, sync, staging-db-admins/*, allow
|
||||
p, role:staging-db-admins, applications, update, staging-db-admins/*, allow
|
||||
p, role:staging-db-admins, logs, get, staging-db-admins/*, allow
|
||||
p, role:staging-db-admins, exec, get, staging-db-admins/*, allow
|
||||
p, role:staging-db-admins, exec, create, staging-db-admins/*, allow
|
||||
p, role:staging-db-admins, projects, get, staging-db-admins, allow
|
||||
g, db-admins, role:staging-db-admins
|
||||
```
|
||||
|
||||
This example defines a *role* called `staging-db-admins` with *eight permissions* that allow that role to perform the *actions* (`create`/`delete`/`get`/`override`/`sync`/`update` applications, `get` logs, `get` exec and `get` appprojects) against `*` (all) objects in the `staging-db-admins` Argo CD AppProject.
|
||||
This example defines a *role* called `staging-db-admins` with *eight permissions* that allow that role to perform the *actions* (`create`/`delete`/`get`/`override`/`sync`/`update` applications, `get` logs, `create` exec and `get` appprojects) against `*` (all) objects in the `staging-db-admins` Argo CD AppProject.
|
||||
|
||||
## Anonymous Access
|
||||
|
||||
|
||||
@@ -210,4 +210,9 @@ Argo CD logs payloads of most API requests except request that are considered se
|
||||
can be found in [server/server.go](https://github.com/argoproj/argo-cd/blob/abba8dddce8cd897ba23320e3715690f465b4a95/server/server.go#L516).
|
||||
|
||||
Argo CD does not log IP addresses of clients requesting API endpoints, since the API server is typically behind a proxy. Instead, it is recommended
|
||||
to configure IP addresses logging in the proxy server that sits in front of the API server.
|
||||
to configure IP addresses logging in the proxy server that sits in front of the API server.
|
||||
|
||||
## ApplicationSets
|
||||
|
||||
Argo CD's ApplicationSets feature has its own [security considerations](./applicationset/Security.md). Be aware of those
|
||||
issues before using ApplicationSets.
|
||||
|
||||
@@ -37,6 +37,7 @@ argocd-application-controller [flags]
|
||||
--metrics-port int Start metrics server on given port (default 8082)
|
||||
-n, --namespace string If present, the namespace scope for this CLI request
|
||||
--operation-processors int Number of application operation processors (default 10)
|
||||
--otlp-address string OpenTelemetry collector address to send traces to
|
||||
--password string Password for basic authentication to the API server
|
||||
--redis string Redis server hostname and port (e.g. argocd-redis:6379).
|
||||
--redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation.
|
||||
|
||||
@@ -6,8 +6,11 @@ The Argo CD Notifications and ApplicationSet are part of Argo CD now. You no lon
|
||||
The Notifications and ApplicationSet components are bundled into default Argo CD installation manifests.
|
||||
|
||||
The bundled manifests are drop-in replacements for the previous versions. If you are using Kustomize to bundle the manifests together then just
|
||||
remove references to https://github.com/argoproj-labs/argocd-notifications and https://github.com/argoproj-labs/applicationset. No action is required
|
||||
if you are using `kubectl apply`.
|
||||
remove references to https://github.com/argoproj-labs/argocd-notifications and https://github.com/argoproj-labs/applicationset.
|
||||
|
||||
If you are using [the argocd-notifications helm chart](https://github.com/argoproj/argo-helm/tree/argocd-notifications-1.8.1/charts/argocd-notifications), you can move the chart [values](https://github.com/argoproj/argo-helm/blob/argocd-notifications-1.8.1/charts/argocd-notifications/values.yaml) to the `notifications` section of the argo-cd chart [values](https://github.com/argoproj/argo-helm/blob/main/charts/argo-cd/values.yaml#L2152). Although most values remain as is, for details please look up the values that are relevant to you.
|
||||
|
||||
No action is required if you are using `kubectl apply`.
|
||||
|
||||
## Configure Additional Argo CD Binaries
|
||||
|
||||
@@ -42,4 +45,4 @@ Note that bundled Kustomize version has been upgraded from 4.2.0 to 4.4.1.
|
||||
|
||||
## Upgraded Helm Version
|
||||
|
||||
Note that bundled Helm version has been upgraded from 3.7.1 to 3.8.0.
|
||||
Note that bundled Helm version has been upgraded from 3.7.1 to 3.8.0.
|
||||
|
||||
@@ -2,20 +2,19 @@
|
||||
|
||||
## KSonnet support is removed
|
||||
|
||||
The [https://ksonnet.io/] had been deprecated in [2019](https://github.com/ksonnet/ksonnet/pull/914/files) and no longer maintained.
|
||||
The time has come to remove it from the ArgoCD.
|
||||
Ksonnet was deprecated in [2019](https://github.com/ksonnet/ksonnet/pull/914/files) and is no longer maintained.
|
||||
The time has come to remove it from the Argo CD.
|
||||
|
||||
## Helm 2 support is removed
|
||||
|
||||
Helm 2 is not been officially supported since [Nov 2020](https://helm.sh/blog/helm-2-becomes-unsupported/). In order to ensure a smooth transition
|
||||
Helm 2 support was preserved in the Argo CD. We feel that Helm 3 is stable and it is time to drop Helm 2 support.
|
||||
|
||||
Helm 2 has not been officially supported since [Nov 2020](https://helm.sh/blog/helm-2-becomes-unsupported/). In order to ensure a smooth transition,
|
||||
Helm 2 support was preserved in the Argo CD. We feel that Helm 3 is stable, and it is time to drop Helm 2 support.
|
||||
|
||||
## Configure RBAC to account for new `exec` resource
|
||||
|
||||
2.4 introduces a new `exec` [RBAC resource](https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/#rbac-resources-and-actions).
|
||||
|
||||
When you upgrade to 2.4, RBAC policies with `*` in the resource field and `create` or `*` in the verb field will automatically grant the `exec` privilege.
|
||||
When you upgrade to 2.4, RBAC policies with `*` in the resource field and `create` or `*` in the action field will automatically grant the `exec` privilege.
|
||||
|
||||
To avoid granting the new privilege, replace the existing policy with a list of new policies explicitly listing the old resources.
|
||||
|
||||
@@ -42,12 +41,61 @@ p, role: org-admin, accounts, create, my-proj/*, allow
|
||||
p, role: org-admin, gpgkeys, create, my-proj/*, allow
|
||||
```
|
||||
|
||||
## Remove the shared volume from any sidecar plugins
|
||||
## Enable logs RBAC enforcement
|
||||
|
||||
2.4 introduced `logs` as a new RBAC resource. In 2.3, users with `applications, get` access automatically get logs
|
||||
access. In 2.5, you will have to explicitly grant `logs, get` access. Logs RBAC enforcement can be enabled with a flag
|
||||
in 2.4. We recommend enabling the flag now for an easier upgrade experience in 2.5.
|
||||
|
||||
To enabled logs RBAC enforcement, add this to your argocd-cm ConfigMap:
|
||||
|
||||
```yaml
|
||||
server.rbac.log.enforce.enable: "true"
|
||||
```
|
||||
|
||||
If you want to allow the same users to continue to have logs access, just find every line that grants
|
||||
`applications, get` access and also grant `logs, get`.
|
||||
|
||||
### Example
|
||||
|
||||
Old:
|
||||
|
||||
```csv
|
||||
p, role:staging-db-admins, applications, get, staging-db-admins/*, allow
|
||||
|
||||
p, role:test-db-admins, applications, *, staging-db-admins/*, allow
|
||||
```
|
||||
|
||||
New:
|
||||
|
||||
```csv
|
||||
p, role:staging-db-admins, applications, get, staging-db-admins/*, allow
|
||||
p, role:staging-db-admins, logs, get, staging-db-admins/*, allow
|
||||
|
||||
p, role:test-db-admins, applications, *, staging-db-admins/*, allow
|
||||
p, role:test-db-admins, logs, get, staging-db-admins/*, allow
|
||||
```
|
||||
|
||||
## Known UI issue
|
||||
|
||||
Currently, upon pressing the "LOGS" tab in pod view by users who don't have an explicit allow get logs policy, the red "unable to load data: Internal error" is received in the bottom of the screen, and "Failed to load data, please try again" is displayed.
|
||||
|
||||
## Test repo-server with its new dedicated Service Account
|
||||
|
||||
As a security enhancement, the argocd-repo-server Deployment uses its own Service Account instead of `default`.
|
||||
|
||||
If you have a custom environment that might depend on repo-server using the `default` Service Account (such as a plugin
|
||||
that uses the Service Account for auth), be sure to test before deploying the 2.4 upgrade to production.
|
||||
|
||||
## Plugins
|
||||
|
||||
### Remove the shared volume from any sidecar plugins
|
||||
|
||||
As a security enhancement, [sidecar plugins](../../user-guide/config-management-plugins.md#option-2-configure-plugin-via-sidecar)
|
||||
no longer share the /tmp directory with the repo-server.
|
||||
|
||||
If you have one or more sidecar plugins enabled, remove the /tmp volume mount from the plugin container definition.
|
||||
If you have one or more sidecar plugins enabled, replace the /tmp volume mount for each sidecar to use a volume specific
|
||||
to each plugin.
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
@@ -60,7 +108,47 @@ spec:
|
||||
containers:
|
||||
- name: your-plugin-name
|
||||
volumeMounts:
|
||||
# Remove the next two lines:
|
||||
- mountPath: /tmp
|
||||
name: tmp
|
||||
name: your-plugin-name-tmp
|
||||
volumes:
|
||||
# Add this volume.
|
||||
- name: your-plugin-name-tmp
|
||||
emptyDir: {}
|
||||
```
|
||||
|
||||
### Update plugins to use newly-prefixed environment variables
|
||||
|
||||
If you use plugins that depend on user-supplied environment variables, then they must be updated to be compatible with
|
||||
Argo CD 2.4. Here is an example of user-supplied environment variables in the `plugin` section of an Application spec:
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
spec:
|
||||
source:
|
||||
plugin:
|
||||
env:
|
||||
- name: FOO
|
||||
value: bar
|
||||
```
|
||||
|
||||
Going forward, all user-supplied environment variables will be prefixed with `ARGOCD_ENV_` before being sent to the
|
||||
plugin's `init`, `generate`, or `discover` commands. This prevents users from setting potentially-sensitive environment
|
||||
variables.
|
||||
|
||||
If you have written a custom plugin which handles user-provided environment variables, update it to handle the new
|
||||
prefix.
|
||||
|
||||
If you use a third-party plugin which does not explicitly advertise Argo CD 2.4 support, it might not handle the
|
||||
prefixed environment variables. Open an issue with the plugin's authors and confirm support before upgrading to Argo CD
|
||||
2.4.
|
||||
|
||||
### Confirm sidecar plugins have all necessary environment variables
|
||||
|
||||
A bug in < 2.4 caused `init` and `generate` commands to receive environment variables from the main repo-server
|
||||
container, taking precedence over environment variables from the plugin's sidecar.
|
||||
|
||||
Starting in 2.4, sidecar plugins will not receive environment variables from the main repo-server container. Make sure
|
||||
that any environment variables necessary for the sidecar plugin to function are set on the sidecar plugin.
|
||||
|
||||
argocd-cm plugins will continue to receive environment variables from the main repo-server container.
|
||||
|
||||
@@ -50,7 +50,7 @@ If you've never configured this, you'll be redirected straight to this if you tr
|
||||
|
||||
### Configure Argo to use OpenID Connect
|
||||
|
||||
Edit `argo-cm` and add the following `dex.config` to the data section, replacing `clientID` and `clientSecret` with the values you saved before:
|
||||
Edit `argocd-cm` and add the following `dex.config` to the data section, replacing `clientID` and `clientSecret` with the values you saved before:
|
||||
|
||||
```yaml
|
||||
data:
|
||||
@@ -113,7 +113,7 @@ data:
|
||||
|
||||
### Configure Argo to use the new Google SAML App
|
||||
|
||||
Edit `argo-cm` and add the following `dex.config` to the data section, replacing the `caData`, `argocd.example.com`, `sso-url`, and optionally `google-entity-id` with your values from the Google SAML App:
|
||||
Edit `argocd-cm` and add the following `dex.config` to the data section, replacing the `caData`, `argocd.example.com`, `sso-url`, and optionally `google-entity-id` with your values from the Google SAML App:
|
||||
|
||||
```yaml
|
||||
data:
|
||||
@@ -211,7 +211,7 @@ Go through the same steps as in [OpenID Connect using Dex](#openid-connect-using
|
||||
defaultMode: 420
|
||||
secretName: argocd-google-groups-json
|
||||
|
||||
3. Edit `argo-cm` and add the following `dex.config` to the data section, replacing `clientID` and `clientSecret` with the values you saved before, `adminEmail` with the address for the admin user you're going to impersonate, and editing `redirectURI` with your Argo CD domain:
|
||||
3. Edit `argocd-cm` and add the following `dex.config` to the data section, replacing `clientID` and `clientSecret` with the values you saved before, `adminEmail` with the address for the admin user you're going to impersonate, and editing `redirectURI` with your Argo CD domain:
|
||||
|
||||
dex.config: |
|
||||
connectors:
|
||||
|
||||
@@ -4,6 +4,10 @@ The [ApplicationSet controller](../operator-manual/applicationset/index.md) is a
|
||||
|
||||
The set of tools provided by the ApplicationSet controller may also be used to allow developers (without access to the Argo CD namespace) to independently create Applications without cluster-administrator intervention.
|
||||
|
||||
!!! warning
|
||||
Be aware of the [security implications](../operator-manual/applicationset/Security.md) before allowing developers to
|
||||
create Applications via ApplicationSets.
|
||||
|
||||
The ApplicationSet controller is installed alongside Argo CD (within the same namespace), and the controller automatically generates Argo CD Applications based on the contents of a new `ApplicationSet` Custom Resource (CR).
|
||||
|
||||
Here is an example of an `ApplicationSet` resource that can be used to target an Argo CD Application to multiple clusters:
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
Argo CD allows integrating more config management tools using config management plugins.
|
||||
|
||||
!!! warning
|
||||
Plugins are granted a level of trust in the Argo CD system, so it is important to implement plugins securely. Argo
|
||||
CD administrators should only install plugins from trusted sources, and they should audit plugins to weigh their
|
||||
particular risks and benefits.
|
||||
|
||||
## Installing a CMP
|
||||
|
||||
There are two ways to install a Config Management Plugin (CMP):
|
||||
@@ -62,8 +67,6 @@ spec:
|
||||
command: [sh, -c, 'echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"']
|
||||
discover:
|
||||
fileName: "./subdir/s*.yaml"
|
||||
allowConcurrency: true
|
||||
lockRepo: false
|
||||
```
|
||||
|
||||
!!! note
|
||||
@@ -85,9 +88,6 @@ If `discover.fileName` is not provided, the `discover.find.command` is executed
|
||||
application repository is supported by the plugin or not. The `find` command should return a non-error exit code
|
||||
and produce output to stdout when the application source type is supported.
|
||||
|
||||
If your plugin makes use of `git` (e.g. `git crypt`), it is advised to set `lockRepo` to `true` so that your plugin will have exclusive access to the
|
||||
repository at the time it is executed. Otherwise, two applications synced at the same time may result in a race condition and sync failure.
|
||||
|
||||
#### 2. Place the plugin configuration file in the sidecar
|
||||
|
||||
Argo CD expects the plugin configuration file to be located at `/home/argocd/cmp-server/config/plugin.yaml` in the sidecar.
|
||||
@@ -114,8 +114,6 @@ data:
|
||||
command: [sh, -c, 'echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"']
|
||||
discover:
|
||||
fileName: "./subdir/s*.yaml"
|
||||
allowConcurrency: true
|
||||
lockRepo: false
|
||||
```
|
||||
|
||||
#### 3. Register the plugin sidecar
|
||||
@@ -140,10 +138,16 @@ containers:
|
||||
- mountPath: /home/argocd/cmp-server/config/plugin.yaml
|
||||
subPath: plugin.yaml
|
||||
name: cmp-plugin
|
||||
volumes:
|
||||
- configMap:
|
||||
name: cmp-plugin
|
||||
# Starting with v2.4, do NOT mount the same tmp volume as the repo-server container. The filesystem separation helps
|
||||
# mitigate path traversal attacks.
|
||||
- mountPath: /tmp
|
||||
name: cmp-tmp
|
||||
volumes:
|
||||
- configMap:
|
||||
name: cmp-plugin
|
||||
name: cmp-plugin
|
||||
- emptyDir: {}
|
||||
name: cmp-tmp
|
||||
```
|
||||
|
||||
!!! important "Double-check these items"
|
||||
@@ -155,7 +159,7 @@ containers:
|
||||
|
||||
CMP commands have access to
|
||||
|
||||
1. The system environment variables
|
||||
1. The system environment variables (of the repo-server container for argocd-cm plugins or of the sidecar for sidecar plugins)
|
||||
2. [Standard build environment](build-environment.md)
|
||||
3. Variables in the application spec (References to system and build variables will get interpolated in the variables' values):
|
||||
|
||||
@@ -172,6 +176,19 @@ spec:
|
||||
value: test-$ARGOCD_APP_REVISION
|
||||
```
|
||||
|
||||
!!! note
|
||||
The `discover.command` command only has access to the above environment starting with v2.4.
|
||||
|
||||
> v2.4
|
||||
|
||||
Before reaching the `init.command`, `generate.command`, and `discover.command` commands, Argo CD prefixes all
|
||||
user-supplied environment variables (#3 above) with `ARGOCD_ENV_`. This prevents users from directly setting
|
||||
potentially-sensitive environment variables.
|
||||
|
||||
If your plugin was written before 2.4 and depends on user-supplied environment variables, then you will need to update
|
||||
your plugin's behavior to work with 2.4. If you use a third-party plugin, make sure they explicitly advertise support
|
||||
for 2.4.
|
||||
|
||||
## Using a CMP
|
||||
|
||||
If your CMP is defined in the `argocd-cm` ConfigMap, you can create a new Application using the CLI. Replace
|
||||
|
||||
7
go.mod
7
go.mod
@@ -9,8 +9,8 @@ require (
|
||||
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible
|
||||
github.com/alicebob/miniredis/v2 v2.14.2
|
||||
github.com/argoproj/gitops-engine v0.6.1-0.20220328190556-73bcea9c8c8f
|
||||
github.com/argoproj/notifications-engine v0.3.1-0.20220322174744-ac18ca10234c
|
||||
github.com/argoproj/gitops-engine v0.7.0
|
||||
github.com/argoproj/notifications-engine v0.3.1-0.20220430155844-567361917320
|
||||
github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0
|
||||
github.com/aws/aws-sdk-go v1.38.49
|
||||
github.com/bombsimon/logrusr/v2 v2.0.1
|
||||
@@ -75,7 +75,7 @@ require (
|
||||
github.com/whilp/git-urls v0.0.0-20191001220047-6db9661140c0
|
||||
github.com/xanzy/go-gitlab v0.60.0
|
||||
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
@@ -237,6 +237,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/PagerDuty/go-pagerduty v1.5.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
|
||||
|
||||
13
go.sum
13
go.sum
@@ -104,6 +104,8 @@ github.com/Microsoft/hcsshim v0.8.22/go.mod h1:91uVCVzvX2QD16sMCenoxxXo6L1wJnLMX
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PagerDuty/go-pagerduty v1.5.0 h1:/p8FGD32G8HGm7MQIjlTPTGXRJ62Qkm8Lmt5BcUVJOo=
|
||||
github.com/PagerDuty/go-pagerduty v1.5.0/go.mod h1:txr8VbObXdk2RkqF+C2an4qWssdGY99fK26XYUDjh+4=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
@@ -144,10 +146,10 @@ github.com/antonmedv/expr v1.8.9/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmH
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/appscode/go v0.0.0-20190808133642-1d4ef1f1c1e0/go.mod h1:iy07dV61Z7QQdCKJCIvUoDL21u6AIceRhZzyleh2ymc=
|
||||
github.com/argoproj/gitops-engine v0.6.1-0.20220328190556-73bcea9c8c8f h1:3x8pG690gbZtGK2G/dlL7b243t/WyyeDT7OUs2n76Nk=
|
||||
github.com/argoproj/gitops-engine v0.6.1-0.20220328190556-73bcea9c8c8f/go.mod h1:pRgVpLW7pZqf7n3COJ7UcDepk4cI61LAcJd64Q3Jq/c=
|
||||
github.com/argoproj/notifications-engine v0.3.1-0.20220322174744-ac18ca10234c h1:n/5BIocdWYtp1qC8/GFgUUV62I+gln54KFZZLgczwDc=
|
||||
github.com/argoproj/notifications-engine v0.3.1-0.20220322174744-ac18ca10234c/go.mod h1:QF4tr3wfWOnhkKSaRpx7k/KEErQAh8iwKQ2pYFu/SfA=
|
||||
github.com/argoproj/gitops-engine v0.7.0 h1:X6W8VP9bWTe74wWxAV3i8KZ0yBmre5DU8g+GWH09FCo=
|
||||
github.com/argoproj/gitops-engine v0.7.0/go.mod h1:pRgVpLW7pZqf7n3COJ7UcDepk4cI61LAcJd64Q3Jq/c=
|
||||
github.com/argoproj/notifications-engine v0.3.1-0.20220430155844-567361917320 h1:XDjtTfccs4rSOT1n+i1zV9RpxQdKky1b4YBic16E0qY=
|
||||
github.com/argoproj/notifications-engine v0.3.1-0.20220430155844-567361917320/go.mod h1:R3zlopt+/juYlebQc9Jarn9vBQ2xZruWOWjUNkfGY9M=
|
||||
github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0 h1:Cfp7rO/HpVxnwlRqJe0jHiBbZ77ZgXhB6HWlYD02Xdc=
|
||||
github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0/go.mod h1:ra+bQPmbVAoEL+gYSKesuigt4m49i3Qa3mE/xQcjCiA=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
@@ -1228,8 +1230,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
||||
@@ -119,6 +119,12 @@ spec:
|
||||
name: argocd-cmd-params-cm
|
||||
key: controller.default.cache.expiration
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: otlp.address
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
@@ -178,4 +184,4 @@ spec:
|
||||
- key: tls.key
|
||||
path: tls.key
|
||||
- key: ca.crt
|
||||
path: ca.crt
|
||||
path: ca.crt
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v2.4.0-rc5
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -15,6 +15,7 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
spec:
|
||||
serviceAccountName: argocd-repo-server
|
||||
automountServiceAccountToken: false
|
||||
containers:
|
||||
- name: argocd-repo-server
|
||||
@@ -98,6 +99,12 @@ spec:
|
||||
name: argocd-cmd-params-cm
|
||||
key: reposerver.default.cache.expiration
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: otlp.address
|
||||
optional: true
|
||||
- name: HELM_CACHE_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_CONFIG_HOME
|
||||
|
||||
8
manifests/base/repo-server/argocd-repo-server-sa.yaml
Normal file
8
manifests/base/repo-server/argocd-repo-server-sa.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
app.kubernetes.io/part-of: argocd
|
||||
app.kubernetes.io/component: repo-server
|
||||
name: argocd-repo-server
|
||||
@@ -2,6 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- argocd-repo-server-sa.yaml
|
||||
- argocd-repo-server-deployment.yaml
|
||||
- argocd-repo-server-service.yaml
|
||||
- argocd-repo-server-network-policy.yaml
|
||||
- argocd-repo-server-network-policy.yaml
|
||||
|
||||
@@ -178,6 +178,12 @@ spec:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.http.cookie.maxnumber
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: otlp.address
|
||||
optional: true
|
||||
volumeMounts:
|
||||
- name: ssh-known-hosts
|
||||
mountPath: /app/config/ssh
|
||||
|
||||
@@ -9017,6 +9017,15 @@ metadata:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
name: argocd-redis
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: repo-server
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
app.kubernetes.io/part-of: argocd
|
||||
name: argocd-repo-server
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
@@ -9376,7 +9385,7 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -9583,13 +9592,19 @@ spec:
|
||||
key: reposerver.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: HELM_CACHE_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_CONFIG_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -9638,7 +9653,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -9650,6 +9665,7 @@ spec:
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
name: var-files
|
||||
serviceAccountName: argocd-repo-server
|
||||
volumes:
|
||||
- configMap:
|
||||
name: argocd-ssh-known-hosts-cm
|
||||
@@ -9818,7 +9834,13 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -12,4 +12,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v2.4.0-rc5
|
||||
|
||||
@@ -11,7 +11,7 @@ patchesStrategicMerge:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v2.4.0-rc5
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/applicationset-controller
|
||||
|
||||
@@ -9042,6 +9042,15 @@ metadata:
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: repo-server
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
app.kubernetes.io/part-of: argocd
|
||||
name: argocd-repo-server
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: server
|
||||
@@ -10311,7 +10320,7 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -10408,7 +10417,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -10448,7 +10457,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -10681,13 +10690,19 @@ spec:
|
||||
key: reposerver.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: HELM_CACHE_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_CONFIG_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -10736,7 +10751,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -10748,6 +10763,7 @@ spec:
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
name: var-files
|
||||
serviceAccountName: argocd-repo-server
|
||||
volumes:
|
||||
- configMap:
|
||||
name: argocd-ssh-known-hosts-cm
|
||||
@@ -10976,7 +10992,13 @@ spec:
|
||||
key: server.http.cookie.maxnumber
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
- name: ARGOCD_SERVER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -11178,7 +11200,13 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -51,6 +51,15 @@ metadata:
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: repo-server
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
app.kubernetes.io/part-of: argocd
|
||||
name: argocd-repo-server
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: server
|
||||
@@ -1235,7 +1244,7 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1332,7 +1341,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1372,7 +1381,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1605,13 +1614,19 @@ spec:
|
||||
key: reposerver.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: HELM_CACHE_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_CONFIG_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1660,7 +1675,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -1672,6 +1687,7 @@ spec:
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
name: var-files
|
||||
serviceAccountName: argocd-repo-server
|
||||
volumes:
|
||||
- configMap:
|
||||
name: argocd-ssh-known-hosts-cm
|
||||
@@ -1900,7 +1916,13 @@ spec:
|
||||
key: server.http.cookie.maxnumber
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
- name: ARGOCD_SERVER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2102,7 +2124,13 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -9033,6 +9033,15 @@ metadata:
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: repo-server
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
app.kubernetes.io/part-of: argocd
|
||||
name: argocd-repo-server
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: server
|
||||
@@ -9683,7 +9692,7 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -9780,7 +9789,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -9820,7 +9829,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -10022,13 +10031,19 @@ spec:
|
||||
key: reposerver.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: HELM_CACHE_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_CONFIG_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -10077,7 +10092,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -10089,6 +10104,7 @@ spec:
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
name: var-files
|
||||
serviceAccountName: argocd-repo-server
|
||||
volumes:
|
||||
- configMap:
|
||||
name: argocd-ssh-known-hosts-cm
|
||||
@@ -10313,7 +10329,13 @@ spec:
|
||||
key: server.http.cookie.maxnumber
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
- name: ARGOCD_SERVER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -10509,7 +10531,13 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -42,6 +42,15 @@ metadata:
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: repo-server
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
app.kubernetes.io/part-of: argocd
|
||||
name: argocd-repo-server
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/component: server
|
||||
@@ -607,7 +616,7 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -704,7 +713,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -744,7 +753,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -946,13 +955,19 @@ spec:
|
||||
key: reposerver.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: HELM_CACHE_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_CONFIG_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1001,7 +1016,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -1013,6 +1028,7 @@ spec:
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
name: var-files
|
||||
serviceAccountName: argocd-repo-server
|
||||
volumes:
|
||||
- configMap:
|
||||
name: argocd-ssh-known-hosts-cm
|
||||
@@ -1237,7 +1253,13 @@ spec:
|
||||
key: server.http.cookie.maxnumber
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
- name: ARGOCD_SERVER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -1433,7 +1455,13 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
- name: ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.0-rc5
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -64,6 +64,7 @@ nav:
|
||||
- operator-manual/notifications/services/mattermost.md
|
||||
- operator-manual/notifications/services/opsgenie.md
|
||||
- operator-manual/notifications/services/overview.md
|
||||
- operator-manual/notifications/services/pagerduty.md
|
||||
- operator-manual/notifications/services/pushover.md
|
||||
- operator-manual/notifications/services/rocketchat.md
|
||||
- operator-manual/notifications/services/slack.md
|
||||
@@ -75,6 +76,7 @@ nav:
|
||||
- Introduction: operator-manual/applicationset/index.md
|
||||
- Installations: operator-manual/applicationset/Getting-Started.md
|
||||
- Use Cases: operator-manual/applicationset/Use-Cases.md
|
||||
- Security: operator-manual/applicationset/Security.md
|
||||
- How ApplicationSet controller interacts with Argo CD: operator-manual/applicationset/Argo-CD-Integration.md
|
||||
- Generators:
|
||||
- operator-manual/applicationset/Generators.md
|
||||
|
||||
@@ -238,9 +238,9 @@ func (m *RevisionMetadataQuery) GetRevision() string {
|
||||
// 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,req,name=resourceNamespace" json:"resourceNamespace,omitempty"`
|
||||
ResourceName *string `protobuf:"bytes,3,req,name=resourceName" json:"resourceName,omitempty"`
|
||||
ResourceUID *string `protobuf:"bytes,4,req,name=resourceUID" json:"resourceUID,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"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -951,7 +951,7 @@ func (m *ApplicationRollbackRequest) GetPrune() bool {
|
||||
|
||||
type ApplicationResourceRequest struct {
|
||||
Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
|
||||
Namespace *string `protobuf:"bytes,2,req,name=namespace" json:"namespace,omitempty"`
|
||||
Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"`
|
||||
ResourceName *string `protobuf:"bytes,3,req,name=resourceName" json:"resourceName,omitempty"`
|
||||
Version *string `protobuf:"bytes,4,req,name=version" json:"version,omitempty"`
|
||||
Group *string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"`
|
||||
@@ -1038,7 +1038,7 @@ func (m *ApplicationResourceRequest) GetKind() string {
|
||||
|
||||
type ApplicationResourcePatchRequest struct {
|
||||
Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
|
||||
Namespace *string `protobuf:"bytes,2,req,name=namespace" json:"namespace,omitempty"`
|
||||
Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"`
|
||||
ResourceName *string `protobuf:"bytes,3,req,name=resourceName" json:"resourceName,omitempty"`
|
||||
Version *string `protobuf:"bytes,4,req,name=version" json:"version,omitempty"`
|
||||
Group *string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"`
|
||||
@@ -1141,7 +1141,7 @@ func (m *ApplicationResourcePatchRequest) GetPatchType() string {
|
||||
|
||||
type ApplicationResourceDeleteRequest struct {
|
||||
Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
|
||||
Namespace *string `protobuf:"bytes,2,req,name=namespace" json:"namespace,omitempty"`
|
||||
Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"`
|
||||
ResourceName *string `protobuf:"bytes,3,req,name=resourceName" json:"resourceName,omitempty"`
|
||||
Version *string `protobuf:"bytes,4,req,name=version" json:"version,omitempty"`
|
||||
Group *string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"`
|
||||
@@ -1244,7 +1244,7 @@ func (m *ApplicationResourceDeleteRequest) GetOrphan() bool {
|
||||
|
||||
type ResourceActionRunRequest struct {
|
||||
Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
|
||||
Namespace *string `protobuf:"bytes,2,req,name=namespace" json:"namespace,omitempty"`
|
||||
Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"`
|
||||
ResourceName *string `protobuf:"bytes,3,req,name=resourceName" json:"resourceName,omitempty"`
|
||||
Version *string `protobuf:"bytes,4,req,name=version" json:"version,omitempty"`
|
||||
Group *string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"`
|
||||
@@ -1433,7 +1433,7 @@ func (m *ApplicationResourceResponse) GetManifest() string {
|
||||
|
||||
type ApplicationPodLogsQuery struct {
|
||||
Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
|
||||
Namespace *string `protobuf:"bytes,2,req,name=namespace" json:"namespace,omitempty"`
|
||||
Namespace *string `protobuf:"bytes,2,opt,name=namespace" json:"namespace,omitempty"`
|
||||
PodName *string `protobuf:"bytes,3,opt,name=podName" json:"podName,omitempty"`
|
||||
Container *string `protobuf:"bytes,4,opt,name=container" json:"container,omitempty"`
|
||||
SinceSeconds *int64 `protobuf:"varint,5,opt,name=sinceSeconds" json:"sinceSeconds,omitempty"`
|
||||
@@ -2100,147 +2100,147 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_df6e82b174b5eaec = []byte{
|
||||
// 2237 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x8f, 0x1c, 0x47,
|
||||
0x15, 0x57, 0xed, 0xe7, 0xcc, 0x1b, 0x7f, 0x56, 0x62, 0xd3, 0x69, 0xaf, 0x37, 0xab, 0xf2, 0xd7,
|
||||
0x7a, 0xed, 0x9d, 0xb1, 0x07, 0x0b, 0x39, 0x1b, 0x10, 0x38, 0x89, 0xd9, 0x38, 0xec, 0x6e, 0x4c,
|
||||
0xaf, 0x8d, 0x51, 0x38, 0x40, 0xa5, 0xbb, 0x66, 0xb6, 0xd9, 0x9e, 0xae, 0x76, 0x77, 0xcf, 0x58,
|
||||
0x23, 0xe3, 0x4b, 0x10, 0x37, 0x04, 0x12, 0xe4, 0x80, 0x10, 0x42, 0x88, 0x28, 0x12, 0x37, 0xe0,
|
||||
0x12, 0x21, 0x71, 0x81, 0x0b, 0x1f, 0x12, 0x07, 0x04, 0xff, 0x00, 0x58, 0x9c, 0xb8, 0x70, 0xe5,
|
||||
0x88, 0xaa, 0xba, 0xba, 0xbb, 0x7a, 0x76, 0xa6, 0x67, 0xcc, 0x4e, 0x14, 0xdf, 0xfa, 0xd5, 0x54,
|
||||
0xbd, 0xf7, 0xab, 0xf7, 0x59, 0xef, 0x69, 0xe0, 0x7c, 0xc4, 0xc2, 0x1e, 0x0b, 0x1b, 0x34, 0x08,
|
||||
0x3c, 0xd7, 0xa6, 0xb1, 0xcb, 0x7d, 0xfd, 0xbb, 0x1e, 0x84, 0x3c, 0xe6, 0xb8, 0xa6, 0x2d, 0x99,
|
||||
0x4b, 0x6d, 0xce, 0xdb, 0x1e, 0x6b, 0xd0, 0xc0, 0x6d, 0x50, 0xdf, 0xe7, 0xb1, 0x5c, 0x8e, 0x92,
|
||||
0xad, 0x26, 0xd9, 0xbf, 0x19, 0xd5, 0x5d, 0x2e, 0x7f, 0xb5, 0x79, 0xc8, 0x1a, 0xbd, 0xeb, 0x8d,
|
||||
0x36, 0xf3, 0x59, 0x48, 0x63, 0xe6, 0xa8, 0x3d, 0x37, 0xf2, 0x3d, 0x1d, 0x6a, 0xef, 0xb9, 0x3e,
|
||||
0x0b, 0xfb, 0x8d, 0x60, 0xbf, 0x2d, 0x16, 0xa2, 0x46, 0x87, 0xc5, 0x74, 0xd8, 0xa9, 0xad, 0xb6,
|
||||
0x1b, 0xef, 0x75, 0xdf, 0xad, 0xdb, 0xbc, 0xd3, 0xa0, 0x61, 0x9b, 0x07, 0x21, 0xff, 0xa6, 0xfc,
|
||||
0x58, 0xb7, 0x9d, 0x46, 0xaf, 0x99, 0x33, 0xd0, 0xef, 0xd2, 0xbb, 0x4e, 0xbd, 0x60, 0x8f, 0x1e,
|
||||
0xe4, 0x76, 0x7b, 0x0c, 0xb7, 0x90, 0x05, 0x5c, 0xe9, 0x46, 0x7e, 0xba, 0x31, 0x0f, 0xfb, 0xda,
|
||||
0x67, 0xc2, 0x86, 0x7c, 0x84, 0xe0, 0xc4, 0xad, 0x5c, 0xde, 0x97, 0xbb, 0x2c, 0xec, 0x63, 0x0c,
|
||||
0x73, 0x3e, 0xed, 0x30, 0x03, 0xad, 0xa0, 0xd5, 0xaa, 0x25, 0xbf, 0xb1, 0x01, 0x8b, 0x21, 0x6b,
|
||||
0x85, 0x2c, 0xda, 0x33, 0x66, 0xe4, 0x72, 0x4a, 0x62, 0x13, 0x2a, 0x42, 0x38, 0xb3, 0xe3, 0xc8,
|
||||
0x98, 0x5d, 0x99, 0x5d, 0xad, 0x5a, 0x19, 0x8d, 0x57, 0xe1, 0x78, 0xc8, 0x22, 0xde, 0x0d, 0x6d,
|
||||
0xf6, 0x15, 0x16, 0x46, 0x2e, 0xf7, 0x8d, 0x39, 0x79, 0x7a, 0x70, 0x59, 0x70, 0x89, 0x98, 0xc7,
|
||||
0xec, 0x98, 0x87, 0xc6, 0xbc, 0xdc, 0x92, 0xd1, 0x02, 0x8f, 0x00, 0x6e, 0x2c, 0x24, 0x78, 0xc4,
|
||||
0x37, 0x79, 0x19, 0xaa, 0x3b, 0xdc, 0x61, 0x23, 0x01, 0x93, 0x4d, 0x38, 0x65, 0xb1, 0x9e, 0x2b,
|
||||
0x98, 0x6f, 0xb3, 0x98, 0x3a, 0x34, 0xa6, 0x83, 0x9b, 0x67, 0xb2, 0xdb, 0x99, 0x50, 0x09, 0xd5,
|
||||
0x66, 0x63, 0x46, 0xae, 0x67, 0x34, 0xf9, 0x05, 0x82, 0x65, 0x4d, 0x45, 0x96, 0x02, 0x7e, 0xbb,
|
||||
0xc7, 0xfc, 0x38, 0x1a, 0xcd, 0xf2, 0x2a, 0x9c, 0x4c, 0xef, 0xb8, 0x43, 0x3b, 0x2c, 0x0a, 0xa8,
|
||||
0xcd, 0x14, 0xef, 0x83, 0x3f, 0x60, 0x02, 0x47, 0xf4, 0x45, 0x63, 0x56, 0x6e, 0x2c, 0xac, 0xe1,
|
||||
0x15, 0xa8, 0xa5, 0xf4, 0xfd, 0x3b, 0x6f, 0x18, 0x73, 0x72, 0x8b, 0xbe, 0x44, 0xde, 0x02, 0x43,
|
||||
0x43, 0xba, 0x4d, 0x7d, 0xb7, 0xc5, 0xa2, 0x78, 0xd2, 0x6b, 0xa3, 0xc2, 0xb5, 0x4f, 0xc1, 0x0b,
|
||||
0xc5, 0x5b, 0x07, 0xdc, 0x8f, 0x18, 0xf9, 0x2d, 0x2a, 0xc8, 0x78, 0x3d, 0x64, 0x34, 0x66, 0x16,
|
||||
0x7b, 0xd8, 0x65, 0x51, 0x8c, 0xf7, 0x41, 0x8f, 0x34, 0x29, 0xaa, 0xd6, 0xbc, 0x53, 0xcf, 0x5d,
|
||||
0xb5, 0x9e, 0xba, 0xaa, 0xfc, 0xf8, 0xba, 0xed, 0xd4, 0x7b, 0xcd, 0x7a, 0xb0, 0xdf, 0xae, 0x0b,
|
||||
0xc7, 0xaf, 0xeb, 0x81, 0x9b, 0x3a, 0x7e, 0x5d, 0x07, 0xa1, 0x73, 0xc7, 0xa7, 0x61, 0xa1, 0x1b,
|
||||
0x44, 0x2c, 0x8c, 0x25, 0xf4, 0x8a, 0xa5, 0x28, 0x71, 0xa9, 0x1e, 0xf5, 0x5c, 0x87, 0xc6, 0x42,
|
||||
0x8d, 0xe2, 0x97, 0x8c, 0x26, 0x1f, 0x14, 0xd1, 0xdf, 0x0f, 0x9c, 0x4f, 0x0a, 0xbd, 0x8e, 0x72,
|
||||
0x66, 0x00, 0x65, 0xaf, 0x00, 0xf2, 0x0d, 0xe6, 0xb1, 0x1c, 0xe4, 0x30, 0x33, 0x1a, 0xb0, 0x68,
|
||||
0xd3, 0xc8, 0xa6, 0x4e, 0xca, 0x2a, 0x25, 0x85, 0x13, 0x06, 0x21, 0x0f, 0x68, 0x5b, 0x72, 0xba,
|
||||
0xcb, 0x3d, 0xd7, 0xee, 0x4b, 0xa5, 0x54, 0xad, 0x83, 0x3f, 0x90, 0x73, 0x50, 0xdb, 0xed, 0xfb,
|
||||
0xf6, 0xdb, 0x81, 0x4c, 0x88, 0xf8, 0x45, 0x98, 0x77, 0x63, 0xd6, 0x89, 0x0c, 0x24, 0xa3, 0x3a,
|
||||
0x21, 0xc8, 0x7f, 0xe7, 0xe0, 0xb4, 0x86, 0x4e, 0x1c, 0x28, 0xc3, 0x56, 0xe2, 0x62, 0xc2, 0x82,
|
||||
0x4e, 0xd8, 0xb7, 0xba, 0xbe, 0xb2, 0x93, 0xa2, 0x84, 0xe0, 0x20, 0xec, 0xfa, 0x4c, 0xe6, 0x8a,
|
||||
0x8a, 0x95, 0x10, 0xb8, 0x05, 0x95, 0x28, 0x16, 0x29, 0xb0, 0xdd, 0x97, 0x19, 0xa2, 0xd6, 0x7c,
|
||||
0xeb, 0x70, 0xb6, 0x11, 0xd0, 0x77, 0x15, 0x47, 0x2b, 0xe3, 0x8d, 0x1f, 0x42, 0x35, 0x8d, 0xa9,
|
||||
0xc8, 0x58, 0x5c, 0x99, 0x5d, 0xad, 0x35, 0x77, 0x0f, 0x2f, 0xe8, 0xed, 0x40, 0xa4, 0x6f, 0x2d,
|
||||
0x7f, 0x58, 0xb9, 0x14, 0xbc, 0x04, 0xd5, 0x8e, 0x0a, 0xd6, 0xc8, 0xa8, 0x48, 0x6d, 0xe7, 0x0b,
|
||||
0xf8, 0xab, 0x30, 0xef, 0xfa, 0x2d, 0x1e, 0x19, 0x55, 0x09, 0xe6, 0xb5, 0xc3, 0x81, 0xb9, 0xe3,
|
||||
0xb7, 0xb8, 0x95, 0x30, 0xc4, 0x0f, 0xe1, 0x68, 0xc8, 0xe2, 0xb0, 0x9f, 0x6a, 0xc1, 0x00, 0xa9,
|
||||
0xd7, 0x2f, 0x1d, 0x4e, 0x82, 0xa5, 0xb3, 0xb4, 0x8a, 0x12, 0xf0, 0x06, 0xd4, 0xa2, 0xdc, 0xc7,
|
||||
0x8c, 0x9a, 0x14, 0x68, 0x14, 0x18, 0x69, 0x3e, 0x68, 0xe9, 0x9b, 0xc9, 0xaf, 0x11, 0x2c, 0x1d,
|
||||
0x88, 0xde, 0xdd, 0x80, 0x95, 0x3a, 0x20, 0x85, 0xb9, 0x28, 0x60, 0xb6, 0x4c, 0xbd, 0xb5, 0xe6,
|
||||
0xf6, 0xd4, 0xc2, 0x59, 0xca, 0x95, 0xac, 0x4b, 0x33, 0x0e, 0x85, 0x4f, 0x69, 0x87, 0xee, 0xd2,
|
||||
0xd8, 0xde, 0x2b, 0x43, 0x2b, 0x5c, 0x5f, 0xec, 0x51, 0x95, 0x22, 0x21, 0x84, 0x7f, 0xc8, 0x8f,
|
||||
0x7b, 0xfd, 0x20, 0x2d, 0x0d, 0xf9, 0x02, 0xf1, 0xc1, 0xd4, 0xd3, 0x0c, 0xf7, 0xbc, 0x77, 0xa9,
|
||||
0xbd, 0x5f, 0x26, 0xe5, 0x18, 0xcc, 0xb8, 0x8e, 0x14, 0x31, 0x6b, 0xcd, 0xb8, 0xce, 0xb3, 0x05,
|
||||
0xa2, 0x78, 0x33, 0x98, 0x43, 0x0a, 0x62, 0x99, 0xc0, 0x25, 0xa8, 0xfa, 0x03, 0x45, 0x30, 0x5f,
|
||||
0x98, 0xa8, 0xf8, 0x19, 0xb0, 0xd8, 0xcb, 0x5e, 0x10, 0xe2, 0xe7, 0x94, 0x14, 0x20, 0xdb, 0x21,
|
||||
0xef, 0x06, 0xea, 0xd9, 0x90, 0x10, 0x02, 0xc5, 0xbe, 0xeb, 0x3b, 0xc6, 0x42, 0x82, 0x42, 0x7c,
|
||||
0x93, 0xff, 0x20, 0x78, 0x79, 0x08, 0xf0, 0xb1, 0x46, 0x79, 0x2e, 0xd0, 0xe7, 0xae, 0xb1, 0x38,
|
||||
0xd2, 0x35, 0x2a, 0x83, 0xae, 0xf1, 0x6f, 0x04, 0x2b, 0x43, 0x6e, 0x3c, 0xbe, 0xa4, 0x3c, 0x37,
|
||||
0x57, 0x6e, 0xf1, 0xd0, 0x66, 0xc6, 0x62, 0xe2, 0x7f, 0x92, 0x10, 0xde, 0xca, 0xc3, 0x60, 0x8f,
|
||||
0xfa, 0x46, 0x25, 0xf1, 0xd6, 0x84, 0x22, 0x7f, 0x41, 0x60, 0xa4, 0x37, 0xbc, 0x65, 0xcb, 0xfb,
|
||||
0x76, 0xfd, 0xe7, 0xff, 0x92, 0xa7, 0x61, 0x81, 0x4a, 0xb4, 0xca, 0xb0, 0x8a, 0x22, 0xdf, 0x41,
|
||||
0x70, 0xa6, 0x78, 0x9d, 0x68, 0xcb, 0x8d, 0xe2, 0xf4, 0x25, 0x86, 0x5b, 0xb0, 0x98, 0xec, 0x4c,
|
||||
0x0a, 0x74, 0xad, 0xb9, 0x75, 0xd8, 0xb4, 0x5d, 0x50, 0x5d, 0xca, 0x9c, 0xbc, 0x02, 0x67, 0x86,
|
||||
0x46, 0xbb, 0x82, 0x61, 0x42, 0x25, 0x2d, 0x55, 0x4a, 0xb9, 0x19, 0x4d, 0xfe, 0x38, 0x5b, 0xcc,
|
||||
0x7e, 0xdc, 0xd9, 0xe2, 0xed, 0x92, 0x37, 0x73, 0xb9, 0x41, 0x0c, 0x58, 0x0c, 0xb8, 0xa3, 0x6c,
|
||||
0x21, 0x5b, 0x10, 0x45, 0x8a, 0x73, 0x36, 0xf7, 0x63, 0x2a, 0x3a, 0x31, 0xd5, 0x60, 0xe4, 0x0b,
|
||||
0xc2, 0x90, 0x91, 0xeb, 0xdb, 0x6c, 0x97, 0xd9, 0xdc, 0x77, 0x22, 0x69, 0x91, 0x59, 0xab, 0xb0,
|
||||
0x86, 0xdf, 0x84, 0xaa, 0xa4, 0xef, 0xb9, 0x1d, 0x26, 0xfb, 0x8c, 0x5a, 0x73, 0xad, 0x9e, 0xb4,
|
||||
0x79, 0x75, 0xbd, 0xcd, 0xcb, 0x75, 0x28, 0xda, 0xbc, 0x7a, 0xef, 0x7a, 0x5d, 0x9c, 0xb0, 0xf2,
|
||||
0xc3, 0x02, 0x4b, 0x4c, 0x5d, 0x6f, 0xcb, 0xf5, 0xe5, 0xf3, 0x41, 0x88, 0xca, 0x17, 0x84, 0xb1,
|
||||
0x5b, 0xdc, 0xf3, 0xf8, 0xa3, 0xd4, 0x77, 0x13, 0x4a, 0x9c, 0xea, 0xfa, 0xb1, 0xeb, 0x49, 0xf9,
|
||||
0xd5, 0xe4, 0x06, 0xd9, 0x82, 0x3c, 0xe5, 0x7a, 0x31, 0x0b, 0x65, 0x81, 0xae, 0x5a, 0x8a, 0xca,
|
||||
0xdc, 0xa9, 0x96, 0xf4, 0x3d, 0x69, 0xcc, 0x24, 0x8e, 0x77, 0x44, 0x77, 0xbc, 0x41, 0x67, 0x3e,
|
||||
0x2a, 0x7f, 0x2c, 0x3a, 0xb3, 0x6c, 0xe4, 0x58, 0xcf, 0xe5, 0xdd, 0xc8, 0x38, 0x96, 0x94, 0xb1,
|
||||
0x94, 0x26, 0xbf, 0x43, 0x50, 0xd9, 0xe2, 0xed, 0xdb, 0x7e, 0x1c, 0xf6, 0xe5, 0x7b, 0x93, 0xfb,
|
||||
0x31, 0xf3, 0x53, 0x8b, 0xa7, 0xa4, 0x50, 0x63, 0xec, 0x76, 0xd8, 0x6e, 0x4c, 0x3b, 0x81, 0xaa,
|
||||
0xb8, 0xcf, 0xa4, 0xc6, 0xec, 0xb0, 0xb8, 0x9a, 0x47, 0xa3, 0x58, 0x46, 0x5d, 0xc5, 0x92, 0xdf,
|
||||
0xe2, 0x12, 0xd9, 0x86, 0xdd, 0x38, 0x54, 0x21, 0x57, 0x58, 0xd3, 0x9d, 0x64, 0x3e, 0xc1, 0xa6,
|
||||
0x48, 0xd2, 0x80, 0x97, 0xb2, 0x47, 0xd8, 0x3d, 0x16, 0x76, 0x5c, 0x9f, 0x96, 0xe6, 0x40, 0x72,
|
||||
0xbd, 0xe0, 0xf8, 0xe2, 0x55, 0xf2, 0xc0, 0xf5, 0x1d, 0xfe, 0x68, 0xb4, 0x03, 0x93, 0xbf, 0x15,
|
||||
0x7b, 0x45, 0xed, 0x4c, 0x16, 0x2f, 0x6f, 0xc2, 0x51, 0x11, 0x59, 0x3d, 0xa6, 0x7e, 0x50, 0xc1,
|
||||
0x4b, 0x0a, 0x41, 0x39, 0x94, 0x87, 0x55, 0x3c, 0x88, 0xb7, 0xe0, 0x38, 0x8d, 0x22, 0xb7, 0xed,
|
||||
0x33, 0x27, 0xe5, 0x35, 0x33, 0x31, 0xaf, 0xc1, 0xa3, 0x49, 0x13, 0x21, 0x77, 0x28, 0x9d, 0xa7,
|
||||
0x24, 0xf9, 0x36, 0x82, 0x53, 0x43, 0x99, 0x64, 0xfe, 0x87, 0xb4, 0x74, 0x26, 0x1a, 0x79, 0x7b,
|
||||
0x8f, 0x39, 0x5d, 0x2f, 0x0d, 0xe1, 0x8c, 0x16, 0xbf, 0x39, 0xdd, 0xc4, 0x02, 0x2a, 0x9d, 0x66,
|
||||
0x34, 0x5e, 0x06, 0xe8, 0x50, 0xbf, 0x4b, 0x3d, 0x09, 0x61, 0x4e, 0x42, 0xd0, 0x56, 0xc8, 0x12,
|
||||
0x98, 0xc3, 0xcc, 0xa7, 0xda, 0xd2, 0x5f, 0x21, 0x38, 0x96, 0xa6, 0x26, 0x65, 0x9f, 0x55, 0x38,
|
||||
0xae, 0xa9, 0x61, 0x27, 0x37, 0xd5, 0xe0, 0xf2, 0x60, 0xda, 0x41, 0xc5, 0xb4, 0x93, 0xda, 0x79,
|
||||
0xb6, 0x38, 0x0d, 0xe9, 0x15, 0xe6, 0x19, 0x13, 0xe7, 0xfd, 0x2c, 0x50, 0xc9, 0xb7, 0xc0, 0xd8,
|
||||
0xa6, 0x3e, 0x6d, 0x33, 0x27, 0x03, 0x9e, 0x39, 0xc9, 0x37, 0xf4, 0xd6, 0xeb, 0xd0, 0x8d, 0x4e,
|
||||
0x56, 0xf6, 0xdd, 0x56, 0x4b, 0xb5, 0x71, 0xcd, 0x7f, 0x2e, 0x03, 0xd6, 0x8d, 0xca, 0xc2, 0x9e,
|
||||
0x6b, 0x33, 0xfc, 0x03, 0x04, 0x73, 0xa2, 0xca, 0xe0, 0xb3, 0xa3, 0x7c, 0x48, 0x2a, 0xd7, 0x9c,
|
||||
0xde, 0x3b, 0x5a, 0x48, 0x23, 0x4b, 0xef, 0xfd, 0xfd, 0x5f, 0x3f, 0x9c, 0x39, 0x8d, 0x5f, 0x94,
|
||||
0x73, 0xb7, 0xde, 0x75, 0x7d, 0x06, 0x16, 0xe1, 0xef, 0x22, 0xc0, 0xaa, 0xf4, 0x69, 0xa3, 0x17,
|
||||
0x7c, 0x65, 0x14, 0xc4, 0x21, 0x23, 0x1a, 0xf3, 0xac, 0x96, 0x86, 0xea, 0x36, 0x0f, 0x99, 0x48,
|
||||
0x3a, 0x72, 0x83, 0x04, 0xb0, 0x26, 0x01, 0x9c, 0xc7, 0x64, 0x18, 0x80, 0xc6, 0x63, 0x61, 0xf4,
|
||||
0x27, 0x0d, 0x96, 0xc8, 0xfd, 0x39, 0x82, 0xf9, 0x07, 0xf2, 0xf1, 0x35, 0x46, 0x49, 0xbb, 0x53,
|
||||
0x53, 0x92, 0x14, 0x27, 0xd1, 0x92, 0x73, 0x12, 0xe9, 0x59, 0x7c, 0x26, 0x45, 0x1a, 0xc5, 0x21,
|
||||
0xa3, 0x9d, 0x02, 0xe0, 0x6b, 0x08, 0x7f, 0x88, 0x60, 0x21, 0x19, 0xce, 0xe0, 0x0b, 0xa3, 0x50,
|
||||
0x16, 0x86, 0x37, 0xe6, 0xf4, 0x26, 0x1d, 0xe4, 0xb2, 0xc4, 0x78, 0x8e, 0x0c, 0x35, 0xe7, 0x46,
|
||||
0x61, 0x0e, 0xf2, 0x3e, 0x82, 0xd9, 0x4d, 0x36, 0xd6, 0xdf, 0xa6, 0x08, 0xee, 0x80, 0x02, 0x87,
|
||||
0x98, 0x1a, 0x7f, 0x80, 0xe0, 0xa5, 0x4d, 0x16, 0x0f, 0xcf, 0xe5, 0x78, 0x75, 0x7c, 0x82, 0x55,
|
||||
0x6e, 0x77, 0x65, 0x82, 0x9d, 0x59, 0x12, 0x6b, 0x48, 0x64, 0x97, 0xf1, 0xa5, 0x32, 0x27, 0x14,
|
||||
0x0d, 0xf1, 0x23, 0x85, 0xe3, 0xcf, 0x08, 0x4e, 0x0c, 0x0e, 0x39, 0x71, 0x31, 0xfb, 0x0f, 0x9d,
|
||||
0x81, 0x9a, 0x3b, 0x87, 0x4d, 0x28, 0x45, 0xa6, 0xe4, 0x96, 0x44, 0xfe, 0x2a, 0x7e, 0xa5, 0x0c,
|
||||
0x79, 0x3a, 0xf7, 0x89, 0x1a, 0x8f, 0xd3, 0xcf, 0x27, 0x72, 0x5a, 0x2e, 0x61, 0xbf, 0x87, 0xe0,
|
||||
0xc8, 0x26, 0x8b, 0xb7, 0xb3, 0xb1, 0xc7, 0x48, 0xb7, 0x2d, 0xcc, 0x35, 0xcd, 0xa5, 0xba, 0x36,
|
||||
0xd4, 0x4e, 0x7f, 0xca, 0x54, 0xba, 0x2e, 0x81, 0x5d, 0xc2, 0x17, 0xca, 0x80, 0xe5, 0xa3, 0x96,
|
||||
0xdf, 0x23, 0x58, 0x48, 0xc6, 0x0a, 0xa3, 0xc5, 0x17, 0x86, 0x86, 0xd3, 0x74, 0xcc, 0xdb, 0x12,
|
||||
0xeb, 0xe7, 0xcd, 0x6b, 0xc3, 0xb1, 0xea, 0xe7, 0x53, 0xad, 0xd5, 0xe5, 0x05, 0x8a, 0x11, 0xf5,
|
||||
0x11, 0x02, 0xc8, 0x47, 0x23, 0xf8, 0x72, 0xf9, 0x3d, 0xb4, 0xf1, 0x89, 0x39, 0xdd, 0xe1, 0x08,
|
||||
0xa9, 0xcb, 0xfb, 0xac, 0x9a, 0x2b, 0xa5, 0xee, 0x1c, 0x30, 0x7b, 0x23, 0x19, 0xa3, 0xfc, 0x0c,
|
||||
0xc1, 0xbc, 0xec, 0xc5, 0xf1, 0xf9, 0x51, 0x98, 0xf5, 0x56, 0x7d, 0x9a, 0xaa, 0xbf, 0x28, 0xa1,
|
||||
0xae, 0x34, 0xcb, 0x72, 0xc2, 0x06, 0x5a, 0xc3, 0x3d, 0x58, 0x48, 0x7a, 0xe7, 0xd1, 0xee, 0x51,
|
||||
0xe8, 0xad, 0xcd, 0x95, 0x92, 0x1a, 0x95, 0x78, 0xa8, 0x4a, 0x47, 0x6b, 0xe3, 0xd2, 0xd1, 0x9c,
|
||||
0xc8, 0x18, 0xf8, 0x5c, 0x59, 0x3e, 0xf9, 0x18, 0x14, 0x73, 0x45, 0xa2, 0xbb, 0x40, 0x56, 0xc6,
|
||||
0xa5, 0x24, 0xa1, 0x9d, 0x1f, 0x21, 0x38, 0x31, 0xf8, 0xa4, 0xc1, 0x67, 0x06, 0xd2, 0x91, 0xfe,
|
||||
0x46, 0x33, 0x8b, 0x5a, 0x1c, 0xf5, 0x1c, 0x22, 0x5f, 0x90, 0x28, 0x36, 0xf0, 0xcd, 0xb1, 0x91,
|
||||
0xb1, 0x93, 0x06, 0xb4, 0x60, 0xb4, 0x9e, 0x4f, 0x58, 0x7f, 0x83, 0xe0, 0x48, 0xca, 0xf7, 0x5e,
|
||||
0xc8, 0x58, 0x39, 0xac, 0xe9, 0x05, 0x82, 0x90, 0x45, 0x3e, 0x2b, 0xe1, 0x7f, 0x06, 0xdf, 0x98,
|
||||
0x10, 0x7e, 0x0a, 0x7b, 0x3d, 0x16, 0x48, 0xff, 0x80, 0xe0, 0xe4, 0x83, 0xc4, 0xef, 0x3f, 0x21,
|
||||
0xfc, 0xaf, 0x4b, 0xfc, 0x9f, 0xc3, 0xaf, 0x96, 0x3c, 0x39, 0xc6, 0x5d, 0xe3, 0x1a, 0xc2, 0xbf,
|
||||
0x44, 0x50, 0x49, 0xa7, 0x93, 0xf8, 0xd2, 0xc8, 0xc0, 0x28, 0xce, 0x2f, 0xa7, 0xe9, 0xcc, 0xaa,
|
||||
0xbe, 0x92, 0xf3, 0xa5, 0x55, 0x4a, 0xc9, 0x17, 0x0e, 0xfd, 0x3e, 0x02, 0x9c, 0xf5, 0x1a, 0x59,
|
||||
0xf7, 0x81, 0x2f, 0x16, 0x44, 0x8d, 0x6c, 0x2a, 0xcd, 0x4b, 0x63, 0xf7, 0x15, 0xab, 0xd4, 0x5a,
|
||||
0x69, 0x95, 0xe2, 0x99, 0xfc, 0xef, 0x21, 0xa8, 0x6d, 0xb2, 0xec, 0x39, 0x5c, 0xa2, 0xcb, 0xe2,
|
||||
0x68, 0xd6, 0x5c, 0x1d, 0xbf, 0x51, 0x21, 0xba, 0x2a, 0x11, 0x5d, 0xc4, 0xe5, 0xaa, 0x4a, 0x01,
|
||||
0xfc, 0x04, 0xc1, 0xd1, 0xbb, 0xba, 0x8b, 0xe2, 0xab, 0xe3, 0x24, 0x15, 0x32, 0xf9, 0xe4, 0xb8,
|
||||
0x3e, 0x2d, 0x71, 0xad, 0x93, 0x89, 0x70, 0x6d, 0xa8, 0x19, 0xe9, 0x4f, 0x11, 0xbc, 0xa0, 0xf7,
|
||||
0x0f, 0x6a, 0x9a, 0xf6, 0xff, 0xea, 0xad, 0x64, 0x28, 0x47, 0x6e, 0x48, 0x7c, 0x75, 0x7c, 0x75,
|
||||
0x12, 0x7c, 0x0d, 0x35, 0x62, 0xc3, 0x3f, 0x46, 0x70, 0x52, 0xce, 0x2a, 0x75, 0xc6, 0x03, 0x25,
|
||||
0x66, 0xd4, 0x64, 0x73, 0x82, 0x12, 0xa3, 0xf2, 0x0f, 0x79, 0x26, 0x50, 0x1b, 0x6a, 0x0e, 0x89,
|
||||
0xbf, 0x8f, 0xe0, 0x58, 0x5a, 0xd4, 0x94, 0x75, 0xd7, 0xc7, 0x29, 0xee, 0x59, 0x8b, 0xa0, 0x72,
|
||||
0xb7, 0xb5, 0xc9, 0xdc, 0xed, 0x43, 0x04, 0x8b, 0x6a, 0x96, 0x58, 0xf2, 0x54, 0xd0, 0x86, 0x8d,
|
||||
0xe6, 0xa9, 0xc2, 0xae, 0x74, 0x90, 0x45, 0xbe, 0x26, 0xc5, 0xde, 0xc7, 0x8d, 0x32, 0xb1, 0x01,
|
||||
0x77, 0xa2, 0xc6, 0x63, 0x35, 0x45, 0x7a, 0xd2, 0xf0, 0x78, 0x3b, 0x7a, 0x87, 0xe0, 0xd2, 0x82,
|
||||
0x28, 0xf6, 0x5c, 0x43, 0xaf, 0x7d, 0xf1, 0x4f, 0x4f, 0x97, 0xd1, 0x5f, 0x9f, 0x2e, 0xa3, 0x7f,
|
||||
0x3c, 0x5d, 0x46, 0xef, 0xdc, 0x9c, 0xec, 0xff, 0x1f, 0xb6, 0xe7, 0x32, 0x3f, 0xd6, 0xd9, 0xfe,
|
||||
0x2f, 0x00, 0x00, 0xff, 0xff, 0xef, 0x54, 0xd5, 0x40, 0xe5, 0x22, 0x00, 0x00,
|
||||
// 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,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@@ -3455,27 +3455,21 @@ func (m *ApplicationResourceEventsQuery) MarshalToSizedBuffer(dAtA []byte) (int,
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if m.ResourceUID == nil {
|
||||
return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("resourceUID")
|
||||
} else {
|
||||
if m.ResourceUID != nil {
|
||||
i -= len(*m.ResourceUID)
|
||||
copy(dAtA[i:], *m.ResourceUID)
|
||||
i = encodeVarintApplication(dAtA, i, uint64(len(*m.ResourceUID)))
|
||||
i--
|
||||
dAtA[i] = 0x22
|
||||
}
|
||||
if m.ResourceName == nil {
|
||||
return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("resourceName")
|
||||
} else {
|
||||
if m.ResourceName != nil {
|
||||
i -= len(*m.ResourceName)
|
||||
copy(dAtA[i:], *m.ResourceName)
|
||||
i = encodeVarintApplication(dAtA, i, uint64(len(*m.ResourceName)))
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
}
|
||||
if m.ResourceNamespace == nil {
|
||||
return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("resourceNamespace")
|
||||
} else {
|
||||
if m.ResourceNamespace != nil {
|
||||
i -= len(*m.ResourceNamespace)
|
||||
copy(dAtA[i:], *m.ResourceNamespace)
|
||||
i = encodeVarintApplication(dAtA, i, uint64(len(*m.ResourceNamespace)))
|
||||
@@ -4136,9 +4130,7 @@ func (m *ApplicationResourceRequest) MarshalToSizedBuffer(dAtA []byte) (int, err
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
}
|
||||
if m.Namespace == nil {
|
||||
return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("namespace")
|
||||
} else {
|
||||
if m.Namespace != nil {
|
||||
i -= len(*m.Namespace)
|
||||
copy(dAtA[i:], *m.Namespace)
|
||||
i = encodeVarintApplication(dAtA, i, uint64(len(*m.Namespace)))
|
||||
@@ -4233,9 +4225,7 @@ func (m *ApplicationResourcePatchRequest) MarshalToSizedBuffer(dAtA []byte) (int
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
}
|
||||
if m.Namespace == nil {
|
||||
return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("namespace")
|
||||
} else {
|
||||
if m.Namespace != nil {
|
||||
i -= len(*m.Namespace)
|
||||
copy(dAtA[i:], *m.Namespace)
|
||||
i = encodeVarintApplication(dAtA, i, uint64(len(*m.Namespace)))
|
||||
@@ -4332,9 +4322,7 @@ func (m *ApplicationResourceDeleteRequest) MarshalToSizedBuffer(dAtA []byte) (in
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
}
|
||||
if m.Namespace == nil {
|
||||
return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("namespace")
|
||||
} else {
|
||||
if m.Namespace != nil {
|
||||
i -= len(*m.Namespace)
|
||||
copy(dAtA[i:], *m.Namespace)
|
||||
i = encodeVarintApplication(dAtA, i, uint64(len(*m.Namespace)))
|
||||
@@ -4420,9 +4408,7 @@ func (m *ResourceActionRunRequest) MarshalToSizedBuffer(dAtA []byte) (int, error
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
}
|
||||
if m.Namespace == nil {
|
||||
return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("namespace")
|
||||
} else {
|
||||
if m.Namespace != nil {
|
||||
i -= len(*m.Namespace)
|
||||
copy(dAtA[i:], *m.Namespace)
|
||||
i = encodeVarintApplication(dAtA, i, uint64(len(*m.Namespace)))
|
||||
@@ -4633,9 +4619,7 @@ func (m *ApplicationPodLogsQuery) MarshalToSizedBuffer(dAtA []byte) (int, error)
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
}
|
||||
if m.Namespace == nil {
|
||||
return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("namespace")
|
||||
} else {
|
||||
if m.Namespace != nil {
|
||||
i -= len(*m.Namespace)
|
||||
copy(dAtA[i:], *m.Namespace)
|
||||
i = encodeVarintApplication(dAtA, i, uint64(len(*m.Namespace)))
|
||||
@@ -6437,7 +6421,6 @@ func (m *ApplicationResourceEventsQuery) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.ResourceNamespace = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000002)
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ResourceName", wireType)
|
||||
@@ -6471,7 +6454,6 @@ func (m *ApplicationResourceEventsQuery) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.ResourceName = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000004)
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ResourceUID", wireType)
|
||||
@@ -6505,7 +6487,6 @@ func (m *ApplicationResourceEventsQuery) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.ResourceUID = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000008)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipApplication(dAtA[iNdEx:])
|
||||
@@ -6525,15 +6506,6 @@ func (m *ApplicationResourceEventsQuery) Unmarshal(dAtA []byte) error {
|
||||
if hasFields[0]&uint64(0x00000001) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("name")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000002) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("resourceNamespace")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000004) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("resourceName")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000008) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("resourceUID")
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
@@ -8123,7 +8095,6 @@ func (m *ApplicationResourceRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Namespace = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000002)
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ResourceName", wireType)
|
||||
@@ -8157,7 +8128,7 @@ func (m *ApplicationResourceRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.ResourceName = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000004)
|
||||
hasFields[0] |= uint64(0x00000002)
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
|
||||
@@ -8191,7 +8162,7 @@ func (m *ApplicationResourceRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Version = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000008)
|
||||
hasFields[0] |= uint64(0x00000004)
|
||||
case 5:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Group", wireType)
|
||||
@@ -8258,7 +8229,7 @@ func (m *ApplicationResourceRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Kind = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000010)
|
||||
hasFields[0] |= uint64(0x00000008)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipApplication(dAtA[iNdEx:])
|
||||
@@ -8279,15 +8250,12 @@ func (m *ApplicationResourceRequest) Unmarshal(dAtA []byte) error {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("name")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000002) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("namespace")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000004) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("resourceName")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000008) == 0 {
|
||||
if hasFields[0]&uint64(0x00000004) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("version")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000010) == 0 {
|
||||
if hasFields[0]&uint64(0x00000008) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("kind")
|
||||
}
|
||||
|
||||
@@ -8393,7 +8361,6 @@ func (m *ApplicationResourcePatchRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Namespace = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000002)
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ResourceName", wireType)
|
||||
@@ -8427,7 +8394,7 @@ func (m *ApplicationResourcePatchRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.ResourceName = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000004)
|
||||
hasFields[0] |= uint64(0x00000002)
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
|
||||
@@ -8461,7 +8428,7 @@ func (m *ApplicationResourcePatchRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Version = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000008)
|
||||
hasFields[0] |= uint64(0x00000004)
|
||||
case 5:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Group", wireType)
|
||||
@@ -8528,7 +8495,7 @@ func (m *ApplicationResourcePatchRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Kind = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000010)
|
||||
hasFields[0] |= uint64(0x00000008)
|
||||
case 7:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Patch", wireType)
|
||||
@@ -8562,7 +8529,7 @@ func (m *ApplicationResourcePatchRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Patch = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000020)
|
||||
hasFields[0] |= uint64(0x00000010)
|
||||
case 8:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field PatchType", wireType)
|
||||
@@ -8596,7 +8563,7 @@ func (m *ApplicationResourcePatchRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.PatchType = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000040)
|
||||
hasFields[0] |= uint64(0x00000020)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipApplication(dAtA[iNdEx:])
|
||||
@@ -8617,21 +8584,18 @@ func (m *ApplicationResourcePatchRequest) Unmarshal(dAtA []byte) error {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("name")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000002) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("namespace")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000004) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("resourceName")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000008) == 0 {
|
||||
if hasFields[0]&uint64(0x00000004) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("version")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000010) == 0 {
|
||||
if hasFields[0]&uint64(0x00000008) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("kind")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000020) == 0 {
|
||||
if hasFields[0]&uint64(0x00000010) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("patch")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000040) == 0 {
|
||||
if hasFields[0]&uint64(0x00000020) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("patchType")
|
||||
}
|
||||
|
||||
@@ -8737,7 +8701,6 @@ func (m *ApplicationResourceDeleteRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Namespace = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000002)
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ResourceName", wireType)
|
||||
@@ -8771,7 +8734,7 @@ func (m *ApplicationResourceDeleteRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.ResourceName = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000004)
|
||||
hasFields[0] |= uint64(0x00000002)
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
|
||||
@@ -8805,7 +8768,7 @@ func (m *ApplicationResourceDeleteRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Version = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000008)
|
||||
hasFields[0] |= uint64(0x00000004)
|
||||
case 5:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Group", wireType)
|
||||
@@ -8872,7 +8835,7 @@ func (m *ApplicationResourceDeleteRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Kind = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000010)
|
||||
hasFields[0] |= uint64(0x00000008)
|
||||
case 7:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Force", wireType)
|
||||
@@ -8935,15 +8898,12 @@ func (m *ApplicationResourceDeleteRequest) Unmarshal(dAtA []byte) error {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("name")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000002) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("namespace")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000004) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("resourceName")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000008) == 0 {
|
||||
if hasFields[0]&uint64(0x00000004) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("version")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000010) == 0 {
|
||||
if hasFields[0]&uint64(0x00000008) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("kind")
|
||||
}
|
||||
|
||||
@@ -9049,7 +9009,6 @@ func (m *ResourceActionRunRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Namespace = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000002)
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ResourceName", wireType)
|
||||
@@ -9083,7 +9042,7 @@ func (m *ResourceActionRunRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.ResourceName = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000004)
|
||||
hasFields[0] |= uint64(0x00000002)
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
|
||||
@@ -9117,7 +9076,7 @@ func (m *ResourceActionRunRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Version = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000008)
|
||||
hasFields[0] |= uint64(0x00000004)
|
||||
case 5:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Group", wireType)
|
||||
@@ -9184,7 +9143,7 @@ func (m *ResourceActionRunRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Kind = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000010)
|
||||
hasFields[0] |= uint64(0x00000008)
|
||||
case 7:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType)
|
||||
@@ -9218,7 +9177,7 @@ func (m *ResourceActionRunRequest) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Action = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000020)
|
||||
hasFields[0] |= uint64(0x00000010)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipApplication(dAtA[iNdEx:])
|
||||
@@ -9239,18 +9198,15 @@ func (m *ResourceActionRunRequest) Unmarshal(dAtA []byte) error {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("name")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000002) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("namespace")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000004) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("resourceName")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000008) == 0 {
|
||||
if hasFields[0]&uint64(0x00000004) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("version")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000010) == 0 {
|
||||
if hasFields[0]&uint64(0x00000008) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("kind")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000020) == 0 {
|
||||
if hasFields[0]&uint64(0x00000010) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("action")
|
||||
}
|
||||
|
||||
@@ -9530,7 +9486,6 @@ func (m *ApplicationPodLogsQuery) Unmarshal(dAtA []byte) error {
|
||||
s := string(dAtA[iNdEx:postIndex])
|
||||
m.Namespace = &s
|
||||
iNdEx = postIndex
|
||||
hasFields[0] |= uint64(0x00000002)
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field PodName", wireType)
|
||||
@@ -9899,9 +9854,6 @@ func (m *ApplicationPodLogsQuery) Unmarshal(dAtA []byte) error {
|
||||
if hasFields[0]&uint64(0x00000001) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("name")
|
||||
}
|
||||
if hasFields[0]&uint64(0x00000002) == 0 {
|
||||
return github_com_gogo_protobuf_proto.NewRequiredNotSetError("namespace")
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
@@ -63,6 +64,8 @@ func NewConnection(address string, timeoutSeconds int, tlsConfig *TLSConfigurati
|
||||
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)),
|
||||
grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(unaryInterceptors...)),
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(MaxGRPCMessageSize), grpc.MaxCallSendMsgSize(MaxGRPCMessageSize)),
|
||||
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()),
|
||||
grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()),
|
||||
}
|
||||
|
||||
tlsC := &tls.Config{}
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/io/files"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/TomOnTime/utfutil"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
@@ -905,7 +907,8 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string,
|
||||
if directory = q.ApplicationSource.Directory; directory == nil {
|
||||
directory = &v1alpha1.ApplicationSourceDirectory{}
|
||||
}
|
||||
targetObjs, err = findManifests(appPath, repoRoot, env, *directory, q.EnabledSourceTypes)
|
||||
logCtx := log.WithField("application", q.AppName)
|
||||
targetObjs, err = findManifests(logCtx, appPath, repoRoot, env, *directory, q.EnabledSourceTypes)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1075,12 +1078,32 @@ func isNullList(obj *unstructured.Unstructured) bool {
|
||||
var manifestFile = regexp.MustCompile(`^.*\.(yaml|yml|json|jsonnet)$`)
|
||||
|
||||
// findManifests looks at all yaml files in a directory and unmarshals them into a list of unstructured objects
|
||||
func findManifests(appPath string, repoRoot string, env *v1alpha1.Env, directory v1alpha1.ApplicationSourceDirectory, enabledManifestGeneration map[string]bool) ([]*unstructured.Unstructured, error) {
|
||||
func findManifests(logCtx *log.Entry, appPath string, repoRoot string, env *v1alpha1.Env, directory v1alpha1.ApplicationSourceDirectory, enabledManifestGeneration map[string]bool) ([]*unstructured.Unstructured, error) {
|
||||
var objs []*unstructured.Unstructured
|
||||
err := filepath.Walk(appPath, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
relPath, err := filepath.Rel(appPath, path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get relative path of symlink: %w", err)
|
||||
}
|
||||
if files.IsSymlink(f) {
|
||||
realPath, err := filepath.EvalSymlinks(path)
|
||||
if err != nil {
|
||||
logCtx.Debugf("error checking symlink realpath: %s", err)
|
||||
if os.IsNotExist(err) {
|
||||
log.Warnf("ignoring out-of-bounds symlink at %q: %s", relPath, err)
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("failed to evaluate symlink at %q: %w", relPath, err)
|
||||
}
|
||||
}
|
||||
if !files.Inbound(realPath, appPath) {
|
||||
logCtx.Warnf("illegal filepath in symlink: %s", realPath)
|
||||
return fmt.Errorf("illegal filepath in symlink at %q", relPath)
|
||||
}
|
||||
}
|
||||
if f.IsDir() {
|
||||
if path != appPath && !directory.Recurse {
|
||||
return filepath.SkipDir
|
||||
@@ -1093,10 +1116,6 @@ func findManifests(appPath string, repoRoot string, env *v1alpha1.Env, directory
|
||||
return nil
|
||||
}
|
||||
|
||||
relPath, err := filepath.Rel(appPath, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if directory.Exclude != "" && glob.Match(directory.Exclude, relPath) {
|
||||
return nil
|
||||
}
|
||||
@@ -1252,7 +1271,7 @@ func runConfigManagementPlugin(appPath, repoRoot string, envVars *v1alpha1.Env,
|
||||
}
|
||||
}
|
||||
|
||||
env, err := getPluginEnvs(envVars, q, creds)
|
||||
env, err := getPluginEnvs(envVars, q, creds, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1270,8 +1289,14 @@ func runConfigManagementPlugin(appPath, repoRoot string, envVars *v1alpha1.Env,
|
||||
return kube.SplitYAML([]byte(out))
|
||||
}
|
||||
|
||||
func getPluginEnvs(envVars *v1alpha1.Env, q *apiclient.ManifestRequest, creds git.Creds) ([]string, error) {
|
||||
env := append(os.Environ(), envVars.Environ()...)
|
||||
func getPluginEnvs(envVars *v1alpha1.Env, q *apiclient.ManifestRequest, creds git.Creds, remote bool) ([]string, error) {
|
||||
env := envVars.Environ()
|
||||
// Local plugins need also to have access to the local environment variables.
|
||||
// Remote side car plugins will use the environment in the side car
|
||||
// container.
|
||||
if !remote {
|
||||
env = append(os.Environ(), env...)
|
||||
}
|
||||
if creds != nil {
|
||||
closer, environ, err := creds.Environ()
|
||||
if err != nil {
|
||||
@@ -1294,27 +1319,29 @@ func getPluginEnvs(envVars *v1alpha1.Env, q *apiclient.ManifestRequest, creds gi
|
||||
|
||||
if q.ApplicationSource.Plugin != nil {
|
||||
pluginEnv := q.ApplicationSource.Plugin.Env
|
||||
for i, j := range pluginEnv {
|
||||
pluginEnv[i].Value = parsedEnv.Envsubst(j.Value)
|
||||
for _, entry := range pluginEnv {
|
||||
newValue := parsedEnv.Envsubst(entry.Value)
|
||||
env = append(env, fmt.Sprintf("ARGOCD_ENV_%s=%s", entry.Name, newValue))
|
||||
}
|
||||
env = append(env, pluginEnv.Environ()...)
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
func runConfigManagementPluginSidecars(ctx context.Context, appPath, repoPath string, envVars *v1alpha1.Env, q *apiclient.ManifestRequest, creds git.Creds, tarDoneCh chan<- bool) ([]*unstructured.Unstructured, error) {
|
||||
// compute variables.
|
||||
env, err := getPluginEnvs(envVars, q, creds, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// detect config management plugin server (sidecar)
|
||||
conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath)
|
||||
conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer io.Close(conn)
|
||||
|
||||
// generate manifests using commands provided in plugin config file in detected cmp-server sidecar
|
||||
env, err := getPluginEnvs(envVars, q, creds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmpManifests, err := generateManifestsCMP(ctx, appPath, repoPath, env, cmpClient, tarDoneCh)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating manifests in cmp: %s", err)
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
//go:build !race
|
||||
// +build !race
|
||||
|
||||
package repository
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
)
|
||||
|
||||
func TestHelmDependencyWithConcurrency(t *testing.T) {
|
||||
|
||||
// !race:
|
||||
// Un-synchronized use of a random source, will be fixed when this is merged:
|
||||
// https://github.com/argoproj/argo-cd/issues/4728
|
||||
|
||||
cleanup := func() {
|
||||
_ = os.Remove(filepath.Join("../../util/helm/testdata/dependency", helmDepUpMarkerFile))
|
||||
_ = os.RemoveAll(filepath.Join("../../util/helm/testdata/dependency", "charts"))
|
||||
}
|
||||
cleanup()
|
||||
defer cleanup()
|
||||
|
||||
helmRepo := argoappv1.Repository{Name: "bitnami", Type: "helm", Repo: "https://charts.bitnami.com/bitnami"}
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(3)
|
||||
for i := 0; i < 3; i++ {
|
||||
go func() {
|
||||
res, err := helmTemplate("../../util/helm/testdata/dependency", "../..", nil, &apiclient.ManifestRequest{
|
||||
ApplicationSource: &argoappv1.ApplicationSource{},
|
||||
Repos: []*argoappv1.Repository{&helmRepo},
|
||||
}, false)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, res)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
goio "io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -136,7 +137,7 @@ func TestGenerateYamlManifestInDir(t *testing.T) {
|
||||
q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &src}
|
||||
|
||||
// update this value if we add/remove manifests
|
||||
const countOfManifests = 46
|
||||
const countOfManifests = 47
|
||||
|
||||
res1, err := service.GenerateManifest(context.Background(), &q)
|
||||
|
||||
@@ -149,6 +150,76 @@ func TestGenerateYamlManifestInDir(t *testing.T) {
|
||||
assert.Equal(t, 3, len(res2.Manifests))
|
||||
}
|
||||
|
||||
func Test_GenerateManifests_NoOutOfBoundsAccess(t *testing.T) {
|
||||
testCases := []struct{
|
||||
name string
|
||||
outOfBoundsFilename string
|
||||
outOfBoundsFileContents string
|
||||
mustNotContain string // Optional string that must not appear in error or manifest output. If empty, use outOfBoundsFileContents.
|
||||
}{
|
||||
{
|
||||
name: "out of bounds JSON file should not appear in error output",
|
||||
outOfBoundsFilename: "test.json",
|
||||
outOfBoundsFileContents: `{"some": "json"}`,
|
||||
},
|
||||
{
|
||||
name: "malformed JSON file contents should not appear in error output",
|
||||
outOfBoundsFilename: "test.json",
|
||||
outOfBoundsFileContents: "$",
|
||||
},
|
||||
{
|
||||
name: "out of bounds JSON manifest should not appear in manifest output",
|
||||
outOfBoundsFilename: "test.json",
|
||||
// JSON marshalling is deterministic. So if there's a leak, exactly this should appear in the manifests.
|
||||
outOfBoundsFileContents: `{"apiVersion":"v1","kind":"Secret","metadata":{"name":"test","namespace":"default"},"type":"Opaque"}`,
|
||||
},
|
||||
{
|
||||
name: "out of bounds YAML manifest should not appear in manifest output",
|
||||
outOfBoundsFilename: "test.yaml",
|
||||
outOfBoundsFileContents: "apiVersion: v1\nkind: Secret\nmetadata:\n name: test\n namespace: default\ntype: Opaque",
|
||||
mustNotContain: `{"apiVersion":"v1","kind":"Secret","metadata":{"name":"test","namespace":"default"},"type":"Opaque"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
testCaseCopy := testCase
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
outOfBoundsDir := t.TempDir()
|
||||
outOfBoundsFile := path.Join(outOfBoundsDir, testCaseCopy.outOfBoundsFilename)
|
||||
err := os.WriteFile(outOfBoundsFile, []byte(testCaseCopy.outOfBoundsFileContents), os.FileMode(0444))
|
||||
require.NoError(t, err)
|
||||
|
||||
repoDir := t.TempDir()
|
||||
err = os.Symlink(outOfBoundsFile, path.Join(repoDir, testCaseCopy.outOfBoundsFilename))
|
||||
require.NoError(t, err)
|
||||
|
||||
var mustNotContain = testCaseCopy.outOfBoundsFileContents
|
||||
if testCaseCopy.mustNotContain != "" {
|
||||
mustNotContain = testCaseCopy.mustNotContain
|
||||
}
|
||||
|
||||
q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &argoappv1.ApplicationSource{}}
|
||||
res, err := GenerateManifests(context.Background(), repoDir, "", "", &q, false, &git.NoopCredsStore{})
|
||||
require.Error(t, err)
|
||||
assert.NotContains(t, err.Error(), mustNotContain)
|
||||
assert.Contains(t, err.Error(), "illegal filepath")
|
||||
assert.Nil(t, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateManifests_MissingSymlinkDestination(t *testing.T) {
|
||||
repoDir := t.TempDir()
|
||||
err := os.Symlink("/obviously/does/not/exist", path.Join(repoDir, "test.yaml"))
|
||||
require.NoError(t, err)
|
||||
|
||||
q := apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: &argoappv1.ApplicationSource{}}
|
||||
_, err = GenerateManifests(context.Background(), repoDir, "", "", &q, false, &git.NoopCredsStore{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGenerateManifests_K8SAPIResetCache(t *testing.T) {
|
||||
service := newService("../..")
|
||||
|
||||
@@ -283,28 +354,6 @@ func TestGenerateJsonnetLibOutside(t *testing.T) {
|
||||
require.Contains(t, err.Error(), "value file '../../../testdata/jsonnet/vendor' resolved to outside repository root")
|
||||
}
|
||||
|
||||
func TestGenerateHelmChartWithDependencies(t *testing.T) {
|
||||
service := newService("../..")
|
||||
|
||||
cleanup := func() {
|
||||
_ = os.Remove(filepath.Join("../../util/helm/testdata/dependency", helmDepUpMarkerFile))
|
||||
_ = os.RemoveAll(filepath.Join("../../util/helm/testdata/dependency", "charts"))
|
||||
}
|
||||
cleanup()
|
||||
defer cleanup()
|
||||
|
||||
helmRepo := argoappv1.Repository{Name: "bitnami", Type: "helm", Repo: "https://charts.bitnami.com/bitnami"}
|
||||
q := apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: "./util/helm/testdata/dependency",
|
||||
},
|
||||
Repos: []*argoappv1.Repository{&helmRepo},
|
||||
}
|
||||
res1, err := service.GenerateManifest(context.Background(), &q)
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, res1.Manifests, 10)
|
||||
}
|
||||
func TestManifestGenErrorCacheByNumRequests(t *testing.T) {
|
||||
|
||||
// Returns the state of the manifest generation cache, by querying the cache for the previously set result
|
||||
@@ -1031,7 +1080,7 @@ func TestRunCustomTool(t *testing.T) {
|
||||
Name: "test",
|
||||
Generate: argoappv1.Command{
|
||||
Command: []string{"sh", "-c"},
|
||||
Args: []string{`echo "{\"kind\": \"FakeObject\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"GIT_ASKPASS\": \"$GIT_ASKPASS\", \"GIT_USERNAME\": \"$GIT_USERNAME\", \"GIT_PASSWORD\": \"$GIT_PASSWORD\"}, \"labels\": {\"revision\": \"$TEST_REVISION\"}}}"`},
|
||||
Args: []string{`echo "{\"kind\": \"FakeObject\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"GIT_ASKPASS\": \"$GIT_ASKPASS\", \"GIT_USERNAME\": \"$GIT_USERNAME\", \"GIT_PASSWORD\": \"$GIT_PASSWORD\"}, \"labels\": {\"revision\": \"$ARGOCD_ENV_TEST_REVISION\"}}}"`},
|
||||
},
|
||||
}},
|
||||
Repo: &argoappv1.Repository{
|
||||
@@ -1080,6 +1129,7 @@ func TestListApps(t *testing.T) {
|
||||
"kustomization_yml": "Kustomize",
|
||||
"my-chart": "Helm",
|
||||
"my-chart-2": "Helm",
|
||||
"values-files": "Helm",
|
||||
}
|
||||
assert.Equal(t, expectedApps, res.Apps)
|
||||
}
|
||||
@@ -1599,7 +1649,7 @@ func TestFindResources(t *testing.T) {
|
||||
for i := range testCases {
|
||||
tc := testCases[i]
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
objs, err := findManifests("testdata/app-include-exclude", ".", nil, argoappv1.ApplicationSourceDirectory{
|
||||
objs, err := findManifests(&log.Entry{}, "testdata/app-include-exclude", ".", nil, argoappv1.ApplicationSourceDirectory{
|
||||
Recurse: true,
|
||||
Include: tc.include,
|
||||
Exclude: tc.exclude,
|
||||
@@ -1617,7 +1667,7 @@ func TestFindResources(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFindManifests_Exclude(t *testing.T) {
|
||||
objs, err := findManifests("testdata/app-include-exclude", ".", nil, argoappv1.ApplicationSourceDirectory{
|
||||
objs, err := findManifests(&log.Entry{}, "testdata/app-include-exclude", ".", nil, argoappv1.ApplicationSourceDirectory{
|
||||
Recurse: true,
|
||||
Exclude: "subdir/deploymentSub.yaml",
|
||||
}, map[string]bool{})
|
||||
@@ -1630,7 +1680,7 @@ func TestFindManifests_Exclude(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFindManifests_Exclude_NothingMatches(t *testing.T) {
|
||||
objs, err := findManifests("testdata/app-include-exclude", ".", nil, argoappv1.ApplicationSourceDirectory{
|
||||
objs, err := findManifests(&log.Entry{}, "testdata/app-include-exclude", ".", nil, argoappv1.ApplicationSourceDirectory{
|
||||
Recurse: true,
|
||||
Exclude: "nothing.yaml",
|
||||
}, map[string]bool{})
|
||||
@@ -1825,3 +1875,32 @@ func runGit(t *testing.T, workDir string, args ...string) string {
|
||||
require.NoError(t, err, stringOut)
|
||||
return stringOut
|
||||
}
|
||||
|
||||
func Test_findHelmValueFilesInPath(t *testing.T) {
|
||||
t.Run("does not exist", func(t *testing.T) {
|
||||
files, err := findHelmValueFilesInPath("/obviously/does/not/exist")
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, files)
|
||||
})
|
||||
t.Run("values files", func(t *testing.T) {
|
||||
files, err := findHelmValueFilesInPath("./testdata/values-files")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, files, 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_populateHelmAppDetails(t *testing.T) {
|
||||
res := apiclient.RepoAppDetailsResponse{}
|
||||
q := apiclient.RepoServerAppDetailsQuery{
|
||||
Repo: &argoappv1.Repository{},
|
||||
Source: &argoappv1.ApplicationSource{
|
||||
Helm: &argoappv1.ApplicationSourceHelm{ValueFiles: []string{"exclude.yaml", "has-the-word-values.yaml"}},
|
||||
},
|
||||
}
|
||||
appPath, err := filepath.Abs("./testdata/values-files/")
|
||||
require.NoError(t, err)
|
||||
err = populateHelmAppDetails(&res, appPath, appPath, &q)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, res.Helm.Parameters, 3)
|
||||
assert.Len(t, res.Helm.ValueFiles, 4)
|
||||
}
|
||||
|
||||
2
reposerver/repository/testdata/values-files/Chart.yaml
vendored
Normal file
2
reposerver/repository/testdata/values-files/Chart.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
name: my-chart
|
||||
version: 1.1.0
|
||||
0
reposerver/repository/testdata/values-files/caps-extn-values.YAML
vendored
Normal file
0
reposerver/repository/testdata/values-files/caps-extn-values.YAML
vendored
Normal file
1
reposerver/repository/testdata/values-files/exclude.yaml
vendored
Normal file
1
reposerver/repository/testdata/values-files/exclude.yaml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
exclude: yaml
|
||||
4
reposerver/repository/testdata/values-files/has-the-word-values.yaml
vendored
Normal file
4
reposerver/repository/testdata/values-files/has-the-word-values.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
has:
|
||||
the:
|
||||
word:
|
||||
values: yaml
|
||||
1
reposerver/repository/testdata/values-files/values.yaml
vendored
Normal file
1
reposerver/repository/testdata/values-files/values.yaml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
values: yaml
|
||||
0
reposerver/repository/testdata/values-files/values.yml
vendored
Normal file
0
reposerver/repository/testdata/values-files/values.yml
vendored
Normal file
@@ -70,7 +70,7 @@ var (
|
||||
watchAPIBufferSize = env.ParseNumFromEnv(argocommon.EnvWatchAPIBufferSize, 1000, 0, math.MaxInt32)
|
||||
)
|
||||
|
||||
// Server provides a Application service
|
||||
// Server provides an Application service
|
||||
type Server struct {
|
||||
ns string
|
||||
kubeclientset kubernetes.Interface
|
||||
@@ -131,11 +131,11 @@ func NewServer(
|
||||
func (s *Server) List(ctx context.Context, q *application.ApplicationQuery) (*appv1.ApplicationList, error) {
|
||||
labelsMap, err := labels.ConvertSelectorToLabelsMap(q.GetSelector())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error converting selector to labels map: %w", err)
|
||||
}
|
||||
apps, err := s.appLister.List(labelsMap.AsSelector())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error listing apps with selectors: %w", err)
|
||||
}
|
||||
newItems := make([]appv1.Application, 0)
|
||||
for _, a := range apps {
|
||||
@@ -146,7 +146,7 @@ func (s *Server) List(ctx context.Context, q *application.ApplicationQuery) (*ap
|
||||
if q.Name != nil {
|
||||
newItems, err = argoutil.FilterByName(newItems, *q.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error filtering applications by name: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq
|
||||
}
|
||||
err := s.validateAndNormalizeApp(ctx, a, validate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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{})
|
||||
if err == nil {
|
||||
@@ -198,7 +198,7 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq
|
||||
return created, nil
|
||||
}
|
||||
if !apierr.IsAlreadyExists(err) {
|
||||
return nil, 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)
|
||||
@@ -221,7 +221,7 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq
|
||||
}
|
||||
updated, err := s.updateApp(existing, a, ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error updating application: %w", err)
|
||||
}
|
||||
return updated, nil
|
||||
}
|
||||
@@ -238,53 +238,53 @@ func (s *Server) queryRepoServer(ctx context.Context, a *v1alpha1.Application, a
|
||||
|
||||
closer, client, err := s.repoClientset.NewRepoServerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error creating repo server client: %w", err)
|
||||
}
|
||||
defer ioutil.Close(closer)
|
||||
repo, err := s.db.GetRepository(ctx, a.Spec.Source.RepoURL)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting repository: %w", err)
|
||||
}
|
||||
kustomizeSettings, err := s.settingsMgr.GetKustomizeSettings()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting kustomize settings: %w", err)
|
||||
}
|
||||
kustomizeOptions, err := kustomizeSettings.GetOptions(a.Spec.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
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)
|
||||
if err != nil {
|
||||
if apierr.IsNotFound(err) {
|
||||
return status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", a.Spec.Project)
|
||||
}
|
||||
return err
|
||||
return fmt.Errorf("error getting application's project: %w", err)
|
||||
}
|
||||
|
||||
helmRepos, err := s.db.ListHelmRepositories(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error listing helm repositories: %w", err)
|
||||
}
|
||||
|
||||
permittedHelmRepos, err := argo.GetPermittedRepos(proj, helmRepos)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error retrieving permitted repos: %w", err)
|
||||
}
|
||||
helmRepositoryCredentials, err := s.db.GetAllHelmRepositoryCredentials(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting helm repository credentials: %w", err)
|
||||
}
|
||||
helmOptions, err := s.settingsMgr.GetHelmSettings()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting helm settings: %w", err)
|
||||
}
|
||||
permittedHelmCredentials, err := argo.GetPermittedReposCredentials(proj, helmRepositoryCredentials)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting permitted repos credentials: %w", err)
|
||||
}
|
||||
enabledSourceTypes, err := s.settingsMgr.GetEnabledSourceTypes()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting settings enabled source types: %w", err)
|
||||
}
|
||||
return action(client, repo, permittedHelmRepos, permittedHelmCredentials, helmOptions, kustomizeOptions, enabledSourceTypes)
|
||||
}
|
||||
@@ -293,7 +293,7 @@ func (s *Server) queryRepoServer(ctx context.Context, a *v1alpha1.Application, a
|
||||
func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationManifestQuery) (*apiclient.ManifestResponse, error) {
|
||||
a, err := s.appLister.Get(*q.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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 {
|
||||
return nil, err
|
||||
@@ -308,26 +308,26 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
|
||||
}
|
||||
appInstanceLabelKey, err := s.settingsMgr.GetAppInstanceLabelKey()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting app instance label key from settings: %w", err)
|
||||
}
|
||||
|
||||
plugins, err := s.plugins()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting plugins: %w", err)
|
||||
}
|
||||
config, err := s.getApplicationClusterConfig(ctx, a)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting application cluster config: %w", err)
|
||||
}
|
||||
|
||||
serverVersion, err := s.kubectl.GetServerVersion(config)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting server version: %w", err)
|
||||
}
|
||||
|
||||
apiResources, err := s.kubectl.GetAPIResources(config, false, kubecache.NewNoopSettings())
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting API resources: %w", err)
|
||||
}
|
||||
|
||||
manifestInfo, err = client.GenerateManifest(ctx, &apiclient.ManifestRequest{
|
||||
@@ -347,7 +347,10 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
|
||||
TrackingMethod: string(argoutil.GetTrackingMethod(s.settingsMgr)),
|
||||
EnabledSourceTypes: enableGenerateManifests,
|
||||
})
|
||||
return err
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating manifests: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -358,16 +361,16 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
|
||||
obj := &unstructured.Unstructured{}
|
||||
err = json.Unmarshal([]byte(manifest), obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error unmarshaling manifest into unstructured: %w", err)
|
||||
}
|
||||
if obj.GetKind() == kube.SecretKind && obj.GroupVersionKind().Group == "" {
|
||||
obj, _, err = diff.HideSecretData(obj, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error hiding secret data: %w", err)
|
||||
}
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error marshaling manifest: %w", err)
|
||||
}
|
||||
manifestInfo.Manifests[i] = string(data)
|
||||
}
|
||||
@@ -386,7 +389,7 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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 {
|
||||
return nil, err
|
||||
@@ -410,7 +413,7 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app
|
||||
|
||||
app, err := argoutil.RefreshApp(appIf, *q.Name, refreshType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error refreshing the app: %w", err)
|
||||
}
|
||||
|
||||
if refreshType == appv1.RefreshTypeHard {
|
||||
@@ -468,7 +471,7 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app
|
||||
func (s *Server) ListResourceEvents(ctx context.Context, q *application.ApplicationResourceEventsQuery) (*v1.EventList, error) {
|
||||
a, err := s.appLister.Get(*q.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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 {
|
||||
return nil, err
|
||||
@@ -492,7 +495,7 @@ func (s *Server) ListResourceEvents(ctx context.Context, q *application.Applicat
|
||||
} else {
|
||||
tree, err := s.GetAppResources(ctx, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting app resources: %w", err)
|
||||
}
|
||||
found := false
|
||||
for _, n := range append(tree.Nodes, tree.OrphanedNodes...) {
|
||||
@@ -509,11 +512,11 @@ func (s *Server) ListResourceEvents(ctx context.Context, q *application.Applicat
|
||||
var config *rest.Config
|
||||
config, err = s.getApplicationClusterConfig(ctx, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting application cluster config: %w", err)
|
||||
}
|
||||
kubeClientset, err = kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error creating kube client: %w", err)
|
||||
}
|
||||
fieldSelector = fields.SelectorFromSet(map[string]string{
|
||||
"involvedObject.name": q.GetResourceName(),
|
||||
@@ -523,7 +526,11 @@ func (s *Server) ListResourceEvents(ctx context.Context, q *application.Applicat
|
||||
}
|
||||
log.Infof("Querying for resource events with field selector: %s", fieldSelector)
|
||||
opts := metav1.ListOptions{FieldSelector: fieldSelector}
|
||||
return kubeClientset.CoreV1().Events(namespace).List(ctx, opts)
|
||||
list, err := kubeClientset.CoreV1().Events(namespace).List(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing resource events: %w", err)
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (s *Server) validateAndUpdateApp(ctx context.Context, newApp *appv1.Application, merge bool, validate bool) (*appv1.Application, error) {
|
||||
@@ -532,15 +539,19 @@ func (s *Server) validateAndUpdateApp(ctx context.Context, newApp *appv1.Applica
|
||||
|
||||
app, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, newApp.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting application: %w", err)
|
||||
}
|
||||
|
||||
err = s.validateAndNormalizeApp(ctx, newApp, validate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error validating and normalizing app: %w", err)
|
||||
}
|
||||
|
||||
return s.updateApp(app, newApp, ctx, merge)
|
||||
a, err := s.updateApp(app, newApp, ctx, merge)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error updating application: %w", err)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func mergeStringMaps(items ...map[string]string) map[string]string {
|
||||
@@ -614,7 +625,7 @@ func (s *Server) updateApp(app *appv1.Application, newApp *appv1.Application, ct
|
||||
|
||||
app, err = s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, newApp.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting application: %w", err)
|
||||
}
|
||||
}
|
||||
return nil, status.Errorf(codes.Internal, "Failed to update application. Too many conflicts")
|
||||
@@ -640,7 +651,7 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat
|
||||
}
|
||||
a, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, *q.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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 {
|
||||
return nil, err
|
||||
@@ -652,7 +663,7 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat
|
||||
}
|
||||
a, err = s.validateAndUpdateApp(ctx, a, false, validate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error validating and updating app: %w", err)
|
||||
}
|
||||
return &a.Spec, nil
|
||||
}
|
||||
@@ -662,7 +673,7 @@ func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchReque
|
||||
|
||||
app, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Get(ctx, *q.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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 {
|
||||
@@ -671,7 +682,7 @@ func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchReque
|
||||
|
||||
jsonApp, err := json.Marshal(app)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error marshaling application: %w", err)
|
||||
}
|
||||
|
||||
var patchApp []byte
|
||||
@@ -680,16 +691,16 @@ func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchReque
|
||||
case "json", "":
|
||||
patch, err := jsonpatch.DecodePatch([]byte(q.GetPatch()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error decoding json patch: %w", err)
|
||||
}
|
||||
patchApp, err = patch.Apply(jsonApp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error applying patch: %w", err)
|
||||
}
|
||||
case "merge":
|
||||
patchApp, err = jsonpatch.MergePatch(jsonApp, []byte(q.GetPatch()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error calculating merge patch: %w", err)
|
||||
}
|
||||
default:
|
||||
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Patch type '%s' is not supported", q.GetPatchType()))
|
||||
@@ -698,7 +709,7 @@ func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchReque
|
||||
newApp := &v1alpha1.Application{}
|
||||
err = json.Unmarshal(patchApp, newApp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error unmarshaling patched app: %w", err)
|
||||
}
|
||||
return s.validateAndUpdateApp(ctx, newApp, false, true)
|
||||
}
|
||||
@@ -707,7 +718,7 @@ func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchReque
|
||||
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{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting application: %w", err)
|
||||
}
|
||||
|
||||
s.projectLock.RLock(a.Spec.Project)
|
||||
@@ -749,17 +760,17 @@ func (s *Server) Delete(ctx context.Context, q *application.ApplicationDeleteReq
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error marshaling finalizers: %w", err)
|
||||
}
|
||||
_, err = s.appclientset.ArgoprojV1alpha1().Applications(a.Namespace).Patch(ctx, a.Name, types.MergePatchType, patch, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error patching application with finalizers: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Delete(ctx, *q.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error deleting application: %w", err)
|
||||
}
|
||||
s.logAppEvent(a, ctx, argo.EventReasonResourceDeleted, "deleted application")
|
||||
return &application.ApplicationResponse{}, nil
|
||||
@@ -777,7 +788,7 @@ func (s *Server) Watch(q *application.ApplicationQuery, ws application.Applicati
|
||||
claims := ws.Context().Value("claims")
|
||||
selector, err := labels.Parse(q.GetSelector())
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error parsing labels with selectors: %w", err)
|
||||
}
|
||||
minVersion := 0
|
||||
if q.GetResourceVersion() != "" {
|
||||
@@ -823,7 +834,7 @@ func (s *Server) Watch(q *application.ApplicationQuery, ws application.Applicati
|
||||
if q.GetResourceVersion() == "" || q.GetName() != "" {
|
||||
apps, err := s.appLister.List(selector)
|
||||
if err != nil {
|
||||
return err
|
||||
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
|
||||
@@ -850,12 +861,12 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica
|
||||
if apierr.IsNotFound(err) {
|
||||
return status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", app.Spec.Project)
|
||||
}
|
||||
return err
|
||||
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 err != nil {
|
||||
if !apierr.IsNotFound(err) {
|
||||
return err
|
||||
return fmt.Errorf("error getting application by name: %w", err)
|
||||
}
|
||||
// Kubernetes go-client will return a pointer to a zero-value app instead of nil, even
|
||||
// though the API response was NotFound. This behavior was confirmed via logs.
|
||||
@@ -876,15 +887,15 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica
|
||||
// If source is Kustomize add build options
|
||||
kustomizeSettings, err := s.settingsMgr.GetKustomizeSettings()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting kustomize settings: %w", err)
|
||||
}
|
||||
kustomizeOptions, err := kustomizeSettings.GetOptions(app.Spec.Source)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting kustomize options from settings: %w", err)
|
||||
}
|
||||
plugins, err := s.plugins()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting plugins: %w", err)
|
||||
}
|
||||
|
||||
if err := argo.ValidateDestination(ctx, &app.Spec.Destination, s.db); err != nil {
|
||||
@@ -895,7 +906,7 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica
|
||||
if validate {
|
||||
conditions, err = argo.ValidateRepo(ctx, app, s.repoClientset, s.db, kustomizeOptions, plugins, s.kubectl, proj, s.settingsMgr)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error validating the repo: %w", err)
|
||||
}
|
||||
if len(conditions) > 0 {
|
||||
return status.Errorf(codes.InvalidArgument, "application spec for %s is invalid: %s", app.Name, argo.FormatAppConditions(conditions))
|
||||
@@ -904,7 +915,7 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica
|
||||
|
||||
conditions, err = argo.ValidatePermissions(ctx, &app.Spec, proj, s.db)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error validating project permissions: %w", err)
|
||||
}
|
||||
if len(conditions) > 0 {
|
||||
return status.Errorf(codes.InvalidArgument, "application spec for %s is invalid: %s", app.Name, argo.FormatAppConditions(conditions))
|
||||
@@ -916,11 +927,11 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica
|
||||
|
||||
func (s *Server) getApplicationClusterConfig(ctx context.Context, a *appv1.Application) (*rest.Config, error) {
|
||||
if err := argo.ValidateDestination(ctx, &a.Spec.Destination, s.db); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error validating destination: %w", err)
|
||||
}
|
||||
clst, err := s.db.GetCluster(ctx, a.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting cluster: %w", err)
|
||||
}
|
||||
config := clst.RESTConfig()
|
||||
return config, err
|
||||
@@ -942,7 +953,7 @@ func (s *Server) getCachedAppState(ctx context.Context, a *appv1.Application, ge
|
||||
Refresh: pointer.StringPtr(string(appv1.RefreshTypeNormal)),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting application by query: %w", err)
|
||||
}
|
||||
return getFromCache()
|
||||
}
|
||||
@@ -954,13 +965,16 @@ func (s *Server) GetAppResources(ctx context.Context, a *appv1.Application) (*ap
|
||||
err := s.getCachedAppState(ctx, a, func() error {
|
||||
return s.cache.GetAppResourcesTree(a.Name, &tree)
|
||||
})
|
||||
return &tree, err
|
||||
if err != nil {
|
||||
return &tree, fmt.Errorf("error getting cached app state: %w", err)
|
||||
}
|
||||
return &tree, nil
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
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 {
|
||||
return nil, nil, nil, err
|
||||
@@ -968,7 +982,7 @@ func (s *Server) getAppLiveResource(ctx context.Context, action string, q *appli
|
||||
|
||||
tree, err := s.GetAppResources(ctx, a)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, nil, fmt.Errorf("error getting app resources: %w", err)
|
||||
}
|
||||
|
||||
found := tree.FindNode(q.GetGroup(), q.GetKind(), q.GetNamespace(), q.GetResourceName())
|
||||
@@ -977,7 +991,7 @@ func (s *Server) getAppLiveResource(ctx context.Context, action string, q *appli
|
||||
}
|
||||
config, err := s.getApplicationClusterConfig(ctx, a)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, nil, fmt.Errorf("error getting application cluster config: %w", err)
|
||||
}
|
||||
return found, config, a, nil
|
||||
}
|
||||
@@ -985,7 +999,7 @@ func (s *Server) getAppLiveResource(ctx context.Context, action string, q *appli
|
||||
func (s *Server) GetResource(ctx context.Context, q *application.ApplicationResourceRequest) (*application.ApplicationResourceResponse, error) {
|
||||
res, config, _, err := s.getAppLiveResource(ctx, rbacpolicy.ActionGet, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting app live resource: %w", err)
|
||||
}
|
||||
|
||||
// make sure to use specified resource version if provided
|
||||
@@ -994,15 +1008,15 @@ func (s *Server) GetResource(ctx context.Context, q *application.ApplicationReso
|
||||
}
|
||||
obj, err := s.kubectl.GetResource(ctx, config, res.GroupKindVersion(), res.Name, res.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting resource: %w", err)
|
||||
}
|
||||
obj, err = replaceSecretValues(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error replacing secret values: %w", err)
|
||||
}
|
||||
data, err := json.Marshal(obj.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error marshaling object: %w", err)
|
||||
}
|
||||
manifest := string(data)
|
||||
return &application.ApplicationResourceResponse{Manifest: &manifest}, nil
|
||||
@@ -1031,7 +1045,7 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe
|
||||
}
|
||||
res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionUpdate, resourceRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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 {
|
||||
return nil, err
|
||||
@@ -1043,15 +1057,15 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe
|
||||
if res.Kind == kube.SecretKind && res.Group == "" {
|
||||
return nil, fmt.Errorf("failed to patch Secret %s/%s", res.Namespace, res.Name)
|
||||
}
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error patching resource: %w", err)
|
||||
}
|
||||
manifest, err = replaceSecretValues(manifest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error replacing secret values: %w", err)
|
||||
}
|
||||
data, err := json.Marshal(manifest.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("erro marshaling manifest object: %w", err)
|
||||
}
|
||||
s.logAppEvent(a, ctx, argo.EventReasonResourceUpdated, fmt.Sprintf("patched resource %s/%s '%s'", q.GetGroup(), q.GetKind(), q.GetResourceName()))
|
||||
m := string(data)
|
||||
@@ -1072,7 +1086,7 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR
|
||||
}
|
||||
res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionDelete, resourceRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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 {
|
||||
return nil, err
|
||||
@@ -1091,7 +1105,7 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR
|
||||
}
|
||||
err = s.kubectl.DeleteResource(ctx, config, res.GroupKindVersion(), res.Name, res.Namespace, deleteOption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error deleting resource: %w", err)
|
||||
}
|
||||
s.logAppEvent(a, ctx, argo.EventReasonResourceDeleted, fmt.Sprintf("deleted resource %s/%s '%s'", q.GetGroup(), q.GetKind(), q.GetResourceName()))
|
||||
return &application.ApplicationResponse{}, nil
|
||||
@@ -1100,7 +1114,7 @@ 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())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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 {
|
||||
return nil, err
|
||||
@@ -1111,7 +1125,7 @@ func (s *Server) ResourceTree(ctx context.Context, q *application.ResourcesQuery
|
||||
func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application.ApplicationService_WatchResourceTreeServer) error {
|
||||
a, err := s.appLister.Get(q.GetApplicationName())
|
||||
if err != nil {
|
||||
return err
|
||||
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 {
|
||||
@@ -1122,7 +1136,7 @@ func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application
|
||||
var tree appv1.ApplicationTree
|
||||
err := s.cache.GetAppResourcesTree(q.GetApplicationName(), &tree)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting app resource tree: %w", err)
|
||||
}
|
||||
return ws.Send(&tree)
|
||||
})
|
||||
@@ -1131,24 +1145,24 @@ 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())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
repo, err := s.db.GetRepository(ctx, a.Spec.Source.RepoURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting repository by URL: %w", err)
|
||||
}
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting app project: %w", err)
|
||||
}
|
||||
conn, repoClient, err := s.repoClientset.NewRepoServerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error creating repo server client: %w", err)
|
||||
}
|
||||
defer ioutil.Close(conn)
|
||||
return repoClient.GetRevisionMetadata(ctx, &apiclient.RepoServerRevisionMetadataRequest{
|
||||
@@ -1168,17 +1182,17 @@ 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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting application: %s", err)
|
||||
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 {
|
||||
return nil, fmt.Errorf("error verifying rbac: %s", err)
|
||||
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)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting cached app state: %s", err)
|
||||
return nil, fmt.Errorf("error getting cached app state: %w", err)
|
||||
}
|
||||
res := &application.ManagedResourcesResponse{}
|
||||
for i := range items {
|
||||
@@ -1227,7 +1241,7 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application.
|
||||
|
||||
a, err := s.appLister.Get(q.GetName())
|
||||
if err != nil {
|
||||
return err
|
||||
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 {
|
||||
@@ -1241,7 +1255,7 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application.
|
||||
// In the future, logs RBAC will be always enforced and the parameter along with this check will be removed
|
||||
serverRBACLogEnforceEnable, err := s.settingsMgr.GetServerRBACLogEnforceEnable()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting RBAC log enforce enable: %w", err)
|
||||
}
|
||||
|
||||
if serverRBACLogEnforceEnable {
|
||||
@@ -1252,17 +1266,17 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application.
|
||||
|
||||
tree, err := s.GetAppResources(ws.Context(), a)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting app resource tree: %w", err)
|
||||
}
|
||||
|
||||
config, err := s.getApplicationClusterConfig(ws.Context(), a)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error getting application cluster config: %w", err)
|
||||
}
|
||||
|
||||
kubeClientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error creating kube client: %w", err)
|
||||
}
|
||||
|
||||
// from the tree find pods which match query of kind, group, and resource name
|
||||
@@ -1425,7 +1439,7 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
|
||||
appIf := s.appclientset.ArgoprojV1alpha1().Applications(s.ns)
|
||||
a, err := appIf.Get(ctx, *syncReq.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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)
|
||||
@@ -1433,11 +1447,11 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
|
||||
if apierr.IsNotFound(err) {
|
||||
return a, status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", a.Spec.Project)
|
||||
}
|
||||
return a, err
|
||||
return a, fmt.Errorf("error getting app project: %w", err)
|
||||
}
|
||||
|
||||
if !proj.Spec.SyncWindows.Matches(a).CanSync(true) {
|
||||
return a, status.Errorf(codes.PermissionDenied, "Cannot sync: Blocked by sync window")
|
||||
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 {
|
||||
@@ -1448,7 +1462,7 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
|
||||
return nil, err
|
||||
}
|
||||
if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.Automated != nil && !syncReq.GetDryRun() {
|
||||
return nil, status.Error(codes.FailedPrecondition, "Cannot use local sync when Automatic Sync Policy is enabled unless for dry run")
|
||||
return nil, status.Error(codes.FailedPrecondition, "cannot use local sync when Automatic Sync Policy is enabled unless for dry run")
|
||||
}
|
||||
}
|
||||
if a.DeletionTimestamp != nil {
|
||||
@@ -1508,25 +1522,26 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
|
||||
}
|
||||
|
||||
a, err = argo.SetAppOperation(appIf, *syncReq.Name, &op)
|
||||
if err == nil {
|
||||
partial := ""
|
||||
if len(syncReq.Resources) > 0 {
|
||||
partial = "partial "
|
||||
}
|
||||
reason := fmt.Sprintf("initiated %ssync to %s", partial, displayRevision)
|
||||
if syncReq.Manifests != nil {
|
||||
reason = fmt.Sprintf("initiated %ssync locally", partial)
|
||||
}
|
||||
s.logAppEvent(a, ctx, argo.EventReasonOperationStarted, reason)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error setting app operation: %w", err)
|
||||
}
|
||||
return a, err
|
||||
partial := ""
|
||||
if len(syncReq.Resources) > 0 {
|
||||
partial = "partial "
|
||||
}
|
||||
reason := fmt.Sprintf("initiated %ssync to %s", partial, displayRevision)
|
||||
if syncReq.Manifests != nil {
|
||||
reason = fmt.Sprintf("initiated %ssync locally", partial)
|
||||
}
|
||||
s.logAppEvent(a, ctx, argo.EventReasonOperationStarted, reason)
|
||||
return a, nil
|
||||
}
|
||||
|
||||
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{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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 {
|
||||
return nil, err
|
||||
@@ -1571,10 +1586,11 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat
|
||||
},
|
||||
}
|
||||
a, err = argo.SetAppOperation(appIf, *rollbackReq.Name, &op)
|
||||
if err == nil {
|
||||
s.logAppEvent(a, ctx, argo.EventReasonOperationStarted, fmt.Sprintf("initiated rollback to %d", rollbackReq.GetId()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error setting app operation: %w", err)
|
||||
}
|
||||
return a, err
|
||||
s.logAppEvent(a, ctx, argo.EventReasonOperationStarted, fmt.Sprintf("initiated rollback to %d", rollbackReq.GetId()))
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// resolveRevision resolves the revision specified either in the sync request, or the
|
||||
@@ -1589,11 +1605,11 @@ func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, sy
|
||||
}
|
||||
repo, err := s.db.GetRepository(ctx, app.Spec.Source.RepoURL)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", "", fmt.Errorf("error getting repository by URL: %w", err)
|
||||
}
|
||||
conn, repoClient, err := s.repoClientset.NewRepoServerClient()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", "", fmt.Errorf("error getting repo server client: %w", err)
|
||||
}
|
||||
defer io.Close(conn)
|
||||
|
||||
@@ -1610,7 +1626,7 @@ func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, sy
|
||||
AmbiguousRevision: ambiguousRevision,
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", "", fmt.Errorf("error resolving repo revision: %w", err)
|
||||
}
|
||||
return resolveRevisionResponse.Revision, resolveRevisionResponse.AmbiguousRevision, nil
|
||||
}
|
||||
@@ -1618,7 +1634,7 @@ 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{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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 {
|
||||
return nil, err
|
||||
@@ -1636,13 +1652,13 @@ func (s *Server) TerminateOperation(ctx context.Context, termOpReq *application.
|
||||
return &application.OperationTerminateResponse{}, nil
|
||||
}
|
||||
if !apierr.IsConflict(err) {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error updating application: %w", err)
|
||||
}
|
||||
log.Warnf("Failed to set operation for app '%s' due to update conflict. Retrying again...", *termOpReq.Name)
|
||||
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{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting application by name: %w", err)
|
||||
}
|
||||
}
|
||||
return nil, status.Errorf(codes.Internal, "Failed to terminate app. Too many conflicts")
|
||||
@@ -1671,24 +1687,24 @@ func (s *Server) logResourceEvent(res *appv1.ResourceNode, ctx context.Context,
|
||||
func (s *Server) ListResourceActions(ctx context.Context, q *application.ApplicationResourceRequest) (*application.ResourceActionsListResponse, error) {
|
||||
res, config, _, err := s.getAppLiveResource(ctx, rbacpolicy.ActionGet, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting app live resource: %w", err)
|
||||
}
|
||||
obj, err := s.kubectl.GetResource(ctx, config, res.GroupKindVersion(), res.Name, res.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting resource: %w", err)
|
||||
}
|
||||
resourceOverrides, err := s.settingsMgr.GetResourceOverrides()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting resource overrides: %w", err)
|
||||
}
|
||||
|
||||
availableActions, err := s.getAvailableActions(resourceOverrides, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting available actions: %w", err)
|
||||
}
|
||||
actionsPtr := []*appv1.ResourceAction{}
|
||||
for _, action := range availableActions {
|
||||
actionsPtr = append(actionsPtr, &action)
|
||||
for i := range availableActions {
|
||||
actionsPtr = append(actionsPtr, &availableActions[i])
|
||||
}
|
||||
|
||||
return &application.ResourceActionsListResponse{Actions: actionsPtr}, nil
|
||||
@@ -1701,14 +1717,14 @@ func (s *Server) getAvailableActions(resourceOverrides map[string]appv1.Resource
|
||||
|
||||
discoveryScript, err := luaVM.GetResourceActionDiscovery(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting Lua discovery script: %w", err)
|
||||
}
|
||||
if discoveryScript == "" {
|
||||
return []appv1.ResourceAction{}, nil
|
||||
}
|
||||
availableActions, err := luaVM.ExecuteResourceActionDiscovery(obj, discoveryScript)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error executing Lua discovery script: %w", err)
|
||||
}
|
||||
return availableActions, nil
|
||||
|
||||
@@ -1726,16 +1742,16 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA
|
||||
actionRequest := fmt.Sprintf("%s/%s/%s/%s", rbacpolicy.ActionAction, q.GetGroup(), q.GetKind(), q.GetAction())
|
||||
res, config, a, err := s.getAppLiveResource(ctx, actionRequest, resourceRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting app live resource: %w", err)
|
||||
}
|
||||
liveObj, err := s.kubectl.GetResource(ctx, config, res.GroupKindVersion(), res.Name, res.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting resource: %w", err)
|
||||
}
|
||||
|
||||
resourceOverrides, err := s.settingsMgr.GetResourceOverrides()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting resource overrides: %w", err)
|
||||
}
|
||||
|
||||
luaVM := lua.VM{
|
||||
@@ -1743,27 +1759,27 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA
|
||||
}
|
||||
action, err := luaVM.GetResourceAction(liveObj, q.GetAction())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting Lua resource action: %w", err)
|
||||
}
|
||||
|
||||
newObj, err := luaVM.ExecuteResourceAction(liveObj, action.ActionLua)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error executing Lua resource action: %w", err)
|
||||
}
|
||||
|
||||
newObjBytes, err := json.Marshal(newObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error marshaling new object: %w", err)
|
||||
}
|
||||
|
||||
liveObjBytes, err := json.Marshal(liveObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error marshaling live object: %w", err)
|
||||
}
|
||||
|
||||
diffBytes, err := jsonpatch.CreateMergePatch(liveObjBytes, newObjBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error calculating merge patch: %w", err)
|
||||
}
|
||||
if string(diffBytes) == "{}" {
|
||||
return &application.ApplicationResponse{}, nil
|
||||
@@ -1778,13 +1794,13 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA
|
||||
// * the other to update only status.
|
||||
nonStatusPatch, statusPatch, err := splitStatusPatch(diffBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error splitting status patch: %w", err)
|
||||
}
|
||||
if statusPatch != nil {
|
||||
_, err = s.kubectl.PatchResource(ctx, config, newObj.GroupVersionKind(), newObj.GetName(), newObj.GetNamespace(), types.MergePatchType, diffBytes, "status")
|
||||
if err != nil {
|
||||
if !apierr.IsNotFound(err) {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error patching resource: %w", err)
|
||||
}
|
||||
// K8s API server returns 404 NotFound when the CRD does not support the status subresource
|
||||
// if we get here, the CRD does not use the status subresource. We will fall back to a normal patch
|
||||
@@ -1797,7 +1813,7 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA
|
||||
if diffBytes != nil {
|
||||
_, err = s.kubectl.PatchResource(ctx, config, newObj.GroupVersionKind(), newObj.GetName(), newObj.GetNamespace(), types.MergePatchType, diffBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error patching resource: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1842,7 +1858,7 @@ func splitStatusPatch(patch []byte) ([]byte, []byte, error) {
|
||||
func (s *Server) plugins() ([]*v1alpha1.ConfigManagementPlugin, error) {
|
||||
plugins, err := s.settingsMgr.GetConfigManagementPlugins()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting config management plugin: %w", err)
|
||||
}
|
||||
tools := make([]*v1alpha1.ConfigManagementPlugin, len(plugins))
|
||||
for i, p := range plugins {
|
||||
@@ -1856,7 +1872,7 @@ func (s *Server) GetApplicationSyncWindows(ctx context.Context, q *application.A
|
||||
appIf := s.appclientset.ArgoprojV1alpha1().Applications(s.ns)
|
||||
a, err := appIf.Get(ctx, *q.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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 {
|
||||
@@ -1865,7 +1881,7 @@ func (s *Server) GetApplicationSyncWindows(ctx context.Context, q *application.A
|
||||
|
||||
proj, err := argo.GetAppProject(&a.Spec, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), a.Namespace, s.settingsMgr, s.db, ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error getting app project: %w", err)
|
||||
}
|
||||
|
||||
windows := proj.Spec.SyncWindows.Matches(a)
|
||||
|
||||
@@ -44,9 +44,9 @@ message RevisionMetadataQuery{
|
||||
// ApplicationEventsQuery is a query for application resource events
|
||||
message ApplicationResourceEventsQuery {
|
||||
required string name = 1;
|
||||
required string resourceNamespace = 2;
|
||||
required string resourceName = 3;
|
||||
required string resourceUID = 4;
|
||||
optional string resourceNamespace = 2;
|
||||
optional string resourceName = 3;
|
||||
optional string resourceUID = 4;
|
||||
}
|
||||
|
||||
// ManifestQuery is a query for manifest resources
|
||||
@@ -115,7 +115,7 @@ message ApplicationRollbackRequest {
|
||||
|
||||
message ApplicationResourceRequest {
|
||||
required string name = 1;
|
||||
required string namespace = 2;
|
||||
optional string namespace = 2;
|
||||
required string resourceName = 3;
|
||||
required string version = 4;
|
||||
optional string group = 5;
|
||||
@@ -124,7 +124,7 @@ message ApplicationResourceRequest {
|
||||
|
||||
message ApplicationResourcePatchRequest {
|
||||
required string name = 1;
|
||||
required string namespace = 2;
|
||||
optional string namespace = 2;
|
||||
required string resourceName = 3;
|
||||
required string version = 4;
|
||||
optional string group = 5;
|
||||
@@ -135,7 +135,7 @@ message ApplicationResourcePatchRequest {
|
||||
|
||||
message ApplicationResourceDeleteRequest {
|
||||
required string name = 1;
|
||||
required string namespace = 2;
|
||||
optional string namespace = 2;
|
||||
required string resourceName = 3;
|
||||
required string version = 4;
|
||||
optional string group = 5;
|
||||
@@ -146,7 +146,7 @@ message ApplicationResourceDeleteRequest {
|
||||
|
||||
message ResourceActionRunRequest {
|
||||
required string name = 1;
|
||||
required string namespace = 2;
|
||||
optional string namespace = 2;
|
||||
required string resourceName = 3;
|
||||
required string version = 4;
|
||||
optional string group = 5;
|
||||
@@ -164,7 +164,7 @@ message ApplicationResourceResponse {
|
||||
|
||||
message ApplicationPodLogsQuery {
|
||||
required string name = 1;
|
||||
required string namespace = 2;
|
||||
optional string namespace = 2;
|
||||
optional string podName = 3;
|
||||
optional string container = 4;
|
||||
optional int64 sinceSeconds = 5;
|
||||
|
||||
@@ -41,6 +41,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/cache"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/grpc"
|
||||
"github.com/argoproj/argo-cd/v2/util/rbac"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
@@ -643,7 +644,9 @@ p, admin, applications, update, default/test-app, allow
|
||||
p, admin, applications, update, my-proj/test-app, allow
|
||||
`)
|
||||
_, err = appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
assert.Equal(t, status.Code(err), codes.PermissionDenied)
|
||||
statusErr := grpc.UnwrapGRPCStatus(err)
|
||||
assert.NotNil(t, statusErr)
|
||||
assert.Equal(t, codes.PermissionDenied, statusErr.Code())
|
||||
|
||||
// Verify inability to change projects without update privileges in new project
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
@@ -659,7 +662,9 @@ p, admin, applications, create, my-proj/test-app, allow
|
||||
p, admin, applications, update, my-proj/test-app, allow
|
||||
`)
|
||||
_, err = appServer.Update(ctx, &application.ApplicationUpdateRequest{Application: testApp})
|
||||
assert.Equal(t, status.Code(err), codes.PermissionDenied)
|
||||
statusErr = grpc.UnwrapGRPCStatus(err)
|
||||
assert.NotNil(t, statusErr)
|
||||
assert.Equal(t, codes.PermissionDenied, statusErr.Code())
|
||||
|
||||
// Verify can update project with proper permissions
|
||||
_ = appServer.enf.SetBuiltinPolicy(`
|
||||
|
||||
@@ -2,11 +2,15 @@ package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
log "github.com/sirupsen/logrus"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
@@ -17,10 +21,10 @@ import (
|
||||
applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
|
||||
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"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/rbac"
|
||||
sessionmgr "github.com/argoproj/argo-cd/v2/util/session"
|
||||
)
|
||||
|
||||
type terminalHandler struct {
|
||||
@@ -54,27 +58,76 @@ func (s *terminalHandler) getApplicationClusterRawConfig(ctx context.Context, a
|
||||
return clst.RawRestConfig(), nil
|
||||
}
|
||||
|
||||
// isValidPodName checks that a podName is valid
|
||||
func isValidPodName(name string) bool {
|
||||
// https://github.com/kubernetes/kubernetes/blob/976a940f4a4e84fe814583848f97b9aafcdb083f/pkg/apis/core/validation/validation.go#L241
|
||||
validationErrors := apimachineryvalidation.NameIsDNSSubdomain(name, false)
|
||||
return len(validationErrors) == 0
|
||||
}
|
||||
|
||||
func isValidAppName(name string) bool {
|
||||
// app names have the same rules as pods.
|
||||
return isValidPodName(name)
|
||||
}
|
||||
|
||||
func isValidProjectName(name string) bool {
|
||||
// project names have the same rules as pods.
|
||||
return isValidPodName(name)
|
||||
}
|
||||
|
||||
// isValidNamespaceName checks that a namespace name is valid
|
||||
func isValidNamespaceName(name string) bool {
|
||||
// https://github.com/kubernetes/kubernetes/blob/976a940f4a4e84fe814583848f97b9aafcdb083f/pkg/apis/core/validation/validation.go#L262
|
||||
validationErrors := apimachineryvalidation.ValidateNamespaceName(name, false)
|
||||
return len(validationErrors) == 0
|
||||
}
|
||||
|
||||
// isValidContainerName checks that a containerName is valid
|
||||
func isValidContainerName(name string) bool {
|
||||
// https://github.com/kubernetes/kubernetes/blob/53a9d106c4aabcd550cc32ae4e8004f32fb0ae7b/pkg/api/validation/validation.go#L280
|
||||
validationErrors := apimachineryvalidation.NameIsDNSLabel(name, false)
|
||||
return len(validationErrors) == 0
|
||||
}
|
||||
|
||||
func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
|
||||
podName := q.Get("pod")
|
||||
container := q.Get("container")
|
||||
app := q.Get("appName")
|
||||
project := q.Get("projectName")
|
||||
namespace := q.Get("namespace")
|
||||
shell := q.Get("shell")
|
||||
|
||||
if podName == "" || container == "" || app == "" || namespace == "" {
|
||||
if podName == "" || container == "" || app == "" || project == "" || namespace == "" {
|
||||
http.Error(w, "Missing required parameters", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
a, err := s.appLister.Get(app)
|
||||
if err != nil {
|
||||
http.Error(w, "Cannot get app", http.StatusBadRequest)
|
||||
if !isValidPodName(podName) {
|
||||
http.Error(w, "Pod name is not valid", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if !isValidContainerName(container) {
|
||||
http.Error(w, "Container name is not valid", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if !isValidAppName(app) {
|
||||
http.Error(w, "App name is not valid", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if !isValidProjectName(project) {
|
||||
http.Error(w, "Project name is not valid", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if !isValidNamespaceName(namespace) {
|
||||
http.Error(w, "Namespace name is not valid", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
shell := q.Get("shell") // No need to validate. Will only buse used if it's in the allow-list.
|
||||
|
||||
appRBACName := apputil.AppRBACName(*a)
|
||||
ctx := r.Context()
|
||||
|
||||
appRBACName := fmt.Sprintf("%s/%s", project, app)
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, appRBACName); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
@@ -85,6 +138,26 @@ func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
if apierr.IsNotFound(err) {
|
||||
http.Error(w, "App not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
fieldLog.Errorf("Error when getting app %q when launching a terminal: %s", app, err)
|
||||
http.Error(w, "Cannot get app", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if a.Spec.Project != project {
|
||||
fieldLog.Warnf("The wrong project (%q) was specified for the app %q when launching a terminal", project, app)
|
||||
http.Error(w, "The wrong project was specified for the app", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := s.getApplicationClusterRawConfig(ctx, a)
|
||||
if err != nil {
|
||||
http.Error(w, "Cannot get raw cluster config", http.StatusBadRequest)
|
||||
@@ -111,6 +184,7 @@ func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
pod, err := kubeClientset.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
fieldLog.Errorf("error retrieving pod: %s", err)
|
||||
http.Error(w, "Cannot find pod", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -128,10 +202,13 @@ func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
if !findContainer {
|
||||
fieldLog.Warn("terminal container not found")
|
||||
http.Error(w, "Cannot find container", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
fieldLog.Info("terminal session starting")
|
||||
|
||||
session, err := newTerminalSession(w, r, nil)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to start terminal session", http.StatusBadRequest)
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
@@ -74,3 +78,146 @@ func TestPodExists(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidPodName(t *testing.T) {
|
||||
for _, tcase := range []struct {
|
||||
name string
|
||||
resourceName string
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
name: "valid pod name",
|
||||
resourceName: "argocd-server-794644486d-r8v9d",
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "not valid contains spaces",
|
||||
resourceName: "kubectl delete pods",
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "not valid",
|
||||
resourceName: "kubectl -n kube-system delete pods --all",
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "not valid contains special characters",
|
||||
resourceName: "delete+*+from+etcd%3b",
|
||||
expectedResult: false,
|
||||
},
|
||||
} {
|
||||
t.Run(tcase.name, func(t *testing.T) {
|
||||
result := isValidPodName(tcase.resourceName)
|
||||
if result != tcase.expectedResult {
|
||||
t.Errorf("Expected result %v, but got %v", tcase.expectedResult, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidNamespaceName(t *testing.T) {
|
||||
for _, tcase := range []struct {
|
||||
name string
|
||||
resourceName string
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
name: "valid pod namespace name",
|
||||
resourceName: "argocd",
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "not valid contains spaces",
|
||||
resourceName: "kubectl delete ns argocd",
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "not valid contains special characters",
|
||||
resourceName: "delete+*+from+etcd%3b",
|
||||
expectedResult: false,
|
||||
},
|
||||
} {
|
||||
t.Run(tcase.name, func(t *testing.T) {
|
||||
result := isValidNamespaceName(tcase.resourceName)
|
||||
if result != tcase.expectedResult {
|
||||
t.Errorf("Expected result %v, but got %v", tcase.expectedResult, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidContainerNameName(t *testing.T) {
|
||||
for _, tcase := range []struct {
|
||||
name string
|
||||
resourceName string
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
name: "valid container name",
|
||||
resourceName: "argocd-server",
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "not valid contains spaces",
|
||||
resourceName: "kubectl delete pods",
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "not valid contains special characters",
|
||||
resourceName: "delete+*+from+etcd%3b",
|
||||
expectedResult: false,
|
||||
},
|
||||
} {
|
||||
t.Run(tcase.name, func(t *testing.T) {
|
||||
result := isValidContainerName(tcase.resourceName)
|
||||
if result != tcase.expectedResult {
|
||||
t.Errorf("Expected result %v, but got %v", tcase.expectedResult, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTerminalHandler_ServeHTTP_empty_params(t *testing.T) {
|
||||
testKeys := []string{
|
||||
"pod",
|
||||
"container",
|
||||
"app",
|
||||
"project",
|
||||
"namespace",
|
||||
}
|
||||
|
||||
// test both empty and invalid
|
||||
testValues := []string{"", "invalid%20name"}
|
||||
|
||||
for _, testKey := range testKeys {
|
||||
testKeyCopy := testKey
|
||||
|
||||
for _, testValue := range testValues {
|
||||
testValueCopy := testValue
|
||||
|
||||
t.Run(testKeyCopy+ " " + testValueCopy, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := terminalHandler{}
|
||||
params := map[string]string{
|
||||
"pod": "valid",
|
||||
"container": "valid",
|
||||
"app": "valid",
|
||||
"project": "valid",
|
||||
"namespace": "valid",
|
||||
}
|
||||
params[testKeyCopy] = testValueCopy
|
||||
var paramsArray []string
|
||||
for key, value := range params {
|
||||
paramsArray = append(paramsArray, key + "=" + value)
|
||||
}
|
||||
paramsString := strings.Join(paramsArray, "&")
|
||||
request := httptest.NewRequest("GET", "https://argocd.example.com/api/v1/terminal?" + paramsString, nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
handler.ServeHTTP(recorder, request)
|
||||
response := recorder.Result()
|
||||
assert.Equal(t, http.StatusBadRequest, response.StatusCode)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,6 +721,8 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
var dOpts []grpc.DialOption
|
||||
dOpts = append(dOpts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(apiclient.MaxGRPCMessageSize)))
|
||||
dOpts = append(dOpts, grpc.WithUserAgent(fmt.Sprintf("%s/%s", common.ArgoCDUserAgentName, common.GetVersion().Version)))
|
||||
dOpts = append(dOpts, grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()))
|
||||
dOpts = append(dOpts, grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()))
|
||||
if a.useTLS() {
|
||||
// The following sets up the dial Options for grpc-gateway to talk to gRPC server over TLS.
|
||||
// grpc-gateway is just translating HTTP/HTTPS requests as gRPC requests over localhost,
|
||||
@@ -752,34 +754,41 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
handler = compressHandler(handler)
|
||||
}
|
||||
mux.Handle("/api/", handler)
|
||||
if a.settings.ExecEnabled {
|
||||
terminalHandler := application.NewHandler(a.appLister, a.db, a.enf, a.Cache, appResourceTreeFn)
|
||||
mux.HandleFunc("/terminal", func(writer http.ResponseWriter, request *http.Request) {
|
||||
if !a.DisableAuth {
|
||||
ctx := request.Context()
|
||||
cookies := request.Cookies()
|
||||
tokenString, err := httputil.JoinCookies(common.AuthCookieName, cookies)
|
||||
if err == nil && jwtutil.IsValid(tokenString) {
|
||||
claims, _, err := a.sessionMgr.VerifyToken(tokenString)
|
||||
if err != nil {
|
||||
// nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, util_session.AuthErrorCtxKey, err)
|
||||
} else if claims != nil {
|
||||
// Add claims to the context to inspect for RBAC
|
||||
// nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", claims)
|
||||
}
|
||||
request = request.WithContext(ctx)
|
||||
} else {
|
||||
writer.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
|
||||
terminalHandler := application.NewHandler(a.appLister, a.db, a.enf, a.Cache, appResourceTreeFn)
|
||||
mux.HandleFunc("/terminal", func(writer http.ResponseWriter, request *http.Request) {
|
||||
argocdSettings, err := a.settingsMgr.GetSettings()
|
||||
if err != nil {
|
||||
http.Error(writer, fmt.Sprintf("Failed to get settings: %v", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if !argocdSettings.ExecEnabled {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if !a.DisableAuth {
|
||||
ctx := request.Context()
|
||||
cookies := request.Cookies()
|
||||
tokenString, err := httputil.JoinCookies(common.AuthCookieName, cookies)
|
||||
if err == nil && jwtutil.IsValid(tokenString) {
|
||||
claims, _, err := a.sessionMgr.VerifyToken(tokenString)
|
||||
if err != nil {
|
||||
// nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, util_session.AuthErrorCtxKey, err)
|
||||
} else if claims != nil {
|
||||
// Add claims to the context to inspect for RBAC
|
||||
// nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", claims)
|
||||
}
|
||||
request = request.WithContext(ctx)
|
||||
} else {
|
||||
writer.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
terminalHandler.ServeHTTP(writer, request)
|
||||
})
|
||||
} else {
|
||||
log.Info("exec is disabled, and /terminal will return a 404")
|
||||
}
|
||||
}
|
||||
terminalHandler.ServeHTTP(writer, request)
|
||||
})
|
||||
|
||||
mustRegisterGWHandler(versionpkg.RegisterVersionServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
mustRegisterGWHandler(clusterpkg.RegisterClusterServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
|
||||
@@ -1000,6 +1009,8 @@ func (a *ArgoCDServer) Authenticate(ctx context.Context) (context.Context, error
|
||||
}
|
||||
if !argoCDSettings.AnonymousUserEnabled {
|
||||
return ctx, claimsErr
|
||||
} else {
|
||||
ctx = context.WithValue(ctx, "claims", "")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ package server
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -13,6 +15,7 @@ import (
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/metadata"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
@@ -434,6 +437,392 @@ func TestAuthenticate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func dexMockHandler(t *testing.T, url string) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
switch r.RequestURI {
|
||||
case "/api/dex/.well-known/openid-configuration":
|
||||
_, err := io.WriteString(w, fmt.Sprintf(`
|
||||
{
|
||||
"issuer": "%[1]s/api/dex",
|
||||
"authorization_endpoint": "%[1]s/api/dex/auth",
|
||||
"token_endpoint": "%[1]s/api/dex/token",
|
||||
"jwks_uri": "%[1]s/api/dex/keys",
|
||||
"userinfo_endpoint": "%[1]s/api/dex/userinfo",
|
||||
"device_authorization_endpoint": "%[1]s/api/dex/device/code",
|
||||
"grant_types_supported": [
|
||||
"authorization_code",
|
||||
"refresh_token",
|
||||
"urn:ietf:params:oauth:grant-type:device_code"
|
||||
],
|
||||
"response_types_supported": [
|
||||
"code"
|
||||
],
|
||||
"subject_types_supported": [
|
||||
"public"
|
||||
],
|
||||
"id_token_signing_alg_values_supported": [
|
||||
"RS256", "HS256"
|
||||
],
|
||||
"code_challenge_methods_supported": [
|
||||
"S256",
|
||||
"plain"
|
||||
],
|
||||
"scopes_supported": [
|
||||
"openid",
|
||||
"email",
|
||||
"groups",
|
||||
"profile",
|
||||
"offline_access"
|
||||
],
|
||||
"token_endpoint_auth_methods_supported": [
|
||||
"client_secret_basic",
|
||||
"client_secret_post"
|
||||
],
|
||||
"claims_supported": [
|
||||
"iss",
|
||||
"sub",
|
||||
"aud",
|
||||
"iat",
|
||||
"exp",
|
||||
"email",
|
||||
"email_verified",
|
||||
"locale",
|
||||
"name",
|
||||
"preferred_username",
|
||||
"at_hash"
|
||||
]
|
||||
}`, url))
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
default:
|
||||
w.WriteHeader(404)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getTestServer(t *testing.T, anonymousEnabled bool, withFakeSSO bool) (argocd *ArgoCDServer, dexURL string) {
|
||||
cm := test.NewFakeConfigMap()
|
||||
if anonymousEnabled {
|
||||
cm.Data["users.anonymous.enabled"] = "true"
|
||||
}
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
return // Start with a placeholder. We need the server URL before setting up the real handler.
|
||||
}))
|
||||
ts.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
dexMockHandler(t, ts.URL)(w, r)
|
||||
})
|
||||
if withFakeSSO {
|
||||
cm.Data["url"] = ts.URL
|
||||
cm.Data["dex.config"] = `
|
||||
connectors:
|
||||
# OIDC
|
||||
- type: OIDC
|
||||
id: oidc
|
||||
name: OIDC
|
||||
config:
|
||||
issuer: https://auth.example.gom
|
||||
clientID: test-client
|
||||
clientSecret: $dex.oidc.clientSecret`
|
||||
}
|
||||
secret := test.NewFakeSecret()
|
||||
kubeclientset := fake.NewSimpleClientset(cm, secret)
|
||||
appClientSet := apps.NewSimpleClientset()
|
||||
argoCDOpts := ArgoCDServerOpts{
|
||||
Namespace: test.FakeArgoCDNamespace,
|
||||
KubeClientset: kubeclientset,
|
||||
AppClientset: appClientSet,
|
||||
}
|
||||
if withFakeSSO {
|
||||
argoCDOpts.DexServerAddr = ts.URL
|
||||
}
|
||||
argocd = NewServer(context.Background(), argoCDOpts)
|
||||
return argocd, ts.URL
|
||||
}
|
||||
|
||||
func TestAuthenticate_3rd_party_JWTs(t *testing.T) {
|
||||
type testData struct {
|
||||
test string
|
||||
anonymousEnabled bool
|
||||
claims jwt.RegisteredClaims
|
||||
expectedErrorContains string
|
||||
expectedClaims interface{}
|
||||
}
|
||||
var tests = []testData{
|
||||
{
|
||||
test: "anonymous disabled, no audience",
|
||||
anonymousEnabled: false,
|
||||
claims: jwt.RegisteredClaims{},
|
||||
expectedErrorContains: "no audience found in the token",
|
||||
expectedClaims: nil,
|
||||
},
|
||||
{
|
||||
test: "anonymous enabled, no audience",
|
||||
anonymousEnabled: true,
|
||||
claims: jwt.RegisteredClaims{},
|
||||
expectedErrorContains: "",
|
||||
expectedClaims: "",
|
||||
},
|
||||
{
|
||||
test: "anonymous disabled, unexpired token, admin claim",
|
||||
anonymousEnabled: false,
|
||||
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))},
|
||||
expectedErrorContains: "id token signed with unsupported algorithm",
|
||||
expectedClaims: nil,
|
||||
},
|
||||
{
|
||||
test: "anonymous enabled, unexpired token, admin claim",
|
||||
anonymousEnabled: true,
|
||||
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))},
|
||||
expectedErrorContains: "",
|
||||
expectedClaims: "",
|
||||
},
|
||||
{
|
||||
test: "anonymous disabled, expired token, admin claim",
|
||||
anonymousEnabled: false,
|
||||
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())},
|
||||
expectedErrorContains: "token is expired",
|
||||
expectedClaims: jwt.RegisteredClaims{Issuer:"sso"},
|
||||
},
|
||||
{
|
||||
test: "anonymous enabled, expired token, admin claim",
|
||||
anonymousEnabled: true,
|
||||
claims: jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now())},
|
||||
expectedErrorContains: "",
|
||||
expectedClaims: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testData := range tests {
|
||||
testDataCopy := testData
|
||||
|
||||
t.Run(testDataCopy.test, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Must be declared here to avoid race.
|
||||
ctx := context.Background() //nolint:ineffassign,staticcheck
|
||||
|
||||
argocd, dexURL := getTestServer(t, testDataCopy.anonymousEnabled, true)
|
||||
testDataCopy.claims.Issuer = fmt.Sprintf("%s/api/dex", dexURL)
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, testDataCopy.claims)
|
||||
tokenString, err := token.SignedString([]byte("key"))
|
||||
require.NoError(t, err)
|
||||
ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs(apiclient.MetaDataTokenKey, tokenString))
|
||||
|
||||
ctx, err = argocd.Authenticate(ctx)
|
||||
claims := ctx.Value("claims")
|
||||
if testDataCopy.expectedClaims == nil {
|
||||
assert.Nil(t, claims)
|
||||
} else {
|
||||
assert.Equal(t, testDataCopy.expectedClaims, claims)
|
||||
}
|
||||
if testDataCopy.expectedErrorContains != "" {
|
||||
assert.ErrorContains(t, err, testDataCopy.expectedErrorContains, "Authenticate should have thrown an error and blocked the request")
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthenticate_no_request_metadata(t *testing.T) {
|
||||
type testData struct {
|
||||
test string
|
||||
anonymousEnabled bool
|
||||
expectedErrorContains string
|
||||
expectedClaims interface{}
|
||||
}
|
||||
var tests = []testData{
|
||||
{
|
||||
test: "anonymous disabled",
|
||||
anonymousEnabled: false,
|
||||
expectedErrorContains: "no session information",
|
||||
expectedClaims: nil,
|
||||
},
|
||||
{
|
||||
test: "anonymous enabled",
|
||||
anonymousEnabled: true,
|
||||
expectedErrorContains: "",
|
||||
expectedClaims: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testData := range tests {
|
||||
testDataCopy := testData
|
||||
|
||||
t.Run(testDataCopy.test, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
argocd, _ := getTestServer(t, testDataCopy.anonymousEnabled, true)
|
||||
ctx := context.Background()
|
||||
|
||||
ctx, err := argocd.Authenticate(ctx)
|
||||
claims := ctx.Value("claims")
|
||||
assert.Equal(t, testDataCopy.expectedClaims, claims)
|
||||
if testDataCopy.expectedErrorContains != "" {
|
||||
assert.ErrorContains(t, err, testDataCopy.expectedErrorContains, "Authenticate should have thrown an error and blocked the request")
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthenticate_no_SSO(t *testing.T) {
|
||||
type testData struct {
|
||||
test string
|
||||
anonymousEnabled bool
|
||||
expectedErrorMessage string
|
||||
expectedClaims interface{}
|
||||
}
|
||||
var tests = []testData{
|
||||
{
|
||||
test: "anonymous disabled",
|
||||
anonymousEnabled: false,
|
||||
expectedErrorMessage: "SSO is not configured",
|
||||
expectedClaims: nil,
|
||||
},
|
||||
{
|
||||
test: "anonymous enabled",
|
||||
anonymousEnabled: true,
|
||||
expectedErrorMessage: "",
|
||||
expectedClaims: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testData := range tests {
|
||||
testDataCopy := testData
|
||||
|
||||
t.Run(testDataCopy.test, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Must be declared here to avoid race.
|
||||
ctx := context.Background() //nolint:ineffassign,staticcheck
|
||||
|
||||
argocd, dexURL := getTestServer(t, testDataCopy.anonymousEnabled, false)
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{Issuer: fmt.Sprintf("%s/api/dex", dexURL)})
|
||||
tokenString, err := token.SignedString([]byte("key"))
|
||||
require.NoError(t, err)
|
||||
ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs(apiclient.MetaDataTokenKey, tokenString))
|
||||
|
||||
ctx, err = argocd.Authenticate(ctx)
|
||||
claims := ctx.Value("claims")
|
||||
assert.Equal(t, testDataCopy.expectedClaims, claims)
|
||||
if testDataCopy.expectedErrorMessage != "" {
|
||||
assert.ErrorContains(t, err, testDataCopy.expectedErrorMessage, "Authenticate should have thrown an error and blocked the request")
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthenticate_bad_request_metadata(t *testing.T) {
|
||||
type testData struct {
|
||||
test string
|
||||
anonymousEnabled bool
|
||||
metadata metadata.MD
|
||||
expectedErrorMessage string
|
||||
expectedClaims interface{}
|
||||
}
|
||||
var tests = []testData{
|
||||
{
|
||||
test: "anonymous disabled, empty metadata",
|
||||
anonymousEnabled: false,
|
||||
metadata: metadata.MD{},
|
||||
expectedErrorMessage: "no session information",
|
||||
expectedClaims: nil,
|
||||
},
|
||||
{
|
||||
test: "anonymous enabled, empty metadata",
|
||||
anonymousEnabled: true,
|
||||
metadata: metadata.MD{},
|
||||
expectedErrorMessage: "",
|
||||
expectedClaims: "",
|
||||
},
|
||||
{
|
||||
test: "anonymous disabled, empty tokens",
|
||||
anonymousEnabled: false,
|
||||
metadata: metadata.MD{apiclient.MetaDataTokenKey: []string{}},
|
||||
expectedErrorMessage: "no session information",
|
||||
expectedClaims: nil,
|
||||
},
|
||||
{
|
||||
test: "anonymous enabled, empty tokens",
|
||||
anonymousEnabled: true,
|
||||
metadata: metadata.MD{apiclient.MetaDataTokenKey: []string{}},
|
||||
expectedErrorMessage: "",
|
||||
expectedClaims: "",
|
||||
},
|
||||
{
|
||||
test: "anonymous disabled, bad tokens",
|
||||
anonymousEnabled: false,
|
||||
metadata: metadata.Pairs(apiclient.MetaDataTokenKey, "bad"),
|
||||
expectedErrorMessage: "token contains an invalid number of segments",
|
||||
expectedClaims: nil,
|
||||
},
|
||||
{
|
||||
test: "anonymous enabled, bad tokens",
|
||||
anonymousEnabled: true,
|
||||
metadata: metadata.Pairs(apiclient.MetaDataTokenKey, "bad"),
|
||||
expectedErrorMessage: "",
|
||||
expectedClaims: "",
|
||||
},
|
||||
{
|
||||
test: "anonymous disabled, bad auth header",
|
||||
anonymousEnabled: false,
|
||||
metadata: metadata.MD{"authorization": []string{"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.TGGTTHuuGpEU8WgobXxkrBtW3NiR3dgw5LR-1DEW3BQ"}},
|
||||
expectedErrorMessage: "no audience found in the token",
|
||||
expectedClaims: nil,
|
||||
},
|
||||
{
|
||||
test: "anonymous enabled, bad auth header",
|
||||
anonymousEnabled: true,
|
||||
metadata: metadata.MD{"authorization": []string{"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.TGGTTHuuGpEU8WgobXxkrBtW3NiR3dgw5LR-1DEW3BQ"}},
|
||||
expectedErrorMessage: "",
|
||||
expectedClaims: "",
|
||||
},
|
||||
{
|
||||
test: "anonymous disabled, bad auth cookie",
|
||||
anonymousEnabled: false,
|
||||
metadata: metadata.MD{"grpcgateway-cookie": []string{"argocd.token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.TGGTTHuuGpEU8WgobXxkrBtW3NiR3dgw5LR-1DEW3BQ"}},
|
||||
expectedErrorMessage: "no audience found in the token",
|
||||
expectedClaims: nil,
|
||||
},
|
||||
{
|
||||
test: "anonymous enabled, bad auth cookie",
|
||||
anonymousEnabled: true,
|
||||
metadata: metadata.MD{"grpcgateway-cookie": []string{"argocd.token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiJ9.TGGTTHuuGpEU8WgobXxkrBtW3NiR3dgw5LR-1DEW3BQ"}},
|
||||
expectedErrorMessage: "",
|
||||
expectedClaims: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testData := range tests {
|
||||
testDataCopy := testData
|
||||
|
||||
t.Run(testDataCopy.test, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Must be declared here to avoid race.
|
||||
ctx := context.Background() //nolint:ineffassign,staticcheck
|
||||
|
||||
argocd, _ := getTestServer(t, testDataCopy.anonymousEnabled, true)
|
||||
ctx = metadata.NewIncomingContext(context.Background(), testDataCopy.metadata)
|
||||
|
||||
ctx, err := argocd.Authenticate(ctx)
|
||||
claims := ctx.Value("claims")
|
||||
assert.Equal(t, testDataCopy.expectedClaims, claims)
|
||||
if testDataCopy.expectedErrorMessage != "" {
|
||||
assert.ErrorContains(t, err, testDataCopy.expectedErrorMessage, "Authenticate should have thrown an error and blocked the request")
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getToken(t *testing.T) {
|
||||
token := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
|
||||
@@ -6,7 +6,7 @@ FROM golang:1.18 as golang
|
||||
|
||||
FROM registry:2.8 as registry
|
||||
|
||||
FROM ubuntu:21.10
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && apt-get install --fix-missing -y \
|
||||
@@ -61,6 +61,11 @@ COPY ./test/fixture/testrepos/ssh_host_*_key* /etc/ssh/
|
||||
# Copy redis binaries to the image
|
||||
COPY --from=redis /usr/local/bin/* /usr/local/bin/
|
||||
|
||||
# Copy redis dependencies/shared libraries
|
||||
# Ubuntu 22.04+ has moved to OpenSSL3 and no longer provides these libraries
|
||||
COPY --from=redis /usr/lib/x86_64-linux-gnu/libssl.so.1.1 /usr/lib/x86_64-linux-gnu/
|
||||
COPY --from=redis /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 /usr/lib/x86_64-linux-gnu/
|
||||
|
||||
# Copy registry binaries to the image
|
||||
COPY --from=registry /bin/registry /usr/local/bin/
|
||||
COPY --from=registry /etc/docker/registry/config.yml /etc/docker/registry/config.yml
|
||||
|
||||
@@ -102,7 +102,7 @@ func TestCustomToolWithEnv(t *testing.T) {
|
||||
Name: Name(),
|
||||
Generate: Command{
|
||||
Command: []string{"sh", "-c"},
|
||||
Args: []string{`echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"`},
|
||||
Args: []string{`echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$ARGOCD_ENV_FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"`},
|
||||
},
|
||||
},
|
||||
).
|
||||
@@ -162,7 +162,7 @@ func TestCustomToolSyncAndDiffLocal(t *testing.T) {
|
||||
Name: Name(),
|
||||
Generate: Command{
|
||||
Command: []string{"sh", "-c"},
|
||||
Args: []string{`echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"`},
|
||||
Args: []string{`echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"Foo\": \"$ARGOCD_ENV_FOO\", \"KubeVersion\": \"$KUBE_VERSION\", \"KubeApiVersion\": \"$KUBE_API_VERSIONS\",\"Bar\": \"baz\"}}}"`},
|
||||
},
|
||||
},
|
||||
).
|
||||
|
||||
@@ -590,10 +590,10 @@ func TestGetVirtualProjectMatch(t *testing.T) {
|
||||
//App trying to sync a resource which is not blacked listed anywhere
|
||||
_, err = fixture.RunCli("app", "sync", fixture.Name(), "--resource", "apps:Deployment:guestbook-ui", "--timeout", fmt.Sprintf("%v", 10))
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "Blocked by sync window")
|
||||
assert.Contains(t, err.Error(), "blocked by sync window")
|
||||
|
||||
//app trying to sync a resource which is black listed by global project
|
||||
_, err = fixture.RunCli("app", "sync", fixture.Name(), "--resource", ":Service:guestbook-ui", "--timeout", fmt.Sprintf("%v", 10))
|
||||
assert.Contains(t, err.Error(), "Blocked by sync window")
|
||||
assert.Contains(t, err.Error(), "blocked by sync window")
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ FROM golang:1.18 AS go
|
||||
RUN go install github.com/mattn/goreman@latest && \
|
||||
go install github.com/kisielk/godepgraph@latest
|
||||
|
||||
FROM ubuntu:21.10
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
|
||||
@@ -145,14 +145,15 @@ $num-stats: 2;
|
||||
background-color: $argo-color-gray-6;
|
||||
}
|
||||
}
|
||||
&__star-icon {
|
||||
&__new-pod-icon {
|
||||
background: none;
|
||||
color: #ffce25;
|
||||
color: #FFCE25;
|
||||
display: block;
|
||||
left: 20px;
|
||||
margin: 0px;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
top: -4px;
|
||||
font-size: 10px;
|
||||
}
|
||||
&__stat-tooltip {
|
||||
text-align: left;
|
||||
|
||||
@@ -219,7 +219,7 @@ export class PodView extends React.Component<PodViewProps> {
|
||||
key={pod.metadata.name}>
|
||||
<div style={{position: 'relative'}}>
|
||||
{isYoungerThanXMinutes(pod, 30) && (
|
||||
<i className='fas fa-star pod-view__node__pod pod-view__node__pod__star-icon' />
|
||||
<i className='fas fa-circle pod-view__node__pod pod-view__node__pod__new-pod-icon' />
|
||||
)}
|
||||
<div className={`pod-view__node__pod pod-view__node__pod--${pod.health.toLowerCase()}`}>
|
||||
<PodHealthIcon state={{status: pod.health, message: ''}} />
|
||||
|
||||
@@ -179,7 +179,6 @@
|
||||
padding-bottom: 5px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ const FavoriteFilter = (props: AppFilterProps) => {
|
||||
}}
|
||||
/>
|
||||
<div style={{marginRight: '5px'}}>
|
||||
<i style={{color: '#1FBDD0'}} className='fas fa-star' />
|
||||
<i style={{color: '#FFCE25'}} className='fas fa-star' />
|
||||
</div>
|
||||
<div className='filter__item__label'>Favorites Only</div>
|
||||
</div>
|
||||
|
||||
@@ -73,11 +73,11 @@ export const ApplicationsTable = (props: {
|
||||
services.viewPreferences.updatePreferences({appList: {...pref.appList, favoritesAppList: favList}});
|
||||
}}>
|
||||
<i
|
||||
className={'fas fa-star'}
|
||||
className={favList?.includes(app.metadata.name) ? 'fas fa-star' : 'far fa-star'}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
marginRight: '7px',
|
||||
color: favList?.includes(app.metadata.name) ? '#1FBDD0' : 'grey'
|
||||
color: favList?.includes(app.metadata.name) ? '#FFCE25' : '#8fa4b1'
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
|
||||
@@ -117,29 +117,51 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat
|
||||
}`}>
|
||||
<div className='row' onClick={e => ctx.navigation.goto(`/applications/${app.metadata.name}`, {view: pref.appDetails.view}, {event: e})}>
|
||||
<div className={`columns small-12 applications-list__info qe-applications-list-${app.metadata.name}`}>
|
||||
<div className='applications-list__external-link'>
|
||||
<ApplicationURLs urls={AppUtils.getExternalUrls(app.metadata.annotations, app.status.summary.externalURLs)} />
|
||||
<Tooltip content={favList?.includes(app.metadata.name) ? 'Remove Favorite' : 'Add Favorite'}>
|
||||
<button
|
||||
className='large-text-height'
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
favList?.includes(app.metadata.name)
|
||||
? favList.splice(favList.indexOf(app.metadata.name), 1)
|
||||
: favList.push(app.metadata.name);
|
||||
services.viewPreferences.updatePreferences({appList: {...pref.appList, favoritesAppList: favList}});
|
||||
}}>
|
||||
<i
|
||||
className={'fas fa-star fa-lg'}
|
||||
style={{cursor: 'pointer', marginLeft: '7px', color: favList?.includes(app.metadata.name) ? '#1FBDD0' : 'grey'}}
|
||||
/>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className='row'>
|
||||
<div className='columns small-12'>
|
||||
<div
|
||||
className={
|
||||
AppUtils.getExternalUrls(app.metadata.annotations, app.status.summary.externalURLs)?.length > 0
|
||||
? 'columns small-10'
|
||||
: 'columns small-11'
|
||||
}>
|
||||
<i className={'icon argo-icon-' + (app.spec.source.chart != null ? 'helm' : 'git')} />
|
||||
<span className='applications-list__title'>{app.metadata.name}</span>
|
||||
{app.metadata.name.length > 30 ? (
|
||||
<Tooltip content={app.metadata.name}>
|
||||
<span className='applications-list__title'>{app.metadata.name}</span>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span className='applications-list__title'>{app.metadata.name}</span>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
AppUtils.getExternalUrls(app.metadata.annotations, app.status.summary.externalURLs)?.length > 0
|
||||
? 'columns small-2'
|
||||
: 'columns small-1'
|
||||
}>
|
||||
<div className='applications-list__external-link'>
|
||||
<ApplicationURLs urls={AppUtils.getExternalUrls(app.metadata.annotations, app.status.summary.externalURLs)} />
|
||||
<Tooltip content={favList?.includes(app.metadata.name) ? 'Remove Favorite' : 'Add Favorite'}>
|
||||
<button
|
||||
className='large-text-height'
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
favList?.includes(app.metadata.name)
|
||||
? favList.splice(favList.indexOf(app.metadata.name), 1)
|
||||
: favList.push(app.metadata.name);
|
||||
services.viewPreferences.updatePreferences({appList: {...pref.appList, favoritesAppList: favList}});
|
||||
}}>
|
||||
<i
|
||||
className={favList?.includes(app.metadata.name) ? 'fas fa-star fa-lg' : 'far fa-star fa-lg'}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
marginLeft: '7px',
|
||||
color: favList?.includes(app.metadata.name) ? '#FFCE25' : '#8fa4b1'
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='row'>
|
||||
|
||||
@@ -12,6 +12,7 @@ import {Context} from '../../../shared/context';
|
||||
import {ErrorNotification, NotificationType} from 'argo-ui';
|
||||
export interface PodTerminalViewerProps {
|
||||
applicationName: string;
|
||||
projectName: string;
|
||||
selectedNode: models.ResourceNode;
|
||||
podState: models.State;
|
||||
}
|
||||
@@ -22,7 +23,7 @@ export interface ShellFrame {
|
||||
cols?: number;
|
||||
}
|
||||
|
||||
export const PodTerminalViewer: React.FC<PodTerminalViewerProps> = ({selectedNode, applicationName, podState}) => {
|
||||
export const PodTerminalViewer: React.FC<PodTerminalViewerProps> = ({selectedNode, applicationName, projectName, podState}) => {
|
||||
const terminalRef = React.useRef(null);
|
||||
const appContext = React.useContext(Context); // used to show toast
|
||||
const fitAddon = new FitAddon();
|
||||
@@ -144,7 +145,7 @@ export const PodTerminalViewer: React.FC<PodTerminalViewerProps> = ({selectedNod
|
||||
`${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/terminal?pod=${name}&container=${AppUtils.getContainerName(
|
||||
podState,
|
||||
activeContainer
|
||||
)}&appName=${applicationName}&namespace=${namespace}`
|
||||
)}&appName=${applicationName}&projectName=${projectName}&namespace=${namespace}`
|
||||
);
|
||||
webSocket.onopen = onConnectionOpen;
|
||||
webSocket.onclose = onConnectionClose;
|
||||
|
||||
@@ -117,7 +117,9 @@ export const ResourceDetails = (props: ResourceDetailsProps) => {
|
||||
key: 'exec',
|
||||
icon: 'fa fa-terminal',
|
||||
title: 'Terminal',
|
||||
content: <PodTerminalViewer applicationName={application.metadata.name} podState={podState} selectedNode={selectedNode} />
|
||||
content: (
|
||||
<PodTerminalViewer applicationName={application.metadata.name} projectName={application.spec.project} podState={podState} selectedNode={selectedNode} />
|
||||
)
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ interface State {
|
||||
loginError: string;
|
||||
loginInProgress: boolean;
|
||||
returnUrl: string;
|
||||
ssoLoginError: string;
|
||||
hasSsoLoginError: boolean;
|
||||
}
|
||||
|
||||
export class Login extends React.Component<RouteComponentProps<{}>, State> {
|
||||
@@ -31,13 +31,13 @@ export class Login extends React.Component<RouteComponentProps<{}>, State> {
|
||||
public static getDerivedStateFromProps(props: RouteComponentProps<{}>): Partial<State> {
|
||||
const search = new URLSearchParams(props.history.location.search);
|
||||
const returnUrl = search.get('return_url') || '';
|
||||
const ssoLoginError = search.get('sso_error') || '';
|
||||
return {ssoLoginError, returnUrl};
|
||||
const hasSsoLoginError = search.get('has_sso_error') === 'true';
|
||||
return {hasSsoLoginError, returnUrl};
|
||||
}
|
||||
|
||||
constructor(props: RouteComponentProps<{}>) {
|
||||
super(props);
|
||||
this.state = {authSettings: null, loginError: null, returnUrl: null, ssoLoginError: null, loginInProgress: false};
|
||||
this.state = {authSettings: null, loginError: null, returnUrl: null, hasSsoLoginError: false, loginInProgress: false};
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
@@ -69,7 +69,7 @@ export class Login extends React.Component<RouteComponentProps<{}>, State> {
|
||||
)}
|
||||
</button>
|
||||
</a>
|
||||
{this.state.ssoLoginError && <div className='argo-form-row__error-msg'>{this.state.ssoLoginError}</div>}
|
||||
{this.state.hasSsoLoginError && <div className='argo-form-row__error-msg'>Login failed.</div>}
|
||||
{authSettings && !authSettings.userLoginsDisabled && (
|
||||
<div className='login__saml-separator'>
|
||||
<span>or</span>
|
||||
|
||||
@@ -22,7 +22,7 @@ export class ApplicationsService {
|
||||
public list(projects: string[], options?: QueryOptions): Promise<models.ApplicationList> {
|
||||
return requests
|
||||
.get('/applications')
|
||||
.query({project: projects, ...optionsToSearch(options)})
|
||||
.query({projects, ...optionsToSearch(options)})
|
||||
.then(res => res.body as models.ApplicationList)
|
||||
.then(list => {
|
||||
list.items = (list.items || []).map(app => this.parseAppFields(app));
|
||||
|
||||
@@ -10,12 +10,7 @@ export class ClustersService {
|
||||
}
|
||||
|
||||
public get(url: string, name: string): Promise<models.Cluster> {
|
||||
let queryName = '';
|
||||
if (url === undefined) {
|
||||
url = '';
|
||||
queryName = `?name=${name}`;
|
||||
}
|
||||
const requestUrl = `/clusters/${encodeURIComponent(url)}` + queryName;
|
||||
const requestUrl = `/clusters/${url ? encodeURIComponent(url) : name}?id.type=${url ? 'url' : 'name'}`;
|
||||
return requests.get(requestUrl).then(res => res.body as models.Cluster);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ func Discover(ctx context.Context, repoPath string, enableGenerateManifests map[
|
||||
apps := make(map[string]string)
|
||||
|
||||
// Check if it is CMP
|
||||
conn, _, err := DetectConfigManagementPlugin(ctx, repoPath)
|
||||
conn, _, err := DetectConfigManagementPlugin(ctx, repoPath, []string{})
|
||||
if err == nil {
|
||||
// Found CMP
|
||||
io.Close(conn)
|
||||
@@ -82,7 +82,7 @@ func AppType(ctx context.Context, path string, enableGenerateManifests map[strin
|
||||
// 3. check isSupported(path)?
|
||||
// 4.a if no then close connection
|
||||
// 4.b if yes then return conn for detected plugin
|
||||
func DetectConfigManagementPlugin(ctx context.Context, repoPath string) (io.Closer, pluginclient.ConfigManagementPluginServiceClient, error) {
|
||||
func DetectConfigManagementPlugin(ctx context.Context, repoPath string, env []string) (io.Closer, pluginclient.ConfigManagementPluginServiceClient, error) {
|
||||
var conn io.Closer
|
||||
var cmpClient pluginclient.ConfigManagementPluginServiceClient
|
||||
|
||||
@@ -106,7 +106,7 @@ func DetectConfigManagementPlugin(ctx context.Context, repoPath string) (io.Clos
|
||||
continue
|
||||
}
|
||||
|
||||
isSupported, err := matchRepositoryCMP(ctx, repoPath, cmpClient)
|
||||
isSupported, err := matchRepositoryCMP(ctx, repoPath, cmpClient, env)
|
||||
if err != nil {
|
||||
log.Errorf("repository %s is not the match because %v", repoPath, err)
|
||||
continue
|
||||
@@ -131,13 +131,13 @@ func DetectConfigManagementPlugin(ctx context.Context, repoPath string) (io.Clos
|
||||
// matchRepositoryCMP will send the repoPath to the cmp-server. The cmp-server will
|
||||
// inspect the files and return true if the repo is supported for manifest generation.
|
||||
// Will return false otherwise.
|
||||
func matchRepositoryCMP(ctx context.Context, repoPath string, client pluginclient.ConfigManagementPluginServiceClient) (bool, error) {
|
||||
func matchRepositoryCMP(ctx context.Context, repoPath string, client pluginclient.ConfigManagementPluginServiceClient, env []string) (bool, error) {
|
||||
matchRepoStream, err := client.MatchRepository(ctx, grpc_retry.Disable())
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error getting stream client: %s", err)
|
||||
}
|
||||
|
||||
err = cmp.SendRepoStream(ctx, repoPath, repoPath, matchRepoStream, []string{})
|
||||
err = cmp.SendRepoStream(ctx, repoPath, repoPath, matchRepoStream, env)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error sending stream: %s", err)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/managedfields"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
k8smanagedfields "k8s.io/apimachinery/pkg/util/managedfields"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/diff"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
@@ -73,6 +74,12 @@ func (b *DiffConfigBuilder) WithLogger(l logr.Logger) *DiffConfigBuilder {
|
||||
return b
|
||||
}
|
||||
|
||||
// WithGVKParser sets the gvkParser in the diff config.
|
||||
func (b *DiffConfigBuilder) WithGVKParser(parser *k8smanagedfields.GvkParser) *DiffConfigBuilder {
|
||||
b.diffConfig.gvkParser = parser
|
||||
return b
|
||||
}
|
||||
|
||||
// Build will first validate the current state of the diff config and return the
|
||||
// DiffConfig implementation if no errors are found. Will return nil and the error
|
||||
// details otherwise.
|
||||
@@ -108,6 +115,9 @@ type DiffConfig interface {
|
||||
IgnoreAggregatedRoles() bool
|
||||
// Logger used during the diff
|
||||
Logger() *logr.Logger
|
||||
// GVKParser returns a parser able to build a TypedValue used in
|
||||
// structured merge diffs.
|
||||
GVKParser() *k8smanagedfields.GvkParser
|
||||
}
|
||||
|
||||
// diffConfig defines the configurations used while applying diffs.
|
||||
@@ -121,6 +131,7 @@ type diffConfig struct {
|
||||
stateCache *appstatecache.Cache
|
||||
ignoreAggregatedRoles bool
|
||||
logger *logr.Logger
|
||||
gvkParser *k8smanagedfields.GvkParser
|
||||
}
|
||||
|
||||
func (c *diffConfig) Ignores() []v1alpha1.ResourceIgnoreDifferences {
|
||||
@@ -150,6 +161,9 @@ func (c *diffConfig) IgnoreAggregatedRoles() bool {
|
||||
func (c *diffConfig) Logger() *logr.Logger {
|
||||
return c.logger
|
||||
}
|
||||
func (c *diffConfig) GVKParser() *k8smanagedfields.GvkParser {
|
||||
return c.gvkParser
|
||||
}
|
||||
|
||||
// Validate will check the current state of this diffConfig and return
|
||||
// error if it finds any required configuration missing.
|
||||
@@ -309,8 +323,9 @@ func preDiffNormalize(lives, targets []*unstructured.Unstructured, diffConfig Di
|
||||
idc := NewIgnoreDiffConfig(diffConfig.Ignores(), diffConfig.Overrides())
|
||||
ok, ignoreDiff := idc.HasIgnoreDifference(gvk.Group, gvk.Kind, target.GetName(), target.GetNamespace())
|
||||
if ok && len(ignoreDiff.ManagedFieldsManagers) > 0 {
|
||||
pt := managedfields.ResolveParseableType(gvk, diffConfig.GVKParser())
|
||||
var err error
|
||||
live, target, err = managedfields.Normalize(live, target, ignoreDiff.ManagedFieldsManagers)
|
||||
live, target, err = managedfields.Normalize(live, target, ignoreDiff.ManagedFieldsManagers, pt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,9 +2,14 @@ package managedfields
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
k8smanagedfields "k8s.io/apimachinery/pkg/util/managedfields"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/typed"
|
||||
)
|
||||
@@ -13,9 +18,11 @@ import (
|
||||
// a field that belongs to one of the trustedManagers it will remove
|
||||
// that field from both live and config objects and return the normalized
|
||||
// objects in this order. This function won't modify the live and config
|
||||
// parameters. It is a no-op if no trustedManagers is provided. It is also
|
||||
// a no-op if live or config are nil.
|
||||
func Normalize(live, config *unstructured.Unstructured, trustedManagers []string) (*unstructured.Unstructured, *unstructured.Unstructured, error) {
|
||||
// parameters. If pt is nil, the normalization will use a deduced parseable
|
||||
// type which means that lists and maps are manipulated atomically.
|
||||
// It is a no-op if no trustedManagers is provided. It is also a no-op if
|
||||
// live or config are nil.
|
||||
func Normalize(live, config *unstructured.Unstructured, trustedManagers []string, pt *typed.ParseableType) (*unstructured.Unstructured, *unstructured.Unstructured, error) {
|
||||
if len(trustedManagers) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
@@ -25,68 +32,87 @@ func Normalize(live, config *unstructured.Unstructured, trustedManagers []string
|
||||
|
||||
liveCopy := live.DeepCopy()
|
||||
configCopy := config.DeepCopy()
|
||||
comparison, err := Compare(liveCopy, configCopy)
|
||||
results, err := newTypedResults(liveCopy, configCopy, pt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, fmt.Errorf("error building typed results: %s", err)
|
||||
}
|
||||
|
||||
normalized := false
|
||||
for _, mf := range live.GetManagedFields() {
|
||||
if trustedManager(mf.Manager, trustedManagers) {
|
||||
err := normalize(liveCopy, configCopy, mf, comparison.Modified)
|
||||
err := normalize(mf, results)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, fmt.Errorf("error normalizing manager %s: %s", mf.Manager, err)
|
||||
}
|
||||
normalized = true
|
||||
}
|
||||
}
|
||||
|
||||
return liveCopy, configCopy, nil
|
||||
if !normalized {
|
||||
return liveCopy, configCopy, nil
|
||||
}
|
||||
lvu := results.live.AsValue().Unstructured()
|
||||
l, ok := lvu.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("error converting live typedValue: expected map got %T", lvu)
|
||||
}
|
||||
normLive := &unstructured.Unstructured{Object: l}
|
||||
|
||||
cvu := results.config.AsValue().Unstructured()
|
||||
c, ok := cvu.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("error converting config typedValue: expected map got %T", cvu)
|
||||
}
|
||||
normConfig := &unstructured.Unstructured{Object: c}
|
||||
return normLive, normConfig, nil
|
||||
}
|
||||
|
||||
// normalize will check if the modified set has fields that are present
|
||||
// in the managed fields entry. If so, it will remove the fields from
|
||||
// the live and config objects so it is ignored in diffs.
|
||||
func normalize(live, config *unstructured.Unstructured, mf v1.ManagedFieldsEntry, modified *fieldpath.Set) error {
|
||||
liveSet := &fieldpath.Set{}
|
||||
err := liveSet.FromJSON(bytes.NewReader(mf.FieldsV1.Raw))
|
||||
func normalize(mf v1.ManagedFieldsEntry, tr *typedResults) error {
|
||||
mfs := &fieldpath.Set{}
|
||||
err := mfs.FromJSON(bytes.NewReader(mf.FieldsV1.Raw))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
intersect := liveSet.Intersection(modified)
|
||||
if !intersect.Empty() {
|
||||
intersect.Iterate(func(p fieldpath.Path) {
|
||||
fields := PathToNestedFields(p)
|
||||
unstructured.RemoveNestedField(config.Object, fields...)
|
||||
unstructured.RemoveNestedField(live.Object, fields...)
|
||||
})
|
||||
intersect := mfs.Intersection(tr.comparison.Modified)
|
||||
if intersect.Empty() {
|
||||
return nil
|
||||
}
|
||||
tr.live = tr.live.RemoveItems(intersect)
|
||||
tr.config = tr.config.RemoveItems(intersect)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compare will compare the live and the config state and returned a typed.Comparison
|
||||
// as a result.
|
||||
func Compare(live, config *unstructured.Unstructured) (*typed.Comparison, error) {
|
||||
typedLive, err := typed.DeducedParseableType.FromUnstructured(live.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typedConfig, err := typed.DeducedParseableType.FromUnstructured(config.Object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return typedLive.Compare(typedConfig)
|
||||
type typedResults struct {
|
||||
live *typed.TypedValue
|
||||
config *typed.TypedValue
|
||||
comparison *typed.Comparison
|
||||
}
|
||||
|
||||
// PathToNestedFields will convert a path into a slice of field names so it
|
||||
// can be used in unstructured nested fields operations.
|
||||
func PathToNestedFields(path fieldpath.Path) []string {
|
||||
fields := []string{}
|
||||
for _, element := range path {
|
||||
if element.FieldName != nil {
|
||||
fields = append(fields, *element.FieldName)
|
||||
}
|
||||
// newTypedResults will convert live and config into a TypedValue using the given pt
|
||||
// and compare them. Returns a typedResults with the coverted types and the comparison.
|
||||
// If pt is nil, will use the DeducedParseableType.
|
||||
func newTypedResults(live, config *unstructured.Unstructured, pt *typed.ParseableType) (*typedResults, error) {
|
||||
typedLive, err := pt.FromUnstructured(live.Object)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating typedLive: %s", err)
|
||||
}
|
||||
return fields
|
||||
|
||||
typedConfig, err := pt.FromUnstructured(config.Object)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating typedConfig: %s", err)
|
||||
}
|
||||
comparison, err := typedLive.Compare(typedConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error comparing typed resources: %s", err)
|
||||
}
|
||||
return &typedResults{
|
||||
live: typedLive,
|
||||
config: typedConfig,
|
||||
comparison: comparison,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// trustedManager will return true if trustedManagers contains curManager.
|
||||
@@ -99,3 +125,60 @@ func trustedManager(curManager string, trustedManagers []string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ResolveParseableType(gvk schema.GroupVersionKind, parser *k8smanagedfields.GvkParser) *typed.ParseableType {
|
||||
if parser == nil {
|
||||
return &typed.DeducedParseableType
|
||||
}
|
||||
pt := resolverFromStaticParser(gvk, parser)
|
||||
if pt == nil {
|
||||
return parser.Type(gvk)
|
||||
}
|
||||
return pt
|
||||
}
|
||||
|
||||
func resolverFromStaticParser(gvk schema.GroupVersionKind, parser *k8smanagedfields.GvkParser) *typed.ParseableType {
|
||||
gvkNameMap := getGvkMap(parser)
|
||||
name := gvkNameMap[gvk]
|
||||
|
||||
p := StaticParser()
|
||||
if p == nil || name == "" {
|
||||
return nil
|
||||
}
|
||||
pt := p.Type(name)
|
||||
if pt.IsValid() {
|
||||
return &pt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var gvkMap map[schema.GroupVersionKind]string
|
||||
var extractOnce sync.Once
|
||||
|
||||
func getGvkMap(parser *k8smanagedfields.GvkParser) map[schema.GroupVersionKind]string {
|
||||
extractOnce.Do(func() {
|
||||
gvkMap = extractGvkMap(parser)
|
||||
})
|
||||
return gvkMap
|
||||
}
|
||||
|
||||
func extractGvkMap(parser *k8smanagedfields.GvkParser) map[schema.GroupVersionKind]string {
|
||||
results := make(map[schema.GroupVersionKind]string)
|
||||
|
||||
value := reflect.ValueOf(parser)
|
||||
gvkValue := reflect.Indirect(value).FieldByName("gvks")
|
||||
iter := gvkValue.MapRange()
|
||||
for iter.Next() {
|
||||
group := iter.Key().FieldByName("Group").String()
|
||||
version := iter.Key().FieldByName("Version").String()
|
||||
kind := iter.Key().FieldByName("Kind").String()
|
||||
gvk := schema.GroupVersionKind{
|
||||
Group: group,
|
||||
Version: version,
|
||||
Kind: kind,
|
||||
}
|
||||
name := iter.Value().String()
|
||||
results[gvk] = name
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
@@ -5,24 +5,32 @@ import (
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
arv1 "k8s.io/api/admissionregistration/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/managedfields"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/testdata"
|
||||
)
|
||||
|
||||
func TestNormalize(t *testing.T) {
|
||||
|
||||
parser := managedfields.StaticParser()
|
||||
t.Run("will remove conflicting fields if managed by trusted managers", func(t *testing.T) {
|
||||
// given
|
||||
desiredState := StrToUnstructured(testdata.DesiredDeploymentYaml)
|
||||
liveState := StrToUnstructured(testdata.LiveDeploymentWithManagedReplicaYaml)
|
||||
trustedManagers := []string{"kube-controller-manager", "revision-history-manager"}
|
||||
pt := parser.Type("io.k8s.api.apps.v1.Deployment")
|
||||
|
||||
// when
|
||||
liveResult, desiredResult, err := managedfields.Normalize(liveState, desiredState, trustedManagers)
|
||||
liveResult, desiredResult, err := managedfields.Normalize(liveState, desiredState, trustedManagers, &pt)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, liveResult)
|
||||
require.NotNil(t, desiredResult)
|
||||
desiredReplicas, ok, err := unstructured.NestedFloat64(desiredResult.Object, "spec", "replicas")
|
||||
assert.False(t, ok)
|
||||
assert.NoError(t, err)
|
||||
@@ -44,9 +52,10 @@ func TestNormalize(t *testing.T) {
|
||||
desiredState := StrToUnstructured(testdata.DesiredDeploymentYaml)
|
||||
liveState := StrToUnstructured(testdata.LiveDeploymentWithManagedReplicaYaml)
|
||||
trustedManagers := []string{"another-manager"}
|
||||
pt := parser.Type("io.k8s.api.apps.v1.Deployment")
|
||||
|
||||
// when
|
||||
liveResult, desiredResult, err := managedfields.Normalize(liveState, desiredState, trustedManagers)
|
||||
liveResult, desiredResult, err := managedfields.Normalize(liveState, desiredState, trustedManagers, &pt)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
@@ -59,9 +68,10 @@ func TestNormalize(t *testing.T) {
|
||||
// given
|
||||
desiredState := StrToUnstructured(testdata.DesiredDeploymentYaml)
|
||||
trustedManagers := []string{"kube-controller-manager"}
|
||||
pt := parser.Type("io.k8s.api.apps.v1.Deployment")
|
||||
|
||||
// when
|
||||
liveResult, desiredResult, err := managedfields.Normalize(nil, desiredState, trustedManagers)
|
||||
liveResult, desiredResult, err := managedfields.Normalize(nil, desiredState, trustedManagers, &pt)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
@@ -75,9 +85,10 @@ func TestNormalize(t *testing.T) {
|
||||
// given
|
||||
liveState := StrToUnstructured(testdata.LiveDeploymentWithManagedReplicaYaml)
|
||||
trustedManagers := []string{"kube-controller-manager"}
|
||||
pt := parser.Type("io.k8s.api.apps.v1.Deployment")
|
||||
|
||||
// when
|
||||
liveResult, desiredResult, err := managedfields.Normalize(liveState, nil, trustedManagers)
|
||||
liveResult, desiredResult, err := managedfields.Normalize(liveState, nil, trustedManagers, &pt)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
@@ -90,9 +101,10 @@ func TestNormalize(t *testing.T) {
|
||||
// given
|
||||
desiredState := StrToUnstructured(testdata.DesiredDeploymentYaml)
|
||||
liveState := StrToUnstructured(testdata.LiveDeploymentWithManagedReplicaYaml)
|
||||
pt := parser.Type("io.k8s.api.apps.v1.Deployment")
|
||||
|
||||
// when
|
||||
liveResult, desiredResult, err := managedfields.Normalize(liveState, desiredState, []string{})
|
||||
liveResult, desiredResult, err := managedfields.Normalize(liveState, desiredState, []string{}, &pt)
|
||||
|
||||
// then
|
||||
assert.NoError(t, err)
|
||||
@@ -103,6 +115,33 @@ func TestNormalize(t *testing.T) {
|
||||
validateNestedFloat64(t, float64(2), liveState, "spec", "replicas")
|
||||
validateNestedFloat64(t, float64(3), liveState, "spec", "revisionHistoryLimit")
|
||||
})
|
||||
t.Run("will normalize successfully inside a list", func(t *testing.T) {
|
||||
// given
|
||||
desiredState := StrToUnstructured(testdata.DesiredValidatingWebhookYaml)
|
||||
liveState := StrToUnstructured(testdata.LiveValidatingWebhookYaml)
|
||||
trustedManagers := []string{"external-secrets"}
|
||||
pt := parser.Type("io.k8s.api.admissionregistration.v1.ValidatingWebhookConfiguration")
|
||||
|
||||
// when
|
||||
liveResult, desiredResult, err := managedfields.Normalize(liveState, desiredState, trustedManagers, &pt)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, liveResult)
|
||||
require.NotNil(t, desiredResult)
|
||||
|
||||
var vwcLive arv1.ValidatingWebhookConfiguration
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(liveResult.Object, &vwcLive)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(vwcLive.Webhooks))
|
||||
assert.Equal(t, "", string(vwcLive.Webhooks[0].ClientConfig.CABundle))
|
||||
|
||||
var vwcConfig arv1.ValidatingWebhookConfiguration
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(desiredResult.Object, &vwcConfig)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(vwcConfig.Webhooks))
|
||||
assert.Equal(t, "", string(vwcConfig.Webhooks[0].ClientConfig.CABundle))
|
||||
})
|
||||
}
|
||||
|
||||
func validateNestedFloat64(t *testing.T, expected float64, obj *unstructured.Unstructured, fields ...string) {
|
||||
|
||||
11691
util/argo/managedfields/parser.go
Normal file
11691
util/argo/managedfields/parser.go
Normal file
File diff suppressed because it is too large
Load Diff
6
util/argo/testdata/data.go
vendored
6
util/argo/testdata/data.go
vendored
@@ -8,4 +8,10 @@ var (
|
||||
|
||||
//go:embed desired_deployment.yaml
|
||||
DesiredDeploymentYaml string
|
||||
|
||||
//go:embed live_validating_webhook.yaml
|
||||
LiveValidatingWebhookYaml string
|
||||
|
||||
//go:embed desired_validating_webhook.yaml
|
||||
DesiredValidatingWebhookYaml string
|
||||
)
|
||||
|
||||
32
util/argo/testdata/desired_validating_webhook.yaml
vendored
Normal file
32
util/argo/testdata/desired_validating_webhook.yaml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: external-secrets
|
||||
external-secrets.io/component: webhook
|
||||
name: externalsecret-validate
|
||||
webhooks:
|
||||
- admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: external-secrets-webhook
|
||||
namespace: external-secrets
|
||||
path: /validate-external-secrets-io-v1beta1-externalsecret
|
||||
name: validate.externalsecret.external-secrets.io
|
||||
rules:
|
||||
- apiGroups:
|
||||
- external-secrets.io
|
||||
apiVersions:
|
||||
- v1beta1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- externalsecrets
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: 5
|
||||
91
util/argo/testdata/live_validating_webhook.yaml
vendored
Normal file
91
util/argo/testdata/live_validating_webhook.yaml
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: >
|
||||
{"apiVersion":"admissionregistration.k8s.io/v1","kind":"ValidatingWebhookConfiguration","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"external-secrets","external-secrets.io/component":"webhook"},"name":"externalsecret-validate"},"webhooks":[{"admissionReviewVersions":["v1","v1beta1"],"clientConfig":{"caBundle":"Cg==","service":{"name":"external-secrets-webhook","namespace":"external-secrets","path":"/validate-external-secrets-io-v1beta1-externalsecret"}},"name":"validate.externalsecret.external-secrets.io","rules":[{"apiGroups":["external-secrets.io"],"apiVersions":["v1beta1"],"operations":["CREATE","UPDATE","DELETE"],"resources":["externalsecrets"],"scope":"Namespaced"}],"sideEffects":"None","timeoutSeconds":5}]}
|
||||
creationTimestamp: '2022-04-12T14:17:35Z'
|
||||
generation: 2
|
||||
labels:
|
||||
app.kubernetes.io/instance: external-secrets
|
||||
external-secrets.io/component: webhook
|
||||
managedFields:
|
||||
- apiVersion: admissionregistration.k8s.io/v1
|
||||
fieldsType: FieldsV1
|
||||
fieldsV1:
|
||||
'f:metadata':
|
||||
'f:annotations':
|
||||
.: {}
|
||||
'f:kubectl.kubernetes.io/last-applied-configuration': {}
|
||||
'f:labels':
|
||||
.: {}
|
||||
'f:app.kubernetes.io/instance': {}
|
||||
'f:external-secrets.io/component': {}
|
||||
'f:webhooks':
|
||||
.: {}
|
||||
'k:{"name":"validate.externalsecret.external-secrets.io"}':
|
||||
.: {}
|
||||
'f:admissionReviewVersions': {}
|
||||
'f:clientConfig':
|
||||
.: {}
|
||||
'f:service':
|
||||
.: {}
|
||||
'f:name': {}
|
||||
'f:namespace': {}
|
||||
'f:path': {}
|
||||
'f:port': {}
|
||||
'f:failurePolicy': {}
|
||||
'f:matchPolicy': {}
|
||||
'f:name': {}
|
||||
'f:namespaceSelector': {}
|
||||
'f:objectSelector': {}
|
||||
'f:rules': {}
|
||||
'f:sideEffects': {}
|
||||
'f:timeoutSeconds': {}
|
||||
manager: argocd
|
||||
operation: Update
|
||||
time: '2022-04-12T14:17:35Z'
|
||||
- apiVersion: admissionregistration.k8s.io/v1
|
||||
fieldsType: FieldsV1
|
||||
fieldsV1:
|
||||
'f:webhooks':
|
||||
'k:{"name":"validate.externalsecret.external-secrets.io"}':
|
||||
'f:clientConfig':
|
||||
'f:caBundle': {}
|
||||
manager: external-secrets
|
||||
operation: Update
|
||||
time: '2022-04-12T14:17:37Z'
|
||||
name: externalsecret-validate
|
||||
resourceVersion: '1644596'
|
||||
uid: b56ccc4e-30d6-4b32-8a6e-7eae41ab3155
|
||||
webhooks:
|
||||
- admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: >-
|
||||
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURSakNDQWk2Z0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREEyTVJrd0Z3WURWUVFLRXhCbGVIUmwKY201aGJDMXpaV055WlhSek1Sa3dGd1lEVlFRREV4QmxlSFJsY201aGJDMXpaV055WlhSek1CNFhEVEl5TURReApNakV6TVRjek4xb1hEVE15TURRd09URTBNVGN6TjFvd05qRVpNQmNHQTFVRUNoTVFaWGgwWlhKdVlXd3RjMlZqCmNtVjBjekVaTUJjR0ExVUVBeE1RWlhoMFpYSnVZV3d0YzJWamNtVjBjekNDQVNJd0RRWUpLb1pJaHZjTkFRRUIKQlFBRGdnRVBBRENDQVFvQ2dnRUJBTU9RQmR2Z210RE1aVjRhNGQ2dUw5ZGNzT3c4SXRnbW9zZ3R1MGplTlF2Ygo4a291TmdRMVpxMlFSVFVNTTVCYlpNRTNGWHM3aWxwNVZVbzN3SnZsaVdVVHhxb3lIMUY2VUszbUsyYmp2aHRrCnVEYWVnNkh4ZzNjRlVybXRvNCtyVHNTT1BlN3ZRajVNbWZzeVEzb1BXamxFbExyMEE5b3RScGZnZGZtNWxncHgKVkE0SFdGeWZmQ3hpUEFaamNYNFdjd1hOdzJSN21aQnNNSW1xTk1YOUhzUEVOdTdzdk1DeXEzU0pvdzNqTXFpNgpHUFZaUmh2ZlRSY2hDcmV2UVE3OTRPNGkrSVk3ZVdvV00yZDgweVM3V09LcUUvNEE1SU9tNWVJK1BhNUlvd3E1CnppckxxU3lsYW15bzZxbWN3TDFEbFpiM2RmSE9GVUx0cFM1YkhTSzQyTWNDQXdFQUFhTmZNRjB3RGdZRFZSMFAKQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk1QMkF1aUh1d2FsczlTcgpYWk1XODdyb2l0UElNQnNHQTFVZEVRUVVNQktDRUdWNGRHVnlibUZzTFhObFkzSmxkSE13RFFZSktvWklodmNOCkFRRUxCUUFEZ2dFQkFMek5BczhnS2FqYjc1N3pyMjdHRzBMVzkxVG1ab1dPQ0ZHMXFrUWJ3T2U0d25kV2NiT08KbThsYkx6a291Wlo5d1I0aXN2OVFHYnNlS0V1UXpyWlZzZXlJTHZoUGVWcGZGd1ZkcVFsQ0laRXM5SSswd0hXawplblFWWGNEamZMTk9zdDhFcDlKVktwSkJwODRIY1NvZkJMY1RPcFdqdGZtZnNudmlzbU5ha2hGNzM2SmJrQUdmClZvdUJDQlU5Z3g2SGI5T2FDaDdpekZLMnVyWHo1NkV5eXhhUUlsckRyYVlZV3Mrb3ZhTlJwdEltKytqcnFBdUkKV0xxdWQvU0tQMy9Fc3o3cmVWb2xGODFIYmdEMEQ0RWlmZWJZeXpnWEJMcVlZcUxUZXIzQzVONFRwcGpJSi82NgpERVBNZ0xUaG9jRkpZNVFBYy9rbGl5Q2VnN3VoWSs5TnFLRT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
|
||||
service:
|
||||
name: external-secrets-webhook
|
||||
namespace: external-secrets
|
||||
path: /validate-external-secrets-io-v1beta1-externalsecret
|
||||
port: 443
|
||||
failurePolicy: Fail
|
||||
matchPolicy: Equivalent
|
||||
name: validate.externalsecret.external-secrets.io
|
||||
namespaceSelector: {}
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- external-secrets.io
|
||||
apiVersions:
|
||||
- v1beta1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- externalsecrets
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: 5
|
||||
@@ -2,7 +2,6 @@ package cmp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
@@ -199,8 +198,11 @@ func compressFiles(appPath string) (*os.File, string, error) {
|
||||
// Returns error if checksum doesn't match the one provided in the fileMetadata.
|
||||
// It is responsibility of the caller to close the returned file.
|
||||
func receiveFile(ctx context.Context, receiver StreamReceiver, checksum, dst string) (*os.File, error) {
|
||||
fileBuffer := bytes.Buffer{}
|
||||
hasher := sha256.New()
|
||||
file, err := ioutil.TempFile(dst, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating file: %w", err)
|
||||
}
|
||||
for {
|
||||
if ctx != nil {
|
||||
if err := ctx.Err(); err != nil {
|
||||
@@ -218,9 +220,9 @@ func receiveFile(ctx context.Context, receiver StreamReceiver, checksum, dst str
|
||||
if f == nil {
|
||||
return nil, fmt.Errorf("stream request file is nil")
|
||||
}
|
||||
_, err = fileBuffer.Write(f.Chunk)
|
||||
_, err = file.Write(f.Chunk)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error writing file buffer: %w", err)
|
||||
return nil, fmt.Errorf("error writing file: %w", err)
|
||||
}
|
||||
_, err = hasher.Write(f.Chunk)
|
||||
if err != nil {
|
||||
@@ -231,15 +233,6 @@ func receiveFile(ctx context.Context, receiver StreamReceiver, checksum, dst str
|
||||
return nil, fmt.Errorf("file checksum validation error")
|
||||
}
|
||||
|
||||
file, err := ioutil.TempFile(dst, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating file: %w", err)
|
||||
}
|
||||
_, err = fileBuffer.WriteTo(file)
|
||||
if err != nil {
|
||||
closeAndDelete(file)
|
||||
return nil, fmt.Errorf("error writing file: %w", err)
|
||||
}
|
||||
_, err = file.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
closeAndDelete(file)
|
||||
|
||||
@@ -3,20 +3,18 @@ package dex
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
)
|
||||
|
||||
var messageRe = regexp.MustCompile(`<p>(.*)([\s\S]*?)<\/p>`)
|
||||
|
||||
func decorateDirector(director func(req *http.Request), target *url.URL) func(req *http.Request) {
|
||||
return func(req *http.Request) {
|
||||
director(req)
|
||||
@@ -44,16 +42,10 @@ func NewDexHTTPReverseProxy(serverAddr string, baseHRef string) func(writer http
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var message string
|
||||
matches := messageRe.FindSubmatch(b)
|
||||
if len(matches) > 1 {
|
||||
message = html.UnescapeString(string(matches[1]))
|
||||
} else {
|
||||
message = "Unknown error"
|
||||
}
|
||||
log.Errorf("received error from dex: %s", string(b))
|
||||
resp.ContentLength = 0
|
||||
resp.Header.Set("Content-Length", strconv.Itoa(0))
|
||||
resp.Header.Set("Location", fmt.Sprintf("%s?sso_error=%s", path.Join(baseHRef, "login"), url.QueryEscape(message)))
|
||||
resp.Header.Set("Location", fmt.Sprintf("%s?has_sso_error=true", path.Join(baseHRef, "login")))
|
||||
resp.StatusCode = http.StatusSeeOther
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader(make([]byte, 0)))
|
||||
return nil
|
||||
|
||||
@@ -408,7 +408,7 @@ func Test_DexReverseProxy(t *testing.T) {
|
||||
assert.Equal(t, http.StatusSeeOther, resp.StatusCode)
|
||||
location, _ := resp.Location()
|
||||
fmt.Printf("%s %s\n", resp.Status, location.RequestURI())
|
||||
assert.True(t, strings.HasPrefix(location.RequestURI(), "/login?sso_error"))
|
||||
assert.True(t, strings.HasPrefix(location.RequestURI(), "/login?has_sso_error=true"))
|
||||
})
|
||||
|
||||
t.Run("Invalid URL for Dex reverse proxy", func(t *testing.T) {
|
||||
|
||||
@@ -11,14 +11,14 @@ import (
|
||||
// Unfortunately, crypto/ssh does not offer public constants or list for
|
||||
// this.
|
||||
var SupportedSSHKeyExchangeAlgorithms = []string{
|
||||
"diffie-hellman-group1-sha1",
|
||||
"diffie-hellman-group14-sha1",
|
||||
"curve25519-sha256",
|
||||
"curve25519-sha256@libssh.org",
|
||||
"ecdh-sha2-nistp256",
|
||||
"ecdh-sha2-nistp384",
|
||||
"ecdh-sha2-nistp521",
|
||||
"curve25519-sha256@libssh.org",
|
||||
"diffie-hellman-group-exchange-sha1",
|
||||
"diffie-hellman-group-exchange-sha256",
|
||||
"diffie-hellman-group14-sha256",
|
||||
"diffie-hellman-group14-sha1",
|
||||
}
|
||||
|
||||
// List of default key exchange algorithms to use. We use those that are
|
||||
|
||||
@@ -20,8 +20,8 @@ func gitErrToGRPC(err error) error {
|
||||
return err
|
||||
}
|
||||
var errMsg = err.Error()
|
||||
if se, ok := err.(interface{ GRPCStatus() *status.Status }); ok {
|
||||
errMsg = se.GRPCStatus().Message()
|
||||
if grpcStatus := UnwrapGRPCStatus(err); grpcStatus != nil {
|
||||
errMsg = grpcStatus.Message()
|
||||
}
|
||||
|
||||
switch errMsg {
|
||||
@@ -31,6 +31,20 @@ func gitErrToGRPC(err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// UnwrapGRPCStatus will attempt to cast the given error into a grpc Status
|
||||
// object unwrapping all existing inner errors. Will return nil if none of the
|
||||
// nested errors can be casted.
|
||||
func UnwrapGRPCStatus(err error) *status.Status {
|
||||
if se, ok := err.(interface{ GRPCStatus() *status.Status }); ok {
|
||||
return se.GRPCStatus()
|
||||
}
|
||||
e := errors.Unwrap(err)
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return UnwrapGRPCStatus(e)
|
||||
}
|
||||
|
||||
func kubeErrToGRPC(err error) error {
|
||||
/*
|
||||
Unmapped source Kubernetes API errors as of 2018-04-16:
|
||||
@@ -72,7 +86,12 @@ func kubeErrToGRPC(err error) error {
|
||||
err = rewrapError(err, codes.DeadlineExceeded)
|
||||
case apierr.IsInternalError(err):
|
||||
err = rewrapError(err, codes.Internal)
|
||||
|
||||
default:
|
||||
// This is necessary as GRPC Status don't support wrapped errors:
|
||||
// https://github.com/grpc/grpc-go/issues/2934
|
||||
if grpcStatus := UnwrapGRPCStatus(err); grpcStatus != nil {
|
||||
err = status.Error(grpcStatus.Code(), grpcStatus.Message())
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@ package grpc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -34,3 +37,83 @@ func Test_gitErrToGRPC(t *testing.T) {
|
||||
assert.Equal(t, se1.GRPCStatus().Code(), codes.NotFound)
|
||||
assert.Equal(t, se1.GRPCStatus().Message(), notFoundMsg)
|
||||
}
|
||||
|
||||
func Test_kubeErrToGRPC(t *testing.T) {
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
givenErrFn func() error
|
||||
expectedErrFn func() error
|
||||
expectedGRPCStatus *status.Status
|
||||
}
|
||||
newForbiddenError := func() error {
|
||||
gr := schema.GroupResource{
|
||||
Group: "apps",
|
||||
Resource: "Deployment",
|
||||
}
|
||||
return apierr.NewForbidden(gr, "some-app", fmt.Errorf("authentication error"))
|
||||
}
|
||||
newUnauthorizedError := func() error {
|
||||
return apierr.NewUnauthorized("unauthenticated")
|
||||
}
|
||||
cases := []*testCase{
|
||||
{
|
||||
name: "will return standard error if not grpc status",
|
||||
givenErrFn: func() error {
|
||||
return fmt.Errorf("standard error")
|
||||
},
|
||||
expectedErrFn: func() error {
|
||||
return fmt.Errorf("standard error")
|
||||
},
|
||||
expectedGRPCStatus: nil,
|
||||
},
|
||||
{
|
||||
name: "will return wrapped status if nested in err",
|
||||
givenErrFn: func() error {
|
||||
grpcStatus := status.New(codes.NotFound, "Not found")
|
||||
return fmt.Errorf("wrapped status: %w", grpcStatus.Err())
|
||||
},
|
||||
expectedErrFn: func() error {
|
||||
grpcStatus := status.New(codes.NotFound, "Not found")
|
||||
return grpcStatus.Err()
|
||||
},
|
||||
expectedGRPCStatus: status.New(codes.NotFound, "Not found"),
|
||||
},
|
||||
{
|
||||
name: "will return permission denied if apierr.IsForbidden",
|
||||
givenErrFn: func() error {
|
||||
return newForbiddenError()
|
||||
},
|
||||
expectedErrFn: func() error {
|
||||
err := newForbiddenError()
|
||||
grpcStatus := status.New(codes.PermissionDenied, err.Error())
|
||||
return grpcStatus.Err()
|
||||
},
|
||||
expectedGRPCStatus: status.New(codes.PermissionDenied, newForbiddenError().Error()),
|
||||
},
|
||||
{
|
||||
name: "will return unauthenticated if apierr.IsUnauthorized",
|
||||
givenErrFn: func() error {
|
||||
return newUnauthorizedError()
|
||||
},
|
||||
expectedErrFn: func() error {
|
||||
err := newUnauthorizedError()
|
||||
grpcStatus := status.New(codes.Unauthenticated, err.Error())
|
||||
return grpcStatus.Err()
|
||||
},
|
||||
expectedGRPCStatus: status.New(codes.Unauthenticated, newUnauthorizedError().Error()),
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
c := c
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
// when
|
||||
err := kubeErrToGRPC(c.givenErrFn())
|
||||
|
||||
// then
|
||||
assert.Equal(t, c.expectedErrFn(), err, "error comparison mismatch")
|
||||
grpcStatus := UnwrapGRPCStatus(err)
|
||||
assert.Equal(t, c.expectedGRPCStatus, grpcStatus, "grpc status mismatch")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user