mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-10 18:38:46 +01:00
Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98203002ba | ||
|
|
a316a413ae | ||
|
|
c8f21e383a | ||
|
|
e2df7315fb | ||
|
|
9e4a0f523e | ||
|
|
7c21486b31 | ||
|
|
caab433303 | ||
|
|
d6cb4e903f | ||
|
|
710ee9a218 | ||
|
|
e0ff56d89f | ||
|
|
91bb155ed2 | ||
|
|
7ff0ccc34e | ||
|
|
b17323b41e | ||
|
|
1701f7edd3 | ||
|
|
ed5d9c3874 | ||
|
|
14f681e3ee | ||
|
|
8281b18831 | ||
|
|
5bbb51ab42 | ||
|
|
6e181d72b3 | ||
|
|
c415d3f3d5 | ||
|
|
759bd2888d | ||
|
|
2a410f3441 | ||
|
|
689a73a729 | ||
|
|
fbb6b20418 | ||
|
|
f05a6b7906 | ||
|
|
7aac4ba0f0 | ||
|
|
7cac5a8946 | ||
|
|
ba62a0a86d | ||
|
|
3fa66ec42c | ||
|
|
cc672e7609 | ||
|
|
cce1e97434 | ||
|
|
f43122d3cd | ||
|
|
f9436641a6 | ||
|
|
7561cc1cdd | ||
|
|
bb06722d98 | ||
|
|
997f89a92c | ||
|
|
f569aa105e | ||
|
|
dac68ac593 | ||
|
|
45d4a071ae | ||
|
|
a10cb870f8 | ||
|
|
8436954759 | ||
|
|
ab3b2e780e | ||
|
|
0e65b848da | ||
|
|
9075601a68 | ||
|
|
31860d09d4 | ||
|
|
65dec01de0 | ||
|
|
6eba5be864 | ||
|
|
cc56c9e8a2 | ||
|
|
30f68e1041 | ||
|
|
e63273e4c1 | ||
|
|
d5eaaa3527 |
2
.github/workflows/ci-build.yaml
vendored
2
.github/workflows/ci-build.yaml
vendored
@@ -428,7 +428,7 @@ jobs:
|
||||
run: |
|
||||
docker pull ghcr.io/dexidp/dex:v2.37.0
|
||||
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
||||
docker pull redis:7.0.11-alpine
|
||||
docker pull redis:7.0.15-alpine
|
||||
- name: Create target directory for binaries in the build-process
|
||||
run: |
|
||||
mkdir -p dist
|
||||
|
||||
4
.github/workflows/image-reuse.yaml
vendored
4
.github/workflows/image-reuse.yaml
vendored
@@ -74,9 +74,7 @@ jobs:
|
||||
go-version: ${{ inputs.go-version }}
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2
|
||||
with:
|
||||
cosign-release: 'v2.0.2'
|
||||
uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0
|
||||
|
||||
- uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2.2.0
|
||||
- uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
|
||||
2
.github/workflows/image.yaml
vendored
2
.github/workflows/image.yaml
vendored
@@ -86,7 +86,7 @@ jobs:
|
||||
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.7.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
|
||||
with:
|
||||
image: ghcr.io/argoproj/argo-cd/argocd
|
||||
digest: ${{ needs.build-and-publish.outputs.image-digest }}
|
||||
|
||||
6
.github/workflows/release.yaml
vendored
6
.github/workflows/release.yaml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.9.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
|
||||
with:
|
||||
image: quay.io/argoproj/argocd
|
||||
digest: ${{ needs.argocd-image.outputs.image-digest }}
|
||||
@@ -120,7 +120,7 @@ jobs:
|
||||
contents: write # Needed for release uploads
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.9.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0
|
||||
with:
|
||||
base64-subjects: "${{ needs.goreleaser.outputs.hashes }}"
|
||||
provenance-name: "argocd-cli.intoto.jsonl"
|
||||
@@ -204,7 +204,7 @@ jobs:
|
||||
contents: write # Needed for release uploads
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.9.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0
|
||||
with:
|
||||
base64-subjects: "${{ needs.generate-sbom.outputs.hashes }}"
|
||||
provenance-name: "argocd-sbom.intoto.jsonl"
|
||||
|
||||
@@ -6,7 +6,7 @@ ARG BASE_IMAGE=docker.io/library/ubuntu:22.04@sha256:0bced47fffa3361afa981854fca
|
||||
####################################################################################################
|
||||
FROM docker.io/library/golang:1.21.3@sha256:02d7116222536a5cf0fcf631f90b507758b669648e0f20186d2dc94a9b419a9b AS builder
|
||||
|
||||
RUN echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||
RUN echo 'deb http://archive.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
openssh-server \
|
||||
|
||||
@@ -50,6 +50,7 @@ import (
|
||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
argoutil "github.com/argoproj/argo-cd/v2/util/argo"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
)
|
||||
@@ -623,7 +624,7 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
|
||||
},
|
||||
}
|
||||
|
||||
action, err := utils.CreateOrUpdate(ctx, appLog, r.Client, applicationSet.Spec.IgnoreApplicationDifferences, found, func() error {
|
||||
action, err := utils.CreateOrUpdate(ctx, appLog, r.Client, applicationSet.Spec.IgnoreApplicationDifferences, normalizers.IgnoreNormalizerOpts{}, found, func() error {
|
||||
// Copy only the Application/ObjectMeta fields that are significant, from the generatedApp
|
||||
found.Spec = generatedApp.Spec
|
||||
|
||||
@@ -687,8 +688,16 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
|
||||
continue
|
||||
}
|
||||
r.updateCache(ctx, found, appLog)
|
||||
r.Recorder.Eventf(&applicationSet, corev1.EventTypeNormal, fmt.Sprint(action), "%s Application %q", action, generatedApp.Name)
|
||||
appLog.Logf(log.InfoLevel, "%s Application", action)
|
||||
|
||||
if action != controllerutil.OperationResultNone {
|
||||
// Don't pollute etcd with "unchanged Application" events
|
||||
r.Recorder.Eventf(&applicationSet, corev1.EventTypeNormal, fmt.Sprint(action), "%s Application %q", action, generatedApp.Name)
|
||||
appLog.Logf(log.InfoLevel, "%s Application", action)
|
||||
} else {
|
||||
// "unchanged Application" can be inferred by Reconcile Complete with no action being listed
|
||||
// Or enable debug logging
|
||||
appLog.Logf(log.DebugLevel, "%s Application", action)
|
||||
}
|
||||
}
|
||||
return firstError
|
||||
}
|
||||
|
||||
@@ -56,12 +56,14 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
|
||||
noRevisionCache := appSet.RefreshRequired()
|
||||
|
||||
var err error
|
||||
var res []map[string]interface{}
|
||||
if len(appSetGenerator.Git.Directories) != 0 {
|
||||
res, err = g.generateParamsForGitDirectories(appSetGenerator, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||
res, err = g.generateParamsForGitDirectories(appSetGenerator, noRevisionCache, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||
} else if len(appSetGenerator.Git.Files) != 0 {
|
||||
res, err = g.generateParamsForGitFiles(appSetGenerator, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||
res, err = g.generateParamsForGitFiles(appSetGenerator, noRevisionCache, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||
} else {
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
@@ -72,10 +74,10 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) {
|
||||
func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache bool, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) {
|
||||
|
||||
// Directories, not files
|
||||
allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision)
|
||||
allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, noRevisionCache)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting directories from repo: %w", err)
|
||||
}
|
||||
@@ -98,12 +100,12 @@ func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoproj
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) {
|
||||
func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache bool, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) {
|
||||
|
||||
// Get all files that match the requested path string, removing duplicates
|
||||
allFiles := make(map[string][]byte)
|
||||
for _, requestedPath := range appSetGenerator.Git.Files {
|
||||
files, err := g.repos.GetFiles(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, requestedPath.Path)
|
||||
files, err := g.repos.GetFiles(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, requestedPath.Path, noRevisionCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
|
||||
var gitGenerator = NewGitGenerator(&argoCDServiceMock)
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
@@ -559,7 +559,7 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
|
||||
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
|
||||
var gitGenerator = NewGitGenerator(&argoCDServiceMock)
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
@@ -918,7 +918,7 @@ cluster:
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
|
||||
|
||||
var gitGenerator = NewGitGenerator(&argoCDServiceMock)
|
||||
@@ -1268,7 +1268,7 @@ cluster:
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
|
||||
|
||||
var gitGenerator = NewGitGenerator(&argoCDServiceMock)
|
||||
|
||||
@@ -1108,7 +1108,7 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
||||
}
|
||||
|
||||
repoServiceMock := &mocks.Repos{}
|
||||
repoServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
|
||||
repoServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
|
||||
"some/path.json": []byte("test: content"),
|
||||
}, nil)
|
||||
gitGenerator := NewGitGenerator(repoServiceMock)
|
||||
|
||||
@@ -13,25 +13,25 @@ type Repos struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// GetDirectories provides a mock function with given fields: ctx, repoURL, revision
|
||||
func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision string) ([]string, error) {
|
||||
ret := _m.Called(ctx, repoURL, revision)
|
||||
// GetDirectories provides a mock function with given fields: ctx, repoURL, revision, noRevisionCache
|
||||
func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error) {
|
||||
ret := _m.Called(ctx, repoURL, revision, noRevisionCache)
|
||||
|
||||
var r0 []string
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]string, error)); ok {
|
||||
return rf(ctx, repoURL, revision)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, bool) ([]string, error)); ok {
|
||||
return rf(ctx, repoURL, revision, noRevisionCache)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) []string); ok {
|
||||
r0 = rf(ctx, repoURL, revision)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, bool) []string); ok {
|
||||
r0 = rf(ctx, repoURL, revision, noRevisionCache)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]string)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
|
||||
r1 = rf(ctx, repoURL, revision)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, bool) error); ok {
|
||||
r1 = rf(ctx, repoURL, revision, noRevisionCache)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -39,25 +39,25 @@ func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision st
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetFiles provides a mock function with given fields: ctx, repoURL, revision, pattern
|
||||
func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string, pattern string) (map[string][]byte, error) {
|
||||
ret := _m.Called(ctx, repoURL, revision, pattern)
|
||||
// GetFiles provides a mock function with given fields: ctx, repoURL, revision, pattern, noRevisionCache
|
||||
func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool) (map[string][]byte, error) {
|
||||
ret := _m.Called(ctx, repoURL, revision, pattern, noRevisionCache)
|
||||
|
||||
var r0 map[string][]byte
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (map[string][]byte, error)); ok {
|
||||
return rf(ctx, repoURL, revision, pattern)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool) (map[string][]byte, error)); ok {
|
||||
return rf(ctx, repoURL, revision, pattern, noRevisionCache)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string) map[string][]byte); ok {
|
||||
r0 = rf(ctx, repoURL, revision, pattern)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool) map[string][]byte); ok {
|
||||
r0 = rf(ctx, repoURL, revision, pattern, noRevisionCache)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(map[string][]byte)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok {
|
||||
r1 = rf(ctx, repoURL, revision, pattern)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, string, bool) error); ok {
|
||||
r1 = rf(ctx, repoURL, revision, pattern, noRevisionCache)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/vektra/mockery/v2@v2.25.1 --name=RepositoryDB
|
||||
|
||||
// RepositoryDB Is a lean facade for ArgoDB,
|
||||
// Using a lean interface makes it easier to test the functionality of the git generator
|
||||
type RepositoryDB interface {
|
||||
@@ -25,13 +27,15 @@ type argoCDService struct {
|
||||
newFileGlobbingEnabled bool
|
||||
}
|
||||
|
||||
//go:generate go run github.com/vektra/mockery/v2@v2.25.1 --name=Repos
|
||||
|
||||
type Repos interface {
|
||||
|
||||
// GetFiles returns content of files (not directories) within the target repo
|
||||
GetFiles(ctx context.Context, repoURL string, revision string, pattern string) (map[string][]byte, error)
|
||||
GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool) (map[string][]byte, error)
|
||||
|
||||
// GetDirectories returns a list of directories (not files) within the target repo
|
||||
GetDirectories(ctx context.Context, repoURL string, revision string) ([]string, error)
|
||||
GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error)
|
||||
}
|
||||
|
||||
func NewArgoCDService(db db.ArgoDB, submoduleEnabled bool, repoClientset apiclient.Clientset, newFileGlobbingEnabled bool) (Repos, error) {
|
||||
@@ -43,7 +47,7 @@ func NewArgoCDService(db db.ArgoDB, submoduleEnabled bool, repoClientset apiclie
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision string, pattern string) (map[string][]byte, error) {
|
||||
func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool) (map[string][]byte, error) {
|
||||
repo, err := a.repositoriesDB.GetRepository(ctx, repoURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error in GetRepository: %w", err)
|
||||
@@ -55,6 +59,7 @@ func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision s
|
||||
Revision: revision,
|
||||
Path: pattern,
|
||||
NewGitFileGlobbingEnabled: a.newFileGlobbingEnabled,
|
||||
NoRevisionCache: noRevisionCache,
|
||||
}
|
||||
closer, client, err := a.repoServerClientSet.NewRepoServerClient()
|
||||
if err != nil {
|
||||
@@ -69,7 +74,7 @@ func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision s
|
||||
return fileResponse.GetMap(), nil
|
||||
}
|
||||
|
||||
func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revision string) ([]string, error) {
|
||||
func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error) {
|
||||
repo, err := a.repositoriesDB.GetRepository(ctx, repoURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error in GetRepository: %w", err)
|
||||
@@ -79,6 +84,7 @@ func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revi
|
||||
Repo: repo,
|
||||
SubmoduleEnabled: a.submoduleEnabled,
|
||||
Revision: revision,
|
||||
NoRevisionCache: noRevisionCache,
|
||||
}
|
||||
|
||||
closer, client, err := a.repoServerClientSet.NewRepoServerClient()
|
||||
|
||||
@@ -25,9 +25,10 @@ func TestGetDirectories(t *testing.T) {
|
||||
repoServerClientFuncs []func(*repo_mocks.RepoServerServiceClient)
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
repoURL string
|
||||
revision string
|
||||
ctx context.Context
|
||||
repoURL string
|
||||
revision string
|
||||
noRevisionCache bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -88,11 +89,11 @@ func TestGetDirectories(t *testing.T) {
|
||||
submoduleEnabled: tt.fields.submoduleEnabled,
|
||||
repoServerClientSet: &repo_mocks.Clientset{RepoServerServiceClient: mockRepoClient},
|
||||
}
|
||||
got, err := a.GetDirectories(tt.args.ctx, tt.args.repoURL, tt.args.revision)
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("GetDirectories(%v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision)) {
|
||||
got, err := a.GetDirectories(tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.noRevisionCache)
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("GetDirectories(%v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.noRevisionCache)) {
|
||||
return
|
||||
}
|
||||
assert.Equalf(t, tt.want, got, "GetDirectories(%v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision)
|
||||
assert.Equalf(t, tt.want, got, "GetDirectories(%v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.noRevisionCache)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -105,10 +106,11 @@ func TestGetFiles(t *testing.T) {
|
||||
repoServerClientFuncs []func(*repo_mocks.RepoServerServiceClient)
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
repoURL string
|
||||
revision string
|
||||
pattern string
|
||||
ctx context.Context
|
||||
repoURL string
|
||||
revision string
|
||||
pattern string
|
||||
noRevisionCache bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -175,11 +177,11 @@ func TestGetFiles(t *testing.T) {
|
||||
submoduleEnabled: tt.fields.submoduleEnabled,
|
||||
repoServerClientSet: &repo_mocks.Clientset{RepoServerServiceClient: mockRepoClient},
|
||||
}
|
||||
got, err := a.GetFiles(tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern)
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("GetFiles(%v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern)) {
|
||||
got, err := a.GetFiles(tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern, tt.args.noRevisionCache)
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("GetFiles(%v, %v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern, tt.args.noRevisionCache)) {
|
||||
return
|
||||
}
|
||||
assert.Equalf(t, tt.want, got, "GetFiles(%v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern)
|
||||
assert.Equalf(t, tt.want, got, "GetFiles(%v, %v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern, tt.args.noRevisionCache)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
)
|
||||
|
||||
// CreateOrUpdate overrides "sigs.k8s.io/controller-runtime" function
|
||||
@@ -35,7 +36,7 @@ import (
|
||||
// The MutateFn is called regardless of creating or updating an object.
|
||||
//
|
||||
// It returns the executed operation and an error.
|
||||
func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ignoreAppDifferences argov1alpha1.ApplicationSetIgnoreDifferences, obj *argov1alpha1.Application, f controllerutil.MutateFn) (controllerutil.OperationResult, error) {
|
||||
func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ignoreAppDifferences argov1alpha1.ApplicationSetIgnoreDifferences, ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts, obj *argov1alpha1.Application, f controllerutil.MutateFn) (controllerutil.OperationResult, error) {
|
||||
|
||||
key := client.ObjectKeyFromObject(obj)
|
||||
if err := c.Get(ctx, key, obj); err != nil {
|
||||
@@ -60,7 +61,7 @@ func CreateOrUpdate(ctx context.Context, logCtx *log.Entry, c client.Client, ign
|
||||
|
||||
// Apply ignoreApplicationDifferences rules to remove ignored fields from both the live and the desired state. This
|
||||
// prevents those differences from appearing in the diff and therefore in the patch.
|
||||
err := applyIgnoreDifferences(ignoreAppDifferences, normalizedLive, obj)
|
||||
err := applyIgnoreDifferences(ignoreAppDifferences, normalizedLive, obj, ignoreNormalizerOpts)
|
||||
if err != nil {
|
||||
return controllerutil.OperationResultNone, fmt.Errorf("failed to apply ignore differences: %w", err)
|
||||
}
|
||||
@@ -134,14 +135,14 @@ func mutate(f controllerutil.MutateFn, key client.ObjectKey, obj client.Object)
|
||||
}
|
||||
|
||||
// applyIgnoreDifferences applies the ignore differences rules to the found application. It modifies the applications in place.
|
||||
func applyIgnoreDifferences(applicationSetIgnoreDifferences argov1alpha1.ApplicationSetIgnoreDifferences, found *argov1alpha1.Application, generatedApp *argov1alpha1.Application) error {
|
||||
func applyIgnoreDifferences(applicationSetIgnoreDifferences argov1alpha1.ApplicationSetIgnoreDifferences, found *argov1alpha1.Application, generatedApp *argov1alpha1.Application, ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts) error {
|
||||
if len(applicationSetIgnoreDifferences) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
generatedAppCopy := generatedApp.DeepCopy()
|
||||
diffConfig, err := argodiff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(applicationSetIgnoreDifferences.ToApplicationIgnoreDifferences(), nil, false).
|
||||
WithDiffSettings(applicationSetIgnoreDifferences.ToApplicationIgnoreDifferences(), nil, false, ignoreNormalizerOpts).
|
||||
WithNoCache().
|
||||
Build()
|
||||
if err != nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
)
|
||||
|
||||
func Test_applyIgnoreDifferences(t *testing.T) {
|
||||
@@ -222,7 +223,7 @@ spec:
|
||||
generatedApp := v1alpha1.Application{TypeMeta: appMeta}
|
||||
err = yaml.Unmarshal([]byte(tc.generatedApp), &generatedApp)
|
||||
require.NoError(t, err, tc.generatedApp)
|
||||
err = applyIgnoreDifferences(tc.ignoreDifferences, &foundApp, &generatedApp)
|
||||
err = applyIgnoreDifferences(tc.ignoreDifferences, &foundApp, &generatedApp, normalizers.IgnoreNormalizerOpts{})
|
||||
require.NoError(t, err)
|
||||
yamlFound, err := yaml.Marshal(tc.foundApp)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -5089,7 +5089,7 @@
|
||||
}
|
||||
},
|
||||
"runtimeRawExtension": {
|
||||
"description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned\nstruct, and Object in your internal struct. You also need to register your\nvarious plugin types.\n\n// Internal package:\ntype MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.Object `json:\"myPlugin\"`\n}\ntype PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// External package:\ntype MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n}\ntype PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// On the wire, the JSON will look something like this:\n{\n\t\"kind\":\"MyAPIObject\",\n\t\"apiVersion\":\"v1\",\n\t\"myPlugin\": {\n\t\t\"kind\":\"PluginA\",\n\t\t\"aOption\":\"foo\",\n\t},\n}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into\nyour external MyAPIObject. That causes the raw JSON to be stored, but not unpacked.\nThe next step is to copy (using pkg/conversion) into the internal struct. The runtime\npackage's DefaultScheme has conversion functions installed which will unpack the\nJSON stored in RawExtension, turning it into the correct object type, and storing it\nin the Object. (TODO: In the case where the object is of an unknown type, a\nruntime.Unknown object will be created and stored.)\n\n+k8s:deepcopy-gen=true\n+protobuf=true\n+k8s:openapi-gen=true",
|
||||
"description": "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned\nstruct, and Object in your internal struct. You also need to register your\nvarious plugin types.\n\n// Internal package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.Object `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// External package:\n\n\ttype MyAPIObject struct {\n\t\truntime.TypeMeta `json:\",inline\"`\n\t\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n\t}\n\n\ttype PluginA struct {\n\t\tAOption string `json:\"aOption\"`\n\t}\n\n// On the wire, the JSON will look something like this:\n\n\t{\n\t\t\"kind\":\"MyAPIObject\",\n\t\t\"apiVersion\":\"v1\",\n\t\t\"myPlugin\": {\n\t\t\t\"kind\":\"PluginA\",\n\t\t\t\"aOption\":\"foo\",\n\t\t},\n\t}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into\nyour external MyAPIObject. That causes the raw JSON to be stored, but not unpacked.\nThe next step is to copy (using pkg/conversion) into the internal struct. The runtime\npackage's DefaultScheme has conversion functions installed which will unpack the\nJSON stored in RawExtension, turning it into the correct object type, and storing it\nin the Object. (TODO: In the case where the object is of an unknown type, a\nruntime.Unknown object will be created and stored.)\n\n+k8s:deepcopy-gen=true\n+protobuf=true\n+k8s:openapi-gen=true",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"raw": {
|
||||
@@ -5571,8 +5571,8 @@
|
||||
}
|
||||
},
|
||||
"v1ObjectReference": {
|
||||
"description": "ObjectReference contains enough information to let you inspect or modify the referred object.\n---\nNew uses of this type are discouraged because of difficulty describing its usage when embedded in APIs.\n 1. Ignored fields. It includes many fields which are not generally honored. For instance, ResourceVersion and FieldPath are both very rarely valid in actual usage.\n 2. Invalid usage help. It is impossible to add specific help for individual usage. In most embedded usages, there are particular\n restrictions like, \"must refer only to types A and B\" or \"UID not honored\" or \"name must be restricted\".\n Those cannot be well described when embedded.\n 3. Inconsistent validation. Because the usages are different, the validation rules are different by usage, which makes it hard for users to predict what will happen.\n 4. The fields are both imprecise and overly precise. Kind is not a precise mapping to a URL. This can produce ambiguity\n during interpretation and require a REST mapping. In most cases, the dependency is on the group,resource tuple\n and the version of the actual struct is irrelevant.\n 5. We cannot easily change it. Because this type is embedded in many locations, updates to this type\n will affect numerous schemas. Don't make new APIs embed an underspecified API type they do not control.\n\nInstead of using this type, create a locally provided and used type that is well-focused on your reference.\nFor example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 .\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+structType=atomic",
|
||||
"type": "object",
|
||||
"title": "ObjectReference contains enough information to let you inspect or modify the referred object.\n---\nNew uses of this type are discouraged because of difficulty describing its usage when embedded in APIs.\n 1. Ignored fields. It includes many fields which are not generally honored. For instance, ResourceVersion and FieldPath are both very rarely valid in actual usage.\n 2. Invalid usage help. It is impossible to add specific help for individual usage. In most embedded usages, there are particular\n restrictions like, \"must refer only to types A and B\" or \"UID not honored\" or \"name must be restricted\".\n Those cannot be well described when embedded.\n 3. Inconsistent validation. Because the usages are different, the validation rules are different by usage, which makes it hard for users to predict what will happen.\n 4. The fields are both imprecise and overly precise. Kind is not a precise mapping to a URL. This can produce ambiguity\n during interpretation and require a REST mapping. In most cases, the dependency is on the group,resource tuple\n and the version of the actual struct is irrelevant.\n 5. We cannot easily change it. Because this type is embedded in many locations, updates to this type\n will affect numerous schemas. Don't make new APIs embed an underspecified API type they do not control.\nInstead of using this type, create a locally provided and used type that is well-focused on your reference.\nFor example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 .\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+structType=atomic",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"type": "string",
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
@@ -68,6 +69,7 @@ func NewCommand() *cobra.Command {
|
||||
persistResourceHealth bool
|
||||
shardingAlgorithm string
|
||||
enableDynamicClusterDistribution bool
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -160,6 +162,7 @@ func NewCommand() *cobra.Command {
|
||||
persistResourceHealth,
|
||||
clusterFilter,
|
||||
applicationNamespaces,
|
||||
ignoreNormalizerOpts,
|
||||
)
|
||||
errors.CheckError(err)
|
||||
cacheutil.CollectMetrics(redisClient, appController.GetMetricsServer())
|
||||
@@ -206,6 +209,7 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().BoolVar(&persistResourceHealth, "persist-resource-health", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_PERSIST_RESOURCE_HEALTH", true), "Enables storing the managed resources health in the Application CRD")
|
||||
command.Flags().StringVar(&shardingAlgorithm, "sharding-method", env.StringFromEnv(common.EnvControllerShardingAlgorithm, common.DefaultShardingAlgorithm), "Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] ")
|
||||
command.Flags().BoolVar(&enableDynamicClusterDistribution, "dynamic-cluster-distribution-enabled", env.ParseBoolFromEnv(common.EnvEnableDynamicClusterDistribution, false), "Enables dynamic cluster distribution.")
|
||||
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout-seconds", env.ParseDurationFromEnv("ARGOCD_IGNORE_NORMALIZER_JQ_TIMEOUT", 0*time.Second, 0, math.MaxInt64), "Set ignore normalizer JQ execution timeout")
|
||||
cacheSource = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
redisClient = client
|
||||
})
|
||||
|
||||
@@ -66,6 +66,7 @@ func NewCommand() *cobra.Command {
|
||||
streamedManifestMaxTarSize string
|
||||
streamedManifestMaxExtractedSize string
|
||||
helmManifestMaxExtractedSize string
|
||||
helmRegistryMaxIndexSize string
|
||||
disableManifestMaxExtractedSize bool
|
||||
)
|
||||
var command = cobra.Command{
|
||||
@@ -108,6 +109,9 @@ func NewCommand() *cobra.Command {
|
||||
helmManifestMaxExtractedSizeQuantity, err := resource.ParseQuantity(helmManifestMaxExtractedSize)
|
||||
errors.CheckError(err)
|
||||
|
||||
helmRegistryMaxIndexSizeQuantity, err := resource.ParseQuantity(helmRegistryMaxIndexSize)
|
||||
errors.CheckError(err)
|
||||
|
||||
askPassServer := askpass.NewServer()
|
||||
metricsServer := metrics.NewMetricsServer()
|
||||
cacheutil.CollectMetrics(redisClient, metricsServer)
|
||||
@@ -123,6 +127,7 @@ func NewCommand() *cobra.Command {
|
||||
StreamedManifestMaxExtractedSize: streamedManifestMaxExtractedSizeQuantity.ToDec().Value(),
|
||||
StreamedManifestMaxTarSize: streamedManifestMaxTarSizeQuantity.ToDec().Value(),
|
||||
HelmManifestMaxExtractedSize: helmManifestMaxExtractedSizeQuantity.ToDec().Value(),
|
||||
HelmRegistryMaxIndexSize: helmRegistryMaxIndexSizeQuantity.ToDec().Value(),
|
||||
}, askPassServer)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -204,6 +209,7 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringVar(&streamedManifestMaxTarSize, "streamed-manifest-max-tar-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_TAR_SIZE", "100M"), "Maximum size of streamed manifest archives")
|
||||
command.Flags().StringVar(&streamedManifestMaxExtractedSize, "streamed-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_STREAMED_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of streamed manifest archives when extracted")
|
||||
command.Flags().StringVar(&helmManifestMaxExtractedSize, "helm-manifest-max-extracted-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_EXTRACTED_SIZE", "1G"), "Maximum size of helm manifest archives when extracted")
|
||||
command.Flags().StringVar(&helmRegistryMaxIndexSize, "helm-registry-max-index-size", env.StringFromEnv("ARGOCD_REPO_SERVER_HELM_MANIFEST_MAX_INDEX_SIZE", "1G"), "Maximum size of registry index file")
|
||||
command.Flags().BoolVar(&disableManifestMaxExtractedSize, "disable-helm-manifest-max-extracted-size", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_HELM_MANIFEST_MAX_EXTRACTED_SIZE", false), "Disable maximum size of helm manifest archives when extracted")
|
||||
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
|
||||
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/pkg/stats"
|
||||
@@ -58,6 +59,7 @@ func NewCommand() *cobra.Command {
|
||||
repoServerAddress string
|
||||
dexServerAddress string
|
||||
disableAuth bool
|
||||
contentTypes string
|
||||
enableGZip bool
|
||||
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
|
||||
cacheSrc func() (*servercache.Cache, error)
|
||||
@@ -162,6 +164,11 @@ func NewCommand() *cobra.Command {
|
||||
baseHRef = rootPath
|
||||
}
|
||||
|
||||
var contentTypesList []string
|
||||
if contentTypes != "" {
|
||||
contentTypesList = strings.Split(contentTypes, ";")
|
||||
}
|
||||
|
||||
argoCDOpts := server.ArgoCDServerOpts{
|
||||
Insecure: insecure,
|
||||
ListenPort: listenPort,
|
||||
@@ -177,6 +184,7 @@ func NewCommand() *cobra.Command {
|
||||
DexServerAddr: dexServerAddress,
|
||||
DexTLSConfig: dexTlsConfig,
|
||||
DisableAuth: disableAuth,
|
||||
ContentTypes: contentTypesList,
|
||||
EnableGZip: enableGZip,
|
||||
TLSConfigCustomizer: tlsConfigCustomizer,
|
||||
Cache: cache,
|
||||
@@ -224,6 +232,7 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringVar(&repoServerAddress, "repo-server", env.StringFromEnv("ARGOCD_SERVER_REPO_SERVER", common.DefaultRepoServerAddr), "Repo server address")
|
||||
command.Flags().StringVar(&dexServerAddress, "dex-server", env.StringFromEnv("ARGOCD_SERVER_DEX_SERVER", common.DefaultDexServerAddr), "Dex server address")
|
||||
command.Flags().BoolVar(&disableAuth, "disable-auth", env.ParseBoolFromEnv("ARGOCD_SERVER_DISABLE_AUTH", false), "Disable client authentication")
|
||||
command.Flags().StringVar(&contentTypes, "api-content-types", env.StringFromEnv("ARGOCD_API_CONTENT_TYPES", "application/json", env.StringFromEnvOpts{AllowEmpty: true}), "Semicolon separated list of allowed content types for non GET api requests. Any content type is allowed if empty.")
|
||||
command.Flags().BoolVar(&enableGZip, "enable-gzip", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_GZIP", true), "Enable GZIP compression")
|
||||
command.AddCommand(cli.NewVersionCmd(cliName))
|
||||
command.Flags().StringVar(&listenHost, "address", env.StringFromEnv("ARGOCD_SERVER_LISTEN_ADDRESS", common.DefaultAddressAPIServer), "Listen on given address")
|
||||
|
||||
@@ -57,7 +57,7 @@ func NewAdminCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
command.AddCommand(NewRepoCommand())
|
||||
command.AddCommand(NewImportCommand())
|
||||
command.AddCommand(NewExportCommand())
|
||||
command.AddCommand(NewDashboardCommand())
|
||||
command.AddCommand(NewDashboardCommand(clientOpts))
|
||||
command.AddCommand(NewNotificationsCommand())
|
||||
command.AddCommand(NewInitialPasswordCommand())
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
appinformers "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions"
|
||||
reposerverclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
@@ -228,11 +229,12 @@ func diffReconcileResults(res1 reconcileResults, res2 reconcileResults) error {
|
||||
|
||||
func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
selector string
|
||||
repoServerAddress string
|
||||
outputFormat string
|
||||
refresh bool
|
||||
clientConfig clientcmd.ClientConfig
|
||||
selector string
|
||||
repoServerAddress string
|
||||
outputFormat string
|
||||
refresh bool
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
)
|
||||
|
||||
var command = &cobra.Command{
|
||||
@@ -270,7 +272,7 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
|
||||
appClientset := appclientset.NewForConfigOrDie(cfg)
|
||||
kubeClientset := kubernetes.NewForConfigOrDie(cfg)
|
||||
result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache)
|
||||
result, err = reconcileApplications(ctx, kubeClientset, appClientset, namespace, repoServerClient, selector, newLiveStateCache, ignoreNormalizerOpts)
|
||||
errors.CheckError(err)
|
||||
} else {
|
||||
appClientset := appclientset.NewForConfigOrDie(cfg)
|
||||
@@ -285,6 +287,7 @@ func NewReconcileCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
command.Flags().StringVar(&selector, "l", "", "Label selector")
|
||||
command.Flags().StringVar(&outputFormat, "o", "yaml", "Output format (yaml|json)")
|
||||
command.Flags().BoolVar(&refresh, "refresh", false, "If set to true then recalculates apps reconciliation")
|
||||
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout")
|
||||
|
||||
return command
|
||||
}
|
||||
@@ -334,6 +337,7 @@ func reconcileApplications(
|
||||
repoServerClient reposerverclient.Clientset,
|
||||
selector string,
|
||||
createLiveStateCache func(argoDB db.ArgoDB, appInformer kubecache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) cache.LiveStateCache,
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts,
|
||||
) ([]appReconcileResult, error) {
|
||||
settingsMgr := settings.NewSettingsManager(ctx, kubeClientset, namespace)
|
||||
argoDB := db.NewDB(namespace, settingsMgr, kubeClientset)
|
||||
@@ -374,7 +378,7 @@ func reconcileApplications(
|
||||
)
|
||||
|
||||
appStateManager := controller.NewAppStateManager(
|
||||
argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking(), false)
|
||||
argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking(), false, ignoreNormalizerOpts)
|
||||
|
||||
appsList, err := appClientset.ArgoprojV1alpha1().Applications(namespace).List(ctx, v1.ListOptions{LabelSelector: selector})
|
||||
if err != nil {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/test"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
@@ -113,6 +114,7 @@ func TestGetReconcileResults_Refresh(t *testing.T) {
|
||||
func(argoDB db.ArgoDB, appInformer cache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) statecache.LiveStateCache {
|
||||
return &liveStateCache
|
||||
},
|
||||
normalizers.IgnoreNormalizerOpts{},
|
||||
)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
|
||||
@@ -3,7 +3,9 @@ package admin
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless"
|
||||
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/initialize"
|
||||
@@ -14,11 +16,12 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
)
|
||||
|
||||
func NewDashboardCommand() *cobra.Command {
|
||||
func NewDashboardCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
port int
|
||||
address string
|
||||
compressionStr string
|
||||
clientConfig clientcmd.ClientConfig
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "dashboard",
|
||||
@@ -28,12 +31,13 @@ func NewDashboardCommand() *cobra.Command {
|
||||
|
||||
compression, err := cache.CompressionTypeFromString(compressionStr)
|
||||
errors.CheckError(err)
|
||||
errors.CheckError(headless.StartLocalServer(ctx, &argocdclient.ClientOptions{Core: true}, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address, compression))
|
||||
clientOpts.Core = true
|
||||
errors.CheckError(headless.MaybeStartLocalServer(ctx, clientOpts, initialize.RetrieveContextIfChanged(cmd.Flag("context")), &port, &address, compression, clientConfig))
|
||||
println(fmt.Sprintf("Argo CD UI is available at http://%s:%d", address, port))
|
||||
<-ctx.Done()
|
||||
},
|
||||
}
|
||||
initialize.InitCommand(cmd)
|
||||
clientConfig = cli.AddKubectlFlagsToSet(cmd.Flags())
|
||||
cmd.Flags().IntVar(&port, "port", common.DefaultPortAPIServer, "Listen on given port")
|
||||
cmd.Flags().StringVar(&address, "address", common.DefaultAddressAdminDashboard, "Listen on given address")
|
||||
cmd.Flags().StringVar(&compressionStr, "redis-compress", env.StringFromEnv("REDIS_COMPRESSION", string(cache.RedisCompressionGZip)), "Enable this if the application controller is configured with redis compression enabled. (possible values: gzip, none)")
|
||||
|
||||
@@ -432,7 +432,7 @@ argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argo
|
||||
// configurations. This requires access to live resources which is not the
|
||||
// purpose of this command. This will just apply jsonPointers and
|
||||
// jqPathExpressions configurations.
|
||||
normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides)
|
||||
normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides, normalizers.IgnoreNormalizerOpts{})
|
||||
errors.CheckError(err)
|
||||
|
||||
normalizedRes := res.DeepCopy()
|
||||
@@ -457,6 +457,9 @@ argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argo
|
||||
}
|
||||
|
||||
func NewResourceIgnoreResourceUpdatesCommand(cmdCtx commandContext) *cobra.Command {
|
||||
var (
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "ignore-resource-updates RESOURCE_YAML_PATH",
|
||||
Short: "Renders fields excluded from resource updates",
|
||||
@@ -478,7 +481,7 @@ argocd admin settings resource-overrides ignore-resource-updates ./deploy.yaml -
|
||||
return
|
||||
}
|
||||
|
||||
normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides)
|
||||
normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides, ignoreNormalizerOpts)
|
||||
errors.CheckError(err)
|
||||
|
||||
normalizedRes := res.DeepCopy()
|
||||
@@ -499,6 +502,7 @@ argocd admin settings resource-overrides ignore-resource-updates ./deploy.yaml -
|
||||
})
|
||||
},
|
||||
}
|
||||
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout")
|
||||
return command
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/repository"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
@@ -964,14 +965,15 @@ type objKeyLiveTarget struct {
|
||||
// NewApplicationDiffCommand returns a new instance of an `argocd app diff` command
|
||||
func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
refresh bool
|
||||
hardRefresh bool
|
||||
exitCode bool
|
||||
local string
|
||||
revision string
|
||||
localRepoRoot string
|
||||
serverSideGenerate bool
|
||||
localIncludes []string
|
||||
refresh bool
|
||||
hardRefresh bool
|
||||
exitCode bool
|
||||
local string
|
||||
revision string
|
||||
localRepoRoot string
|
||||
serverSideGenerate bool
|
||||
localIncludes []string
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
)
|
||||
shortDesc := "Perform a diff against the target and live state."
|
||||
var command = &cobra.Command{
|
||||
@@ -1031,13 +1033,14 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
defer argoio.Close(conn)
|
||||
cluster, err := clusterIf.Get(ctx, &clusterpkg.ClusterQuery{Name: app.Spec.Destination.Name, Server: app.Spec.Destination.Server})
|
||||
errors.CheckError(err)
|
||||
|
||||
diffOption.local = local
|
||||
diffOption.localRepoRoot = localRepoRoot
|
||||
diffOption.cluster = cluster
|
||||
}
|
||||
}
|
||||
proj := getProject(c, clientOpts, ctx, app.Spec.Project)
|
||||
foundDiffs := findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption)
|
||||
foundDiffs := findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption, ignoreNormalizerOpts)
|
||||
if foundDiffs && exitCode {
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -1051,6 +1054,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
command.Flags().StringVar(&localRepoRoot, "local-repo-root", "/", "Path to the repository root. Used together with --local allows setting the repository root")
|
||||
command.Flags().BoolVar(&serverSideGenerate, "server-side-generate", false, "Used with --local, this will send your manifests to the server for diffing")
|
||||
command.Flags().StringArrayVar(&localIncludes, "local-include", []string{"*.yaml", "*.yml", "*.json"}, "Used with --server-side-generate, specify patterns of filenames to send. Matching is based on filename and not path.")
|
||||
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -1065,7 +1069,7 @@ type DifferenceOption struct {
|
||||
}
|
||||
|
||||
// findandPrintDiff ... Prints difference between application current state and state stored in git or locally, returns boolean as true if difference is found else returns false
|
||||
func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, resources *application.ManagedResourcesResponse, argoSettings *settings.Settings, diffOptions *DifferenceOption) bool {
|
||||
func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, resources *application.ManagedResourcesResponse, argoSettings *settings.Settings, diffOptions *DifferenceOption, ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts) bool {
|
||||
var foundDiffs bool
|
||||
liveObjs, err := cmdutil.LiveObjects(resources.Items)
|
||||
errors.CheckError(err)
|
||||
@@ -1120,7 +1124,7 @@ func findandPrintDiff(ctx context.Context, app *argoappv1.Application, proj *arg
|
||||
// compareOptions in the protobuf
|
||||
ignoreAggregatedRoles := false
|
||||
diffConfig, err := argodiff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, overrides, ignoreAggregatedRoles).
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, overrides, ignoreAggregatedRoles, ignoreNormalizerOpts).
|
||||
WithTracking(argoSettings.AppLabelKey, argoSettings.TrackingMethod).
|
||||
WithNoCache().
|
||||
Build()
|
||||
@@ -1613,6 +1617,8 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
diffChangesConfirm bool
|
||||
projects []string
|
||||
output string
|
||||
appNamespace string
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "sync [APPNAME... | -l selector | --project project-name]",
|
||||
@@ -1837,7 +1843,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
fmt.Printf("====== Previewing differences between live and desired state of application %s ======\n", appQualifiedName)
|
||||
|
||||
proj := getProject(c, clientOpts, ctx, app.Spec.Project)
|
||||
foundDiffs = findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption)
|
||||
foundDiffs = findandPrintDiff(ctx, app, proj.Project, resources, argoSettings, diffOption, ignoreNormalizerOpts)
|
||||
if foundDiffs {
|
||||
if !diffChangesConfirm {
|
||||
yesno := cli.AskToProceed(fmt.Sprintf("Please review changes to application %s shown above. Do you want to continue the sync process? (y/n): ", appQualifiedName))
|
||||
@@ -1895,6 +1901,8 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
command.Flags().BoolVar(&diffChanges, "preview-changes", false, "Preview difference against the target and live state before syncing app and wait for user confirmation")
|
||||
command.Flags().StringArrayVar(&projects, "project", []string{}, "Sync apps that belong to the specified projects. This option may be specified repeatedly.")
|
||||
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|tree|tree=detailed")
|
||||
command.Flags().StringVarP(&appNamespace, "app-namespace", "N", "", "Only sync an application in namespace")
|
||||
command.Flags().DurationVar(&ignoreNormalizerOpts.JQExecutionTimeout, "ignore-normalizer-jq-execution-timeout", normalizers.DefaultJQExecutionTimeout, "Set ignore normalizer JQ execution timeout")
|
||||
return command
|
||||
}
|
||||
|
||||
|
||||
@@ -148,13 +148,19 @@ func testAPI(ctx context.Context, clientOpts *apiclient.ClientOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartLocalServer allows executing command in a headless mode: on the fly starts Argo CD API server and
|
||||
// changes provided client options to use started API server port
|
||||
func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string, compression cache.RedisCompressionType) error {
|
||||
flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError)
|
||||
clientConfig := cli.AddKubectlFlagsToSet(flags)
|
||||
// MaybeStartLocalServer allows executing command in a headless mode. If we're in core mode, starts the Argo CD API
|
||||
// server on the fly and changes provided client options to use started API server port.
|
||||
//
|
||||
// If the clientOpts enables core mode, but the local config does not have core mode enabled, this function will
|
||||
// not start the local server.
|
||||
func MaybeStartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions, ctxStr string, port *int, address *string, compression cache.RedisCompressionType, clientConfig clientcmd.ClientConfig) error {
|
||||
if clientConfig == nil {
|
||||
flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError)
|
||||
clientConfig = cli.AddKubectlFlagsToSet(flags)
|
||||
}
|
||||
startInProcessAPI := clientOpts.Core
|
||||
if !startInProcessAPI {
|
||||
// Core mode is enabled on client options. Check the local config to see if we should start the API server.
|
||||
localCfg, err := localconfig.ReadLocalConfig(clientOpts.ConfigPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading local config: %w", err)
|
||||
@@ -164,9 +170,11 @@ func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions,
|
||||
if err != nil {
|
||||
return fmt.Errorf("error resolving context: %w", err)
|
||||
}
|
||||
// There was a local config file, so determine whether core mode is enabled per the config file.
|
||||
startInProcessAPI = configCtx.Server.Core
|
||||
}
|
||||
}
|
||||
// If we're in core mode, start the API server on the fly.
|
||||
if !startInProcessAPI {
|
||||
return nil
|
||||
}
|
||||
@@ -238,6 +246,7 @@ func StartLocalServer(ctx context.Context, clientOpts *apiclient.ClientOptions,
|
||||
if !cache2.WaitForCacheSync(ctx.Done(), srv.Initialized) {
|
||||
log.Fatal("Timed out waiting for project cache to sync")
|
||||
}
|
||||
|
||||
tries := 5
|
||||
for i := 0; i < tries; i++ {
|
||||
err = testAPI(ctx, clientOpts)
|
||||
@@ -257,7 +266,9 @@ func NewClientOrDie(opts *apiclient.ClientOptions, c *cobra.Command) apiclient.C
|
||||
ctx := c.Context()
|
||||
|
||||
ctxStr := initialize.RetrieveContextIfChanged(c.Flag("context"))
|
||||
err := StartLocalServer(ctx, opts, ctxStr, nil, nil, cache.RedisCompressionNone)
|
||||
// If we're in core mode, start the API server on the fly and configure the client `opts` to use it.
|
||||
// If we're not in core mode, this function call will do nothing.
|
||||
err := MaybeStartLocalServer(ctx, opts, ctxStr, nil, nil, cache.RedisCompressionNone, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
@@ -127,6 +128,7 @@ type ApplicationController struct {
|
||||
clusterFilter func(cluster *appv1.Cluster) bool
|
||||
projByNameCache sync.Map
|
||||
applicationNamespaces []string
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
}
|
||||
|
||||
// NewApplicationController creates new instance of ApplicationController.
|
||||
@@ -148,6 +150,7 @@ func NewApplicationController(
|
||||
persistResourceHealth bool,
|
||||
clusterFilter func(cluster *appv1.Cluster) bool,
|
||||
applicationNamespaces []string,
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts,
|
||||
) (*ApplicationController, error) {
|
||||
log.Infof("appResyncPeriod=%v, appHardResyncPeriod=%v", appResyncPeriod, appHardResyncPeriod)
|
||||
db := db.NewDB(namespace, settingsMgr, kubeClientset)
|
||||
@@ -173,6 +176,7 @@ func NewApplicationController(
|
||||
clusterFilter: clusterFilter,
|
||||
projByNameCache: sync.Map{},
|
||||
applicationNamespaces: applicationNamespaces,
|
||||
ignoreNormalizerOpts: ignoreNormalizerOpts,
|
||||
}
|
||||
if kubectlParallelismLimit > 0 {
|
||||
ctrl.kubectlSemaphore = semaphore.NewWeighted(kubectlParallelismLimit)
|
||||
@@ -247,7 +251,7 @@ func NewApplicationController(
|
||||
}
|
||||
}
|
||||
stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settingsMgr, kubectl, ctrl.metricsServer, ctrl.handleObjectUpdated, clusterFilter, argo.NewResourceTracking())
|
||||
appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectl, ctrl.settingsMgr, stateCache, projInformer, ctrl.metricsServer, argoCache, ctrl.statusRefreshTimeout, argo.NewResourceTracking(), persistResourceHealth)
|
||||
appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectl, ctrl.settingsMgr, stateCache, projInformer, ctrl.metricsServer, argoCache, ctrl.statusRefreshTimeout, argo.NewResourceTracking(), persistResourceHealth, ignoreNormalizerOpts)
|
||||
ctrl.appInformer = appInformer
|
||||
ctrl.appLister = appLister
|
||||
ctrl.projInformer = projInformer
|
||||
@@ -698,7 +702,7 @@ func (ctrl *ApplicationController) hideSecretData(app *appv1.Application, compar
|
||||
return nil, fmt.Errorf("error getting cluster cache: %s", err)
|
||||
}
|
||||
diffConfig, err := argodiff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, resourceOverrides, compareOptions.IgnoreAggregatedRoles).
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, resourceOverrides, compareOptions.IgnoreAggregatedRoles, ctrl.ignoreNormalizerOpts).
|
||||
WithTracking(appLabelKey, trackingMethod).
|
||||
WithNoCache().
|
||||
WithLogger(logutils.NewLogrusLogger(logutils.NewWithCurrentConfig())).
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
mockrepoclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/test"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
@@ -123,6 +124,7 @@ func newFakeController(data *fakeData) *ApplicationController {
|
||||
true,
|
||||
nil,
|
||||
data.applicationNamespaces,
|
||||
normalizers.IgnoreNormalizerOpts{},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
20
controller/cache/cache.go
vendored
20
controller/cache/cache.go
vendored
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
logutils "github.com/argoproj/argo-cd/v2/util/log"
|
||||
@@ -196,14 +197,15 @@ type cacheSettings struct {
|
||||
}
|
||||
|
||||
type liveStateCache struct {
|
||||
db db.ArgoDB
|
||||
appInformer cache.SharedIndexInformer
|
||||
onObjectUpdated ObjectUpdatedHandler
|
||||
kubectl kube.Kubectl
|
||||
settingsMgr *settings.SettingsManager
|
||||
metricsServer *metrics.MetricsServer
|
||||
clusterFilter func(cluster *appv1.Cluster) bool
|
||||
resourceTracking argo.ResourceTracking
|
||||
db db.ArgoDB
|
||||
appInformer cache.SharedIndexInformer
|
||||
onObjectUpdated ObjectUpdatedHandler
|
||||
kubectl kube.Kubectl
|
||||
settingsMgr *settings.SettingsManager
|
||||
metricsServer *metrics.MetricsServer
|
||||
clusterFilter func(cluster *appv1.Cluster) bool
|
||||
resourceTracking argo.ResourceTracking
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
|
||||
clusters map[string]clustercache.ClusterCache
|
||||
cacheSettings cacheSettings
|
||||
@@ -486,7 +488,7 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
|
||||
gvk := un.GroupVersionKind()
|
||||
|
||||
if cacheSettings.ignoreResourceUpdatesEnabled && shouldHashManifest(appName, gvk) {
|
||||
hash, err := generateManifestHash(un, nil, cacheSettings.resourceOverrides)
|
||||
hash, err := generateManifestHash(un, nil, cacheSettings.resourceOverrides, c.ignoreNormalizerOpts)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to generate manifest hash: %v", err)
|
||||
} else {
|
||||
|
||||
4
controller/cache/info.go
vendored
4
controller/cache/info.go
vendored
@@ -390,8 +390,8 @@ func populateHostNodeInfo(un *unstructured.Unstructured, res *ResourceInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
func generateManifestHash(un *unstructured.Unstructured, ignores []v1alpha1.ResourceIgnoreDifferences, overrides map[string]v1alpha1.ResourceOverride) (string, error) {
|
||||
normalizer, err := normalizers.NewIgnoreNormalizer(ignores, overrides)
|
||||
func generateManifestHash(un *unstructured.Unstructured, ignores []v1alpha1.ResourceIgnoreDifferences, overrides map[string]v1alpha1.ResourceOverride, opts normalizers.IgnoreNormalizerOpts) (string, error) {
|
||||
normalizer, err := normalizers.NewIgnoreNormalizer(ignores, overrides, opts)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating normalizer: %w", err)
|
||||
}
|
||||
|
||||
3
controller/cache/info_test.go
vendored
3
controller/cache/info_test.go
vendored
@@ -16,6 +16,7 @@ import (
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
)
|
||||
|
||||
func strToUnstructured(jsonStr string) *unstructured.Unstructured {
|
||||
@@ -749,7 +750,7 @@ func TestManifestHash(t *testing.T) {
|
||||
|
||||
expected := hash(data)
|
||||
|
||||
hash, err := generateManifestHash(manifest, ignores, nil)
|
||||
hash, err := generateManifestHash(manifest, ignores, nil, normalizers.IgnoreNormalizerOpts{})
|
||||
assert.Equal(t, expected, hash)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/diff"
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync"
|
||||
@@ -32,6 +33,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/gpg"
|
||||
@@ -105,8 +107,13 @@ type appStateManager struct {
|
||||
statusRefreshTimeout time.Duration
|
||||
resourceTracking argo.ResourceTracking
|
||||
persistResourceHealth bool
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts
|
||||
}
|
||||
|
||||
// getRepoObjs will generate the manifests for the given application delegating the
|
||||
// task to the repo-server. It returns the list of generated manifests as unstructured
|
||||
// objects. It also returns the full response from all calls to the repo server as the
|
||||
// second argument.
|
||||
func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, sources []v1alpha1.ApplicationSource, appLabelKey string, revisions []string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject) ([]*unstructured.Unstructured, []*apiclient.ManifestResponse, error) {
|
||||
|
||||
ts := stats.NewTimingStats()
|
||||
@@ -558,21 +565,16 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
|
||||
manifestRevisions = append(manifestRevisions, manifestInfo.Revision)
|
||||
}
|
||||
|
||||
// restore comparison using cached diff result if previous comparison was performed for the same revision
|
||||
revisionChanged := len(manifestInfos) != len(sources) || !reflect.DeepEqual(app.Status.Sync.Revisions, manifestRevisions)
|
||||
specChanged := !reflect.DeepEqual(app.Status.Sync.ComparedTo, v1alpha1.ComparedTo{Source: app.Spec.GetSource(), Destination: app.Spec.Destination, Sources: sources, IgnoreDifferences: app.Spec.IgnoreDifferences})
|
||||
|
||||
_, refreshRequested := app.IsRefreshRequested()
|
||||
noCache = noCache || refreshRequested || app.Status.Expired(m.statusRefreshTimeout) || specChanged || revisionChanged
|
||||
useDiffCache := useDiffCache(noCache, manifestInfos, sources, app, manifestRevisions, m.statusRefreshTimeout, logCtx)
|
||||
|
||||
diffConfigBuilder := argodiff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, resourceOverrides, compareOptions.IgnoreAggregatedRoles).
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, resourceOverrides, compareOptions.IgnoreAggregatedRoles, m.ignoreNormalizerOpts).
|
||||
WithTracking(appLabelKey, string(trackingMethod))
|
||||
|
||||
if noCache {
|
||||
diffConfigBuilder.WithNoCache()
|
||||
if useDiffCache {
|
||||
diffConfigBuilder.WithCache(m.cache, app.InstanceName(m.namespace))
|
||||
} else {
|
||||
diffConfigBuilder.WithCache(m.cache, app.GetName())
|
||||
diffConfigBuilder.WithNoCache()
|
||||
}
|
||||
|
||||
gvkParser, err := m.getGVKParser(app.Spec.Destination.Server)
|
||||
@@ -779,6 +781,46 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *v1
|
||||
return &compRes
|
||||
}
|
||||
|
||||
// useDiffCache will determine if the diff should be calculated based
|
||||
// on the existing live state cache or not.
|
||||
func useDiffCache(noCache bool, manifestInfos []*apiclient.ManifestResponse, sources []v1alpha1.ApplicationSource, app *v1alpha1.Application, manifestRevisions []string, statusRefreshTimeout time.Duration, log *log.Entry) bool {
|
||||
|
||||
if noCache {
|
||||
log.WithField("useDiffCache", "false").Debug("noCache is true")
|
||||
return false
|
||||
}
|
||||
_, refreshRequested := app.IsRefreshRequested()
|
||||
if refreshRequested {
|
||||
log.WithField("useDiffCache", "false").Debug("refreshRequested")
|
||||
return false
|
||||
}
|
||||
if app.Status.Expired(statusRefreshTimeout) {
|
||||
log.WithField("useDiffCache", "false").Debug("app.status.expired")
|
||||
return false
|
||||
}
|
||||
|
||||
if len(manifestInfos) != len(sources) {
|
||||
log.WithField("useDiffCache", "false").Debug("manifestInfos len != sources len")
|
||||
return false
|
||||
}
|
||||
|
||||
revisionChanged := !reflect.DeepEqual(app.Status.GetRevisions(), manifestRevisions)
|
||||
if revisionChanged {
|
||||
log.WithField("useDiffCache", "false").Debug("revisionChanged")
|
||||
return false
|
||||
}
|
||||
|
||||
currentSpec := app.BuildComparedToStatus()
|
||||
specChanged := !reflect.DeepEqual(app.Status.Sync.ComparedTo, currentSpec)
|
||||
if specChanged {
|
||||
log.WithField("useDiffCache", "false").Debug("specChanged")
|
||||
return false
|
||||
}
|
||||
|
||||
log.WithField("useDiffCache", "true").Debug("using diff cache")
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, revisions []string, sources []v1alpha1.ApplicationSource, hasMultipleSources bool, startedAt metav1.Time) error {
|
||||
var nextID int64
|
||||
if len(app.Status.History) > 0 {
|
||||
@@ -832,6 +874,7 @@ func NewAppStateManager(
|
||||
statusRefreshTimeout time.Duration,
|
||||
resourceTracking argo.ResourceTracking,
|
||||
persistResourceHealth bool,
|
||||
ignoreNormalizerOpts normalizers.IgnoreNormalizerOpts,
|
||||
) AppStateManager {
|
||||
return &appStateManager{
|
||||
liveStateCache: liveStateCache,
|
||||
@@ -847,6 +890,7 @@ func NewAppStateManager(
|
||||
statusRefreshTimeout: statusRefreshTimeout,
|
||||
resourceTracking: resourceTracking,
|
||||
persistResourceHealth: persistResourceHealth,
|
||||
ignoreNormalizerOpts: ignoreNormalizerOpts,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@ import (
|
||||
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
. "github.com/argoproj/gitops-engine/pkg/utils/testing"
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/sirupsen/logrus"
|
||||
logrustest "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -1339,3 +1342,252 @@ func TestIsLiveResourceManaged(t *testing.T) {
|
||||
assert.True(t, manager.isSelfReferencedObj(managedWrongAPIGroup, config, appName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
})
|
||||
}
|
||||
|
||||
func TestUseDiffCache(t *testing.T) {
|
||||
type fixture struct {
|
||||
testName string
|
||||
noCache bool
|
||||
manifestInfos []*apiclient.ManifestResponse
|
||||
sources []argoappv1.ApplicationSource
|
||||
app *argoappv1.Application
|
||||
manifestRevisions []string
|
||||
statusRefreshTimeout time.Duration
|
||||
expectedUseCache bool
|
||||
}
|
||||
|
||||
manifestInfos := func(revision string) []*apiclient.ManifestResponse {
|
||||
return []*apiclient.ManifestResponse{
|
||||
{
|
||||
Manifests: []string{
|
||||
"{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"labels\":{\"app.kubernetes.io/instance\":\"httpbin\"},\"name\":\"httpbin-svc\",\"namespace\":\"httpbin\"},\"spec\":{\"ports\":[{\"name\":\"http-port\",\"port\":7777,\"targetPort\":80},{\"name\":\"test\",\"port\":333}],\"selector\":{\"app\":\"httpbin\"}}}",
|
||||
"{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"labels\":{\"app.kubernetes.io/instance\":\"httpbin\"},\"name\":\"httpbin-deployment\",\"namespace\":\"httpbin\"},\"spec\":{\"replicas\":2,\"selector\":{\"matchLabels\":{\"app\":\"httpbin\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"httpbin\"}},\"spec\":{\"containers\":[{\"image\":\"kennethreitz/httpbin\",\"imagePullPolicy\":\"Always\",\"name\":\"httpbin\",\"ports\":[{\"containerPort\":80}]}]}}}}",
|
||||
},
|
||||
Namespace: "",
|
||||
Server: "",
|
||||
Revision: revision,
|
||||
SourceType: "Kustomize",
|
||||
VerifyResult: "",
|
||||
},
|
||||
}
|
||||
}
|
||||
sources := func() []argoappv1.ApplicationSource {
|
||||
return []argoappv1.ApplicationSource{
|
||||
{
|
||||
RepoURL: "https://some-repo.com",
|
||||
Path: "argocd/httpbin",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
app := func(namespace string, revision string, refresh bool, a *argoappv1.Application) *argoappv1.Application {
|
||||
app := &argoappv1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: argoappv1.ApplicationSpec{
|
||||
Source: &argoappv1.ApplicationSource{
|
||||
RepoURL: "https://some-repo.com",
|
||||
Path: "argocd/httpbin",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
Destination: argoappv1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "httpbin",
|
||||
},
|
||||
Project: "default",
|
||||
SyncPolicy: &argoappv1.SyncPolicy{
|
||||
SyncOptions: []string{
|
||||
"CreateNamespace=true",
|
||||
"ServerSideApply=true",
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: argoappv1.ApplicationStatus{
|
||||
Resources: []argoappv1.ResourceStatus{},
|
||||
Sync: argoappv1.SyncStatus{
|
||||
Status: argoappv1.SyncStatusCodeSynced,
|
||||
ComparedTo: argoappv1.ComparedTo{
|
||||
Source: argoappv1.ApplicationSource{
|
||||
RepoURL: "https://some-repo.com",
|
||||
Path: "argocd/httpbin",
|
||||
TargetRevision: "HEAD",
|
||||
},
|
||||
Destination: argoappv1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "httpbin",
|
||||
},
|
||||
},
|
||||
Revision: revision,
|
||||
Revisions: []string{},
|
||||
},
|
||||
ReconciledAt: &metav1.Time{
|
||||
Time: time.Now().Add(-time.Hour),
|
||||
},
|
||||
},
|
||||
}
|
||||
if refresh {
|
||||
annotations := make(map[string]string)
|
||||
annotations[argoappv1.AnnotationKeyRefresh] = string(argoappv1.RefreshTypeNormal)
|
||||
app.SetAnnotations(annotations)
|
||||
}
|
||||
if a != nil {
|
||||
err := mergo.Merge(app, a, mergo.WithOverride, mergo.WithOverwriteWithEmptyValue)
|
||||
if err != nil {
|
||||
t.Fatalf("error merging app: %s", err)
|
||||
}
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
cases := []fixture{
|
||||
{
|
||||
testName: "will use diff cache",
|
||||
noCache: false,
|
||||
manifestInfos: manifestInfos("rev1"),
|
||||
sources: sources(),
|
||||
app: app("httpbin", "rev1", false, nil),
|
||||
manifestRevisions: []string{"rev1"},
|
||||
statusRefreshTimeout: time.Hour * 24,
|
||||
expectedUseCache: true,
|
||||
},
|
||||
{
|
||||
testName: "will use diff cache for multisource",
|
||||
noCache: false,
|
||||
manifestInfos: manifestInfos("rev1"),
|
||||
sources: sources(),
|
||||
app: app("httpbin", "", false, &argoappv1.Application{
|
||||
Spec: argoappv1.ApplicationSpec{
|
||||
Source: nil,
|
||||
Sources: argoappv1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "multisource repo1",
|
||||
},
|
||||
{
|
||||
RepoURL: "multisource repo2",
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: argoappv1.ApplicationStatus{
|
||||
Resources: []argoappv1.ResourceStatus{},
|
||||
Sync: argoappv1.SyncStatus{
|
||||
Status: argoappv1.SyncStatusCodeSynced,
|
||||
ComparedTo: argoappv1.ComparedTo{
|
||||
Source: argoappv1.ApplicationSource{},
|
||||
Sources: argoappv1.ApplicationSources{
|
||||
{
|
||||
RepoURL: "multisource repo1",
|
||||
},
|
||||
{
|
||||
RepoURL: "multisource repo2",
|
||||
},
|
||||
},
|
||||
},
|
||||
Revisions: []string{"rev1", "rev2"},
|
||||
},
|
||||
ReconciledAt: &metav1.Time{
|
||||
Time: time.Now().Add(-time.Hour),
|
||||
},
|
||||
},
|
||||
}),
|
||||
manifestRevisions: []string{"rev1", "rev2"},
|
||||
statusRefreshTimeout: time.Hour * 24,
|
||||
expectedUseCache: true,
|
||||
},
|
||||
{
|
||||
testName: "will return false if nocache is true",
|
||||
noCache: true,
|
||||
manifestInfos: manifestInfos("rev1"),
|
||||
sources: sources(),
|
||||
app: app("httpbin", "rev1", false, nil),
|
||||
manifestRevisions: []string{"rev1"},
|
||||
statusRefreshTimeout: time.Hour * 24,
|
||||
expectedUseCache: false,
|
||||
},
|
||||
{
|
||||
testName: "will return false if requested refresh",
|
||||
noCache: false,
|
||||
manifestInfos: manifestInfos("rev1"),
|
||||
sources: sources(),
|
||||
app: app("httpbin", "rev1", true, nil),
|
||||
manifestRevisions: []string{"rev1"},
|
||||
statusRefreshTimeout: time.Hour * 24,
|
||||
expectedUseCache: false,
|
||||
},
|
||||
{
|
||||
testName: "will return false if status expired",
|
||||
noCache: false,
|
||||
manifestInfos: manifestInfos("rev1"),
|
||||
sources: sources(),
|
||||
app: app("httpbin", "rev1", false, nil),
|
||||
manifestRevisions: []string{"rev1"},
|
||||
statusRefreshTimeout: time.Minute,
|
||||
expectedUseCache: false,
|
||||
},
|
||||
{
|
||||
testName: "will return false if there is a new revision",
|
||||
noCache: false,
|
||||
manifestInfos: manifestInfos("rev1"),
|
||||
sources: sources(),
|
||||
app: app("httpbin", "rev1", false, nil),
|
||||
manifestRevisions: []string{"rev2"},
|
||||
statusRefreshTimeout: time.Hour * 24,
|
||||
expectedUseCache: false,
|
||||
},
|
||||
{
|
||||
testName: "will return false if app spec repo changed",
|
||||
noCache: false,
|
||||
manifestInfos: manifestInfos("rev1"),
|
||||
sources: sources(),
|
||||
app: app("httpbin", "rev1", false, &argoappv1.Application{
|
||||
Spec: argoappv1.ApplicationSpec{
|
||||
Source: &argoappv1.ApplicationSource{
|
||||
RepoURL: "new-repo",
|
||||
},
|
||||
},
|
||||
}),
|
||||
manifestRevisions: []string{"rev1"},
|
||||
statusRefreshTimeout: time.Hour * 24,
|
||||
expectedUseCache: false,
|
||||
},
|
||||
{
|
||||
testName: "will return false if app spec IgnoreDifferences changed",
|
||||
noCache: false,
|
||||
manifestInfos: manifestInfos("rev1"),
|
||||
sources: sources(),
|
||||
app: app("httpbin", "rev1", false, &argoappv1.Application{
|
||||
Spec: argoappv1.ApplicationSpec{
|
||||
IgnoreDifferences: []argoappv1.ResourceIgnoreDifferences{
|
||||
{
|
||||
Group: "app/v1",
|
||||
Kind: "application",
|
||||
Name: "httpbin",
|
||||
Namespace: "httpbin",
|
||||
JQPathExpressions: []string{"."},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
manifestRevisions: []string{"rev1"},
|
||||
statusRefreshTimeout: time.Hour * 24,
|
||||
expectedUseCache: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
// Given
|
||||
t.Parallel()
|
||||
logger, _ := logrustest.NewNullLogger()
|
||||
log := logrus.NewEntry(logger)
|
||||
|
||||
// When
|
||||
useDiffCache := useDiffCache(tc.noCache, tc.manifestInfos, tc.sources, tc.app, tc.manifestRevisions, tc.statusRefreshTimeout, log)
|
||||
|
||||
// Then
|
||||
assert.Equal(t, useDiffCache, tc.expectedUseCache)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
@@ -10,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
cdcommon "github.com/argoproj/argo-cd/v2/common"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/sync"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/managedfields"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/kubectl/pkg/util/openapi"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/controller/metrics"
|
||||
@@ -370,11 +371,10 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
}
|
||||
}
|
||||
|
||||
// normalizeTargetResources will apply the diff normalization in all live and target resources.
|
||||
// Then it calculates the merge patch between the normalized live and the current live resources.
|
||||
// Finally it applies the merge patch in the normalized target resources. This is done to ensure
|
||||
// that target resources have the same ignored diff fields values from live ones to avoid them to
|
||||
// be applied in the cluster. Returns the list of normalized target resources.
|
||||
// normalizeTargetResources modifies target resources to ensure ignored fields are not touched during synchronization:
|
||||
// - applies normalization to the target resources based on the live resources
|
||||
// - copies ignored fields from the matching live resources: apply normalizer to the live resource,
|
||||
// calculates the patch performed by normalizer and applies the patch to the target resource
|
||||
func normalizeTargetResources(cr *comparisonResult) ([]*unstructured.Unstructured, error) {
|
||||
// normalize live and target resources
|
||||
normalized, err := diff.Normalize(cr.reconciliationResult.Live, cr.reconciliationResult.Target, cr.diffConfig)
|
||||
@@ -393,94 +393,35 @@ func normalizeTargetResources(cr *comparisonResult) ([]*unstructured.Unstructure
|
||||
patchedTargets = append(patchedTargets, originalTarget)
|
||||
continue
|
||||
}
|
||||
// calculate targetPatch between normalized and target resource
|
||||
targetPatch, err := getMergePatch(normalizedTarget, originalTarget)
|
||||
|
||||
var lookupPatchMeta *strategicpatch.PatchMetaFromStruct
|
||||
versionedObject, err := scheme.Scheme.New(normalizedTarget.GroupVersionKind())
|
||||
if err == nil {
|
||||
meta, err := strategicpatch.NewPatchMetaFromStruct(versionedObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lookupPatchMeta = &meta
|
||||
}
|
||||
|
||||
livePatch, err := getMergePatch(normalized.Lives[idx], live, lookupPatchMeta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check if there is a patch to apply. An empty patch is identified by a '{}' string.
|
||||
if len(targetPatch) > 2 {
|
||||
livePatch, err := getMergePatch(normalized.Lives[idx], live)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// generate a minimal patch that uses the fields from targetPatch (template)
|
||||
// with livePatch values
|
||||
patch, err := compilePatch(targetPatch, livePatch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
normalizedTarget, err = applyMergePatch(normalizedTarget, patch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// if there is no patch just use the original target
|
||||
normalizedTarget = originalTarget
|
||||
normalizedTarget, err = applyMergePatch(normalizedTarget, livePatch, versionedObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patchedTargets = append(patchedTargets, normalizedTarget)
|
||||
}
|
||||
return patchedTargets, nil
|
||||
}
|
||||
|
||||
// compilePatch will generate a patch using the fields from templatePatch with
|
||||
// the values from valuePatch.
|
||||
func compilePatch(templatePatch, valuePatch []byte) ([]byte, error) {
|
||||
templateMap := make(map[string]interface{})
|
||||
err := json.Unmarshal(templatePatch, &templateMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valueMap := make(map[string]interface{})
|
||||
err = json.Unmarshal(valuePatch, &valueMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resultMap := intersectMap(templateMap, valueMap)
|
||||
return json.Marshal(resultMap)
|
||||
}
|
||||
|
||||
// intersectMap will return map with the fields intersection from the 2 provided
|
||||
// maps populated with the valueMap values.
|
||||
func intersectMap(templateMap, valueMap map[string]interface{}) map[string]interface{} {
|
||||
result := make(map[string]interface{})
|
||||
for k, v := range templateMap {
|
||||
if innerTMap, ok := v.(map[string]interface{}); ok {
|
||||
if innerVMap, ok := valueMap[k].(map[string]interface{}); ok {
|
||||
result[k] = intersectMap(innerTMap, innerVMap)
|
||||
}
|
||||
} else if innerTSlice, ok := v.([]interface{}); ok {
|
||||
if innerVSlice, ok := valueMap[k].([]interface{}); ok {
|
||||
items := []interface{}{}
|
||||
for idx, innerTSliceValue := range innerTSlice {
|
||||
if idx < len(innerVSlice) {
|
||||
if tSliceValueMap, ok := innerTSliceValue.(map[string]interface{}); ok {
|
||||
if vSliceValueMap, ok := innerVSlice[idx].(map[string]interface{}); ok {
|
||||
item := intersectMap(tSliceValueMap, vSliceValueMap)
|
||||
items = append(items, item)
|
||||
}
|
||||
} else {
|
||||
items = append(items, innerVSlice[idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(items) > 0 {
|
||||
result[k] = items
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, ok := valueMap[k]; ok {
|
||||
result[k] = valueMap[k]
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// getMergePatch calculates and returns the patch between the original and the
|
||||
// modified unstructures.
|
||||
func getMergePatch(original, modified *unstructured.Unstructured) ([]byte, error) {
|
||||
func getMergePatch(original, modified *unstructured.Unstructured, lookupPatchMeta *strategicpatch.PatchMetaFromStruct) ([]byte, error) {
|
||||
originalJSON, err := original.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -489,20 +430,30 @@ func getMergePatch(original, modified *unstructured.Unstructured) ([]byte, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if lookupPatchMeta != nil {
|
||||
return strategicpatch.CreateThreeWayMergePatch(modifiedJSON, modifiedJSON, originalJSON, lookupPatchMeta, true)
|
||||
}
|
||||
|
||||
return jsonpatch.CreateMergePatch(originalJSON, modifiedJSON)
|
||||
}
|
||||
|
||||
// applyMergePatch will apply the given patch in the obj and return the patched
|
||||
// unstructure.
|
||||
func applyMergePatch(obj *unstructured.Unstructured, patch []byte) (*unstructured.Unstructured, error) {
|
||||
func applyMergePatch(obj *unstructured.Unstructured, patch []byte, versionedObject interface{}) (*unstructured.Unstructured, error) {
|
||||
originalJSON, err := obj.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patchedJSON, err := jsonpatch.MergePatch(originalJSON, patch)
|
||||
var patchedJSON []byte
|
||||
if versionedObject == nil {
|
||||
patchedJSON, err = jsonpatch.MergePatch(originalJSON, patch)
|
||||
} else {
|
||||
patchedJSON, err = strategicpatch.StrategicMergePatch(originalJSON, patch, versionedObject)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patchedObj := &unstructured.Unstructured{}
|
||||
_, _, err = unstructured.UnstructuredJSONScheme.Decode(patchedJSON, nil, patchedObj)
|
||||
if err != nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/test"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/diff"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
)
|
||||
|
||||
func TestPersistRevisionHistory(t *testing.T) {
|
||||
@@ -261,7 +262,7 @@ func TestNormalizeTargetResources(t *testing.T) {
|
||||
setup := func(t *testing.T, ignores []v1alpha1.ResourceIgnoreDifferences) *fixture {
|
||||
t.Helper()
|
||||
dc, err := diff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(ignores, nil, true).
|
||||
WithDiffSettings(ignores, nil, true, normalizers.IgnoreNormalizerOpts{}).
|
||||
WithNoCache().
|
||||
Build()
|
||||
require.NoError(t, err)
|
||||
@@ -386,3 +387,207 @@ func TestNormalizeTargetResources(t *testing.T) {
|
||||
assert.Equal(t, 2, len(containers))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNormalizeTargetResourcesWithList(t *testing.T) {
|
||||
type fixture struct {
|
||||
comparisonResult *comparisonResult
|
||||
}
|
||||
setupHttpProxy := func(t *testing.T, ignores []v1alpha1.ResourceIgnoreDifferences) *fixture {
|
||||
t.Helper()
|
||||
dc, err := diff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(ignores, nil, true, normalizers.IgnoreNormalizerOpts{}).
|
||||
WithNoCache().
|
||||
Build()
|
||||
require.NoError(t, err)
|
||||
live := test.YamlToUnstructured(testdata.LiveHTTPProxy)
|
||||
target := test.YamlToUnstructured(testdata.TargetHTTPProxy)
|
||||
return &fixture{
|
||||
&comparisonResult{
|
||||
reconciliationResult: sync.ReconciliationResult{
|
||||
Live: []*unstructured.Unstructured{live},
|
||||
Target: []*unstructured.Unstructured{target},
|
||||
},
|
||||
diffConfig: dc,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("will properly ignore nested fields within arrays", func(t *testing.T) {
|
||||
// given
|
||||
ignores := []v1alpha1.ResourceIgnoreDifferences{
|
||||
{
|
||||
Group: "projectcontour.io",
|
||||
Kind: "HTTPProxy",
|
||||
JQPathExpressions: []string{".spec.routes[]"},
|
||||
//JSONPointers: []string{"/spec/routes"},
|
||||
},
|
||||
}
|
||||
f := setupHttpProxy(t, ignores)
|
||||
target := test.YamlToUnstructured(testdata.TargetHTTPProxy)
|
||||
f.comparisonResult.reconciliationResult.Target = []*unstructured.Unstructured{target}
|
||||
|
||||
// when
|
||||
patchedTargets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(f.comparisonResult.reconciliationResult.Live))
|
||||
require.Equal(t, 1, len(f.comparisonResult.reconciliationResult.Target))
|
||||
require.Equal(t, 1, len(patchedTargets))
|
||||
|
||||
// live should have 1 entry
|
||||
require.Equal(t, 1, len(dig[[]any](f.comparisonResult.reconciliationResult.Live[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors"})))
|
||||
// assert some arbitrary field to show `entries[0]` is not an empty object
|
||||
require.Equal(t, "sample-header", dig[string](f.comparisonResult.reconciliationResult.Live[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries", 0, "requestHeader", "headerName"}))
|
||||
|
||||
// target has 2 entries
|
||||
require.Equal(t, 2, len(dig[[]any](f.comparisonResult.reconciliationResult.Target[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries"})))
|
||||
// assert some arbitrary field to show `entries[0]` is not an empty object
|
||||
require.Equal(t, "sample-header", dig[string](f.comparisonResult.reconciliationResult.Target[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries", 0, "requestHeaderValueMatch", "headers", 0, "name"}))
|
||||
|
||||
// It should be *1* entries in the array
|
||||
require.Equal(t, 1, len(dig[[]any](patchedTargets[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors"})))
|
||||
// and it should NOT equal an empty object
|
||||
require.Len(t, dig[any](patchedTargets[0].Object, []interface{}{"spec", "routes", 0, "rateLimitPolicy", "global", "descriptors", 0, "entries", 0}), 1)
|
||||
|
||||
})
|
||||
t.Run("will correctly set array entries if new entries have been added", func(t *testing.T) {
|
||||
// given
|
||||
ignores := []v1alpha1.ResourceIgnoreDifferences{
|
||||
{
|
||||
Group: "apps",
|
||||
Kind: "Deployment",
|
||||
JQPathExpressions: []string{".spec.template.spec.containers[].env[] | select(.name == \"SOME_ENV_VAR\")"},
|
||||
},
|
||||
}
|
||||
f := setupHttpProxy(t, ignores)
|
||||
live := test.YamlToUnstructured(testdata.LiveDeploymentEnvVarsYaml)
|
||||
target := test.YamlToUnstructured(testdata.TargetDeploymentEnvVarsYaml)
|
||||
f.comparisonResult.reconciliationResult.Live = []*unstructured.Unstructured{live}
|
||||
f.comparisonResult.reconciliationResult.Target = []*unstructured.Unstructured{target}
|
||||
|
||||
// when
|
||||
targets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(targets))
|
||||
containers, ok, err := unstructured.NestedSlice(targets[0].Object, "spec", "template", "spec", "containers")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 1, len(containers))
|
||||
|
||||
ports := containers[0].(map[string]interface{})["ports"].([]interface{})
|
||||
assert.Equal(t, 1, len(ports))
|
||||
|
||||
env := containers[0].(map[string]interface{})["env"].([]interface{})
|
||||
assert.Equal(t, 3, len(env))
|
||||
|
||||
first := env[0]
|
||||
second := env[1]
|
||||
third := env[2]
|
||||
|
||||
// Currently the defined order at this time is the insertion order of the target manifest.
|
||||
assert.Equal(t, "SOME_ENV_VAR", first.(map[string]interface{})["name"])
|
||||
assert.Equal(t, "some_value", first.(map[string]interface{})["value"])
|
||||
|
||||
assert.Equal(t, "SOME_OTHER_ENV_VAR", second.(map[string]interface{})["name"])
|
||||
assert.Equal(t, "some_other_value", second.(map[string]interface{})["value"])
|
||||
|
||||
assert.Equal(t, "YET_ANOTHER_ENV_VAR", third.(map[string]interface{})["name"])
|
||||
assert.Equal(t, "yet_another_value", third.(map[string]interface{})["value"])
|
||||
})
|
||||
|
||||
t.Run("ignore-deployment-image-replicas-changes-additive", func(t *testing.T) {
|
||||
// given
|
||||
|
||||
ignores := []v1alpha1.ResourceIgnoreDifferences{
|
||||
{
|
||||
Group: "apps",
|
||||
Kind: "Deployment",
|
||||
JSONPointers: []string{"/spec/replicas"},
|
||||
}, {
|
||||
Group: "apps",
|
||||
Kind: "Deployment",
|
||||
JQPathExpressions: []string{".spec.template.spec.containers[].image"},
|
||||
},
|
||||
}
|
||||
f := setupHttpProxy(t, ignores)
|
||||
live := test.YamlToUnstructured(testdata.MinimalImageReplicaDeploymentYaml)
|
||||
target := test.YamlToUnstructured(testdata.AdditionalImageReplicaDeploymentYaml)
|
||||
f.comparisonResult.reconciliationResult.Live = []*unstructured.Unstructured{live}
|
||||
f.comparisonResult.reconciliationResult.Target = []*unstructured.Unstructured{target}
|
||||
|
||||
// when
|
||||
targets, err := normalizeTargetResources(f.comparisonResult)
|
||||
|
||||
// then
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(targets))
|
||||
metadata, ok, err := unstructured.NestedMap(targets[0].Object, "metadata")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
labels, ok := metadata["labels"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 2, len(labels))
|
||||
assert.Equal(t, "web", labels["appProcess"])
|
||||
|
||||
spec, ok, err := unstructured.NestedMap(targets[0].Object, "spec")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
|
||||
assert.Equal(t, int64(1), spec["replicas"])
|
||||
|
||||
template, ok := spec["template"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
|
||||
tMetadata, ok := template["metadata"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
tLabels, ok := tMetadata["labels"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 2, len(tLabels))
|
||||
assert.Equal(t, "web", tLabels["appProcess"])
|
||||
|
||||
tSpec, ok := template["spec"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
containers, ok, err := unstructured.NestedSlice(tSpec, "containers")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 1, len(containers))
|
||||
|
||||
first := containers[0].(map[string]interface{})
|
||||
assert.Equal(t, "alpine:3", first["image"])
|
||||
|
||||
resources, ok := first["resources"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
requests, ok := resources["requests"].(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "400m", requests["cpu"])
|
||||
|
||||
env, ok, err := unstructured.NestedSlice(first, "env")
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 1, len(env))
|
||||
|
||||
env0 := env[0].(map[string]interface{})
|
||||
assert.Equal(t, "EV", env0["name"])
|
||||
assert.Equal(t, "here", env0["value"])
|
||||
})
|
||||
}
|
||||
|
||||
func dig[T any](obj interface{}, path []interface{}) T {
|
||||
i := obj
|
||||
|
||||
for _, segment := range path {
|
||||
switch segment.(type) {
|
||||
case int:
|
||||
i = i.([]interface{})[segment.(int)]
|
||||
case string:
|
||||
i = i.(map[string]interface{})[segment.(string)]
|
||||
default:
|
||||
panic("invalid path for object")
|
||||
}
|
||||
}
|
||||
|
||||
return i.(T)
|
||||
}
|
||||
|
||||
28
controller/testdata/additional-image-replicas-deployment.yaml
vendored
Normal file
28
controller/testdata/additional-image-replicas-deployment.yaml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: client
|
||||
appProcess: web
|
||||
name: client
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: client
|
||||
strategy: {}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: client
|
||||
appProcess: web
|
||||
spec:
|
||||
containers:
|
||||
- image: alpine:2
|
||||
name: alpine
|
||||
resources:
|
||||
requests:
|
||||
cpu: 400m
|
||||
env:
|
||||
- name: EV
|
||||
value: here
|
||||
18
controller/testdata/data.go
vendored
18
controller/testdata/data.go
vendored
@@ -11,4 +11,22 @@ var (
|
||||
|
||||
//go:embed target-deployment-new-entries.yaml
|
||||
TargetDeploymentNewEntries string
|
||||
|
||||
//go:embed live-httpproxy.yaml
|
||||
LiveHTTPProxy string
|
||||
|
||||
//go:embed target-httpproxy.yaml
|
||||
TargetHTTPProxy string
|
||||
|
||||
//go:embed live-deployment-env-vars.yaml
|
||||
LiveDeploymentEnvVarsYaml string
|
||||
|
||||
//go:embed target-deployment-env-vars.yaml
|
||||
TargetDeploymentEnvVarsYaml string
|
||||
|
||||
//go:embed minimal-image-replicas-deployment.yaml
|
||||
MinimalImageReplicaDeploymentYaml string
|
||||
|
||||
//go:embed additional-image-replicas-deployment.yaml
|
||||
AdditionalImageReplicaDeploymentYaml string
|
||||
)
|
||||
|
||||
177
controller/testdata/live-deployment-env-vars.yaml
vendored
Normal file
177
controller/testdata/live-deployment-env-vars.yaml
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
argocd.argoproj.io/tracking-id: 'guestbook:apps/Deployment:default/kustomize-guestbook-ui'
|
||||
deployment.kubernetes.io/revision: '9'
|
||||
iksm-version: '2.0'
|
||||
kubectl.kubernetes.io/last-applied-configuration: >
|
||||
{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"argocd.argoproj.io/tracking-id":"guestbook:apps/Deployment:default/kustomize-guestbook-ui","iksm-version":"2.0"},"name":"kustomize-guestbook-ui","namespace":"default"},"spec":{"replicas":4,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook-ui"}},"template":{"metadata":{"labels":{"app":"guestbook-ui"}},"spec":{"containers":[{"env":[{"name":"SOME_ENV_VAR","value":"some_value"}],"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook-ui","ports":[{"containerPort":80}],"resources":{"requests":{"cpu":"50m","memory":"100Mi"}}}]}}}}
|
||||
creationTimestamp: '2022-01-05T15:45:21Z'
|
||||
generation: 119
|
||||
managedFields:
|
||||
- apiVersion: apps/v1
|
||||
fieldsType: FieldsV1
|
||||
fieldsV1:
|
||||
'f:metadata':
|
||||
'f:annotations':
|
||||
'f:iksm-version': {}
|
||||
manager: janitor
|
||||
operation: Apply
|
||||
time: '2022-01-06T18:21:04Z'
|
||||
- apiVersion: apps/v1
|
||||
fieldsType: FieldsV1
|
||||
fieldsV1:
|
||||
'f:metadata':
|
||||
'f:annotations':
|
||||
.: {}
|
||||
'f:argocd.argoproj.io/tracking-id': {}
|
||||
'f:kubectl.kubernetes.io/last-applied-configuration': {}
|
||||
'f:spec':
|
||||
'f:progressDeadlineSeconds': {}
|
||||
'f:replicas': {}
|
||||
'f:revisionHistoryLimit': {}
|
||||
'f:selector': {}
|
||||
'f:strategy':
|
||||
'f:rollingUpdate':
|
||||
.: {}
|
||||
'f:maxSurge': {}
|
||||
'f:maxUnavailable': {}
|
||||
'f:type': {}
|
||||
'f:template':
|
||||
'f:metadata':
|
||||
'f:labels':
|
||||
.: {}
|
||||
'f:app': {}
|
||||
'f:spec':
|
||||
'f:containers':
|
||||
'k:{"name":"guestbook-ui"}':
|
||||
.: {}
|
||||
'f:env':
|
||||
.: {}
|
||||
'k:{"name":"SOME_ENV_VAR"}':
|
||||
.: {}
|
||||
'f:name': {}
|
||||
'f:value': {}
|
||||
'f:image': {}
|
||||
'f:imagePullPolicy': {}
|
||||
'f:name': {}
|
||||
'f:ports':
|
||||
.: {}
|
||||
'k:{"containerPort":80,"protocol":"TCP"}':
|
||||
.: {}
|
||||
'f:containerPort': {}
|
||||
'f:protocol': {}
|
||||
'f:resources':
|
||||
.: {}
|
||||
'f:requests':
|
||||
.: {}
|
||||
'f:cpu': {}
|
||||
'f:memory': {}
|
||||
'f:terminationMessagePath': {}
|
||||
'f:terminationMessagePolicy': {}
|
||||
'f:dnsPolicy': {}
|
||||
'f:restartPolicy': {}
|
||||
'f:schedulerName': {}
|
||||
'f:securityContext': {}
|
||||
'f:terminationGracePeriodSeconds': {}
|
||||
manager: argocd
|
||||
operation: Update
|
||||
time: '2022-01-06T15:04:15Z'
|
||||
- apiVersion: apps/v1
|
||||
fieldsType: FieldsV1
|
||||
fieldsV1:
|
||||
'f:metadata':
|
||||
'f:annotations':
|
||||
'f:deployment.kubernetes.io/revision': {}
|
||||
'f:status':
|
||||
'f:availableReplicas': {}
|
||||
'f:conditions':
|
||||
.: {}
|
||||
'k:{"type":"Available"}':
|
||||
.: {}
|
||||
'f:lastTransitionTime': {}
|
||||
'f:lastUpdateTime': {}
|
||||
'f:message': {}
|
||||
'f:reason': {}
|
||||
'f:status': {}
|
||||
'f:type': {}
|
||||
'k:{"type":"Progressing"}':
|
||||
.: {}
|
||||
'f:lastTransitionTime': {}
|
||||
'f:lastUpdateTime': {}
|
||||
'f:message': {}
|
||||
'f:reason': {}
|
||||
'f:status': {}
|
||||
'f:type': {}
|
||||
'f:observedGeneration': {}
|
||||
'f:readyReplicas': {}
|
||||
'f:replicas': {}
|
||||
'f:updatedReplicas': {}
|
||||
manager: kube-controller-manager
|
||||
operation: Update
|
||||
time: '2022-01-06T18:15:14Z'
|
||||
name: kustomize-guestbook-ui
|
||||
namespace: default
|
||||
resourceVersion: '8289211'
|
||||
uid: ef253575-ce44-4c5e-84ad-16e81d0df6eb
|
||||
spec:
|
||||
progressDeadlineSeconds: 600
|
||||
replicas: 4
|
||||
revisionHistoryLimit: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: guestbook-ui
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: SOME_ENV_VAR
|
||||
value: some_value
|
||||
image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: guestbook-ui
|
||||
ports:
|
||||
- containerPort: 80
|
||||
protocol: TCP
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 100Mi
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
dnsPolicy: ClusterFirst
|
||||
restartPolicy: Always
|
||||
schedulerName: default-scheduler
|
||||
securityContext: {}
|
||||
terminationGracePeriodSeconds: 30
|
||||
status:
|
||||
availableReplicas: 4
|
||||
conditions:
|
||||
- lastTransitionTime: '2022-01-05T22:20:37Z'
|
||||
lastUpdateTime: '2022-01-05T22:43:47Z'
|
||||
message: >-
|
||||
ReplicaSet "kustomize-guestbook-ui-6549d54677" has successfully
|
||||
progressed.
|
||||
reason: NewReplicaSetAvailable
|
||||
status: 'True'
|
||||
type: Progressing
|
||||
- lastTransitionTime: '2022-01-06T18:15:14Z'
|
||||
lastUpdateTime: '2022-01-06T18:15:14Z'
|
||||
message: Deployment has minimum availability.
|
||||
reason: MinimumReplicasAvailable
|
||||
status: 'True'
|
||||
type: Available
|
||||
observedGeneration: 119
|
||||
readyReplicas: 4
|
||||
replicas: 4
|
||||
updatedReplicas: 4
|
||||
14
controller/testdata/live-httpproxy.yaml
vendored
Normal file
14
controller/testdata/live-httpproxy.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: projectcontour.io/v1
|
||||
kind: HTTPProxy
|
||||
metadata:
|
||||
name: my-http-proxy
|
||||
namespace: default
|
||||
spec:
|
||||
routes:
|
||||
- rateLimitPolicy:
|
||||
global:
|
||||
descriptors:
|
||||
- entries:
|
||||
- requestHeader:
|
||||
descriptorKey: sample-key
|
||||
headerName: sample-header
|
||||
21
controller/testdata/minimal-image-replicas-deployment.yaml
vendored
Normal file
21
controller/testdata/minimal-image-replicas-deployment.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: client
|
||||
name: client
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: client
|
||||
strategy: {}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: client
|
||||
spec:
|
||||
containers:
|
||||
- image: alpine:3
|
||||
name: alpine
|
||||
resources: {}
|
||||
35
controller/testdata/target-deployment-env-vars.yaml
vendored
Normal file
35
controller/testdata/target-deployment-env-vars.yaml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations:
|
||||
argocd.argoproj.io/tracking-id: 'guestbook:apps/Deployment:default/kustomize-guestbook-ui'
|
||||
iksm-version: '1.0'
|
||||
name: kustomize-guestbook-ui
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: guestbook-ui
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: SOME_OTHER_ENV_VAR
|
||||
value: some_other_value
|
||||
- name: YET_ANOTHER_ENV_VAR
|
||||
value: yet_another_value
|
||||
- name: SOME_ENV_VAR
|
||||
value: different_value!
|
||||
image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
|
||||
name: guestbook-ui
|
||||
ports:
|
||||
- containerPort: 80
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 100Mi
|
||||
23
controller/testdata/target-httpproxy.yaml
vendored
Normal file
23
controller/testdata/target-httpproxy.yaml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
apiVersion: projectcontour.io/v1
|
||||
kind: HTTPProxy
|
||||
metadata:
|
||||
name: my-http-proxy
|
||||
namespace: default
|
||||
spec:
|
||||
routes:
|
||||
- rateLimitPolicy:
|
||||
global:
|
||||
descriptors:
|
||||
- entries:
|
||||
- requestHeaderValueMatch:
|
||||
headers:
|
||||
- contains: sample-key
|
||||
name: sample-header
|
||||
value: third
|
||||
- requestHeader:
|
||||
descriptorKey: sample-key
|
||||
headerName: sample-header
|
||||
- entries:
|
||||
- requestHeader:
|
||||
descriptorKey: sample-key
|
||||
headerName: sample-header
|
||||
@@ -72,6 +72,9 @@ data:
|
||||
server.rootpath: ""
|
||||
# Directory path that contains additional static assets
|
||||
server.staticassets: "/shared/app"
|
||||
# Semicolon-separated list of content types allowed on non-GET requests. Set an empty string to allow all. Be aware
|
||||
# that allowing content types besides application/json may make your API more vulnerable to CSRF attacks.
|
||||
server.api.content.types: "application/json"
|
||||
|
||||
# Set the logging format. One of: text|json (default "text")
|
||||
server.log.format: "text"
|
||||
|
||||
@@ -159,6 +159,7 @@ data:
|
||||
|
||||
g, your-github-org:your-team, role:org-admin
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
Another `policy.csv` example might look as follows:
|
||||
|
||||
@@ -15,59 +15,60 @@ argocd-application-controller [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--app-hard-resync int Time period in seconds for application hard resync.
|
||||
--app-resync int Time period in seconds for application resync. (default 180)
|
||||
--app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s)
|
||||
--application-namespaces strings List of additional namespaces that applications are allowed to be reconciled from
|
||||
--as string Username to impersonate for the operation
|
||||
--as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
|
||||
--as-uid string UID to impersonate for the operation
|
||||
--certificate-authority string Path to a cert file for the certificate authority
|
||||
--client-certificate string Path to a client certificate file for TLS
|
||||
--client-key string Path to a client key file for TLS
|
||||
--cluster string The name of the kubeconfig cluster to use
|
||||
--context string The name of the kubeconfig context to use
|
||||
--default-cache-expiration duration Cache expiration default (default 24h0m0s)
|
||||
--dynamic-cluster-distribution-enabled Enables dynamic cluster distribution.
|
||||
--gloglevel int Set the glog logging level
|
||||
-h, --help help for argocd-application-controller
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--kubeconfig string Path to a kube config. Only required if out-of-cluster
|
||||
--kubectl-parallelism-limit int Number of allowed concurrent kubectl fork/execs. Any value less than 1 means no limit. (default 20)
|
||||
--logformat string Set the logging format. One of: text|json (default "text")
|
||||
--loglevel string Set the logging level. One of: debug|info|warn|error (default "info")
|
||||
--metrics-application-labels strings List of Application labels that will be added to the argocd_application_labels metric
|
||||
--metrics-cache-expiration duration Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)
|
||||
--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
|
||||
--otlp-attrs strings List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)
|
||||
--password string Password for basic authentication to the API server
|
||||
--persist-resource-health Enables storing the managed resources health in the Application CRD (default true)
|
||||
--proxy-url string If provided, this URL will be used to connect via proxy
|
||||
--redis string Redis server hostname and port (e.g. argocd-redis:6379).
|
||||
--redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation.
|
||||
--redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt).
|
||||
--redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt).
|
||||
--redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none) (default "gzip")
|
||||
--redis-insecure-skip-tls-verify Skip Redis server certificate validation.
|
||||
--redis-use-tls Use TLS when connecting to Redis.
|
||||
--redisdb int Redis database.
|
||||
--repo-server string Repo server address. (default "argocd-repo-server:8081")
|
||||
--repo-server-plaintext Disable TLS on connections to repo server
|
||||
--repo-server-strict-tls Whether to use strict validation of the TLS cert presented by the repo server
|
||||
--repo-server-timeout-seconds int Repo server RPC call timeout seconds. (default 60)
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
|
||||
--self-heal-timeout-seconds int Specifies timeout between application self heal attempts (default 5)
|
||||
--sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379).
|
||||
--sentinelmaster string Redis sentinel master group name. (default "master")
|
||||
--server string The address and port of the Kubernetes API server
|
||||
--sharding-method string Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] (default "legacy")
|
||||
--status-processors int Number of application status processors (default 20)
|
||||
--tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used.
|
||||
--token string Bearer token for authentication to the API server
|
||||
--user string The name of the kubeconfig user to use
|
||||
--username string Username for basic authentication to the API server
|
||||
--app-hard-resync int Time period in seconds for application hard resync.
|
||||
--app-resync int Time period in seconds for application resync. (default 180)
|
||||
--app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s)
|
||||
--application-namespaces strings List of additional namespaces that applications are allowed to be reconciled from
|
||||
--as string Username to impersonate for the operation
|
||||
--as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
|
||||
--as-uid string UID to impersonate for the operation
|
||||
--certificate-authority string Path to a cert file for the certificate authority
|
||||
--client-certificate string Path to a client certificate file for TLS
|
||||
--client-key string Path to a client key file for TLS
|
||||
--cluster string The name of the kubeconfig cluster to use
|
||||
--context string The name of the kubeconfig context to use
|
||||
--default-cache-expiration duration Cache expiration default (default 24h0m0s)
|
||||
--dynamic-cluster-distribution-enabled Enables dynamic cluster distribution.
|
||||
--gloglevel int Set the glog logging level
|
||||
-h, --help help for argocd-application-controller
|
||||
--ignore-normalizer-jq-execution-timeout-seconds duration Set ignore normalizer JQ execution timeout
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--kubeconfig string Path to a kube config. Only required if out-of-cluster
|
||||
--kubectl-parallelism-limit int Number of allowed concurrent kubectl fork/execs. Any value less than 1 means no limit. (default 20)
|
||||
--logformat string Set the logging format. One of: text|json (default "text")
|
||||
--loglevel string Set the logging level. One of: debug|info|warn|error (default "info")
|
||||
--metrics-application-labels strings List of Application labels that will be added to the argocd_application_labels metric
|
||||
--metrics-cache-expiration duration Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)
|
||||
--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
|
||||
--otlp-attrs strings List of OpenTelemetry collector extra attrs when send traces, each attribute is separated by a colon(e.g. key:value)
|
||||
--password string Password for basic authentication to the API server
|
||||
--persist-resource-health Enables storing the managed resources health in the Application CRD (default true)
|
||||
--proxy-url string If provided, this URL will be used to connect via proxy
|
||||
--redis string Redis server hostname and port (e.g. argocd-redis:6379).
|
||||
--redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation.
|
||||
--redis-client-certificate string Path to Redis client certificate (e.g. /etc/certs/redis/client.crt).
|
||||
--redis-client-key string Path to Redis client key (e.g. /etc/certs/redis/client.crt).
|
||||
--redis-compress string Enable compression for data sent to Redis with the required compression algorithm. (possible values: gzip, none) (default "gzip")
|
||||
--redis-insecure-skip-tls-verify Skip Redis server certificate validation.
|
||||
--redis-use-tls Use TLS when connecting to Redis.
|
||||
--redisdb int Redis database.
|
||||
--repo-server string Repo server address. (default "argocd-repo-server:8081")
|
||||
--repo-server-plaintext Disable TLS on connections to repo server
|
||||
--repo-server-strict-tls Whether to use strict validation of the TLS cert presented by the repo server
|
||||
--repo-server-timeout-seconds int Repo server RPC call timeout seconds. (default 60)
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
|
||||
--self-heal-timeout-seconds int Specifies timeout between application self heal attempts (default 5)
|
||||
--sentinel stringArray Redis sentinel hostname and port (e.g. argocd-redis-ha-announce-0:6379).
|
||||
--sentinelmaster string Redis sentinel master group name. (default "master")
|
||||
--server string The address and port of the Kubernetes API server
|
||||
--sharding-method string Enables choice of sharding method. Supported sharding methods are : [legacy, round-robin] (default "legacy")
|
||||
--status-processors int Number of application status processors (default 20)
|
||||
--tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used.
|
||||
--token string Bearer token for authentication to the API server
|
||||
--user string The name of the kubeconfig user to use
|
||||
--username string Username for basic authentication to the API server
|
||||
```
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ argocd-repo-server [flags]
|
||||
--disable-helm-manifest-max-extracted-size Disable maximum size of helm manifest archives when extracted
|
||||
--disable-tls Disable TLS on the gRPC endpoint
|
||||
--helm-manifest-max-extracted-size string Maximum size of helm manifest archives when extracted (default "1G")
|
||||
--helm-registry-max-index-size string Maximum size of registry index file (default "1G")
|
||||
-h, --help help for argocd-repo-server
|
||||
--logformat string Set the logging format. One of: text|json (default "text")
|
||||
--loglevel string Set the logging level. One of: debug|info|warn|error (default "info")
|
||||
|
||||
@@ -16,6 +16,7 @@ argocd-server [flags]
|
||||
|
||||
```
|
||||
--address string Listen on given address (default "0.0.0.0")
|
||||
--api-content-types string Semicolon separated list of allowed content types for non GET api requests. Any content type is allowed if empty. (default "application/json")
|
||||
--app-state-cache-expiration duration Cache expiration for app state (default 1h0m0s)
|
||||
--application-namespaces strings List of additional namespaces where application resources can be managed in
|
||||
--as string Username to impersonate for the operation
|
||||
|
||||
@@ -11,30 +11,31 @@ argocd admin app get-reconcile-results PATH [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--as string Username to impersonate for the operation
|
||||
--as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
|
||||
--as-uid string UID to impersonate for the operation
|
||||
--certificate-authority string Path to a cert file for the certificate authority
|
||||
--client-certificate string Path to a client certificate file for TLS
|
||||
--client-key string Path to a client key file for TLS
|
||||
--cluster string The name of the kubeconfig cluster to use
|
||||
--context string The name of the kubeconfig context to use
|
||||
-h, --help help for get-reconcile-results
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--kubeconfig string Path to a kube config. Only required if out-of-cluster
|
||||
--l string Label selector
|
||||
-n, --namespace string If present, the namespace scope for this CLI request
|
||||
--o string Output format (yaml|json) (default "yaml")
|
||||
--password string Password for basic authentication to the API server
|
||||
--proxy-url string If provided, this URL will be used to connect via proxy
|
||||
--refresh If set to true then recalculates apps reconciliation
|
||||
--repo-server string Repo server address.
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
|
||||
--server string The address and port of the Kubernetes API server
|
||||
--tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used.
|
||||
--token string Bearer token for authentication to the API server
|
||||
--user string The name of the kubeconfig user to use
|
||||
--username string Username for basic authentication to the API server
|
||||
--as string Username to impersonate for the operation
|
||||
--as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
|
||||
--as-uid string UID to impersonate for the operation
|
||||
--certificate-authority string Path to a cert file for the certificate authority
|
||||
--client-certificate string Path to a client certificate file for TLS
|
||||
--client-key string Path to a client key file for TLS
|
||||
--cluster string The name of the kubeconfig cluster to use
|
||||
--context string The name of the kubeconfig context to use
|
||||
-h, --help help for get-reconcile-results
|
||||
--ignore-normalizer-jq-execution-timeout duration Set ignore normalizer JQ execution timeout (default 1s)
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--kubeconfig string Path to a kube config. Only required if out-of-cluster
|
||||
--l string Label selector
|
||||
-n, --namespace string If present, the namespace scope for this CLI request
|
||||
--o string Output format (yaml|json) (default "yaml")
|
||||
--password string Password for basic authentication to the API server
|
||||
--proxy-url string If provided, this URL will be used to connect via proxy
|
||||
--refresh If set to true then recalculates apps reconciliation
|
||||
--repo-server string Repo server address.
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
|
||||
--server string The address and port of the Kubernetes API server
|
||||
--tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used.
|
||||
--token string Bearer token for authentication to the API server
|
||||
--user string The name of the kubeconfig user to use
|
||||
--username string Username for basic authentication to the API server
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -29,6 +29,7 @@ argocd admin dashboard [flags]
|
||||
--proxy-url string If provided, this URL will be used to connect via proxy
|
||||
--redis-compress string Enable this if the application controller is configured with redis compression enabled. (possible values: gzip, none) (default "gzip")
|
||||
--request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0")
|
||||
--server string The address and port of the Kubernetes API server
|
||||
--tls-server-name string If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used.
|
||||
--token string Bearer token for authentication to the API server
|
||||
--user string The name of the kubeconfig user to use
|
||||
@@ -58,7 +59,6 @@ argocd admin dashboard [flags]
|
||||
--redis-haproxy-name string Name of the Redis HA Proxy; set this or the ARGOCD_REDIS_HAPROXY_NAME environment variable when the HA Proxy's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis-ha-haproxy")
|
||||
--redis-name string Name of the Redis deployment; set this or the ARGOCD_REDIS_NAME environment variable when the Redis's name label differs from the default, for example when installing via the Helm chart (default "argocd-redis")
|
||||
--repo-server-name string Name of the Argo CD Repo server; set this or the ARGOCD_REPO_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-repo-server")
|
||||
--server string Argo CD server address
|
||||
--server-crt string Server certificate file
|
||||
--server-name string Name of the Argo CD API server; set this or the ARGOCD_SERVER_NAME environment variable when the server's name label differs from the default, for example when installing via the Helm chart (default "argocd-server")
|
||||
```
|
||||
|
||||
@@ -22,7 +22,8 @@ argocd admin settings resource-overrides ignore-resource-updates ./deploy.yaml -
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for ignore-resource-updates
|
||||
-h, --help help for ignore-resource-updates
|
||||
--ignore-normalizer-jq-execution-timeout duration Set ignore normalizer JQ execution timeout (default 1s)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -17,15 +17,16 @@ argocd app diff APPNAME [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--exit-code Return non-zero exit code when there is a diff (default true)
|
||||
--hard-refresh Refresh application data as well as target manifests cache
|
||||
-h, --help help for diff
|
||||
--local string Compare live app to a local manifests
|
||||
--local-include stringArray Used with --server-side-generate, specify patterns of filenames to send. Matching is based on filename and not path. (default [*.yaml,*.yml,*.json])
|
||||
--local-repo-root string Path to the repository root. Used together with --local allows setting the repository root (default "/")
|
||||
--refresh Refresh application data when retrieving
|
||||
--revision string Compare live app to a particular revision
|
||||
--server-side-generate Used with --local, this will send your manifests to the server for diffing
|
||||
--exit-code Return non-zero exit code when there is a diff (default true)
|
||||
--hard-refresh Refresh application data as well as target manifests cache
|
||||
-h, --help help for diff
|
||||
--ignore-normalizer-jq-execution-timeout duration Set ignore normalizer JQ execution timeout (default 1s)
|
||||
--local string Compare live app to a local manifests
|
||||
--local-include stringArray Used with --server-side-generate, specify patterns of filenames to send. Matching is based on filename and not path. (default [*.yaml,*.yml,*.json])
|
||||
--local-repo-root string Path to the repository root. Used together with --local allows setting the repository root (default "/")
|
||||
--refresh Refresh application data when retrieving
|
||||
--revision string Compare live app to a particular revision
|
||||
--server-side-generate Used with --local, this will send your manifests to the server for diffing
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -38,31 +38,33 @@ argocd app sync [APPNAME... | -l selector | --project project-name] [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--apply-out-of-sync-only Sync only out-of-sync resources
|
||||
--assumeYes Assume yes as answer for all user queries or prompts
|
||||
--async Do not wait for application to sync before continuing
|
||||
--dry-run Preview apply without affecting cluster
|
||||
--force Use a force apply
|
||||
-h, --help help for sync
|
||||
--info stringArray A list of key-value pairs during sync process. These infos will be persisted in app.
|
||||
--label stringArray Sync only specific resources with a label. This option may be specified repeatedly.
|
||||
--local string Path to a local directory. When this flag is present no git queries will be made
|
||||
--local-repo-root string Path to the repository root. Used together with --local allows setting the repository root (default "/")
|
||||
-o, --output string Output format. One of: json|yaml|wide|tree|tree=detailed (default "wide")
|
||||
--preview-changes Preview difference against the target and live state before syncing app and wait for user confirmation
|
||||
--project stringArray Sync apps that belong to the specified projects. This option may be specified repeatedly.
|
||||
--prune Allow deleting unexpected resources
|
||||
--replace Use a kubectl create/replace instead apply
|
||||
--resource stringArray Sync only specific resources as GROUP:KIND:NAME or !GROUP:KIND:NAME. Fields may be blank and '*' can be used. This option may be specified repeatedly
|
||||
--retry-backoff-duration duration Retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h) (default 5s)
|
||||
--retry-backoff-factor int Factor multiplies the base duration after each failed retry (default 2)
|
||||
--retry-backoff-max-duration duration Max retry backoff duration. Input needs to be a duration (e.g. 2m, 1h) (default 3m0s)
|
||||
--retry-limit int Max number of allowed sync retries
|
||||
--revision string Sync to a specific revision. Preserves parameter overrides
|
||||
-l, --selector string Sync apps that match this label. Supports '=', '==', '!=', in, notin, exists & not exists. Matching apps must satisfy all of the specified label constraints.
|
||||
--server-side Use server-side apply while syncing the application
|
||||
--strategy string Sync strategy (one of: apply|hook)
|
||||
--timeout uint Time out after this many seconds
|
||||
-N, --app-namespace string Only sync an application in namespace
|
||||
--apply-out-of-sync-only Sync only out-of-sync resources
|
||||
--assumeYes Assume yes as answer for all user queries or prompts
|
||||
--async Do not wait for application to sync before continuing
|
||||
--dry-run Preview apply without affecting cluster
|
||||
--force Use a force apply
|
||||
-h, --help help for sync
|
||||
--ignore-normalizer-jq-execution-timeout duration Set ignore normalizer JQ execution timeout (default 1s)
|
||||
--info stringArray A list of key-value pairs during sync process. These infos will be persisted in app.
|
||||
--label stringArray Sync only specific resources with a label. This option may be specified repeatedly.
|
||||
--local string Path to a local directory. When this flag is present no git queries will be made
|
||||
--local-repo-root string Path to the repository root. Used together with --local allows setting the repository root (default "/")
|
||||
-o, --output string Output format. One of: json|yaml|wide|tree|tree=detailed (default "wide")
|
||||
--preview-changes Preview difference against the target and live state before syncing app and wait for user confirmation
|
||||
--project stringArray Sync apps that belong to the specified projects. This option may be specified repeatedly.
|
||||
--prune Allow deleting unexpected resources
|
||||
--replace Use a kubectl create/replace instead apply
|
||||
--resource stringArray Sync only specific resources as GROUP:KIND:NAME or !GROUP:KIND:NAME. Fields may be blank and '*' can be used. This option may be specified repeatedly
|
||||
--retry-backoff-duration duration Retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h) (default 5s)
|
||||
--retry-backoff-factor int Factor multiplies the base duration after each failed retry (default 2)
|
||||
--retry-backoff-max-duration duration Max retry backoff duration. Input needs to be a duration (e.g. 2m, 1h) (default 3m0s)
|
||||
--retry-limit int Max number of allowed sync retries
|
||||
--revision string Sync to a specific revision. Preserves parameter overrides
|
||||
-l, --selector string Sync apps that match this label. Supports '=', '==', '!=', in, notin, exists & not exists. Matching apps must satisfy all of the specified label constraints.
|
||||
--server-side Use server-side apply while syncing the application
|
||||
--strategy string Sync strategy (one of: apply|hook)
|
||||
--timeout uint Time out after this many seconds
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -182,3 +182,16 @@ data:
|
||||
```
|
||||
|
||||
The list of supported Kubernetes types is available in [diffing_known_types.txt](https://raw.githubusercontent.com/argoproj/argo-cd/master/util/argo/normalizers/diffing_known_types.txt)
|
||||
|
||||
|
||||
### JQ Path expression timeout
|
||||
|
||||
By default, the evaluation of a JQPathExpression is limited to one second. If you encounter a "JQ patch execution timed out" error message due to a complex JQPathExpression that requires more time to evaluate, you can extend the timeout period by configuring the `ignore.normalizer.jq.timeout` setting within the `argocd-cmd-params-cm` ConfigMap.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-cmd-params-cm
|
||||
data:
|
||||
ignore.normalizer.jq.timeout: "5s"
|
||||
|
||||
103
go.mod
103
go.mod
@@ -25,7 +25,8 @@ require (
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/gfleury/go-bitbucket-v1 v0.0.0-20220301131131-8e7ed04b843e
|
||||
github.com/go-git/go-git/v5 v5.8.1
|
||||
github.com/go-git/go-git/v5 v5.11.0
|
||||
github.com/go-jose/go-jose/v3 v3.0.1
|
||||
github.com/go-logr/logr v1.2.4
|
||||
github.com/go-openapi/loads v0.21.2
|
||||
github.com/go-openapi/runtime v0.26.0
|
||||
@@ -36,7 +37,7 @@ require (
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang/protobuf v1.5.3
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/go-github/v35 v35.3.0
|
||||
github.com/google/go-jsonnet v0.20.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
@@ -77,23 +78,22 @@ require (
|
||||
go.opentelemetry.io/otel v1.16.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0
|
||||
go.opentelemetry.io/otel/sdk v1.16.0
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/crypto v0.16.0
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
||||
golang.org/x/oauth2 v0.11.0
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/term v0.13.0
|
||||
golang.org/x/term v0.15.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc
|
||||
google.golang.org/grpc v1.56.2
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.24.2
|
||||
k8s.io/api v0.24.17
|
||||
k8s.io/apiextensions-apiserver v0.24.2
|
||||
k8s.io/apimachinery v0.24.2
|
||||
k8s.io/apiserver v0.24.2
|
||||
k8s.io/client-go v0.24.2
|
||||
k8s.io/code-generator v0.24.2
|
||||
k8s.io/apimachinery v0.24.17
|
||||
k8s.io/apiserver v0.24.17
|
||||
k8s.io/client-go v0.24.17
|
||||
k8s.io/code-generator v0.24.17
|
||||
k8s.io/klog/v2 v2.70.1
|
||||
k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8
|
||||
k8s.io/kubectl v0.24.2
|
||||
@@ -151,9 +151,8 @@ require (
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/PagerDuty/go-pagerduty v1.7.0 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
|
||||
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20210112200207-10ab4d695d60 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.4 // indirect
|
||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
@@ -175,8 +174,7 @@ require (
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.4.1 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/analysis v0.21.4 // indirect
|
||||
github.com/go-openapi/errors v0.20.3 // indirect
|
||||
@@ -234,7 +232,7 @@ require (
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0
|
||||
github.com/prometheus/common v0.42.0 // indirect
|
||||
github.com/prometheus/procfs v0.10.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
@@ -243,7 +241,7 @@ require (
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/skeema/knownhosts v1.2.0 // indirect
|
||||
github.com/skeema/knownhosts v1.2.1 // indirect
|
||||
github.com/slack-go/slack v0.12.2 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
@@ -260,12 +258,12 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.16.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
|
||||
go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect
|
||||
golang.org/x/mod v0.9.0 // indirect
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/net v0.19.0
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
gomodules.xyz/envconfig v1.3.1-0.20190308184047-426f31af0d45 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
|
||||
gomodules.xyz/notify v0.1.1 // indirect
|
||||
@@ -274,12 +272,12 @@ require (
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
k8s.io/cli-runtime v0.24.2 // indirect
|
||||
k8s.io/component-base v0.24.2 // indirect
|
||||
k8s.io/component-helpers v0.24.2 // indirect
|
||||
k8s.io/cli-runtime v0.24.17 // indirect
|
||||
k8s.io/component-base v0.24.17 // indirect
|
||||
k8s.io/component-helpers v0.24.17 // indirect
|
||||
k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 // indirect
|
||||
k8s.io/kube-aggregator v0.24.2 // indirect
|
||||
k8s.io/kubernetes v1.24.2 // indirect
|
||||
k8s.io/kubernetes v1.24.17 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20220525155127-227cbc7cc124 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.11.5 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.13.7 // indirect
|
||||
@@ -292,6 +290,9 @@ replace (
|
||||
github.com/golang/protobuf => github.com/golang/protobuf v1.4.2
|
||||
github.com/grpc-ecosystem/grpc-gateway => github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||
|
||||
// Avoid CVE-2023-46402
|
||||
github.com/whilp/git-urls => github.com/chainguard-dev/git-urls v1.0.2
|
||||
|
||||
// Avoid CVE-2022-3064
|
||||
gopkg.in/yaml.v2 => gopkg.in/yaml.v2 v2.4.0
|
||||
|
||||
@@ -299,29 +300,31 @@ replace (
|
||||
gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1
|
||||
|
||||
// https://github.com/kubernetes/kubernetes/issues/79384#issuecomment-505627280
|
||||
k8s.io/api => k8s.io/api v0.24.2
|
||||
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.24.2
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.24.2
|
||||
k8s.io/apiserver => k8s.io/apiserver v0.24.2
|
||||
k8s.io/cli-runtime => k8s.io/cli-runtime v0.24.2
|
||||
k8s.io/client-go => k8s.io/client-go v0.24.2
|
||||
k8s.io/cloud-provider => k8s.io/cloud-provider v0.24.2
|
||||
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.24.2
|
||||
k8s.io/code-generator => k8s.io/code-generator v0.24.2
|
||||
k8s.io/component-base => k8s.io/component-base v0.24.2
|
||||
k8s.io/component-helpers => k8s.io/component-helpers v0.24.2
|
||||
k8s.io/controller-manager => k8s.io/controller-manager v0.24.2
|
||||
k8s.io/cri-api => k8s.io/cri-api v0.24.2
|
||||
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.24.2
|
||||
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.24.2
|
||||
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.24.2
|
||||
k8s.io/kube-proxy => k8s.io/kube-proxy v0.24.2
|
||||
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.24.2
|
||||
k8s.io/kubectl => k8s.io/kubectl v0.24.2
|
||||
k8s.io/kubelet => k8s.io/kubelet v0.24.2
|
||||
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.24.2
|
||||
k8s.io/metrics => k8s.io/metrics v0.24.2
|
||||
k8s.io/mount-utils => k8s.io/mount-utils v0.24.2
|
||||
k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.24.2
|
||||
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.24.2
|
||||
k8s.io/api => k8s.io/api v0.24.17
|
||||
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.24.17
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.24.17
|
||||
k8s.io/apiserver => k8s.io/apiserver v0.24.17
|
||||
k8s.io/cli-runtime => k8s.io/cli-runtime v0.24.17
|
||||
k8s.io/client-go => k8s.io/client-go v0.24.17
|
||||
k8s.io/cloud-provider => k8s.io/cloud-provider v0.24.17
|
||||
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.24.17
|
||||
k8s.io/code-generator => k8s.io/code-generator v0.24.17
|
||||
k8s.io/component-base => k8s.io/component-base v0.24.17
|
||||
k8s.io/component-helpers => k8s.io/component-helpers v0.24.17
|
||||
k8s.io/controller-manager => k8s.io/controller-manager v0.24.17
|
||||
k8s.io/cri-api => k8s.io/cri-api v0.24.17
|
||||
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.24.17
|
||||
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.24.17
|
||||
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.24.17
|
||||
k8s.io/kube-proxy => k8s.io/kube-proxy v0.24.17
|
||||
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.24.17
|
||||
k8s.io/kubectl => k8s.io/kubectl v0.24.17
|
||||
k8s.io/kubelet => k8s.io/kubelet v0.24.17
|
||||
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.24.17
|
||||
k8s.io/metrics => k8s.io/metrics v0.24.17
|
||||
k8s.io/mount-utils => k8s.io/mount-utils v0.24.17
|
||||
k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.24.17
|
||||
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.24.17
|
||||
k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.24.17
|
||||
k8s.io/sample-controller => k8s.io/sample-controller v0.24.17
|
||||
)
|
||||
|
||||
@@ -36,3 +36,11 @@ rules:
|
||||
verbs:
|
||||
- create
|
||||
- list
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- deployments
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
|
||||
@@ -155,6 +155,12 @@ spec:
|
||||
name: argocd-cmd-params-cm
|
||||
key: controller.kubectl.parallelism.limit
|
||||
optional: true
|
||||
- name: ARGOCD_IGNORE_NORMALIZER_JQ_TIMEOUT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:latest
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.9.2
|
||||
newTag: v2.9.13
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
serviceAccountName: argocd-redis
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: Always
|
||||
args:
|
||||
- "--save"
|
||||
|
||||
@@ -25,136 +25,136 @@ spec:
|
||||
env:
|
||||
- name: ARGOCD_SERVER_INSECURE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.insecure
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.insecure
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_BASEHREF
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.basehref
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.basehref
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_ROOTPATH
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.rootpath
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.rootpath
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_LOGFORMAT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.log.format
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.log.format
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_LOG_LEVEL
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.log.level
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.log.level
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_REPO_SERVER
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: repo.server
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: repo.server
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_DEX_SERVER
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.dex.server
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.dex.server
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_DISABLE_AUTH
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.disable.auth
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.disable.auth
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_ENABLE_GZIP
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.enable.gzip
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.enable.gzip
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_REPO_SERVER_TIMEOUT_SECONDS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.repo.server.timeout.seconds
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.repo.server.timeout.seconds
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_X_FRAME_OPTIONS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.x.frame.options
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.x.frame.options
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_CONTENT_SECURITY_POLICY
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.content.security.policy
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.content.security.policy
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_REPO_SERVER_PLAINTEXT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.repo.server.plaintext
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.repo.server.plaintext
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_REPO_SERVER_STRICT_TLS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.repo.server.strict.tls
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.repo.server.strict.tls
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_DEX_SERVER_PLAINTEXT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.dex.server.plaintext
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.dex.server.plaintext
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_DEX_SERVER_STRICT_TLS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.dex.server.strict.tls
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.dex.server.strict.tls
|
||||
optional: true
|
||||
- name: ARGOCD_TLS_MIN_VERSION
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.tls.minversion
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.tls.minversion
|
||||
optional: true
|
||||
- name: ARGOCD_TLS_MAX_VERSION
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.tls.maxversion
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.tls.maxversion
|
||||
optional: true
|
||||
- name: ARGOCD_TLS_CIPHERS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.tls.ciphers
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.tls.ciphers
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_CONNECTION_STATUS_CACHE_EXPIRATION
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.connection.status.cache.expiration
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.connection.status.cache.expiration
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_OIDC_CACHE_EXPIRATION
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.oidc.cache.expiration
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.oidc.cache.expiration
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_LOGIN_ATTEMPTS_EXPIRATION
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.login.attempts.expiration
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.login.attempts.expiration
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_STATIC_ASSETS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -163,16 +163,16 @@ spec:
|
||||
optional: true
|
||||
- name: ARGOCD_APP_STATE_CACHE_EXPIRATION
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.app.state.cache.expiration
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.app.state.cache.expiration
|
||||
optional: true
|
||||
- name: REDIS_SERVER
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: redis.server
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: redis.server
|
||||
optional: true
|
||||
- name: REDIS_COMPRESSION
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
@@ -181,52 +181,58 @@ spec:
|
||||
optional: true
|
||||
- name: REDISDB
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: redis.db
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: redis.db
|
||||
optional: true
|
||||
- name: ARGOCD_DEFAULT_CACHE_EXPIRATION
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.default.cache.expiration
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.default.cache.expiration
|
||||
optional: true
|
||||
- name: ARGOCD_MAX_COOKIE_NUMBER
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.http.cookie.maxnumber
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.http.cookie.maxnumber
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_LISTEN_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.listen.address
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.listen.address
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_METRICS_LISTEN_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.metrics.listen.address
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.metrics.listen.address
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_OTLP_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: otlp.address
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: otlp.address
|
||||
optional: true
|
||||
- name: ARGOCD_APPLICATION_NAMESPACES
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: application.namespaces
|
||||
optional: true
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: application.namespaces
|
||||
optional: true
|
||||
- name: ARGOCD_SERVER_ENABLE_PROXY_EXTENSION
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.enable.proxy.extension
|
||||
optional: true
|
||||
- name: ARGOCD_API_CONTENT_TYPES
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: server.api.content.types
|
||||
optional: true
|
||||
volumeMounts:
|
||||
- name: ssh-known-hosts
|
||||
mountPath: /app/config/ssh
|
||||
|
||||
@@ -20312,6 +20312,14 @@ rules:
|
||||
verbs:
|
||||
- create
|
||||
- list
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- deployments
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
@@ -20742,7 +20750,7 @@ spec:
|
||||
key: applicationsetcontroller.allowed.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -20837,7 +20845,7 @@ spec:
|
||||
- ""
|
||||
- --appendonly
|
||||
- "no"
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -21042,7 +21050,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -21094,7 +21102,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -21313,7 +21321,13 @@ spec:
|
||||
key: controller.kubectl.parallelism.limit
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
- name: ARGOCD_IGNORE_NORMALIZER_JQ_TIMEOUT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -12,4 +12,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.9.2
|
||||
newTag: v2.9.13
|
||||
|
||||
@@ -12,7 +12,7 @@ patches:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.9.2
|
||||
newTag: v2.9.13
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/applicationset-controller
|
||||
|
||||
@@ -1207,7 +1207,7 @@ spec:
|
||||
automountServiceAccountToken: false
|
||||
initContainers:
|
||||
- name: config-init
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
resources:
|
||||
{}
|
||||
@@ -1241,7 +1241,7 @@ spec:
|
||||
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- redis-server
|
||||
@@ -1298,7 +1298,7 @@ spec:
|
||||
- /bin/sh
|
||||
- /readonly-config/trigger-failover-if-master.sh
|
||||
- name: sentinel
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- redis-sentinel
|
||||
@@ -1349,7 +1349,7 @@ spec:
|
||||
{}
|
||||
|
||||
- name: split-brain-fix
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- sh
|
||||
|
||||
@@ -20,7 +20,7 @@ redis-ha:
|
||||
metrics:
|
||||
enabled: true
|
||||
image:
|
||||
tag: 7.0.11-alpine
|
||||
tag: 7.0.15-alpine
|
||||
containerSecurityContext: null
|
||||
sentinel:
|
||||
bind: "0.0.0.0"
|
||||
|
||||
@@ -20348,6 +20348,14 @@ rules:
|
||||
verbs:
|
||||
- create
|
||||
- list
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- deployments
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
@@ -21999,7 +22007,7 @@ spec:
|
||||
key: applicationsetcontroller.allowed.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -22122,7 +22130,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -22198,7 +22206,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -22529,7 +22537,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -22581,7 +22589,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -22870,7 +22878,13 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
- name: ARGOCD_API_CONTENT_TYPES
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: server.api.content.types
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -23116,7 +23130,13 @@ spec:
|
||||
key: controller.kubectl.parallelism.limit
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
- name: ARGOCD_IGNORE_NORMALIZER_JQ_TIMEOUT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
@@ -23193,7 +23213,7 @@ spec:
|
||||
- /data/conf/redis.conf
|
||||
command:
|
||||
- redis-server
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle:
|
||||
preStop:
|
||||
@@ -23247,7 +23267,7 @@ spec:
|
||||
- /data/conf/sentinel.conf
|
||||
command:
|
||||
- redis-sentinel
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle: {}
|
||||
livenessProbe:
|
||||
@@ -23300,7 +23320,7 @@ spec:
|
||||
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
|
||||
- name: SENTINEL_ID_2
|
||||
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: split-brain-fix
|
||||
resources: {}
|
||||
@@ -23330,7 +23350,7 @@ spec:
|
||||
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
|
||||
- name: SENTINEL_ID_2
|
||||
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: config-init
|
||||
securityContext:
|
||||
|
||||
@@ -109,6 +109,14 @@ rules:
|
||||
verbs:
|
||||
- create
|
||||
- list
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- deployments
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
@@ -1654,7 +1662,7 @@ spec:
|
||||
key: applicationsetcontroller.allowed.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1777,7 +1785,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1853,7 +1861,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -2184,7 +2192,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -2236,7 +2244,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -2525,7 +2533,13 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
- name: ARGOCD_API_CONTENT_TYPES
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: server.api.content.types
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2771,7 +2785,13 @@ spec:
|
||||
key: controller.kubectl.parallelism.limit
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
- name: ARGOCD_IGNORE_NORMALIZER_JQ_TIMEOUT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
@@ -2848,7 +2868,7 @@ spec:
|
||||
- /data/conf/redis.conf
|
||||
command:
|
||||
- redis-server
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle:
|
||||
preStop:
|
||||
@@ -2902,7 +2922,7 @@ spec:
|
||||
- /data/conf/sentinel.conf
|
||||
command:
|
||||
- redis-sentinel
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle: {}
|
||||
livenessProbe:
|
||||
@@ -2955,7 +2975,7 @@ spec:
|
||||
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
|
||||
- name: SENTINEL_ID_2
|
||||
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: split-brain-fix
|
||||
resources: {}
|
||||
@@ -2985,7 +3005,7 @@ spec:
|
||||
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
|
||||
- name: SENTINEL_ID_2
|
||||
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: config-init
|
||||
securityContext:
|
||||
|
||||
@@ -20339,6 +20339,14 @@ rules:
|
||||
verbs:
|
||||
- create
|
||||
- list
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- deployments
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
@@ -21094,7 +21102,7 @@ spec:
|
||||
key: applicationsetcontroller.allowed.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -21217,7 +21225,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -21293,7 +21301,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -21370,7 +21378,7 @@ spec:
|
||||
- ""
|
||||
- --appendonly
|
||||
- "no"
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -21575,7 +21583,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -21627,7 +21635,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -21914,7 +21922,13 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
- name: ARGOCD_API_CONTENT_TYPES
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: server.api.content.types
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -22160,7 +22174,13 @@ spec:
|
||||
key: controller.kubectl.parallelism.limit
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
- name: ARGOCD_IGNORE_NORMALIZER_JQ_TIMEOUT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -100,6 +100,14 @@ rules:
|
||||
verbs:
|
||||
- create
|
||||
- list
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- deployments
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
@@ -749,7 +757,7 @@ spec:
|
||||
key: applicationsetcontroller.allowed.scm.providers
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -872,7 +880,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -948,7 +956,7 @@ spec:
|
||||
key: application.namespaces
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1025,7 +1033,7 @@ spec:
|
||||
- ""
|
||||
- --appendonly
|
||||
- "no"
|
||||
image: redis:7.0.11-alpine
|
||||
image: redis:7.0.15-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -1230,7 +1238,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1282,7 +1290,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -1569,7 +1577,13 @@ spec:
|
||||
key: server.enable.proxy.extension
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
- name: ARGOCD_API_CONTENT_TYPES
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: server.api.content.types
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -1815,7 +1829,13 @@ spec:
|
||||
key: controller.kubectl.parallelism.limit
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.2
|
||||
- name: ARGOCD_IGNORE_NORMALIZER_JQ_TIMEOUT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: controller.ignore.normalizer.jq.timeout
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.9.13
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
|
||||
@@ -13,9 +13,11 @@ import (
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
argocderrors "github.com/argoproj/argo-cd/v2/util/errors"
|
||||
argoio "github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/rand"
|
||||
@@ -112,6 +114,11 @@ func (c *client) startGRPCProxy() (*grpc.Server, net.Listener, error) {
|
||||
}
|
||||
proxySrv := grpc.NewServer(
|
||||
grpc.ForceServerCodec(&noopCodec{}),
|
||||
grpc.KeepaliveEnforcementPolicy(
|
||||
keepalive.EnforcementPolicy{
|
||||
MinTime: common.GRPCKeepAliveEnforcementMinimum,
|
||||
},
|
||||
),
|
||||
grpc.UnknownServiceHandler(func(srv interface{}, stream grpc.ServerStream) error {
|
||||
fullMethodName, ok := grpc.MethodFromServerStream(stream)
|
||||
if !ok {
|
||||
|
||||
@@ -98,6 +98,9 @@ API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/applicat
|
||||
API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourcePluginParameter,String_
|
||||
API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ClusterCacheInfo,APIsCount
|
||||
API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ConnectionState,ModifiedAt
|
||||
API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ErrApplicationNotAllowedToUseProject,application
|
||||
API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ErrApplicationNotAllowedToUseProject,namespace
|
||||
API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ErrApplicationNotAllowedToUseProject,project
|
||||
API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,HelmOptions,ValuesFileSchemes
|
||||
API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,JWTToken,ExpiresAt
|
||||
API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,JWTToken,IssuedAt
|
||||
|
||||
@@ -17,6 +17,24 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type ErrApplicationNotAllowedToUseProject struct {
|
||||
application string
|
||||
namespace string
|
||||
project string
|
||||
}
|
||||
|
||||
func NewErrApplicationNotAllowedToUseProject(application, namespace, project string) error {
|
||||
return &ErrApplicationNotAllowedToUseProject{
|
||||
application: application,
|
||||
namespace: namespace,
|
||||
project: project,
|
||||
}
|
||||
}
|
||||
|
||||
func (err *ErrApplicationNotAllowedToUseProject) Error() string {
|
||||
return fmt.Sprintf("application '%s' in namespace '%s' is not allowed to use project %s", err.application, err.namespace, err.project)
|
||||
}
|
||||
|
||||
// AppProjectList is list of AppProject resources
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type AppProjectList struct {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -893,6 +893,9 @@ message EnvEntry {
|
||||
optional string value = 2;
|
||||
}
|
||||
|
||||
message ErrApplicationNotAllowedToUseProject {
|
||||
}
|
||||
|
||||
// ExecProviderConfig is config used to call an external command to perform cluster authentication
|
||||
// See: https://godoc.org/k8s.io/client-go/tools/clientcmd/api#ExecConfig
|
||||
message ExecProviderConfig {
|
||||
|
||||
@@ -70,6 +70,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ConnectionState": schema_pkg_apis_application_v1alpha1_ConnectionState(ref),
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.DuckTypeGenerator": schema_pkg_apis_application_v1alpha1_DuckTypeGenerator(ref),
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.EnvEntry": schema_pkg_apis_application_v1alpha1_EnvEntry(ref),
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ErrApplicationNotAllowedToUseProject": schema_pkg_apis_application_v1alpha1_ErrApplicationNotAllowedToUseProject(ref),
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.ExecProviderConfig": schema_pkg_apis_application_v1alpha1_ExecProviderConfig(ref),
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitDirectoryGeneratorItem": schema_pkg_apis_application_v1alpha1_GitDirectoryGeneratorItem(ref),
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GitFileGeneratorItem": schema_pkg_apis_application_v1alpha1_GitFileGeneratorItem(ref),
|
||||
@@ -3186,6 +3187,40 @@ func schema_pkg_apis_application_v1alpha1_EnvEntry(ref common.ReferenceCallback)
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_application_v1alpha1_ErrApplicationNotAllowedToUseProject(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"application": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"namespace": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"project": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"application", "namespace", "project"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_application_v1alpha1_ExecProviderConfig(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
|
||||
@@ -951,6 +951,35 @@ type ApplicationStatus struct {
|
||||
ControllerNamespace string `json:"controllerNamespace,omitempty" protobuf:"bytes,13,opt,name=controllerNamespace"`
|
||||
}
|
||||
|
||||
// GetRevisions will return the current revision associated with the Application.
|
||||
// If app has multisources, it will return all corresponding revisions preserving
|
||||
// order from the app.spec.sources. If app has only one source, it will return a
|
||||
// single revision in the list.
|
||||
func (a *ApplicationStatus) GetRevisions() []string {
|
||||
revisions := []string{}
|
||||
if len(a.Sync.Revisions) > 0 {
|
||||
revisions = a.Sync.Revisions
|
||||
} else if a.Sync.Revision != "" {
|
||||
revisions = append(revisions, a.Sync.Revision)
|
||||
}
|
||||
return revisions
|
||||
}
|
||||
|
||||
// BuildComparedToStatus will build a ComparedTo object based on the current
|
||||
// Application state.
|
||||
func (app *Application) BuildComparedToStatus() ComparedTo {
|
||||
ct := ComparedTo{
|
||||
Destination: app.Spec.Destination,
|
||||
IgnoreDifferences: app.Spec.IgnoreDifferences,
|
||||
}
|
||||
if app.Spec.HasMultipleSources() {
|
||||
ct.Sources = app.Spec.Sources
|
||||
} else {
|
||||
ct.Source = app.Spec.GetSource()
|
||||
}
|
||||
return ct
|
||||
}
|
||||
|
||||
// JWTTokens represents a list of JWT tokens
|
||||
type JWTTokens struct {
|
||||
Items []JWTToken `json:"items,omitempty" protobuf:"bytes,1,opt,name=items"`
|
||||
|
||||
@@ -1852,6 +1852,22 @@ func (in *EnvEntry) DeepCopy() *EnvEntry {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ErrApplicationNotAllowedToUseProject) DeepCopyInto(out *ErrApplicationNotAllowedToUseProject) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ErrApplicationNotAllowedToUseProject.
|
||||
func (in *ErrApplicationNotAllowedToUseProject) DeepCopy() *ErrApplicationNotAllowedToUseProject {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ErrApplicationNotAllowedToUseProject)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ExecProviderConfig) DeepCopyInto(out *ExecProviderConfig) {
|
||||
*out = *in
|
||||
|
||||
@@ -1910,6 +1910,7 @@ type GitFilesRequest struct {
|
||||
Revision string `protobuf:"bytes,3,opt,name=revision,proto3" json:"revision,omitempty"`
|
||||
Path string `protobuf:"bytes,4,opt,name=path,proto3" json:"path,omitempty"`
|
||||
NewGitFileGlobbingEnabled bool `protobuf:"varint,5,opt,name=NewGitFileGlobbingEnabled,proto3" json:"NewGitFileGlobbingEnabled,omitempty"`
|
||||
NoRevisionCache bool `protobuf:"varint,6,opt,name=noRevisionCache,proto3" json:"noRevisionCache,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -1983,6 +1984,13 @@ func (m *GitFilesRequest) GetNewGitFileGlobbingEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *GitFilesRequest) GetNoRevisionCache() bool {
|
||||
if m != nil {
|
||||
return m.NoRevisionCache
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type GitFilesResponse struct {
|
||||
// Map consisting of path of the path to its contents in bytes
|
||||
Map map[string][]byte `protobuf:"bytes,1,rep,name=map,proto3" json:"map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
@@ -2035,6 +2043,7 @@ type GitDirectoriesRequest struct {
|
||||
Repo *v1alpha1.Repository `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
|
||||
SubmoduleEnabled bool `protobuf:"varint,2,opt,name=submoduleEnabled,proto3" json:"submoduleEnabled,omitempty"`
|
||||
Revision string `protobuf:"bytes,3,opt,name=revision,proto3" json:"revision,omitempty"`
|
||||
NoRevisionCache bool `protobuf:"varint,4,opt,name=noRevisionCache,proto3" json:"noRevisionCache,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -2094,6 +2103,13 @@ func (m *GitDirectoriesRequest) GetRevision() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *GitDirectoriesRequest) GetNoRevisionCache() bool {
|
||||
if m != nil {
|
||||
return m.NoRevisionCache
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type GitDirectoriesResponse struct {
|
||||
// A set of directory paths
|
||||
Paths []string `protobuf:"bytes,1,rep,name=paths,proto3" json:"paths,omitempty"`
|
||||
@@ -2189,140 +2205,140 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_dd8723cfcc820480 = []byte{
|
||||
// 2114 bytes of a gzipped FileDescriptorProto
|
||||
// 2127 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x5a, 0x5b, 0x6f, 0x1b, 0xc7,
|
||||
0x15, 0xe6, 0x92, 0xba, 0x90, 0x47, 0xb2, 0x44, 0x8d, 0x75, 0x59, 0x31, 0x8e, 0xa0, 0x6c, 0x6b,
|
||||
0x43, 0xb5, 0x13, 0x12, 0x92, 0x91, 0xb8, 0x70, 0xd2, 0x14, 0x8a, 0x62, 0x4b, 0x8e, 0x2d, 0x5b,
|
||||
0x5d, 0xbb, 0x2d, 0xd2, 0xba, 0x2d, 0x86, 0xcb, 0x21, 0xb9, 0xe1, 0x5e, 0xc6, 0xbb, 0xb3, 0x0a,
|
||||
0x64, 0xa0, 0x0f, 0x45, 0x8b, 0x02, 0xfd, 0x03, 0x7d, 0xe8, 0xff, 0x28, 0xfa, 0x54, 0xf4, 0xa9,
|
||||
0x97, 0xc7, 0xa0, 0x7f, 0xa0, 0x85, 0x1f, 0xfb, 0x2b, 0x8a, 0xb9, 0xec, 0x95, 0x2b, 0xd9, 0x29,
|
||||
0x65, 0x19, 0xcd, 0x8b, 0xbd, 0x73, 0xe6, 0xcc, 0x39, 0x67, 0xce, 0x9c, 0xcb, 0x37, 0x43, 0xc1,
|
||||
0xb5, 0x80, 0x50, 0x3f, 0x24, 0xc1, 0x31, 0x09, 0x3a, 0xe2, 0xd3, 0x66, 0x7e, 0x70, 0x92, 0xf9,
|
||||
0x6c, 0xd3, 0xc0, 0x67, 0x3e, 0x82, 0x94, 0xd2, 0x7a, 0x30, 0xb0, 0xd9, 0x30, 0xea, 0xb6, 0x2d,
|
||||
0xdf, 0xed, 0xe0, 0x60, 0xe0, 0xd3, 0xc0, 0xff, 0x42, 0x7c, 0xbc, 0x67, 0xf5, 0x3a, 0xc7, 0x3b,
|
||||
0x1d, 0x3a, 0x1a, 0x74, 0x30, 0xb5, 0xc3, 0x0e, 0xa6, 0xd4, 0xb1, 0x2d, 0xcc, 0x6c, 0xdf, 0xeb,
|
||||
0x1c, 0x6f, 0x63, 0x87, 0x0e, 0xf1, 0x76, 0x67, 0x40, 0x3c, 0x12, 0x60, 0x46, 0x7a, 0x52, 0x72,
|
||||
0xeb, 0xad, 0x81, 0xef, 0x0f, 0x1c, 0xd2, 0x11, 0xa3, 0x6e, 0xd4, 0xef, 0x10, 0x97, 0x32, 0xa5,
|
||||
0xd6, 0xf8, 0xcf, 0x3c, 0x2c, 0x1e, 0x62, 0xcf, 0xee, 0x93, 0x90, 0x99, 0xe4, 0x59, 0x44, 0x42,
|
||||
0x86, 0x9e, 0xc2, 0x14, 0x37, 0x46, 0xd7, 0x36, 0xb5, 0xad, 0xb9, 0x9d, 0x83, 0x76, 0x6a, 0x4d,
|
||||
0x3b, 0xb6, 0x46, 0x7c, 0xfc, 0xc2, 0xea, 0xb5, 0x8f, 0x77, 0xda, 0x74, 0x34, 0x68, 0x73, 0x6b,
|
||||
0xda, 0x19, 0x6b, 0xda, 0xb1, 0x35, 0x6d, 0x33, 0xd9, 0x96, 0x29, 0xa4, 0xa2, 0x16, 0xd4, 0x03,
|
||||
0x72, 0x6c, 0x87, 0xb6, 0xef, 0xe9, 0xd5, 0x4d, 0x6d, 0xab, 0x61, 0x26, 0x63, 0xa4, 0xc3, 0xac,
|
||||
0xe7, 0xef, 0x61, 0x6b, 0x48, 0xf4, 0xda, 0xa6, 0xb6, 0x55, 0x37, 0xe3, 0x21, 0xda, 0x84, 0x39,
|
||||
0x4c, 0xe9, 0x03, 0xdc, 0x25, 0xce, 0x7d, 0x72, 0xa2, 0x4f, 0x89, 0x85, 0x59, 0x12, 0x5f, 0x8b,
|
||||
0x29, 0x7d, 0x88, 0x5d, 0xa2, 0x4f, 0x8b, 0xd9, 0x78, 0x88, 0xae, 0x40, 0xc3, 0xc3, 0x2e, 0x09,
|
||||
0x29, 0xb6, 0x88, 0x5e, 0x17, 0x73, 0x29, 0x01, 0xfd, 0x12, 0x96, 0x32, 0x86, 0x3f, 0xf6, 0xa3,
|
||||
0xc0, 0x22, 0x3a, 0x88, 0xad, 0x3f, 0x9a, 0x6c, 0xeb, 0xbb, 0x45, 0xb1, 0xe6, 0xb8, 0x26, 0xf4,
|
||||
0x73, 0x98, 0x16, 0x27, 0xaf, 0xcf, 0x6d, 0xd6, 0xce, 0xd5, 0xdb, 0x52, 0x2c, 0xf2, 0x60, 0x96,
|
||||
0x3a, 0xd1, 0xc0, 0xf6, 0x42, 0x7d, 0x5e, 0x68, 0x78, 0x32, 0x99, 0x86, 0x3d, 0xdf, 0xeb, 0xdb,
|
||||
0x83, 0x43, 0xec, 0xe1, 0x01, 0x71, 0x89, 0xc7, 0x8e, 0x84, 0x70, 0x33, 0x56, 0x82, 0x9e, 0x43,
|
||||
0x73, 0x14, 0x85, 0xcc, 0x77, 0xed, 0xe7, 0xe4, 0x11, 0xe5, 0x6b, 0x43, 0xfd, 0x92, 0xf0, 0xe6,
|
||||
0xc3, 0xc9, 0x14, 0xdf, 0x2f, 0x48, 0x35, 0xc7, 0xf4, 0xf0, 0x20, 0x19, 0x45, 0x5d, 0xf2, 0x23,
|
||||
0x12, 0x88, 0xe8, 0x5a, 0x90, 0x41, 0x92, 0x21, 0xc9, 0x30, 0xb2, 0xd5, 0x28, 0xd4, 0x17, 0x37,
|
||||
0x6b, 0x32, 0x8c, 0x12, 0x12, 0xda, 0x82, 0xc5, 0x63, 0x12, 0xd8, 0xfd, 0x93, 0xc7, 0xf6, 0xc0,
|
||||
0xc3, 0x2c, 0x0a, 0x88, 0xde, 0x14, 0xa1, 0x58, 0x24, 0x23, 0x17, 0x2e, 0x0d, 0x89, 0xe3, 0x72,
|
||||
0x97, 0xef, 0x05, 0xa4, 0x17, 0xea, 0x4b, 0xc2, 0xbf, 0xfb, 0x93, 0x9f, 0xa0, 0x10, 0x67, 0xe6,
|
||||
0xa5, 0x73, 0xc3, 0x3c, 0xdf, 0x54, 0x99, 0x22, 0x73, 0x04, 0x49, 0xc3, 0x0a, 0x64, 0x74, 0x0d,
|
||||
0x16, 0x58, 0x80, 0xad, 0x91, 0xed, 0x0d, 0x0e, 0x09, 0x1b, 0xfa, 0x3d, 0xfd, 0xb2, 0xf0, 0x44,
|
||||
0x81, 0x8a, 0x2c, 0x40, 0xc4, 0xc3, 0x5d, 0x87, 0xf4, 0x64, 0x2c, 0x3e, 0x39, 0xa1, 0x24, 0xd4,
|
||||
0x97, 0xc5, 0x2e, 0x6e, 0xb6, 0x33, 0x15, 0xaa, 0x50, 0x20, 0xda, 0x77, 0xc6, 0x56, 0xdd, 0xf1,
|
||||
0x58, 0x70, 0x62, 0x96, 0x88, 0x43, 0x23, 0x98, 0xe3, 0xfb, 0x88, 0x43, 0x61, 0x45, 0x84, 0xc2,
|
||||
0xbd, 0xc9, 0x7c, 0x74, 0x90, 0x0a, 0x34, 0xb3, 0xd2, 0x51, 0x1b, 0xd0, 0x10, 0x87, 0x87, 0x91,
|
||||
0xc3, 0x6c, 0xea, 0x10, 0x69, 0x46, 0xa8, 0xaf, 0x0a, 0x37, 0x95, 0xcc, 0xa0, 0xfb, 0x00, 0x01,
|
||||
0xe9, 0xc7, 0x7c, 0x6b, 0x62, 0xe7, 0x37, 0xce, 0xda, 0xb9, 0x99, 0x70, 0xcb, 0x1d, 0x67, 0x96,
|
||||
0x73, 0xe5, 0x7c, 0x1b, 0xc4, 0x62, 0x2a, 0xdb, 0x45, 0x5a, 0xeb, 0x22, 0xc4, 0x4a, 0x66, 0x78,
|
||||
0x2c, 0x2a, 0xaa, 0x28, 0x5a, 0xeb, 0x32, 0x5a, 0x33, 0xa4, 0xd6, 0x1d, 0x58, 0x3b, 0xc5, 0xd5,
|
||||
0xa8, 0x09, 0xb5, 0x11, 0x39, 0x11, 0x25, 0xba, 0x61, 0xf2, 0x4f, 0xb4, 0x0c, 0xd3, 0xc7, 0xd8,
|
||||
0x89, 0x88, 0x28, 0xaa, 0x75, 0x53, 0x0e, 0x6e, 0x57, 0xbf, 0xab, 0xb5, 0x7e, 0xab, 0xc1, 0x62,
|
||||
0xc1, 0xf0, 0x92, 0xf5, 0x3f, 0xcb, 0xae, 0x3f, 0x87, 0x30, 0xee, 0x3f, 0xc1, 0xc1, 0x80, 0xb0,
|
||||
0x8c, 0x21, 0xc6, 0x3f, 0x35, 0xd0, 0x0b, 0x1e, 0xfd, 0xb1, 0xcd, 0x86, 0x77, 0x6d, 0x87, 0x84,
|
||||
0xe8, 0x16, 0xcc, 0x06, 0x92, 0xa6, 0x1a, 0xcf, 0x5b, 0x67, 0x1c, 0xc4, 0x41, 0xc5, 0x8c, 0xb9,
|
||||
0xd1, 0xc7, 0x50, 0x77, 0x09, 0xc3, 0x3d, 0xcc, 0xb0, 0xb2, 0x7d, 0xb3, 0x6c, 0x25, 0xd7, 0x72,
|
||||
0xa8, 0xf8, 0x0e, 0x2a, 0x66, 0xb2, 0x06, 0xbd, 0x0f, 0xd3, 0xd6, 0x30, 0xf2, 0x46, 0xa2, 0xe5,
|
||||
0xcc, 0xed, 0xbc, 0x7d, 0xda, 0xe2, 0x3d, 0xce, 0x74, 0x50, 0x31, 0x25, 0xf7, 0x27, 0x33, 0x30,
|
||||
0x45, 0x71, 0xc0, 0x8c, 0xbb, 0xb0, 0x5c, 0xa6, 0x82, 0xf7, 0x39, 0x6b, 0x48, 0xac, 0x51, 0x18,
|
||||
0xb9, 0xca, 0xcd, 0xc9, 0x18, 0x21, 0x98, 0x0a, 0xed, 0xe7, 0xd2, 0xd5, 0x35, 0x53, 0x7c, 0x1b,
|
||||
0xdf, 0x81, 0xa5, 0x31, 0x6d, 0xfc, 0x50, 0xa5, 0x6d, 0x5c, 0xc2, 0xbc, 0x52, 0x6d, 0x44, 0xb0,
|
||||
0xf2, 0x44, 0xf8, 0x22, 0x29, 0xf6, 0x17, 0xd1, 0xb9, 0x8d, 0x03, 0x58, 0x2d, 0xaa, 0x0d, 0xa9,
|
||||
0xef, 0x85, 0x84, 0x87, 0xbe, 0xa8, 0x8e, 0x36, 0xe9, 0xa5, 0xb3, 0xc2, 0x8a, 0xba, 0x59, 0x32,
|
||||
0x63, 0xfc, 0xaa, 0x0a, 0xab, 0x26, 0x09, 0x7d, 0xe7, 0x98, 0xc4, 0xa5, 0xeb, 0x62, 0xc0, 0xc7,
|
||||
0x4f, 0xa1, 0x86, 0x29, 0x55, 0x61, 0x72, 0xef, 0xdc, 0xda, 0xbb, 0xc9, 0xa5, 0xa2, 0x77, 0x61,
|
||||
0x09, 0xbb, 0x5d, 0x7b, 0x10, 0xf9, 0x51, 0x18, 0x6f, 0x4b, 0x04, 0x55, 0xc3, 0x1c, 0x9f, 0x30,
|
||||
0x2c, 0x58, 0x1b, 0x73, 0x81, 0x72, 0x67, 0x16, 0x22, 0x69, 0x05, 0x88, 0x54, 0xaa, 0xa4, 0x7a,
|
||||
0x9a, 0x92, 0xbf, 0x69, 0xd0, 0x4c, 0x53, 0x47, 0x89, 0xbf, 0x02, 0x0d, 0x57, 0xd1, 0x42, 0x5d,
|
||||
0x13, 0xf5, 0x29, 0x25, 0xe4, 0xd1, 0x52, 0xb5, 0x88, 0x96, 0x56, 0x61, 0x46, 0x82, 0x59, 0xb5,
|
||||
0x31, 0x35, 0xca, 0x99, 0x3c, 0x55, 0x30, 0x79, 0x03, 0x20, 0x4c, 0xea, 0x97, 0x3e, 0x23, 0x66,
|
||||
0x33, 0x14, 0x64, 0xc0, 0xbc, 0xec, 0xad, 0x26, 0x09, 0x23, 0x87, 0xe9, 0xb3, 0x82, 0x23, 0x47,
|
||||
0x33, 0x7c, 0x58, 0x7c, 0x60, 0xf3, 0x3d, 0xf4, 0xc3, 0x8b, 0x09, 0xf6, 0x0f, 0x60, 0x8a, 0x2b,
|
||||
0xe3, 0x1b, 0xeb, 0x06, 0xd8, 0xb3, 0x86, 0x24, 0xf6, 0x55, 0x32, 0xe6, 0x69, 0xcc, 0xf0, 0x20,
|
||||
0xd4, 0xab, 0x82, 0x2e, 0xbe, 0x8d, 0x3f, 0x55, 0xa5, 0xa5, 0xbb, 0x94, 0x86, 0x6f, 0x1e, 0x50,
|
||||
0x97, 0xb7, 0xf8, 0xda, 0x78, 0x8b, 0x2f, 0x98, 0xfc, 0x75, 0x5a, 0xfc, 0x39, 0xb5, 0x29, 0x23,
|
||||
0x82, 0xd9, 0x5d, 0x4a, 0xb9, 0x21, 0x68, 0x1b, 0xa6, 0x30, 0xa5, 0xd2, 0xe1, 0x85, 0x8a, 0xac,
|
||||
0x58, 0xf8, 0xff, 0xca, 0x24, 0xc1, 0xda, 0xba, 0x05, 0x8d, 0x84, 0xf4, 0x32, 0xb5, 0x8d, 0xac,
|
||||
0xda, 0x4d, 0x00, 0x89, 0x61, 0xef, 0x79, 0x7d, 0x9f, 0x1f, 0x29, 0x0f, 0x76, 0xb5, 0x54, 0x7c,
|
||||
0x1b, 0xb7, 0x63, 0x0e, 0x61, 0xdb, 0xbb, 0x30, 0x6d, 0x33, 0xe2, 0xc6, 0xc6, 0xad, 0x66, 0x8d,
|
||||
0x4b, 0x05, 0x99, 0x92, 0xc9, 0xf8, 0x7b, 0x1d, 0xd6, 0xf9, 0x89, 0x3d, 0x16, 0x69, 0xb2, 0x4b,
|
||||
0xe9, 0xa7, 0x84, 0x61, 0xdb, 0x09, 0x7f, 0x10, 0x91, 0xe0, 0xe4, 0x35, 0x07, 0xc6, 0x00, 0x66,
|
||||
0x64, 0x96, 0xa9, 0x7a, 0x77, 0xee, 0xd7, 0x19, 0x25, 0x3e, 0xbd, 0xc3, 0xd4, 0x5e, 0xcf, 0x1d,
|
||||
0xa6, 0xec, 0x4e, 0x31, 0x75, 0x41, 0x77, 0x8a, 0xd3, 0xaf, 0x95, 0x99, 0xcb, 0xea, 0x4c, 0xfe,
|
||||
0xb2, 0x5a, 0x02, 0xd5, 0x67, 0x5f, 0x15, 0xaa, 0xd7, 0x4b, 0xa1, 0xba, 0x5b, 0x9a, 0xc7, 0x0d,
|
||||
0xe1, 0xee, 0xef, 0x65, 0x23, 0xf0, 0xd4, 0x58, 0x9b, 0x04, 0xb4, 0xc3, 0x6b, 0x05, 0xed, 0x3f,
|
||||
0xcc, 0x81, 0x70, 0x79, 0x0d, 0x7e, 0xff, 0xd5, 0xf6, 0x74, 0x06, 0x1c, 0xff, 0xc6, 0x81, 0xe7,
|
||||
0xdf, 0x08, 0xcc, 0x44, 0xfd, 0xd4, 0x07, 0x49, 0x43, 0xe7, 0x7d, 0x88, 0xb7, 0x56, 0x55, 0xb4,
|
||||
0xf8, 0x37, 0xba, 0x01, 0x53, 0xdc, 0xc9, 0x0a, 0xd4, 0xae, 0x65, 0xfd, 0xc9, 0x4f, 0x62, 0x97,
|
||||
0xd2, 0xc7, 0x94, 0x58, 0xa6, 0x60, 0x42, 0xb7, 0xa1, 0x91, 0x04, 0xbe, 0xca, 0xac, 0x2b, 0xd9,
|
||||
0x15, 0x49, 0x9e, 0xc4, 0xcb, 0x52, 0x76, 0xbe, 0xb6, 0x67, 0x07, 0xc4, 0x12, 0x90, 0x6f, 0x7a,
|
||||
0x7c, 0xed, 0xa7, 0xf1, 0x64, 0xb2, 0x36, 0x61, 0x47, 0xdb, 0x30, 0x23, 0xdf, 0x0d, 0x44, 0x06,
|
||||
0xcd, 0xed, 0xac, 0x8f, 0x17, 0xd3, 0x78, 0x95, 0x62, 0x34, 0xfe, 0xaa, 0xc1, 0x3b, 0x69, 0x40,
|
||||
0xc4, 0xd9, 0x14, 0xa3, 0xee, 0x37, 0xdf, 0x71, 0xaf, 0xc1, 0x82, 0x80, 0xf9, 0xe9, 0xf3, 0x81,
|
||||
0x7c, 0xc9, 0x2a, 0x50, 0x8d, 0x3f, 0x6a, 0x70, 0x75, 0x7c, 0x1f, 0x7b, 0x43, 0x1c, 0xb0, 0xe4,
|
||||
0x78, 0x2f, 0x62, 0x2f, 0x71, 0xc3, 0xab, 0xa6, 0x0d, 0x2f, 0xb7, 0xbf, 0x5a, 0x7e, 0x7f, 0xc6,
|
||||
0x5f, 0xaa, 0x30, 0x97, 0x09, 0xa0, 0xb2, 0x86, 0xc9, 0x01, 0x9f, 0x88, 0x5b, 0x71, 0xb1, 0x13,
|
||||
0x4d, 0xa1, 0x61, 0x66, 0x28, 0x68, 0x04, 0x40, 0x71, 0x80, 0x5d, 0xc2, 0x48, 0xc0, 0x2b, 0x39,
|
||||
0xcf, 0xf8, 0xfb, 0x93, 0x57, 0x97, 0xa3, 0x58, 0xa6, 0x99, 0x11, 0xcf, 0x11, 0xab, 0x50, 0x1d,
|
||||
0xaa, 0xfa, 0xad, 0x46, 0xe8, 0x4b, 0x58, 0xe8, 0xdb, 0x0e, 0x39, 0x4a, 0x0d, 0x99, 0x11, 0x86,
|
||||
0x3c, 0x9a, 0xdc, 0x90, 0xbb, 0x59, 0xb9, 0x66, 0x41, 0x8d, 0x71, 0x1d, 0x9a, 0xc5, 0x7c, 0xe2,
|
||||
0x46, 0xda, 0x2e, 0x1e, 0x24, 0xde, 0x52, 0x23, 0x03, 0x41, 0xb3, 0x98, 0x3f, 0xc6, 0xbf, 0xaa,
|
||||
0xb0, 0x92, 0x88, 0xdb, 0xf5, 0x3c, 0x3f, 0xf2, 0x2c, 0xf1, 0x14, 0x57, 0x7a, 0x16, 0xcb, 0x30,
|
||||
0xcd, 0x6c, 0xe6, 0x24, 0xc0, 0x47, 0x0c, 0x78, 0xef, 0x62, 0xbe, 0xef, 0x30, 0x9b, 0xaa, 0x03,
|
||||
0x8e, 0x87, 0xf2, 0xec, 0x9f, 0x45, 0x76, 0x40, 0x7a, 0xa2, 0x12, 0xd4, 0xcd, 0x64, 0xcc, 0xe7,
|
||||
0x38, 0xaa, 0x11, 0x30, 0x5e, 0x3a, 0x33, 0x19, 0x8b, 0xb8, 0xf7, 0x1d, 0x87, 0x58, 0xdc, 0x1d,
|
||||
0x19, 0xa0, 0x5f, 0xa0, 0x8a, 0x0b, 0x04, 0x0b, 0x6c, 0x6f, 0xa0, 0x60, 0xbe, 0x1a, 0x71, 0x3b,
|
||||
0x71, 0x10, 0xe0, 0x13, 0xbd, 0x2e, 0x1c, 0x20, 0x07, 0xe8, 0x23, 0xa8, 0xb9, 0x98, 0xaa, 0x46,
|
||||
0x77, 0x3d, 0x57, 0x1d, 0xca, 0x3c, 0xd0, 0x3e, 0xc4, 0x54, 0x76, 0x02, 0xbe, 0xac, 0xf5, 0x01,
|
||||
0xd4, 0x63, 0xc2, 0xd7, 0x82, 0x84, 0x5f, 0xc0, 0xa5, 0x5c, 0xf1, 0x41, 0x9f, 0xc3, 0x6a, 0x1a,
|
||||
0x51, 0x59, 0x85, 0x0a, 0x04, 0xbe, 0xf3, 0x52, 0xcb, 0xcc, 0x53, 0x04, 0x18, 0xcf, 0x60, 0x89,
|
||||
0x87, 0x8c, 0x48, 0xfc, 0x0b, 0xba, 0xda, 0x7c, 0x08, 0x8d, 0x44, 0x65, 0x69, 0xcc, 0xb4, 0xa0,
|
||||
0x7e, 0x1c, 0x3f, 0x91, 0xca, 0xbb, 0x4d, 0x32, 0x36, 0x76, 0x01, 0x65, 0xed, 0x55, 0x1d, 0xe8,
|
||||
0x46, 0x1e, 0x14, 0xaf, 0x14, 0xdb, 0x8d, 0x60, 0x8f, 0x31, 0xf1, 0xef, 0xaa, 0xb0, 0xb8, 0x6f,
|
||||
0x8b, 0x57, 0x8e, 0x0b, 0x2a, 0x72, 0xd7, 0xa1, 0x19, 0x46, 0x5d, 0xd7, 0xef, 0x45, 0x0e, 0x51,
|
||||
0xa0, 0x40, 0x75, 0xfa, 0x31, 0xfa, 0x59, 0xc5, 0x8f, 0x3b, 0x8b, 0x62, 0x36, 0x54, 0x37, 0x5c,
|
||||
0xf1, 0x8d, 0x3e, 0x82, 0xf5, 0x87, 0xe4, 0x4b, 0xb5, 0x9f, 0x7d, 0xc7, 0xef, 0x76, 0x6d, 0x6f,
|
||||
0x10, 0x2b, 0x99, 0x16, 0x4a, 0x4e, 0x67, 0x30, 0x7e, 0xad, 0x41, 0x33, 0xf5, 0x85, 0xf2, 0xe6,
|
||||
0x2d, 0x19, 0xf5, 0xd2, 0x97, 0x57, 0xb3, 0xbe, 0x2c, 0xb2, 0xfe, 0xef, 0x01, 0x3f, 0x9f, 0x0d,
|
||||
0xf8, 0x3f, 0x6b, 0xb0, 0xb2, 0x6f, 0xb3, 0xb8, 0xd4, 0xd8, 0xff, 0x67, 0xe7, 0x62, 0xb4, 0x61,
|
||||
0xb5, 0x68, 0xbe, 0x72, 0xe5, 0x32, 0x4c, 0xf3, 0x53, 0x8a, 0xef, 0xee, 0x72, 0xb0, 0xf3, 0x55,
|
||||
0x03, 0x96, 0xd2, 0xe6, 0xcb, 0xff, 0xb5, 0x2d, 0x82, 0x1e, 0x41, 0x73, 0x5f, 0xfd, 0x76, 0x16,
|
||||
0xbf, 0x99, 0xa0, 0xb3, 0x1e, 0x21, 0x5b, 0x57, 0xca, 0x27, 0xa5, 0x6a, 0xa3, 0x82, 0x2c, 0x58,
|
||||
0x2f, 0x0a, 0x4c, 0xdf, 0x3b, 0xbf, 0x7d, 0x86, 0xe4, 0x84, 0xeb, 0x65, 0x2a, 0xb6, 0x34, 0xf4,
|
||||
0x39, 0x2c, 0xe4, 0x5f, 0xe5, 0x50, 0xae, 0x1a, 0x95, 0x3e, 0x14, 0xb6, 0x8c, 0xb3, 0x58, 0x12,
|
||||
0xfb, 0x9f, 0x72, 0xe8, 0x9b, 0x7b, 0xa2, 0x42, 0x46, 0x1e, 0x98, 0x97, 0x3d, 0xe1, 0xb5, 0xbe,
|
||||
0x75, 0x26, 0x4f, 0x22, 0xfd, 0x43, 0xa8, 0xc7, 0x4f, 0x3a, 0x79, 0x37, 0x17, 0x1e, 0x7a, 0x5a,
|
||||
0xcd, 0xbc, 0xbc, 0x7e, 0x68, 0x54, 0xd0, 0xc7, 0x72, 0x31, 0xbf, 0xf2, 0x8f, 0x2f, 0xce, 0x3c,
|
||||
0x64, 0xb4, 0x2e, 0x97, 0x3c, 0x1e, 0x18, 0x15, 0xf4, 0x7d, 0x98, 0xe3, 0x5f, 0x47, 0xea, 0x57,
|
||||
0xab, 0xd5, 0xb6, 0xfc, 0x91, 0xb4, 0x1d, 0xff, 0x48, 0xda, 0xbe, 0xe3, 0x52, 0x76, 0xd2, 0x2a,
|
||||
0xb9, 0xdd, 0x2b, 0x01, 0x4f, 0xe1, 0xd2, 0x3e, 0x61, 0x29, 0x18, 0x47, 0x57, 0x5f, 0xe9, 0xca,
|
||||
0xd2, 0x32, 0x8a, 0x6c, 0xe3, 0x78, 0xde, 0xa8, 0xa0, 0xdf, 0x6b, 0x70, 0x79, 0x9f, 0xb0, 0x22,
|
||||
0xbc, 0x45, 0xef, 0x95, 0x2b, 0x39, 0x05, 0x06, 0xb7, 0x1e, 0x4e, 0x9a, 0xaf, 0x79, 0xb1, 0x46,
|
||||
0x05, 0xfd, 0x41, 0x83, 0xb5, 0x8c, 0x61, 0x59, 0xbc, 0x8a, 0xb6, 0xcf, 0x36, 0xae, 0x04, 0xdb,
|
||||
0xb6, 0x3e, 0x9b, 0xf0, 0xc7, 0xc8, 0x8c, 0x48, 0xa3, 0x82, 0x8e, 0xc4, 0x99, 0xa4, 0xed, 0x09,
|
||||
0xbd, 0x5d, 0xda, 0x87, 0x12, 0xed, 0x1b, 0xa7, 0x4d, 0x27, 0xe7, 0xf0, 0x19, 0xcc, 0xed, 0x13,
|
||||
0x16, 0x57, 0xdd, 0x7c, 0xa4, 0x15, 0x5a, 0x58, 0x3e, 0x55, 0x8b, 0x85, 0x5a, 0x44, 0xcc, 0x92,
|
||||
0x94, 0x95, 0xa9, 0x53, 0xf9, 0x5c, 0x2d, 0x2d, 0xc1, 0xf9, 0x88, 0x29, 0x2f, 0x73, 0x46, 0xe5,
|
||||
0x93, 0xdd, 0x7f, 0xbc, 0xd8, 0xd0, 0xbe, 0x7a, 0xb1, 0xa1, 0xfd, 0xfb, 0xc5, 0x86, 0xf6, 0x93,
|
||||
0x9b, 0x2f, 0xf9, 0x0b, 0x82, 0xcc, 0x1f, 0x25, 0x60, 0x6a, 0x5b, 0x8e, 0x4d, 0x3c, 0xd6, 0x9d,
|
||||
0x11, 0xc1, 0x7f, 0xf3, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x86, 0xe4, 0x0d, 0xb3, 0x20,
|
||||
0x00, 0x00,
|
||||
0xf5, 0xe7, 0x92, 0x94, 0x44, 0x1e, 0xd9, 0x12, 0x35, 0xd6, 0x65, 0xc5, 0x38, 0x82, 0xb2, 0xff,
|
||||
0xbf, 0x0d, 0xd5, 0x4e, 0x48, 0x48, 0x46, 0xe2, 0xc2, 0x49, 0x53, 0x28, 0x8a, 0x2d, 0x39, 0xb6,
|
||||
0x6c, 0x75, 0xed, 0xb6, 0x48, 0xeb, 0xb6, 0x18, 0x2e, 0x87, 0xe4, 0x86, 0x7b, 0x19, 0xef, 0xce,
|
||||
0x2a, 0x90, 0x81, 0x3e, 0x14, 0x2d, 0xfa, 0x11, 0xfa, 0xd0, 0xaf, 0x51, 0x14, 0x7d, 0xec, 0x53,
|
||||
0x2f, 0x8f, 0x41, 0xbf, 0x40, 0x0b, 0xbf, 0x14, 0xe8, 0xa7, 0x28, 0xe6, 0xb2, 0x57, 0xae, 0x64,
|
||||
0xa7, 0x94, 0x15, 0xb4, 0x2f, 0xf6, 0xce, 0x99, 0x33, 0xe7, 0x9c, 0x39, 0x73, 0x2e, 0xbf, 0x19,
|
||||
0x0a, 0xae, 0x07, 0x84, 0xfa, 0x21, 0x09, 0x8e, 0x49, 0xd0, 0x15, 0x9f, 0x36, 0xf3, 0x83, 0x93,
|
||||
0xcc, 0x67, 0x87, 0x06, 0x3e, 0xf3, 0x11, 0xa4, 0x94, 0xf6, 0xc3, 0xa1, 0xcd, 0x46, 0x51, 0xaf,
|
||||
0x63, 0xf9, 0x6e, 0x17, 0x07, 0x43, 0x9f, 0x06, 0xfe, 0x17, 0xe2, 0xe3, 0x3d, 0xab, 0xdf, 0x3d,
|
||||
0xde, 0xe9, 0xd2, 0xf1, 0xb0, 0x8b, 0xa9, 0x1d, 0x76, 0x31, 0xa5, 0x8e, 0x6d, 0x61, 0x66, 0xfb,
|
||||
0x5e, 0xf7, 0x78, 0x1b, 0x3b, 0x74, 0x84, 0xb7, 0xbb, 0x43, 0xe2, 0x91, 0x00, 0x33, 0xd2, 0x97,
|
||||
0x92, 0xdb, 0x6f, 0x0d, 0x7d, 0x7f, 0xe8, 0x90, 0xae, 0x18, 0xf5, 0xa2, 0x41, 0x97, 0xb8, 0x94,
|
||||
0x29, 0xb5, 0xc6, 0xbf, 0x2e, 0xc1, 0xe2, 0x21, 0xf6, 0xec, 0x01, 0x09, 0x99, 0x49, 0x9e, 0x47,
|
||||
0x24, 0x64, 0xe8, 0x19, 0xd4, 0xb9, 0x31, 0xba, 0xb6, 0xa9, 0x6d, 0xcd, 0xef, 0x1c, 0x74, 0x52,
|
||||
0x6b, 0x3a, 0xb1, 0x35, 0xe2, 0xe3, 0x67, 0x56, 0xbf, 0x73, 0xbc, 0xd3, 0xa1, 0xe3, 0x61, 0x87,
|
||||
0x5b, 0xd3, 0xc9, 0x58, 0xd3, 0x89, 0xad, 0xe9, 0x98, 0xc9, 0xb6, 0x4c, 0x21, 0x15, 0xb5, 0xa1,
|
||||
0x11, 0x90, 0x63, 0x3b, 0xb4, 0x7d, 0x4f, 0xaf, 0x6e, 0x6a, 0x5b, 0x4d, 0x33, 0x19, 0x23, 0x1d,
|
||||
0xe6, 0x3c, 0x7f, 0x0f, 0x5b, 0x23, 0xa2, 0xd7, 0x36, 0xb5, 0xad, 0x86, 0x19, 0x0f, 0xd1, 0x26,
|
||||
0xcc, 0x63, 0x4a, 0x1f, 0xe2, 0x1e, 0x71, 0x1e, 0x90, 0x13, 0xbd, 0x2e, 0x16, 0x66, 0x49, 0x7c,
|
||||
0x2d, 0xa6, 0xf4, 0x11, 0x76, 0x89, 0x3e, 0x23, 0x66, 0xe3, 0x21, 0xba, 0x0a, 0x4d, 0x0f, 0xbb,
|
||||
0x24, 0xa4, 0xd8, 0x22, 0x7a, 0x43, 0xcc, 0xa5, 0x04, 0xf4, 0x73, 0x58, 0xca, 0x18, 0xfe, 0xc4,
|
||||
0x8f, 0x02, 0x8b, 0xe8, 0x20, 0xb6, 0xfe, 0x78, 0xba, 0xad, 0xef, 0x16, 0xc5, 0x9a, 0x93, 0x9a,
|
||||
0xd0, 0x4f, 0x61, 0x46, 0x9c, 0xbc, 0x3e, 0xbf, 0x59, 0x3b, 0x57, 0x6f, 0x4b, 0xb1, 0xc8, 0x83,
|
||||
0x39, 0xea, 0x44, 0x43, 0xdb, 0x0b, 0xf5, 0x4b, 0x42, 0xc3, 0xd3, 0xe9, 0x34, 0xec, 0xf9, 0xde,
|
||||
0xc0, 0x1e, 0x1e, 0x62, 0x0f, 0x0f, 0x89, 0x4b, 0x3c, 0x76, 0x24, 0x84, 0x9b, 0xb1, 0x12, 0xf4,
|
||||
0x02, 0x5a, 0xe3, 0x28, 0x64, 0xbe, 0x6b, 0xbf, 0x20, 0x8f, 0x29, 0x5f, 0x1b, 0xea, 0x97, 0x85,
|
||||
0x37, 0x1f, 0x4d, 0xa7, 0xf8, 0x41, 0x41, 0xaa, 0x39, 0xa1, 0x87, 0x07, 0xc9, 0x38, 0xea, 0x91,
|
||||
0x1f, 0x90, 0x40, 0x44, 0xd7, 0x82, 0x0c, 0x92, 0x0c, 0x49, 0x86, 0x91, 0xad, 0x46, 0xa1, 0xbe,
|
||||
0xb8, 0x59, 0x93, 0x61, 0x94, 0x90, 0xd0, 0x16, 0x2c, 0x1e, 0x93, 0xc0, 0x1e, 0x9c, 0x3c, 0xb1,
|
||||
0x87, 0x1e, 0x66, 0x51, 0x40, 0xf4, 0x96, 0x08, 0xc5, 0x22, 0x19, 0xb9, 0x70, 0x79, 0x44, 0x1c,
|
||||
0x97, 0xbb, 0x7c, 0x2f, 0x20, 0xfd, 0x50, 0x5f, 0x12, 0xfe, 0xdd, 0x9f, 0xfe, 0x04, 0x85, 0x38,
|
||||
0x33, 0x2f, 0x9d, 0x1b, 0xe6, 0xf9, 0xa6, 0xca, 0x14, 0x99, 0x23, 0x48, 0x1a, 0x56, 0x20, 0xa3,
|
||||
0xeb, 0xb0, 0xc0, 0x02, 0x6c, 0x8d, 0x6d, 0x6f, 0x78, 0x48, 0xd8, 0xc8, 0xef, 0xeb, 0x57, 0x84,
|
||||
0x27, 0x0a, 0x54, 0x64, 0x01, 0x22, 0x1e, 0xee, 0x39, 0xa4, 0x2f, 0x63, 0xf1, 0xe9, 0x09, 0x25,
|
||||
0xa1, 0xbe, 0x2c, 0x76, 0x71, 0xab, 0x93, 0xa9, 0x50, 0x85, 0x02, 0xd1, 0xb9, 0x3b, 0xb1, 0xea,
|
||||
0xae, 0xc7, 0x82, 0x13, 0xb3, 0x44, 0x1c, 0x1a, 0xc3, 0x3c, 0xdf, 0x47, 0x1c, 0x0a, 0x2b, 0x22,
|
||||
0x14, 0xee, 0x4f, 0xe7, 0xa3, 0x83, 0x54, 0xa0, 0x99, 0x95, 0x8e, 0x3a, 0x80, 0x46, 0x38, 0x3c,
|
||||
0x8c, 0x1c, 0x66, 0x53, 0x87, 0x48, 0x33, 0x42, 0x7d, 0x55, 0xb8, 0xa9, 0x64, 0x06, 0x3d, 0x00,
|
||||
0x08, 0xc8, 0x20, 0xe6, 0x5b, 0x13, 0x3b, 0xbf, 0x79, 0xd6, 0xce, 0xcd, 0x84, 0x5b, 0xee, 0x38,
|
||||
0xb3, 0x9c, 0x2b, 0xe7, 0xdb, 0x20, 0x16, 0x53, 0xd9, 0x2e, 0xd2, 0x5a, 0x17, 0x21, 0x56, 0x32,
|
||||
0xc3, 0x63, 0x51, 0x51, 0x45, 0xd1, 0x5a, 0x97, 0xd1, 0x9a, 0x21, 0xb5, 0xef, 0xc2, 0xda, 0x29,
|
||||
0xae, 0x46, 0x2d, 0xa8, 0x8d, 0xc9, 0x89, 0x28, 0xd1, 0x4d, 0x93, 0x7f, 0xa2, 0x65, 0x98, 0x39,
|
||||
0xc6, 0x4e, 0x44, 0x44, 0x51, 0x6d, 0x98, 0x72, 0x70, 0xa7, 0xfa, 0x6d, 0xad, 0xfd, 0x6b, 0x0d,
|
||||
0x16, 0x0b, 0x86, 0x97, 0xac, 0xff, 0x49, 0x76, 0xfd, 0x39, 0x84, 0xf1, 0xe0, 0x29, 0x0e, 0x86,
|
||||
0x84, 0x65, 0x0c, 0x31, 0xfe, 0xa6, 0x81, 0x5e, 0xf0, 0xe8, 0x0f, 0x6d, 0x36, 0xba, 0x67, 0x3b,
|
||||
0x24, 0x44, 0xb7, 0x61, 0x2e, 0x90, 0x34, 0xd5, 0x78, 0xde, 0x3a, 0xe3, 0x20, 0x0e, 0x2a, 0x66,
|
||||
0xcc, 0x8d, 0x3e, 0x86, 0x86, 0x4b, 0x18, 0xee, 0x63, 0x86, 0x95, 0xed, 0x9b, 0x65, 0x2b, 0xb9,
|
||||
0x96, 0x43, 0xc5, 0x77, 0x50, 0x31, 0x93, 0x35, 0xe8, 0x7d, 0x98, 0xb1, 0x46, 0x91, 0x37, 0x16,
|
||||
0x2d, 0x67, 0x7e, 0xe7, 0xed, 0xd3, 0x16, 0xef, 0x71, 0xa6, 0x83, 0x8a, 0x29, 0xb9, 0x3f, 0x99,
|
||||
0x85, 0x3a, 0xc5, 0x01, 0x33, 0xee, 0xc1, 0x72, 0x99, 0x0a, 0xde, 0xe7, 0xac, 0x11, 0xb1, 0xc6,
|
||||
0x61, 0xe4, 0x2a, 0x37, 0x27, 0x63, 0x84, 0xa0, 0x1e, 0xda, 0x2f, 0xa4, 0xab, 0x6b, 0xa6, 0xf8,
|
||||
0x36, 0xbe, 0x05, 0x4b, 0x13, 0xda, 0xf8, 0xa1, 0x4a, 0xdb, 0xb8, 0x84, 0x4b, 0x4a, 0xb5, 0x11,
|
||||
0xc1, 0xca, 0x53, 0xe1, 0x8b, 0xa4, 0xd8, 0x5f, 0x44, 0xe7, 0x36, 0x0e, 0x60, 0xb5, 0xa8, 0x36,
|
||||
0xa4, 0xbe, 0x17, 0x12, 0x1e, 0xfa, 0xa2, 0x3a, 0xda, 0xa4, 0x9f, 0xce, 0x0a, 0x2b, 0x1a, 0x66,
|
||||
0xc9, 0x8c, 0xf1, 0x8b, 0x2a, 0xac, 0x9a, 0x24, 0xf4, 0x9d, 0x63, 0x12, 0x97, 0xae, 0x8b, 0x01,
|
||||
0x1f, 0x3f, 0x86, 0x1a, 0xa6, 0x54, 0x85, 0xc9, 0xfd, 0x73, 0x6b, 0xef, 0x26, 0x97, 0x8a, 0xde,
|
||||
0x85, 0x25, 0xec, 0xf6, 0xec, 0x61, 0xe4, 0x47, 0x61, 0xbc, 0x2d, 0x11, 0x54, 0x4d, 0x73, 0x72,
|
||||
0xc2, 0xb0, 0x60, 0x6d, 0xc2, 0x05, 0xca, 0x9d, 0x59, 0x88, 0xa4, 0x15, 0x20, 0x52, 0xa9, 0x92,
|
||||
0xea, 0x69, 0x4a, 0xfe, 0xac, 0x41, 0x2b, 0x4d, 0x1d, 0x25, 0xfe, 0x2a, 0x34, 0x5d, 0x45, 0x0b,
|
||||
0x75, 0x4d, 0xd4, 0xa7, 0x94, 0x90, 0x47, 0x4b, 0xd5, 0x22, 0x5a, 0x5a, 0x85, 0x59, 0x09, 0x66,
|
||||
0xd5, 0xc6, 0xd4, 0x28, 0x67, 0x72, 0xbd, 0x60, 0xf2, 0x06, 0x40, 0x98, 0xd4, 0x2f, 0x7d, 0x56,
|
||||
0xcc, 0x66, 0x28, 0xc8, 0x80, 0x4b, 0xb2, 0xb7, 0x9a, 0x24, 0x8c, 0x1c, 0xa6, 0xcf, 0x09, 0x8e,
|
||||
0x1c, 0xcd, 0xf0, 0x61, 0xf1, 0xa1, 0xcd, 0xf7, 0x30, 0x08, 0x2f, 0x26, 0xd8, 0x3f, 0x80, 0x3a,
|
||||
0x57, 0xc6, 0x37, 0xd6, 0x0b, 0xb0, 0x67, 0x8d, 0x48, 0xec, 0xab, 0x64, 0xcc, 0xd3, 0x98, 0xe1,
|
||||
0x61, 0xa8, 0x57, 0x05, 0x5d, 0x7c, 0x1b, 0x7f, 0xa8, 0x4a, 0x4b, 0x77, 0x29, 0x0d, 0xbf, 0x79,
|
||||
0x40, 0x5d, 0xde, 0xe2, 0x6b, 0x93, 0x2d, 0xbe, 0x60, 0xf2, 0xd7, 0x69, 0xf1, 0xe7, 0xd4, 0xa6,
|
||||
0x8c, 0x08, 0xe6, 0x76, 0x29, 0xe5, 0x86, 0xa0, 0x6d, 0xa8, 0x63, 0x4a, 0xa5, 0xc3, 0x0b, 0x15,
|
||||
0x59, 0xb1, 0xf0, 0xff, 0x95, 0x49, 0x82, 0xb5, 0x7d, 0x1b, 0x9a, 0x09, 0xe9, 0x55, 0x6a, 0x9b,
|
||||
0x59, 0xb5, 0x9b, 0x00, 0x12, 0xc3, 0xde, 0xf7, 0x06, 0x3e, 0x3f, 0x52, 0x1e, 0xec, 0x6a, 0xa9,
|
||||
0xf8, 0x36, 0xee, 0xc4, 0x1c, 0xc2, 0xb6, 0x77, 0x61, 0xc6, 0x66, 0xc4, 0x8d, 0x8d, 0x5b, 0xcd,
|
||||
0x1a, 0x97, 0x0a, 0x32, 0x25, 0x93, 0xf1, 0x97, 0x06, 0xac, 0xf3, 0x13, 0x7b, 0x22, 0xd2, 0x64,
|
||||
0x97, 0xd2, 0x4f, 0x09, 0xc3, 0xb6, 0x13, 0x7e, 0x2f, 0x22, 0xc1, 0xc9, 0x1b, 0x0e, 0x8c, 0x21,
|
||||
0xcc, 0xca, 0x2c, 0x53, 0xf5, 0xee, 0xdc, 0xaf, 0x33, 0x4a, 0x7c, 0x7a, 0x87, 0xa9, 0xbd, 0x99,
|
||||
0x3b, 0x4c, 0xd9, 0x9d, 0xa2, 0x7e, 0x41, 0x77, 0x8a, 0xd3, 0xaf, 0x95, 0x99, 0xcb, 0xea, 0x6c,
|
||||
0xfe, 0xb2, 0x5a, 0x02, 0xd5, 0xe7, 0x5e, 0x17, 0xaa, 0x37, 0x4a, 0xa1, 0xba, 0x5b, 0x9a, 0xc7,
|
||||
0x4d, 0xe1, 0xee, 0xef, 0x64, 0x23, 0xf0, 0xd4, 0x58, 0x9b, 0x06, 0xb4, 0xc3, 0x1b, 0x05, 0xed,
|
||||
0xdf, 0xcf, 0x81, 0x70, 0x79, 0x0d, 0x7e, 0xff, 0xf5, 0xf6, 0x74, 0x06, 0x1c, 0xff, 0x9f, 0x03,
|
||||
0xcf, 0xbf, 0x12, 0x98, 0x89, 0xfa, 0xa9, 0x0f, 0x92, 0x86, 0xce, 0xfb, 0x10, 0x6f, 0xad, 0xaa,
|
||||
0x68, 0xf1, 0x6f, 0x74, 0x13, 0xea, 0xdc, 0xc9, 0x0a, 0xd4, 0xae, 0x65, 0xfd, 0xc9, 0x4f, 0x62,
|
||||
0x97, 0xd2, 0x27, 0x94, 0x58, 0xa6, 0x60, 0x42, 0x77, 0xa0, 0x99, 0x04, 0xbe, 0xca, 0xac, 0xab,
|
||||
0xd9, 0x15, 0x49, 0x9e, 0xc4, 0xcb, 0x52, 0x76, 0xbe, 0xb6, 0x6f, 0x07, 0xc4, 0x12, 0x90, 0x6f,
|
||||
0x66, 0x72, 0xed, 0xa7, 0xf1, 0x64, 0xb2, 0x36, 0x61, 0x47, 0xdb, 0x30, 0x2b, 0xdf, 0x0d, 0x44,
|
||||
0x06, 0xcd, 0xef, 0xac, 0x4f, 0x16, 0xd3, 0x78, 0x95, 0x62, 0x34, 0xfe, 0xa4, 0xc1, 0x3b, 0x69,
|
||||
0x40, 0xc4, 0xd9, 0x14, 0xa3, 0xee, 0x6f, 0xbe, 0xe3, 0x5e, 0x87, 0x05, 0x01, 0xf3, 0xd3, 0xe7,
|
||||
0x03, 0xf9, 0x92, 0x55, 0xa0, 0x1a, 0xbf, 0xd7, 0xe0, 0xda, 0xe4, 0x3e, 0xf6, 0x46, 0x38, 0x60,
|
||||
0xc9, 0xf1, 0x5e, 0xc4, 0x5e, 0xe2, 0x86, 0x57, 0x4d, 0x1b, 0x5e, 0x6e, 0x7f, 0xb5, 0xfc, 0xfe,
|
||||
0x8c, 0x3f, 0x56, 0x61, 0x3e, 0x13, 0x40, 0x65, 0x0d, 0x93, 0x03, 0x3e, 0x11, 0xb7, 0xe2, 0x62,
|
||||
0x27, 0x9a, 0x42, 0xd3, 0xcc, 0x50, 0xd0, 0x18, 0x80, 0xe2, 0x00, 0xbb, 0x84, 0x91, 0x80, 0x57,
|
||||
0x72, 0x9e, 0xf1, 0x0f, 0xa6, 0xaf, 0x2e, 0x47, 0xb1, 0x4c, 0x33, 0x23, 0x9e, 0x23, 0x56, 0xa1,
|
||||
0x3a, 0x54, 0xf5, 0x5b, 0x8d, 0xd0, 0x97, 0xb0, 0x30, 0xb0, 0x1d, 0x72, 0x94, 0x1a, 0x32, 0x2b,
|
||||
0x0c, 0x79, 0x3c, 0xbd, 0x21, 0xf7, 0xb2, 0x72, 0xcd, 0x82, 0x1a, 0xe3, 0x06, 0xb4, 0x8a, 0xf9,
|
||||
0xc4, 0x8d, 0xb4, 0x5d, 0x3c, 0x4c, 0xbc, 0xa5, 0x46, 0x06, 0x82, 0x56, 0x31, 0x7f, 0x8c, 0xbf,
|
||||
0x57, 0x61, 0x25, 0x11, 0xb7, 0xeb, 0x79, 0x7e, 0xe4, 0x59, 0xe2, 0x29, 0xae, 0xf4, 0x2c, 0x96,
|
||||
0x61, 0x86, 0xd9, 0xcc, 0x49, 0x80, 0x8f, 0x18, 0xf0, 0xde, 0xc5, 0x7c, 0xdf, 0x61, 0x36, 0x55,
|
||||
0x07, 0x1c, 0x0f, 0xe5, 0xd9, 0x3f, 0x8f, 0xec, 0x80, 0xf4, 0x45, 0x25, 0x68, 0x98, 0xc9, 0x98,
|
||||
0xcf, 0x71, 0x54, 0x23, 0x60, 0xbc, 0x74, 0x66, 0x32, 0x16, 0x71, 0xef, 0x3b, 0x0e, 0xb1, 0xb8,
|
||||
0x3b, 0x32, 0x40, 0xbf, 0x40, 0x15, 0x17, 0x08, 0x16, 0xd8, 0xde, 0x50, 0xc1, 0x7c, 0x35, 0xe2,
|
||||
0x76, 0xe2, 0x20, 0xc0, 0x27, 0x7a, 0x43, 0x38, 0x40, 0x0e, 0xd0, 0x47, 0x50, 0x73, 0x31, 0x55,
|
||||
0x8d, 0xee, 0x46, 0xae, 0x3a, 0x94, 0x79, 0xa0, 0x73, 0x88, 0xa9, 0xec, 0x04, 0x7c, 0x59, 0xfb,
|
||||
0x03, 0x68, 0xc4, 0x84, 0xaf, 0x05, 0x09, 0xbf, 0x80, 0xcb, 0xb9, 0xe2, 0x83, 0x3e, 0x87, 0xd5,
|
||||
0x34, 0xa2, 0xb2, 0x0a, 0x15, 0x08, 0x7c, 0xe7, 0x95, 0x96, 0x99, 0xa7, 0x08, 0x30, 0x9e, 0xc3,
|
||||
0x12, 0x0f, 0x19, 0x91, 0xf8, 0x17, 0x74, 0xb5, 0xf9, 0x10, 0x9a, 0x89, 0xca, 0xd2, 0x98, 0x69,
|
||||
0x43, 0xe3, 0x38, 0x7e, 0x22, 0x95, 0x77, 0x9b, 0x64, 0x6c, 0xec, 0x02, 0xca, 0xda, 0xab, 0x3a,
|
||||
0xd0, 0xcd, 0x3c, 0x28, 0x5e, 0x29, 0xb6, 0x1b, 0xc1, 0x1e, 0x63, 0xe2, 0xdf, 0x55, 0x61, 0x71,
|
||||
0xdf, 0x16, 0xaf, 0x1c, 0x17, 0x54, 0xe4, 0x6e, 0x40, 0x2b, 0x8c, 0x7a, 0xae, 0xdf, 0x8f, 0x1c,
|
||||
0xa2, 0x40, 0x81, 0xea, 0xf4, 0x13, 0xf4, 0xb3, 0x8a, 0x1f, 0x77, 0x16, 0xc5, 0x6c, 0xa4, 0x6e,
|
||||
0xb8, 0xe2, 0x1b, 0x7d, 0x04, 0xeb, 0x8f, 0xc8, 0x97, 0x6a, 0x3f, 0xfb, 0x8e, 0xdf, 0xeb, 0xd9,
|
||||
0xde, 0x30, 0x56, 0x32, 0x23, 0x94, 0x9c, 0xce, 0x50, 0x06, 0x15, 0x67, 0x4b, 0xa1, 0xa2, 0xf1,
|
||||
0x4b, 0x0d, 0x5a, 0xa9, 0xd7, 0x94, 0xdf, 0x6f, 0xcb, 0xfc, 0x90, 0x5e, 0xbf, 0x96, 0xf5, 0x7a,
|
||||
0x91, 0xf5, 0x3f, 0x4f, 0x8d, 0x4b, 0xd9, 0xd4, 0xf8, 0xa7, 0x06, 0x2b, 0xfb, 0x36, 0x8b, 0x8b,
|
||||
0x92, 0xfd, 0xdf, 0x76, 0x82, 0x25, 0xfe, 0xae, 0x97, 0xfb, 0xbb, 0x03, 0xab, 0xc5, 0x8d, 0x2a,
|
||||
0xa7, 0x2f, 0xc3, 0x0c, 0x3f, 0xf9, 0xf8, 0x3d, 0x40, 0x0e, 0x76, 0xbe, 0x6a, 0xc2, 0x52, 0xda,
|
||||
0xd0, 0xf9, 0xbf, 0xb6, 0x45, 0xd0, 0x63, 0x68, 0xed, 0xab, 0xdf, 0xe3, 0xe2, 0x77, 0x18, 0x74,
|
||||
0xd6, 0xc3, 0x66, 0xfb, 0x6a, 0xf9, 0xa4, 0x54, 0x6d, 0x54, 0x90, 0x05, 0xeb, 0x45, 0x81, 0xe9,
|
||||
0x1b, 0xea, 0xff, 0x9f, 0x21, 0x39, 0xe1, 0x7a, 0x95, 0x8a, 0x2d, 0x0d, 0x7d, 0x0e, 0x0b, 0xf9,
|
||||
0x97, 0x3e, 0x94, 0xab, 0x70, 0xa5, 0x8f, 0x8f, 0x6d, 0xe3, 0x2c, 0x96, 0xc4, 0xfe, 0x67, 0x1c,
|
||||
0x4e, 0xe7, 0x9e, 0xbd, 0x90, 0x91, 0x07, 0xfb, 0x65, 0xcf, 0x82, 0xed, 0xff, 0x3b, 0x93, 0x27,
|
||||
0x91, 0xfe, 0x21, 0x34, 0xe2, 0x67, 0xa2, 0xbc, 0x9b, 0x0b, 0x8f, 0x47, 0xed, 0x56, 0x5e, 0xde,
|
||||
0x20, 0x34, 0x2a, 0xe8, 0x63, 0xb9, 0x78, 0x97, 0xd2, 0x92, 0xc5, 0x99, 0xc7, 0x91, 0xf6, 0x95,
|
||||
0x92, 0x07, 0x09, 0xa3, 0x82, 0xbe, 0x0b, 0xf3, 0xfc, 0xeb, 0x48, 0xfd, 0x12, 0xb6, 0xda, 0x91,
|
||||
0x3f, 0xbc, 0x76, 0xe2, 0x1f, 0x5e, 0x3b, 0x77, 0x5d, 0xca, 0x4e, 0xda, 0x25, 0x2f, 0x06, 0x4a,
|
||||
0xc0, 0x33, 0xb8, 0xbc, 0x4f, 0x58, 0x0a, 0xf0, 0xd1, 0xb5, 0xd7, 0xba, 0x06, 0xb5, 0x8d, 0x22,
|
||||
0xdb, 0xe4, 0x1d, 0xc1, 0xa8, 0xa0, 0xdf, 0x68, 0x70, 0x65, 0x9f, 0xb0, 0x22, 0x64, 0x46, 0xef,
|
||||
0x95, 0x2b, 0x39, 0x05, 0x5a, 0xb7, 0x1f, 0x4d, 0x9b, 0xd9, 0x79, 0xb1, 0x46, 0x05, 0xfd, 0x56,
|
||||
0x83, 0xb5, 0x8c, 0x61, 0x59, 0x0c, 0x8c, 0xb6, 0xcf, 0x36, 0xae, 0x04, 0x2f, 0xb7, 0x3f, 0x9b,
|
||||
0xf2, 0x07, 0xce, 0x8c, 0x48, 0xa3, 0x82, 0x8e, 0xc4, 0x99, 0xa4, 0x2d, 0x0f, 0xbd, 0x5d, 0xda,
|
||||
0xdb, 0x12, 0xed, 0x1b, 0xa7, 0x4d, 0x27, 0xe7, 0xf0, 0x19, 0xcc, 0xef, 0x13, 0x16, 0xd7, 0xe7,
|
||||
0x7c, 0xa4, 0x15, 0xda, 0x62, 0x3e, 0x55, 0x8b, 0x25, 0x5d, 0x44, 0xcc, 0x92, 0x94, 0x95, 0xa9,
|
||||
0x53, 0xf9, 0x5c, 0x2d, 0x2d, 0xd6, 0xf9, 0x88, 0x29, 0x2f, 0x73, 0x46, 0xe5, 0x93, 0xdd, 0xbf,
|
||||
0xbe, 0xdc, 0xd0, 0xbe, 0x7a, 0xb9, 0xa1, 0xfd, 0xe3, 0xe5, 0x86, 0xf6, 0xa3, 0x5b, 0xaf, 0xf8,
|
||||
0xab, 0x84, 0xcc, 0x1f, 0x3a, 0x60, 0x6a, 0x5b, 0x8e, 0x4d, 0x3c, 0xd6, 0x9b, 0x15, 0xc1, 0x7f,
|
||||
0xeb, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf2, 0x91, 0xe2, 0xd9, 0x07, 0x21, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@@ -4679,6 +4695,16 @@ func (m *GitFilesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if m.NoRevisionCache {
|
||||
i--
|
||||
if m.NoRevisionCache {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x30
|
||||
}
|
||||
if m.NewGitFileGlobbingEnabled {
|
||||
i--
|
||||
if m.NewGitFileGlobbingEnabled {
|
||||
@@ -4800,6 +4826,16 @@ func (m *GitDirectoriesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if m.NoRevisionCache {
|
||||
i--
|
||||
if m.NoRevisionCache {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x20
|
||||
}
|
||||
if len(m.Revision) > 0 {
|
||||
i -= len(m.Revision)
|
||||
copy(dAtA[i:], m.Revision)
|
||||
@@ -5686,6 +5722,9 @@ func (m *GitFilesRequest) Size() (n int) {
|
||||
if m.NewGitFileGlobbingEnabled {
|
||||
n += 2
|
||||
}
|
||||
if m.NoRevisionCache {
|
||||
n += 2
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
@@ -5733,6 +5772,9 @@ func (m *GitDirectoriesRequest) Size() (n int) {
|
||||
if l > 0 {
|
||||
n += 1 + l + sovRepository(uint64(l))
|
||||
}
|
||||
if m.NoRevisionCache {
|
||||
n += 2
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
@@ -10874,6 +10916,26 @@ func (m *GitFilesRequest) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
}
|
||||
m.NewGitFileGlobbingEnabled = bool(v != 0)
|
||||
case 6:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field NoRevisionCache", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowRepository
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.NoRevisionCache = bool(v != 0)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipRepository(dAtA[iNdEx:])
|
||||
@@ -11192,6 +11254,26 @@ func (m *GitDirectoriesRequest) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
m.Revision = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field NoRevisionCache", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowRepository
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.NoRevisionCache = bool(v != 0)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipRepository(dAtA[iNdEx:])
|
||||
|
||||
71
reposerver/cache/mocks/reposervercache.go
vendored
Normal file
71
reposerver/cache/mocks/reposervercache.go
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
cacheutilmocks "github.com/argoproj/argo-cd/v2/util/cache/mocks"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type MockCacheType int
|
||||
|
||||
const (
|
||||
MockCacheTypeRedis MockCacheType = iota
|
||||
MockCacheTypeInMem
|
||||
)
|
||||
|
||||
type MockRepoCache struct {
|
||||
mock.Mock
|
||||
RedisClient *cacheutilmocks.MockCacheClient
|
||||
StopRedisCallback func()
|
||||
}
|
||||
|
||||
type MockCacheOptions struct {
|
||||
RepoCacheExpiration time.Duration
|
||||
RevisionCacheExpiration time.Duration
|
||||
ReadDelay time.Duration
|
||||
WriteDelay time.Duration
|
||||
}
|
||||
|
||||
type CacheCallCounts struct {
|
||||
ExternalSets int
|
||||
ExternalGets int
|
||||
ExternalDeletes int
|
||||
}
|
||||
|
||||
// Checks that the cache was called the expected number of times
|
||||
func (mockCache *MockRepoCache) AssertCacheCalledTimes(t *testing.T, calls *CacheCallCounts) {
|
||||
mockCache.RedisClient.AssertNumberOfCalls(t, "Get", calls.ExternalGets)
|
||||
mockCache.RedisClient.AssertNumberOfCalls(t, "Set", calls.ExternalSets)
|
||||
mockCache.RedisClient.AssertNumberOfCalls(t, "Delete", calls.ExternalDeletes)
|
||||
}
|
||||
|
||||
func (mockCache *MockRepoCache) ConfigureDefaultCallbacks() {
|
||||
mockCache.RedisClient.On("Get", mock.Anything, mock.Anything).Return(nil)
|
||||
mockCache.RedisClient.On("Set", mock.Anything).Return(nil)
|
||||
mockCache.RedisClient.On("Delete", mock.Anything).Return(nil)
|
||||
}
|
||||
|
||||
func NewInMemoryRedis() (*redis.Client, func()) {
|
||||
cacheutil.NewInMemoryCache(5 * time.Second)
|
||||
mr, err := miniredis.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return redis.NewClient(&redis.Options{Addr: mr.Addr()}), mr.Close
|
||||
}
|
||||
|
||||
func NewMockRepoCache(cacheOpts *MockCacheOptions) *MockRepoCache {
|
||||
redisClient, stopRedis := NewInMemoryRedis()
|
||||
redisCacheClient := &cacheutilmocks.MockCacheClient{
|
||||
ReadDelay: cacheOpts.ReadDelay,
|
||||
WriteDelay: cacheOpts.WriteDelay,
|
||||
BaseCache: cacheutil.NewRedisCache(redisClient, cacheOpts.RepoCacheExpiration, cacheutil.RedisCompressionNone)}
|
||||
newMockCache := &MockRepoCache{RedisClient: redisCacheClient, StopRedisCallback: stopRedis}
|
||||
newMockCache.ConfigureDefaultCallbacks()
|
||||
return newMockCache
|
||||
}
|
||||
@@ -107,6 +107,7 @@ type RepoServerInitConstants struct {
|
||||
StreamedManifestMaxExtractedSize int64
|
||||
StreamedManifestMaxTarSize int64
|
||||
HelmManifestMaxExtractedSize int64
|
||||
HelmRegistryMaxIndexSize int64
|
||||
DisableHelmManifestMaxExtractedSize bool
|
||||
}
|
||||
|
||||
@@ -300,6 +301,7 @@ func (s *Service) runRepoOperation(
|
||||
var gitClient git.Client
|
||||
var helmClient helm.Client
|
||||
var err error
|
||||
gitClientOpts := git.WithCache(s.cache, !settings.noRevisionCache && !settings.noCache)
|
||||
revision = textutils.FirstNonEmpty(revision, source.TargetRevision)
|
||||
unresolvedRevision := revision
|
||||
if source.IsHelm() {
|
||||
@@ -308,13 +310,13 @@ func (s *Service) runRepoOperation(
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
gitClient, revision, err = s.newClientResolveRevision(repo, revision, git.WithCache(s.cache, !settings.noRevisionCache && !settings.noCache))
|
||||
gitClient, revision, err = s.newClientResolveRevision(repo, revision, gitClientOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
repoRefs, err := resolveReferencedSources(hasMultipleSources, source.Helm, refSources, s.newClientResolveRevision)
|
||||
repoRefs, err := resolveReferencedSources(hasMultipleSources, source.Helm, refSources, s.newClientResolveRevision, gitClientOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -463,7 +465,7 @@ type gitClientGetter func(repo *v1alpha1.Repository, revision string, opts ...gi
|
||||
//
|
||||
// Much of this logic is duplicated in runManifestGenAsync. If making changes here, check whether runManifestGenAsync
|
||||
// should be updated.
|
||||
func resolveReferencedSources(hasMultipleSources bool, source *v1alpha1.ApplicationSourceHelm, refSources map[string]*v1alpha1.RefTarget, newClientResolveRevision gitClientGetter) (map[string]string, error) {
|
||||
func resolveReferencedSources(hasMultipleSources bool, source *v1alpha1.ApplicationSourceHelm, refSources map[string]*v1alpha1.RefTarget, newClientResolveRevision gitClientGetter, gitClientOpts git.ClientOpts) (map[string]string, error) {
|
||||
repoRefs := make(map[string]string)
|
||||
if !hasMultipleSources || source == nil {
|
||||
return repoRefs, nil
|
||||
@@ -490,7 +492,7 @@ func resolveReferencedSources(hasMultipleSources bool, source *v1alpha1.Applicat
|
||||
normalizedRepoURL := git.NormalizeGitURL(refSourceMapping.Repo.Repo)
|
||||
_, ok = repoRefs[normalizedRepoURL]
|
||||
if !ok {
|
||||
_, referencedCommitSHA, err := newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision)
|
||||
_, referencedCommitSHA, err := newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision, gitClientOpts)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get git client for repo %s: %v", refSourceMapping.Repo.Repo, err)
|
||||
return nil, fmt.Errorf("failed to get git client for repo %s", refSourceMapping.Repo.Repo)
|
||||
@@ -728,7 +730,7 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA,
|
||||
return
|
||||
}
|
||||
} else {
|
||||
gitClient, referencedCommitSHA, err := s.newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision)
|
||||
gitClient, referencedCommitSHA, err := s.newClientResolveRevision(&refSourceMapping.Repo, refSourceMapping.TargetRevision, git.WithCache(s.cache, !q.NoRevisionCache && !q.NoCache))
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get git client for repo %s: %v", refSourceMapping.Repo.Repo, err)
|
||||
ch.errCh <- fmt.Errorf("failed to get git client for repo %s", refSourceMapping.Repo.Repo)
|
||||
@@ -1021,6 +1023,10 @@ func getHelmDependencyRepos(appPath string) ([]*v1alpha1.Repository, error) {
|
||||
repos = append(repos, &v1alpha1.Repository{
|
||||
Name: r.Repository[1:],
|
||||
})
|
||||
} else if strings.HasPrefix(r.Repository, "alias:") {
|
||||
repos = append(repos, &v1alpha1.Repository{
|
||||
Name: strings.TrimPrefix(r.Repository, "alias:"),
|
||||
})
|
||||
} else if u, err := url.Parse(r.Repository); err == nil && (u.Scheme == "https" || u.Scheme == "oci") {
|
||||
repo := &v1alpha1.Repository{
|
||||
// trimming oci:// prefix since it is currently not supported by Argo CD (OCI repos just have no scheme)
|
||||
@@ -1373,7 +1379,7 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string,
|
||||
if q.KustomizeOptions != nil {
|
||||
kustomizeBinary = q.KustomizeOptions.BinaryPath
|
||||
}
|
||||
k := kustomize.NewKustomizeApp(appPath, q.Repo.GetGitCreds(gitCredsStore), repoURL, kustomizeBinary)
|
||||
k := kustomize.NewKustomizeApp(repoRoot, appPath, q.Repo.GetGitCreds(gitCredsStore), repoURL, kustomizeBinary)
|
||||
targetObjs, _, err = k.Build(q.ApplicationSource.Kustomize, q.KustomizeOptions, env)
|
||||
case v1alpha1.ApplicationSourceTypePlugin:
|
||||
pluginName := ""
|
||||
@@ -1960,7 +1966,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD
|
||||
return err
|
||||
}
|
||||
case v1alpha1.ApplicationSourceTypeKustomize:
|
||||
if err := populateKustomizeAppDetails(res, q, opContext.appPath, commitSHA, s.gitCredsStore); err != nil {
|
||||
if err := populateKustomizeAppDetails(res, q, repoRoot, opContext.appPath, commitSHA, s.gitCredsStore); err != nil {
|
||||
return err
|
||||
}
|
||||
case v1alpha1.ApplicationSourceTypePlugin:
|
||||
@@ -2101,13 +2107,13 @@ func findHelmValueFilesInPath(path string) ([]string, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func populateKustomizeAppDetails(res *apiclient.RepoAppDetailsResponse, q *apiclient.RepoServerAppDetailsQuery, appPath string, reversion string, credsStore git.CredsStore) error {
|
||||
func populateKustomizeAppDetails(res *apiclient.RepoAppDetailsResponse, q *apiclient.RepoServerAppDetailsQuery, repoRoot string, appPath string, reversion string, credsStore git.CredsStore) error {
|
||||
res.Kustomize = &apiclient.KustomizeAppSpec{}
|
||||
kustomizeBinary := ""
|
||||
if q.KustomizeOptions != nil {
|
||||
kustomizeBinary = q.KustomizeOptions.BinaryPath
|
||||
}
|
||||
k := kustomize.NewKustomizeApp(appPath, q.Repo.GetGitCreds(credsStore), q.Repo.Repo, kustomizeBinary)
|
||||
k := kustomize.NewKustomizeApp(repoRoot, appPath, q.Repo.GetGitCreds(credsStore), q.Repo.Repo, kustomizeBinary)
|
||||
fakeManifestRequest := apiclient.ManifestRequest{
|
||||
AppName: q.AppName,
|
||||
Namespace: "", // FIXME: omit it for now
|
||||
@@ -2340,7 +2346,7 @@ func (s *Service) newHelmClientResolveRevision(repo *v1alpha1.Repository, revisi
|
||||
return helmClient, version.String(), nil
|
||||
}
|
||||
|
||||
index, err := helmClient.GetIndex(noRevisionCache)
|
||||
index, err := helmClient.GetIndex(noRevisionCache, s.initConstants.HelmRegistryMaxIndexSize)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
@@ -2418,7 +2424,7 @@ func checkoutRevision(gitClient git.Client, revision string, submoduleEnabled bo
|
||||
}
|
||||
|
||||
func (s *Service) GetHelmCharts(ctx context.Context, q *apiclient.HelmChartsRequest) (*apiclient.HelmChartsResponse, error) {
|
||||
index, err := s.newHelmClient(q.Repo.Repo, q.Repo.GetHelmCreds(), q.Repo.EnableOCI, q.Repo.Proxy, helm.WithChartPaths(s.chartPaths)).GetIndex(true)
|
||||
index, err := s.newHelmClient(q.Repo.Repo, q.Repo.GetHelmCreds(), q.Repo.EnableOCI, q.Repo.Proxy, helm.WithChartPaths(s.chartPaths)).GetIndex(true, s.initConstants.HelmRegistryMaxIndexSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -2453,7 +2459,7 @@ func (s *Service) TestRepository(ctx context.Context, q *apiclient.TestRepositor
|
||||
_, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).TestHelmOCI()
|
||||
return err
|
||||
} else {
|
||||
_, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).GetIndex(false)
|
||||
_, err := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI, repo.Proxy).GetIndex(false, s.initConstants.HelmRegistryMaxIndexSize)
|
||||
return err
|
||||
}
|
||||
},
|
||||
@@ -2505,6 +2511,7 @@ func (s *Service) GetGitFiles(_ context.Context, request *apiclient.GitFilesRequ
|
||||
repo := request.GetRepo()
|
||||
revision := request.GetRevision()
|
||||
gitPath := request.GetPath()
|
||||
noRevisionCache := request.GetNoRevisionCache()
|
||||
enableNewGitFileGlobbing := request.GetNewGitFileGlobbingEnabled()
|
||||
if gitPath == "" {
|
||||
gitPath = "."
|
||||
@@ -2514,7 +2521,7 @@ func (s *Service) GetGitFiles(_ context.Context, request *apiclient.GitFilesRequ
|
||||
return nil, status.Error(codes.InvalidArgument, "must pass a valid repo")
|
||||
}
|
||||
|
||||
gitClient, revision, err := s.newClientResolveRevision(repo, revision, git.WithCache(s.cache, true))
|
||||
gitClient, revision, err := s.newClientResolveRevision(repo, revision, git.WithCache(s.cache, !noRevisionCache))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "unable to resolve git revision %s: %v", revision, err)
|
||||
}
|
||||
@@ -2567,12 +2574,12 @@ func (s *Service) GetGitFiles(_ context.Context, request *apiclient.GitFilesRequ
|
||||
func (s *Service) GetGitDirectories(_ context.Context, request *apiclient.GitDirectoriesRequest) (*apiclient.GitDirectoriesResponse, error) {
|
||||
repo := request.GetRepo()
|
||||
revision := request.GetRevision()
|
||||
|
||||
noRevisionCache := request.GetNoRevisionCache()
|
||||
if repo == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "must pass a valid repo")
|
||||
}
|
||||
|
||||
gitClient, revision, err := s.newClientResolveRevision(repo, revision, git.WithCache(s.cache, true))
|
||||
gitClient, revision, err := s.newClientResolveRevision(repo, revision, git.WithCache(s.cache, !noRevisionCache))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "unable to resolve git revision %s: %v", revision, err)
|
||||
}
|
||||
|
||||
@@ -236,6 +236,7 @@ message GitFilesRequest {
|
||||
string revision = 3;
|
||||
string path = 4;
|
||||
bool NewGitFileGlobbingEnabled = 5;
|
||||
bool noRevisionCache = 6;
|
||||
}
|
||||
|
||||
message GitFilesResponse {
|
||||
@@ -247,6 +248,7 @@ message GitDirectoriesRequest {
|
||||
github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.Repository repo = 1;
|
||||
bool submoduleEnabled = 2;
|
||||
string revision = 3;
|
||||
bool noRevisionCache = 4;
|
||||
}
|
||||
|
||||
message GitDirectoriesResponse {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
7
reposerver/repository/testdata/helm-with-dependencies-alias/Chart.yaml
vendored
Normal file
7
reposerver/repository/testdata/helm-with-dependencies-alias/Chart.yaml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
apiVersion: v2
|
||||
name: helm-with-dependencies-alias
|
||||
version: v1.0.0
|
||||
dependencies:
|
||||
- name: helm
|
||||
repository: "alias:custom-repo-alias"
|
||||
version: v1.0.0
|
||||
@@ -38,12 +38,18 @@ if job.metadata == nil then
|
||||
end
|
||||
job.metadata.name = obj.metadata.name .. "-" ..os.date("!%Y%m%d%H%M")
|
||||
job.metadata.namespace = obj.metadata.namespace
|
||||
if job.metadata.annotations == nil then
|
||||
job.metadata.annotations = {}
|
||||
end
|
||||
job.metadata.annotations['cronjob.kubernetes.io/instantiate'] = "manual"
|
||||
|
||||
local ownerRef = {}
|
||||
ownerRef.apiVersion = obj.apiVersion
|
||||
ownerRef.kind = obj.kind
|
||||
ownerRef.name = obj.metadata.name
|
||||
ownerRef.uid = obj.metadata.uid
|
||||
ownerRef.blockOwnerDeletion = true
|
||||
ownerRef.controller = true
|
||||
job.metadata.ownerReferences = {}
|
||||
job.metadata.ownerReferences[1] = ownerRef
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
labels:
|
||||
my: label
|
||||
annotations:
|
||||
cronjob.kubernetes.io/instantiate: manual
|
||||
my: annotation
|
||||
spec:
|
||||
ttlSecondsAfterFinished: 100
|
||||
@@ -26,4 +27,4 @@
|
||||
- /bin/sh
|
||||
- -c
|
||||
- date; echo Hello from the Kubernetes cluster
|
||||
restartPolicy: OnFailure
|
||||
restartPolicy: OnFailure
|
||||
|
||||
@@ -148,7 +148,7 @@ func NewServer(
|
||||
//
|
||||
// If the user does provide a "project," we can respond more specifically. If the user does not have access to the given
|
||||
// app name in the given project, we return "permission denied." If the app exists, but the project is different from
|
||||
func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespace, name string, getApp func() (*appv1.Application, error)) (*appv1.Application, error) {
|
||||
func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespace, name string, getApp func() (*appv1.Application, error)) (*appv1.Application, *appv1.AppProject, error) {
|
||||
logCtx := log.WithFields(map[string]interface{}{
|
||||
"application": name,
|
||||
"namespace": namespace,
|
||||
@@ -165,7 +165,7 @@ func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespa
|
||||
// but the app is in a different project" response. We don't want the user inferring the existence of the
|
||||
// app from response time.
|
||||
_, _ = getApp()
|
||||
return nil, permissionDeniedErr
|
||||
return nil, nil, permissionDeniedErr
|
||||
}
|
||||
}
|
||||
a, err := getApp()
|
||||
@@ -173,15 +173,15 @@ func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespa
|
||||
if apierr.IsNotFound(err) {
|
||||
if project != "" {
|
||||
// We know that the user was allowed to get the Application, but the Application does not exist. Return 404.
|
||||
return nil, status.Errorf(codes.NotFound, apierr.NewNotFound(schema.GroupResource{Group: "argoproj.io", Resource: "applications"}, name).Error())
|
||||
return nil, nil, status.Errorf(codes.NotFound, apierr.NewNotFound(schema.GroupResource{Group: "argoproj.io", Resource: "applications"}, name).Error())
|
||||
}
|
||||
// We don't know if the user was allowed to get the Application, and we don't want to leak information about
|
||||
// the Application's existence. Return 403.
|
||||
logCtx.Warn("application does not exist")
|
||||
return nil, permissionDeniedErr
|
||||
return nil, nil, permissionDeniedErr
|
||||
}
|
||||
logCtx.Errorf("failed to get application: %s", err)
|
||||
return nil, permissionDeniedErr
|
||||
return nil, nil, permissionDeniedErr
|
||||
}
|
||||
// Even if we performed an initial RBAC check (because the request was fully parameterized), we still need to
|
||||
// perform a second RBAC check to ensure that the user has access to the actual Application's project (not just the
|
||||
@@ -195,11 +195,11 @@ func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespa
|
||||
// The user specified a project. We would have returned a 404 if the user had access to the app, but the app
|
||||
// did not exist. So we have to return a 404 when the app does exist, but the user does not have access.
|
||||
// Otherwise, they could infer that the app exists based on the error code.
|
||||
return nil, status.Errorf(codes.NotFound, apierr.NewNotFound(schema.GroupResource{Group: "argoproj.io", Resource: "applications"}, name).Error())
|
||||
return nil, nil, status.Errorf(codes.NotFound, apierr.NewNotFound(schema.GroupResource{Group: "argoproj.io", Resource: "applications"}, name).Error())
|
||||
}
|
||||
// The user didn't specify a project. We always return permission denied for both lack of access and lack of
|
||||
// existence.
|
||||
return nil, permissionDeniedErr
|
||||
return nil, nil, permissionDeniedErr
|
||||
}
|
||||
effectiveProject := "default"
|
||||
if a.Spec.Project != "" {
|
||||
@@ -212,15 +212,20 @@ func (s *Server) getAppEnforceRBAC(ctx context.Context, action, project, namespa
|
||||
}).Warnf("user tried to %s application in project %s, but the application is in project %s", action, project, effectiveProject)
|
||||
// The user has access to the app, but the app is in a different project. Return 404, meaning "app doesn't
|
||||
// exist in that project".
|
||||
return nil, status.Errorf(codes.NotFound, apierr.NewNotFound(schema.GroupResource{Group: "argoproj.io", Resource: "applications"}, name).Error())
|
||||
return nil, nil, status.Errorf(codes.NotFound, apierr.NewNotFound(schema.GroupResource{Group: "argoproj.io", Resource: "applications"}, name).Error())
|
||||
}
|
||||
return a, nil
|
||||
// Get the app's associated project, and make sure all project restrictions are enforced.
|
||||
proj, err := s.getAppProject(ctx, a, logCtx)
|
||||
if err != nil {
|
||||
return a, nil, err
|
||||
}
|
||||
return a, proj, nil
|
||||
}
|
||||
|
||||
// getApplicationEnforceRBACInformer uses an informer to get an Application. If the app does not exist, permission is
|
||||
// denied, or any other error occurs when getting the app, we return a permission denied error to obscure any sensitive
|
||||
// information.
|
||||
func (s *Server) getApplicationEnforceRBACInformer(ctx context.Context, action, project, namespace, name string) (*appv1.Application, error) {
|
||||
func (s *Server) getApplicationEnforceRBACInformer(ctx context.Context, action, project, namespace, name string) (*appv1.Application, *appv1.AppProject, error) {
|
||||
namespaceOrDefault := s.appNamespaceOrDefault(namespace)
|
||||
return s.getAppEnforceRBAC(ctx, action, project, namespaceOrDefault, name, func() (*appv1.Application, error) {
|
||||
return s.appLister.Applications(namespaceOrDefault).Get(name)
|
||||
@@ -230,7 +235,7 @@ func (s *Server) getApplicationEnforceRBACInformer(ctx context.Context, action,
|
||||
// getApplicationEnforceRBACClient uses a client to get an Application. If the app does not exist, permission is denied,
|
||||
// or any other error occurs when getting the app, we return a permission denied error to obscure any sensitive
|
||||
// information.
|
||||
func (s *Server) getApplicationEnforceRBACClient(ctx context.Context, action, project, namespace, name, resourceVersion string) (*appv1.Application, error) {
|
||||
func (s *Server) getApplicationEnforceRBACClient(ctx context.Context, action, project, namespace, name, resourceVersion string) (*appv1.Application, *appv1.AppProject, error) {
|
||||
namespaceOrDefault := s.appNamespaceOrDefault(namespace)
|
||||
return s.getAppEnforceRBAC(ctx, action, project, namespaceOrDefault, name, func() (*appv1.Application, error) {
|
||||
if !s.isNamespaceEnabled(namespaceOrDefault) {
|
||||
@@ -314,7 +319,13 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq
|
||||
if q.Validate != nil {
|
||||
validate = *q.Validate
|
||||
}
|
||||
err := s.validateAndNormalizeApp(ctx, a, validate)
|
||||
|
||||
proj, err := s.getAppProject(ctx, a, log.WithField("application", a.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.validateAndNormalizeApp(ctx, a, proj, validate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while validating and normalizing app: %w", err)
|
||||
}
|
||||
@@ -325,6 +336,15 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq
|
||||
return nil, security.NamespaceNotPermittedError(appNs)
|
||||
}
|
||||
|
||||
// Don't let the app creator set the operation explicitly. Those requests should always go through the Sync API.
|
||||
if a.Operation != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"application": a.Name,
|
||||
argocommon.SecurityField: argocommon.SecurityLow,
|
||||
}).Warn("User attempted to set operation on application creation. This could have allowed them to bypass branch protection rules by setting manifests directly. Ignoring the set operation.")
|
||||
a.Operation = nil
|
||||
}
|
||||
|
||||
created, err := s.appclientset.ArgoprojV1alpha1().Applications(appNs).Create(ctx, a, metav1.CreateOptions{})
|
||||
if err == nil {
|
||||
s.logAppEvent(created, ctx, argo.EventReasonResourceCreated, "created application")
|
||||
@@ -361,7 +381,7 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
func (s *Server) queryRepoServer(ctx context.Context, a *appv1.Application, action func(
|
||||
func (s *Server) queryRepoServer(ctx context.Context, a *appv1.Application, proj *appv1.AppProject, action func(
|
||||
client apiclient.RepoServerServiceClient,
|
||||
repo *appv1.Repository,
|
||||
helmRepos []*appv1.Repository,
|
||||
@@ -388,13 +408,6 @@ func (s *Server) queryRepoServer(ctx context.Context, a *appv1.Application, acti
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting kustomize settings options: %w", err)
|
||||
}
|
||||
proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx)
|
||||
if err != nil {
|
||||
if apierr.IsNotFound(err) {
|
||||
return status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", a.Spec.Project)
|
||||
}
|
||||
return fmt.Errorf("error getting application's project: %w", err)
|
||||
}
|
||||
|
||||
helmRepos, err := s.db.ListHelmRepositories(ctx)
|
||||
if err != nil {
|
||||
@@ -429,7 +442,7 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
|
||||
if q.Name == nil || *q.Name == "" {
|
||||
return nil, fmt.Errorf("invalid request: application name is missing")
|
||||
}
|
||||
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
a, proj, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -441,7 +454,7 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
|
||||
}
|
||||
|
||||
var manifestInfo *apiclient.ManifestResponse
|
||||
err = s.queryRepoServer(ctx, a, func(
|
||||
err = s.queryRepoServer(ctx, a, proj, func(
|
||||
client apiclient.RepoServerServiceClient, repo *appv1.Repository, helmRepos []*appv1.Repository, helmCreds []*appv1.RepoCreds, helmOptions *appv1.HelmOptions, kustomizeOptions *appv1.KustomizeOptions, enableGenerateManifests map[string]bool) error {
|
||||
revision := source.TargetRevision
|
||||
if q.GetRevision() != "" {
|
||||
@@ -467,11 +480,6 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
|
||||
return fmt.Errorf("error getting API resources: %w", err)
|
||||
}
|
||||
|
||||
proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting app project: %w", err)
|
||||
}
|
||||
|
||||
manifestInfo, err = client.GenerateManifest(ctx, &apiclient.ManifestRequest{
|
||||
Repo: repo,
|
||||
Revision: revision,
|
||||
@@ -534,13 +542,13 @@ func (s *Server) GetManifestsWithFiles(stream application.ApplicationService_Get
|
||||
return fmt.Errorf("invalid request: application name is missing")
|
||||
}
|
||||
|
||||
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, query.GetProject(), query.GetAppNamespace(), query.GetName())
|
||||
a, proj, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, query.GetProject(), query.GetAppNamespace(), query.GetName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var manifestInfo *apiclient.ManifestResponse
|
||||
err = s.queryRepoServer(ctx, a, func(
|
||||
err = s.queryRepoServer(ctx, a, proj, func(
|
||||
client apiclient.RepoServerServiceClient, repo *appv1.Repository, helmRepos []*appv1.Repository, helmCreds []*appv1.RepoCreds, helmOptions *appv1.HelmOptions, kustomizeOptions *appv1.KustomizeOptions, enableGenerateManifests map[string]bool) error {
|
||||
|
||||
appInstanceLabelKey, err := s.settingsMgr.GetAppInstanceLabelKey()
|
||||
@@ -651,7 +659,7 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app
|
||||
// We must use a client Get instead of an informer Get, because it's common to call Get immediately
|
||||
// following a Watch (which is not yet powered by an informer), and the Get must reflect what was
|
||||
// previously seen by the client.
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, project, appNs, appName, q.GetResourceVersion())
|
||||
a, proj, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, project, appNs, appName, q.GetResourceVersion())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -682,7 +690,7 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app
|
||||
|
||||
if refreshType == appv1.RefreshTypeHard {
|
||||
// force refresh cached application details
|
||||
if err := s.queryRepoServer(ctx, a, func(
|
||||
if err := s.queryRepoServer(ctx, a, proj, func(
|
||||
client apiclient.RepoServerServiceClient,
|
||||
repo *appv1.Repository,
|
||||
helmRepos []*appv1.Repository,
|
||||
@@ -734,7 +742,7 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app
|
||||
|
||||
// ListResourceEvents returns a list of event resources
|
||||
func (s *Server) ListResourceEvents(ctx context.Context, q *application.ApplicationResourceEventsQuery) (*v1.EventList, error) {
|
||||
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
a, _, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -802,12 +810,12 @@ func (s *Server) validateAndUpdateApp(ctx context.Context, newApp *appv1.Applica
|
||||
s.projectLock.RLock(newApp.Spec.GetProject())
|
||||
defer s.projectLock.RUnlock(newApp.Spec.GetProject())
|
||||
|
||||
app, err := s.getApplicationEnforceRBACClient(ctx, action, currentProject, newApp.Namespace, newApp.Name, "")
|
||||
app, proj, err := s.getApplicationEnforceRBACClient(ctx, action, currentProject, newApp.Namespace, newApp.Name, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.validateAndNormalizeApp(ctx, newApp, validate)
|
||||
err = s.validateAndNormalizeApp(ctx, newApp, proj, validate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error validating and normalizing app: %w", err)
|
||||
}
|
||||
@@ -906,7 +914,7 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat
|
||||
if q.GetSpec() == nil {
|
||||
return nil, fmt.Errorf("error updating application spec: spec is nil in request")
|
||||
}
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionUpdate, q.GetProject(), q.GetAppNamespace(), q.GetName(), "")
|
||||
a, _, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionUpdate, q.GetProject(), q.GetAppNamespace(), q.GetName(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -925,7 +933,7 @@ func (s *Server) UpdateSpec(ctx context.Context, q *application.ApplicationUpdat
|
||||
|
||||
// Patch patches an application
|
||||
func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchRequest) (*appv1.Application, error) {
|
||||
app, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName(), "")
|
||||
app, _, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -968,11 +976,35 @@ func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchReque
|
||||
return s.validateAndUpdateApp(ctx, newApp, false, true, rbacpolicy.ActionUpdate, q.GetProject())
|
||||
}
|
||||
|
||||
func (s *Server) getAppProject(ctx context.Context, a *appv1.Application, logCtx *log.Entry) (*appv1.AppProject, error) {
|
||||
proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx)
|
||||
if err == nil {
|
||||
return proj, nil
|
||||
}
|
||||
|
||||
// If there's a permission issue or the app doesn't exist, return a vague error to avoid letting the user enumerate project names.
|
||||
vagueError := status.Errorf(codes.InvalidArgument, "app is not allowed in project %q, or the project does not exist", a.Spec.Project)
|
||||
|
||||
if apierr.IsNotFound(err) {
|
||||
return nil, vagueError
|
||||
}
|
||||
|
||||
if _, ok := err.(*appv1.ErrApplicationNotAllowedToUseProject); ok {
|
||||
logCtx.WithFields(map[string]interface{}{
|
||||
"project": a.Spec.Project,
|
||||
argocommon.SecurityField: argocommon.SecurityMedium,
|
||||
}).Warnf("error getting app project: %s", err)
|
||||
return nil, vagueError
|
||||
}
|
||||
|
||||
return nil, vagueError
|
||||
}
|
||||
|
||||
// Delete removes an application and all associated resources
|
||||
func (s *Server) Delete(ctx context.Context, q *application.ApplicationDeleteRequest) (*application.ApplicationResponse, error) {
|
||||
appName := q.GetName()
|
||||
appNs := s.appNamespaceOrDefault(q.GetAppNamespace())
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetProject(), appNs, appName, "")
|
||||
a, _, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetProject(), appNs, appName, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1127,16 +1159,7 @@ func (s *Server) Watch(q *application.ApplicationQuery, ws application.Applicati
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Application, validate bool) error {
|
||||
proj, err := argo.GetAppProject(app, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx)
|
||||
if err != nil {
|
||||
if apierr.IsNotFound(err) {
|
||||
// Offer no hint that the project does not exist.
|
||||
log.Warnf("User attempted to create/update application in non-existent project %q", app.Spec.Project)
|
||||
return permissionDeniedErr
|
||||
}
|
||||
return fmt.Errorf("error getting application's project: %w", err)
|
||||
}
|
||||
func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Application, proj *appv1.AppProject, validate bool) error {
|
||||
if app.GetName() == "" {
|
||||
return fmt.Errorf("resource name may not be empty")
|
||||
}
|
||||
@@ -1240,7 +1263,7 @@ func (s *Server) getAppResources(ctx context.Context, a *appv1.Application) (*ap
|
||||
}
|
||||
|
||||
func (s *Server) getAppLiveResource(ctx context.Context, action string, q *application.ApplicationResourceRequest) (*appv1.ResourceNode, *rest.Config, *appv1.Application, error) {
|
||||
a, err := s.getApplicationEnforceRBACInformer(ctx, action, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
a, _, err := s.getApplicationEnforceRBACInformer(ctx, action, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
@@ -1377,7 +1400,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.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetApplicationName())
|
||||
a, _, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetApplicationName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1386,7 +1409,7 @@ func (s *Server) ResourceTree(ctx context.Context, q *application.ResourcesQuery
|
||||
}
|
||||
|
||||
func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application.ApplicationService_WatchResourceTreeServer) error {
|
||||
_, err := s.getApplicationEnforceRBACInformer(ws.Context(), rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetApplicationName())
|
||||
_, _, err := s.getApplicationEnforceRBACInformer(ws.Context(), rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetApplicationName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1403,7 +1426,7 @@ func (s *Server) WatchResourceTree(q *application.ResourcesQuery, ws application
|
||||
}
|
||||
|
||||
func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMetadataQuery) (*appv1.RevisionMetadata, error) {
|
||||
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
a, proj, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1413,12 +1436,6 @@ func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMe
|
||||
if err != nil {
|
||||
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, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting app project: %w", err)
|
||||
}
|
||||
conn, repoClient, err := s.repoClientset.NewRepoServerClient()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating repo server client: %w", err)
|
||||
@@ -1433,7 +1450,7 @@ func (s *Server) RevisionMetadata(ctx context.Context, q *application.RevisionMe
|
||||
|
||||
// RevisionChartDetails returns the helm chart metadata, as fetched from the reposerver
|
||||
func (s *Server) RevisionChartDetails(ctx context.Context, q *application.RevisionMetadataQuery) (*appv1.ChartDetails, error) {
|
||||
a, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
a, _, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1464,7 +1481,7 @@ 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.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetApplicationName())
|
||||
a, _, err := s.getApplicationEnforceRBACInformer(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetApplicationName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1521,7 +1538,7 @@ func (s *Server) PodLogs(q *application.ApplicationPodLogsQuery, ws application.
|
||||
}
|
||||
}
|
||||
|
||||
a, err := s.getApplicationEnforceRBACInformer(ws.Context(), rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
a, _, err := s.getApplicationEnforceRBACInformer(ws.Context(), rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1713,19 +1730,11 @@ func isTheSelectedOne(currentNode *appv1.ResourceNode, q *application.Applicatio
|
||||
|
||||
// Sync syncs an application to its target state
|
||||
func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncRequest) (*appv1.Application, error) {
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, syncReq.GetProject(), syncReq.GetAppNamespace(), syncReq.GetName(), "")
|
||||
a, proj, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, syncReq.GetProject(), syncReq.GetAppNamespace(), syncReq.GetName(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx)
|
||||
if err != nil {
|
||||
if apierr.IsNotFound(err) {
|
||||
return a, status.Errorf(codes.InvalidArgument, "application references project %s which does not exist", a.Spec.Project)
|
||||
}
|
||||
return a, fmt.Errorf("error getting app project: %w", err)
|
||||
}
|
||||
|
||||
s.inferResourcesStatusHealth(a)
|
||||
|
||||
if !proj.Spec.SyncWindows.Matches(a).CanSync(true) {
|
||||
@@ -1822,7 +1831,7 @@ func (s *Server) Sync(ctx context.Context, syncReq *application.ApplicationSyncR
|
||||
}
|
||||
|
||||
func (s *Server) Rollback(ctx context.Context, rollbackReq *application.ApplicationRollbackRequest) (*appv1.Application, error) {
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, rollbackReq.GetProject(), rollbackReq.GetAppNamespace(), rollbackReq.GetName(), "")
|
||||
a, _, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, rollbackReq.GetProject(), rollbackReq.GetAppNamespace(), rollbackReq.GetName(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1881,7 +1890,7 @@ func (s *Server) Rollback(ctx context.Context, rollbackReq *application.Applicat
|
||||
}
|
||||
|
||||
func (s *Server) ListLinks(ctx context.Context, req *application.ListAppLinksRequest) (*application.LinksResponse, error) {
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, req.GetProject(), req.GetNamespace(), req.GetName(), "")
|
||||
a, proj, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, req.GetProject(), req.GetNamespace(), req.GetName(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1896,7 +1905,7 @@ func (s *Server) ListLinks(ctx context.Context, req *application.ListAppLinksReq
|
||||
return nil, fmt.Errorf("failed to read application deep links from configmap: %w", err)
|
||||
}
|
||||
|
||||
clstObj, _, err := s.getObjectsForDeepLinks(ctx, a)
|
||||
clstObj, _, err := s.getObjectsForDeepLinks(ctx, a, proj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1911,12 +1920,7 @@ func (s *Server) ListLinks(ctx context.Context, req *application.ListAppLinksReq
|
||||
return finalList, nil
|
||||
}
|
||||
|
||||
func (s *Server) getObjectsForDeepLinks(ctx context.Context, app *appv1.Application) (cluster *unstructured.Unstructured, project *unstructured.Unstructured, err error) {
|
||||
proj, err := argo.GetAppProject(app, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error getting app project: %w", err)
|
||||
}
|
||||
|
||||
func (s *Server) getObjectsForDeepLinks(ctx context.Context, app *appv1.Application, proj *appv1.AppProject) (cluster *unstructured.Unstructured, project *unstructured.Unstructured, err error) {
|
||||
// sanitize project jwt tokens
|
||||
proj.Status = appv1.AppProjectStatus{}
|
||||
|
||||
@@ -1979,7 +1983,12 @@ func (s *Server) ListResourceLinks(ctx context.Context, req *application.Applica
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clstObj, projObj, err := s.getObjectsForDeepLinks(ctx, app)
|
||||
proj, err := s.getAppProject(ctx, app, log.WithField("application", app.GetName()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clstObj, projObj, err := s.getObjectsForDeepLinks(ctx, app, proj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -2035,7 +2044,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) {
|
||||
appName := termOpReq.GetName()
|
||||
appNs := s.appNamespaceOrDefault(termOpReq.GetAppNamespace())
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, termOpReq.GetProject(), appNs, appName, "")
|
||||
a, _, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionSync, termOpReq.GetProject(), appNs, appName, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -2108,7 +2117,7 @@ func (s *Server) ListResourceActions(ctx context.Context, q *application.Applica
|
||||
|
||||
func (s *Server) getUnstructuredLiveResourceOrApp(ctx context.Context, rbacRequest string, q *application.ApplicationResourceRequest) (obj *unstructured.Unstructured, res *appv1.ResourceNode, app *appv1.Application, config *rest.Config, err error) {
|
||||
if q.GetKind() == applicationType.ApplicationKind && q.GetGroup() == applicationType.Group && q.GetName() == q.GetResourceName() {
|
||||
app, err = s.getApplicationEnforceRBACInformer(ctx, rbacRequest, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
app, _, err = s.getApplicationEnforceRBACInformer(ctx, rbacRequest, q.GetProject(), q.GetAppNamespace(), q.GetName())
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
@@ -2204,6 +2213,11 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA
|
||||
}
|
||||
}
|
||||
|
||||
proj, err := s.getAppProject(ctx, a, log.WithField("application", a.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// First, make sure all the returned resources are permitted, for each operation.
|
||||
// Also perform create with dry-runs for all create-operation resources.
|
||||
// This is performed separately to reduce the risk of only some of the resources being successfully created later.
|
||||
@@ -2211,7 +2225,7 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA
|
||||
// the dry-run for relevant apply/delete operation would have to be invoked as well.
|
||||
for _, impactedResource := range newObjects {
|
||||
newObj := impactedResource.UnstructuredObj
|
||||
err := s.verifyResourcePermitted(ctx, app, newObj)
|
||||
err := s.verifyResourcePermitted(ctx, app, proj, newObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -2305,14 +2319,7 @@ func (s *Server) patchResource(ctx context.Context, config *rest.Config, liveObj
|
||||
return &application.ApplicationResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *Server) verifyResourcePermitted(ctx context.Context, app *appv1.Application, obj *unstructured.Unstructured) error {
|
||||
proj, err := argo.GetAppProject(app, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx)
|
||||
if err != nil {
|
||||
if apierr.IsNotFound(err) {
|
||||
return fmt.Errorf("application references project %s which does not exist", app.Spec.Project)
|
||||
}
|
||||
return fmt.Errorf("failed to get project %s: %w", app.Spec.Project, err)
|
||||
}
|
||||
func (s *Server) verifyResourcePermitted(ctx context.Context, app *appv1.Application, proj *appv1.AppProject, obj *unstructured.Unstructured) error {
|
||||
permitted, err := proj.IsResourcePermitted(schema.GroupKind{Group: obj.GroupVersionKind().Group, Kind: obj.GroupVersionKind().Kind}, obj.GetNamespace(), app.Spec.Destination, func(project string) ([]*appv1.Cluster, error) {
|
||||
clusters, err := s.db.GetProjectClusters(context.TODO(), project)
|
||||
if err != nil {
|
||||
@@ -2372,16 +2379,11 @@ func splitStatusPatch(patch []byte) ([]byte, []byte, error) {
|
||||
}
|
||||
|
||||
func (s *Server) GetApplicationSyncWindows(ctx context.Context, q *application.ApplicationSyncWindowsQuery) (*application.ApplicationSyncWindowsResponse, error) {
|
||||
a, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName(), "")
|
||||
a, proj, err := s.getApplicationEnforceRBACClient(ctx, rbacpolicy.ActionGet, q.GetProject(), q.GetAppNamespace(), q.GetName(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proj, err := argo.GetAppProject(a, applisters.NewAppProjectLister(s.projInformer.GetIndexer()), s.ns, s.settingsMgr, s.db, ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting app project: %w", err)
|
||||
}
|
||||
|
||||
windows := proj.Spec.SyncWindows.Matches(a)
|
||||
sync := windows.CanSync(true)
|
||||
|
||||
|
||||
@@ -1439,6 +1439,27 @@ func TestCreateAppWithDestName(t *testing.T) {
|
||||
assert.Equal(t, app.Spec.Destination.Server, "https://cluster-api.com")
|
||||
}
|
||||
|
||||
// TestCreateAppWithOperation tests that an application created with an operation is created with the operation removed.
|
||||
// Avoids regressions of https://github.com/argoproj/argo-cd/security/advisories/GHSA-g623-jcgg-mhmm
|
||||
func TestCreateAppWithOperation(t *testing.T) {
|
||||
appServer := newTestAppServer(t)
|
||||
testApp := newTestAppWithDestName()
|
||||
testApp.Operation = &appsv1.Operation{
|
||||
Sync: &appsv1.SyncOperation{
|
||||
Manifests: []string{
|
||||
"test",
|
||||
},
|
||||
},
|
||||
}
|
||||
createReq := application.ApplicationCreateRequest{
|
||||
Application: testApp,
|
||||
}
|
||||
app, err := appServer.Create(context.Background(), &createReq)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, app)
|
||||
assert.Nil(t, app.Operation)
|
||||
}
|
||||
|
||||
func TestUpdateApp(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
@@ -1797,7 +1818,7 @@ func TestServer_GetApplicationSyncWindowsState(t *testing.T) {
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
|
||||
active, err := appServer.GetApplicationSyncWindows(context.Background(), &application.ApplicationSyncWindowsQuery{Name: &testApp.Name})
|
||||
assert.Contains(t, err.Error(), "not found")
|
||||
assert.Contains(t, err.Error(), "not exist")
|
||||
assert.Nil(t, active)
|
||||
})
|
||||
}
|
||||
@@ -2407,7 +2428,16 @@ func TestAppNamespaceRestrictions(t *testing.T) {
|
||||
t.Run("Get application in other namespace when allowed", func(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
testApp.Namespace = "argocd-1"
|
||||
appServer := newTestAppServer(t, testApp)
|
||||
testApp.Spec.Project = "other-ns"
|
||||
otherNsProj := &appsv1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "other-ns", Namespace: "default"},
|
||||
Spec: appsv1.AppProjectSpec{
|
||||
SourceRepos: []string{"*"},
|
||||
Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
|
||||
SourceNamespaces: []string{"argocd-1"},
|
||||
},
|
||||
}
|
||||
appServer := newTestAppServer(t, testApp, otherNsProj)
|
||||
appServer.enabledNamespaces = []string{"argocd-1"}
|
||||
app, err := appServer.Get(context.TODO(), &application.ApplicationQuery{
|
||||
Name: pointer.String("test-app"),
|
||||
@@ -2418,6 +2448,28 @@ func TestAppNamespaceRestrictions(t *testing.T) {
|
||||
require.Equal(t, "argocd-1", app.Namespace)
|
||||
require.Equal(t, "test-app", app.Name)
|
||||
})
|
||||
t.Run("Get application in other namespace when project is not allowed", func(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
testApp.Namespace = "argocd-1"
|
||||
testApp.Spec.Project = "other-ns"
|
||||
otherNsProj := &appsv1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "other-ns", Namespace: "default"},
|
||||
Spec: appsv1.AppProjectSpec{
|
||||
SourceRepos: []string{"*"},
|
||||
Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
|
||||
SourceNamespaces: []string{"argocd-2"},
|
||||
},
|
||||
}
|
||||
appServer := newTestAppServer(t, testApp, otherNsProj)
|
||||
appServer.enabledNamespaces = []string{"argocd-1"}
|
||||
app, err := appServer.Get(context.TODO(), &application.ApplicationQuery{
|
||||
Name: pointer.String("test-app"),
|
||||
AppNamespace: pointer.String("argocd-1"),
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Nil(t, app)
|
||||
require.ErrorContains(t, err, "app is not allowed in project")
|
||||
})
|
||||
t.Run("Create application in other namespace when allowed", func(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
testApp.Namespace = "argocd-1"
|
||||
@@ -2460,7 +2512,7 @@ func TestAppNamespaceRestrictions(t *testing.T) {
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Nil(t, app)
|
||||
require.ErrorContains(t, err, "not allowed to use project")
|
||||
require.ErrorContains(t, err, "app is not allowed in project")
|
||||
})
|
||||
|
||||
t.Run("Create application in other namespace when not allowed by configuration", func(t *testing.T) {
|
||||
@@ -2484,5 +2536,84 @@ func TestAppNamespaceRestrictions(t *testing.T) {
|
||||
require.Nil(t, app)
|
||||
require.ErrorContains(t, err, "namespace 'argocd-1' is not permitted")
|
||||
})
|
||||
|
||||
t.Run("Get application sync window in other namespace when project is allowed", func(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
testApp.Namespace = "argocd-1"
|
||||
testApp.Spec.Project = "other-ns"
|
||||
otherNsProj := &appsv1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "other-ns", Namespace: "default"},
|
||||
Spec: appsv1.AppProjectSpec{
|
||||
SourceRepos: []string{"*"},
|
||||
Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
|
||||
SourceNamespaces: []string{"argocd-1"},
|
||||
},
|
||||
}
|
||||
appServer := newTestAppServer(t, testApp, otherNsProj)
|
||||
appServer.enabledNamespaces = []string{"argocd-1"}
|
||||
active, err := appServer.GetApplicationSyncWindows(context.TODO(), &application.ApplicationSyncWindowsQuery{Name: &testApp.Name, AppNamespace: &testApp.Namespace})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(active.ActiveWindows))
|
||||
})
|
||||
t.Run("Get application sync window in other namespace when project is not allowed", func(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
testApp.Namespace = "argocd-1"
|
||||
testApp.Spec.Project = "other-ns"
|
||||
otherNsProj := &appsv1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "other-ns", Namespace: "default"},
|
||||
Spec: appsv1.AppProjectSpec{
|
||||
SourceRepos: []string{"*"},
|
||||
Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
|
||||
SourceNamespaces: []string{"argocd-2"},
|
||||
},
|
||||
}
|
||||
appServer := newTestAppServer(t, testApp, otherNsProj)
|
||||
appServer.enabledNamespaces = []string{"argocd-1"}
|
||||
active, err := appServer.GetApplicationSyncWindows(context.TODO(), &application.ApplicationSyncWindowsQuery{Name: &testApp.Name, AppNamespace: &testApp.Namespace})
|
||||
require.Error(t, err)
|
||||
require.Nil(t, active)
|
||||
require.ErrorContains(t, err, "app is not allowed in project")
|
||||
})
|
||||
t.Run("Get list of links in other namespace when project is not allowed", func(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
testApp.Namespace = "argocd-1"
|
||||
testApp.Spec.Project = "other-ns"
|
||||
otherNsProj := &appsv1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "other-ns", Namespace: "default"},
|
||||
Spec: appsv1.AppProjectSpec{
|
||||
SourceRepos: []string{"*"},
|
||||
Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
|
||||
SourceNamespaces: []string{"argocd-2"},
|
||||
},
|
||||
}
|
||||
appServer := newTestAppServer(t, testApp, otherNsProj)
|
||||
appServer.enabledNamespaces = []string{"argocd-1"}
|
||||
links, err := appServer.ListLinks(context.TODO(), &application.ListAppLinksRequest{
|
||||
Name: pointer.String("test-app"),
|
||||
Namespace: pointer.String("argocd-1"),
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Nil(t, links)
|
||||
require.ErrorContains(t, err, "app is not allowed in project")
|
||||
})
|
||||
t.Run("Get list of links in other namespace when project is allowed", func(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
testApp.Namespace = "argocd-1"
|
||||
testApp.Spec.Project = "other-ns"
|
||||
otherNsProj := &appsv1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "other-ns", Namespace: "default"},
|
||||
Spec: appsv1.AppProjectSpec{
|
||||
SourceRepos: []string{"*"},
|
||||
Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
|
||||
SourceNamespaces: []string{"argocd-1"},
|
||||
},
|
||||
}
|
||||
appServer := newTestAppServer(t, testApp, otherNsProj)
|
||||
appServer.enabledNamespaces = []string{"argocd-1"}
|
||||
links, err := appServer.ListLinks(context.TODO(), &application.ListAppLinksRequest{
|
||||
Name: pointer.String("test-app"),
|
||||
Namespace: pointer.String("argocd-1"),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, len(links.Items))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,12 +38,12 @@ type terminalHandler struct {
|
||||
allowedShells []string
|
||||
namespace string
|
||||
enabledNamespaces []string
|
||||
sessionManager util_session.SessionManager
|
||||
sessionManager *util_session.SessionManager
|
||||
}
|
||||
|
||||
// NewHandler returns a new terminal handler.
|
||||
func NewHandler(appLister applisters.ApplicationLister, namespace string, enabledNamespaces []string, db db.ArgoDB, enf *rbac.Enforcer, cache *servercache.Cache,
|
||||
appResourceTree AppResourceTreeFn, allowedShells []string, sessionManager util_session.SessionManager) *terminalHandler {
|
||||
appResourceTree AppResourceTreeFn, allowedShells []string, sessionManager *util_session.SessionManager) *terminalHandler {
|
||||
return &terminalHandler{
|
||||
appLister: appLister,
|
||||
db: db,
|
||||
|
||||
@@ -37,7 +37,7 @@ type terminalSession struct {
|
||||
tty bool
|
||||
readLock sync.Mutex
|
||||
writeLock sync.Mutex
|
||||
sessionManager util_session.SessionManager
|
||||
sessionManager *util_session.SessionManager
|
||||
token *string
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func getToken(r *http.Request) (string, error) {
|
||||
}
|
||||
|
||||
// newTerminalSession create terminalSession
|
||||
func newTerminalSession(w http.ResponseWriter, r *http.Request, responseHeader http.Header, sessionManager util_session.SessionManager) (*terminalSession, error) {
|
||||
func newTerminalSession(w http.ResponseWriter, r *http.Request, responseHeader http.Header, sessionManager *util_session.SessionManager) (*terminalSession, error) {
|
||||
token, err := getToken(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -197,6 +197,7 @@ type ArgoCDServer struct {
|
||||
|
||||
type ArgoCDServerOpts struct {
|
||||
DisableAuth bool
|
||||
ContentTypes []string
|
||||
EnableGZip bool
|
||||
Insecure bool
|
||||
StaticAssetsDir string
|
||||
@@ -989,9 +990,14 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
if a.EnableGZip {
|
||||
handler = compressHandler(handler)
|
||||
}
|
||||
if len(a.ContentTypes) > 0 {
|
||||
handler = enforceContentTypes(handler, a.ContentTypes)
|
||||
} else {
|
||||
log.WithField(common.SecurityField, common.SecurityHigh).Warnf("Content-Type enforcement is disabled, which may make your API vulnerable to CSRF attacks")
|
||||
}
|
||||
mux.Handle("/api/", handler)
|
||||
|
||||
terminal := application.NewHandler(a.appLister, a.Namespace, a.ApplicationNamespaces, a.db, a.enf, a.Cache, appResourceTreeFn, a.settings.ExecShells, *a.sessionMgr).
|
||||
terminal := application.NewHandler(a.appLister, a.Namespace, a.ApplicationNamespaces, a.db, a.enf, a.Cache, appResourceTreeFn, a.settings.ExecShells, a.sessionMgr).
|
||||
WithFeatureFlagMiddleware(a.settingsMgr.GetSettings)
|
||||
th := util_session.WithAuthMiddleware(a.DisableAuth, a.sessionMgr, terminal)
|
||||
mux.Handle("/terminal", th)
|
||||
@@ -1055,6 +1061,20 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
return &httpS
|
||||
}
|
||||
|
||||
func enforceContentTypes(handler http.Handler, types []string) http.Handler {
|
||||
allowedTypes := map[string]bool{}
|
||||
for _, t := range types {
|
||||
allowedTypes[strings.ToLower(t)] = true
|
||||
}
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet || allowedTypes[strings.ToLower(r.Header.Get("Content-Type"))] {
|
||||
handler.ServeHTTP(w, r)
|
||||
} else {
|
||||
http.Error(w, "Invalid content type", http.StatusUnsupportedMediaType)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// registerExtensions will try to register all configured extensions
|
||||
// in the given mux. If any error is returned while registering
|
||||
// extensions handlers, no route will be added in the given mux.
|
||||
|
||||
@@ -1425,3 +1425,46 @@ func TestReplaceBaseHRef(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_enforceContentTypes(t *testing.T) {
|
||||
getBaseHandler := func(t *testing.T, allow bool) http.Handler {
|
||||
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
assert.True(t, allow, "http handler was hit when it should have been blocked by content type enforcement")
|
||||
writer.WriteHeader(200)
|
||||
})
|
||||
}
|
||||
|
||||
t.Parallel()
|
||||
|
||||
t.Run("GET - not providing a content type, should still succeed", func(t *testing.T) {
|
||||
handler := enforceContentTypes(getBaseHandler(t, true), []string{"application/json"}).(http.HandlerFunc)
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
handler(w, req)
|
||||
resp := w.Result()
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("POST", func(t *testing.T) {
|
||||
handler := enforceContentTypes(getBaseHandler(t, true), []string{"application/json"}).(http.HandlerFunc)
|
||||
req := httptest.NewRequest("POST", "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
handler(w, req)
|
||||
resp := w.Result()
|
||||
assert.Equal(t, 415, resp.StatusCode, "didn't provide a content type, should have gotten an error")
|
||||
|
||||
req = httptest.NewRequest("POST", "/", nil)
|
||||
req.Header = map[string][]string{"Content-Type": {"application/json"}}
|
||||
w = httptest.NewRecorder()
|
||||
handler(w, req)
|
||||
resp = w.Result()
|
||||
assert.Equal(t, 200, resp.StatusCode, "should have passed, since an allowed content type was provided")
|
||||
|
||||
req = httptest.NewRequest("POST", "/", nil)
|
||||
req.Header = map[string][]string{"Content-Type": {"not-allowed"}}
|
||||
w = httptest.NewRecorder()
|
||||
handler(w, req)
|
||||
resp = w.Result()
|
||||
assert.Equal(t, 415, resp.StatusCode, "should not have passed, since a disallowed content type was provided")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -432,7 +432,7 @@ func TestNamespacedInvalidAppProject(t *testing.T) {
|
||||
Then().
|
||||
// We're not allowed to infer whether the project exists based on this error message. Instead, we get a generic
|
||||
// permission denied error.
|
||||
Expect(Error("", "permission denied"))
|
||||
Expect(Error("", "is not allowed"))
|
||||
}
|
||||
|
||||
func TestNamespacedAppDeletion(t *testing.T) {
|
||||
@@ -748,7 +748,7 @@ func TestNamespacedResourceDiffing(t *testing.T) {
|
||||
Then().
|
||||
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
|
||||
And(func(app *Application) {
|
||||
diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local", "testdata/guestbook")
|
||||
diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local-repo-root", ".", "--local", "testdata/guestbook")
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, diffOutput, fmt.Sprintf("===== apps/Deployment %s/guestbook-ui ======", DeploymentNamespace()))
|
||||
}).
|
||||
@@ -761,7 +761,7 @@ func TestNamespacedResourceDiffing(t *testing.T) {
|
||||
Then().
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
And(func(app *Application) {
|
||||
diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local", "testdata/guestbook")
|
||||
diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local-repo-root", ".", "--local", "testdata/guestbook")
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, diffOutput)
|
||||
}).
|
||||
@@ -897,7 +897,7 @@ func testNSEdgeCasesApplicationResources(t *testing.T, appPath string, statusCod
|
||||
expect.
|
||||
Expect(HealthIs(statusCode)).
|
||||
And(func(app *Application) {
|
||||
diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local", path.Join("testdata", appPath))
|
||||
diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local-repo-root", ".", "--local", path.Join("testdata", appPath))
|
||||
assert.Empty(t, diffOutput)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
@@ -998,7 +998,7 @@ func TestNamespacedLocalManifestSync(t *testing.T) {
|
||||
Given().
|
||||
LocalPath(guestbookPathLocal).
|
||||
When().
|
||||
Sync().
|
||||
Sync("--local-repo-root", ".").
|
||||
Then().
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
And(func(app *Application) {
|
||||
@@ -1066,7 +1066,7 @@ func TestNamespacedLocalSyncDryRunWithASEnabled(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
appBefore := app.DeepCopy()
|
||||
_, err = RunCli("app", "sync", app.QualifiedName(), "--dry-run", "--local", guestbookPathLocal)
|
||||
_, err = RunCli("app", "sync", app.QualifiedName(), "--dry-run", "--local-repo-root", ".", "--local", guestbookPathLocal)
|
||||
assert.NoError(t, err)
|
||||
|
||||
appAfter := app.DeepCopy()
|
||||
|
||||
@@ -547,7 +547,7 @@ func TestInvalidAppProject(t *testing.T) {
|
||||
Then().
|
||||
// We're not allowed to infer whether the project exists based on this error message. Instead, we get a generic
|
||||
// permission denied error.
|
||||
Expect(Error("", "permission denied"))
|
||||
Expect(Error("", "is not allowed"))
|
||||
}
|
||||
|
||||
func TestAppDeletion(t *testing.T) {
|
||||
@@ -1324,7 +1324,7 @@ func TestLocalManifestSync(t *testing.T) {
|
||||
Given().
|
||||
LocalPath(guestbookPathLocal).
|
||||
When().
|
||||
Sync().
|
||||
Sync("--local-repo-root", ".").
|
||||
Then().
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
And(func(app *Application) {
|
||||
@@ -1385,7 +1385,7 @@ func TestLocalSyncDryRunWithAutosyncEnabled(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
appBefore := app.DeepCopy()
|
||||
_, err = RunCli("app", "sync", app.Name, "--dry-run", "--local", guestbookPathLocal)
|
||||
_, err = RunCli("app", "sync", app.Name, "--dry-run", "--local-repo-root", ".", "--local", guestbookPathLocal)
|
||||
assert.NoError(t, err)
|
||||
|
||||
appAfter := app.DeepCopy()
|
||||
|
||||
@@ -54,12 +54,14 @@ func TestDeclarativeInvalidProject(t *testing.T) {
|
||||
Expect(Success("")).
|
||||
Expect(HealthIs(health.HealthStatusUnknown)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeUnknown)).
|
||||
Expect(Condition(ApplicationConditionInvalidSpecError, "Application referencing project garbage which does not exist")).
|
||||
When().
|
||||
Delete(false).
|
||||
Then().
|
||||
Expect(Success("")).
|
||||
Expect(DoesNotExist())
|
||||
Expect(Condition(ApplicationConditionInvalidSpecError, "Application referencing project garbage which does not exist"))
|
||||
// TODO: you can`t delete application with invalid project due to enforcment that was recently added,
|
||||
// in https://github.com/argoproj/argo-cd/security/advisories/GHSA-2gvw-w6fj-7m3c
|
||||
//When().
|
||||
//Delete(false).
|
||||
//Then().
|
||||
//Expect(Success("")).
|
||||
//Expect(DoesNotExist())
|
||||
}
|
||||
|
||||
func TestDeclarativeInvalidRepoURL(t *testing.T) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package fixture
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -27,7 +28,15 @@ func DoHttpRequest(method string, path string, data ...byte) (*http.Response, er
|
||||
return nil, err
|
||||
}
|
||||
req.AddCookie(&http.Cookie{Name: common.AuthCookieName, Value: token})
|
||||
return http.DefaultClient.Do(req)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: IsRemote()},
|
||||
},
|
||||
}
|
||||
|
||||
return httpClient.Do(req)
|
||||
}
|
||||
|
||||
// DoHttpJsonRequest executes a http request against the Argo CD API server and unmarshals the response body as JSON
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testEnvironment: 'jsdom',
|
||||
reporters: ['default', 'jest-junit'],
|
||||
collectCoverage: true,
|
||||
transformIgnorePatterns: ['node_modules/(?!(argo-ui)/)'],
|
||||
globals: {
|
||||
'self': {},
|
||||
'window': {localStorage: { getItem: () => '{}', setItem: () => null }},
|
||||
'ts-jest': {
|
||||
isolatedModules: true,
|
||||
},
|
||||
@@ -17,20 +16,3 @@ module.exports = {
|
||||
'.+\\.(css|styl|less|sass|scss)$': 'jest-transform-css',
|
||||
},
|
||||
};
|
||||
|
||||
const localStorageMock = (() => {
|
||||
let store = {};
|
||||
return {
|
||||
getItem: (key) => store[key],
|
||||
setItem: (key, value) => {
|
||||
store[key] = value.toString();
|
||||
},
|
||||
clear: () => {
|
||||
store = {};
|
||||
},
|
||||
removeItem: (key) => {
|
||||
delete store[key];
|
||||
}
|
||||
};
|
||||
})();
|
||||
global.localStorage = localStorageMock;
|
||||
@@ -8,16 +8,16 @@ $header: 120px;
|
||||
.application-details {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
&__status-panel {
|
||||
position: fixed;
|
||||
left: $sidebar-width;
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
@media screen and (max-width: map-get($breakpoints, xlarge)) {
|
||||
top: 150px;
|
||||
}
|
||||
@media screen and (max-width: map-get($breakpoints, large)) {
|
||||
top: 146px;
|
||||
|
||||
&__wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - 2 * $top-bar-height);
|
||||
overflow: hidden;
|
||||
|
||||
@media screen and (max-width: map-get($breakpoints, xxlarge)) {
|
||||
height: calc(100vh - 3 * $top-bar-height);
|
||||
margin-top: $top-bar-height;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,13 +27,11 @@ $header: 120px;
|
||||
|
||||
&__tree {
|
||||
padding: 1em;
|
||||
|
||||
flex: 1;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
margin-top: 150px;
|
||||
height: calc(100vh - 2 * 70px - 115px);
|
||||
@media screen and (max-width: map-get($breakpoints, xlarge)) {
|
||||
margin-top: 165px;
|
||||
}
|
||||
overscroll-behavior-x: none;
|
||||
}
|
||||
|
||||
&__sliding-panel-pagination-wrap {
|
||||
|
||||
@@ -416,125 +416,20 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{app
|
||||
</React.Fragment>
|
||||
)
|
||||
}}>
|
||||
<div className='application-details__status-panel'>
|
||||
<ApplicationStatusPanel
|
||||
application={application}
|
||||
showDiff={() => this.selectNode(appFullName, 0, 'diff')}
|
||||
showOperation={() => this.setOperationStatusVisible(true)}
|
||||
showConditions={() => this.setConditionsStatusVisible(true)}
|
||||
showMetadataInfo={revision => this.setState({...this.state, revision})}
|
||||
/>
|
||||
</div>
|
||||
<div className='application-details__tree'>
|
||||
{refreshing && <p className='application-details__refreshing-label'>Refreshing</p>}
|
||||
{((pref.view === 'tree' || pref.view === 'network') && (
|
||||
<>
|
||||
<DataLoader load={() => services.viewPreferences.getPreferences()}>
|
||||
{viewPref => (
|
||||
<ApplicationDetailsFilters
|
||||
pref={pref}
|
||||
tree={tree}
|
||||
onSetFilter={setFilter}
|
||||
onClearFilter={clearFilter}
|
||||
collapsed={viewPref.hideSidebar}
|
||||
resourceNodes={this.state.filteredGraph}
|
||||
/>
|
||||
)}
|
||||
</DataLoader>
|
||||
<div className='graph-options-panel'>
|
||||
<a
|
||||
className={`group-nodes-button`}
|
||||
onClick={() => {
|
||||
toggleNameDirection();
|
||||
}}
|
||||
title={this.state.truncateNameOnRight ? 'Truncate resource name right' : 'Truncate resource name left'}>
|
||||
<i
|
||||
className={classNames({
|
||||
'fa fa-align-right': this.state.truncateNameOnRight,
|
||||
'fa fa-align-left': !this.state.truncateNameOnRight
|
||||
})}
|
||||
/>
|
||||
</a>
|
||||
{(pref.view === 'tree' || pref.view === 'network') && (
|
||||
<Tooltip
|
||||
content={AppUtils.userMsgsList[showToolTip?.msgKey] || 'Group Nodes'}
|
||||
visible={pref.groupNodes && showToolTip !== undefined && !showToolTip?.display}
|
||||
duration={showToolTip?.duration}
|
||||
zIndex={1}>
|
||||
<a
|
||||
className={`group-nodes-button group-nodes-button${!pref.groupNodes ? '' : '-on'}`}
|
||||
title={pref.view === 'tree' ? 'Group Nodes' : 'Collapse Pods'}
|
||||
onClick={() => this.toggleCompactView(application.metadata.name, pref)}>
|
||||
<i className={classNames('fa fa-object-group fa-fw')} />
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<span className={`separator`} />
|
||||
<a className={`group-nodes-button`} onClick={() => expandAll()} title='Expand all child nodes of all parent nodes'>
|
||||
<i className='fa fa-plus fa-fw' />
|
||||
</a>
|
||||
<a className={`group-nodes-button`} onClick={() => collapseAll()} title='Collapse all child nodes of all parent nodes'>
|
||||
<i className='fa fa-minus fa-fw' />
|
||||
</a>
|
||||
<span className={`separator`} />
|
||||
<span>
|
||||
<a className={`group-nodes-button`} onClick={() => setZoom(0.1)} title='Zoom in'>
|
||||
<i className='fa fa-search-plus fa-fw' />
|
||||
</a>
|
||||
<a className={`group-nodes-button`} onClick={() => setZoom(-0.1)} title='Zoom out'>
|
||||
<i className='fa fa-search-minus fa-fw' />
|
||||
</a>
|
||||
<div className={`zoom-value`}>{zoomNum}%</div>
|
||||
</span>
|
||||
</div>
|
||||
<ApplicationResourceTree
|
||||
nodeFilter={node => this.filterTreeNode(node, treeFilter)}
|
||||
selectedNodeFullName={this.selectedNodeKey}
|
||||
onNodeClick={fullName => this.selectNode(fullName)}
|
||||
nodeMenu={node =>
|
||||
AppUtils.renderResourceMenu(node, application, tree, this.appContext.apis, this.appChanged, () =>
|
||||
this.getApplicationActionMenu(application, false)
|
||||
)
|
||||
}
|
||||
showCompactNodes={pref.groupNodes}
|
||||
userMsgs={pref.userHelpTipMsgs}
|
||||
tree={tree}
|
||||
app={application}
|
||||
showOrphanedResources={pref.orphanedResources}
|
||||
useNetworkingHierarchy={pref.view === 'network'}
|
||||
onClearFilter={clearFilter}
|
||||
onGroupdNodeClick={groupdedNodeIds => openGroupNodeDetails(groupdedNodeIds)}
|
||||
zoom={pref.zoom}
|
||||
podGroupCount={pref.podGroupCount}
|
||||
appContext={this.appContext}
|
||||
nameDirection={this.state.truncateNameOnRight}
|
||||
filters={pref.resourceFilter}
|
||||
setTreeFilterGraph={setFilterGraph}
|
||||
updateUsrHelpTipMsgs={updateHelpTipState}
|
||||
setShowCompactNodes={setShowCompactNodes}
|
||||
setNodeExpansion={(node, isExpanded) => this.setNodeExpansion(node, isExpanded)}
|
||||
getNodeExpansion={node => this.getNodeExpansion(node)}
|
||||
/>
|
||||
</>
|
||||
)) ||
|
||||
(pref.view === 'pods' && (
|
||||
<PodView
|
||||
tree={tree}
|
||||
app={application}
|
||||
onItemClick={fullName => this.selectNode(fullName)}
|
||||
nodeMenu={node =>
|
||||
AppUtils.renderResourceMenu(node, application, tree, this.appContext.apis, this.appChanged, () =>
|
||||
this.getApplicationActionMenu(application, false)
|
||||
)
|
||||
}
|
||||
quickStarts={node => AppUtils.renderResourceButtons(node, application, tree, this.appContext.apis, this.appChanged)}
|
||||
/>
|
||||
)) ||
|
||||
(this.state.extensionsMap[pref.view] != null && (
|
||||
<ExtensionView extension={this.state.extensionsMap[pref.view]} application={application} tree={tree} />
|
||||
)) || (
|
||||
<div>
|
||||
<div className='application-details__wrapper'>
|
||||
<div className='application-details__status-panel'>
|
||||
<ApplicationStatusPanel
|
||||
application={application}
|
||||
showDiff={() => this.selectNode(appFullName, 0, 'diff')}
|
||||
showOperation={() => this.setOperationStatusVisible(true)}
|
||||
showConditions={() => this.setConditionsStatusVisible(true)}
|
||||
showMetadataInfo={revision => this.setState({...this.state, revision})}
|
||||
/>
|
||||
</div>
|
||||
<div className='application-details__tree'>
|
||||
{refreshing && <p className='application-details__refreshing-label'>Refreshing</p>}
|
||||
{((pref.view === 'tree' || pref.view === 'network') && (
|
||||
<>
|
||||
<DataLoader load={() => services.viewPreferences.getPreferences()}>
|
||||
{viewPref => (
|
||||
<ApplicationDetailsFilters
|
||||
@@ -543,42 +438,149 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{app
|
||||
onSetFilter={setFilter}
|
||||
onClearFilter={clearFilter}
|
||||
collapsed={viewPref.hideSidebar}
|
||||
resourceNodes={filteredRes}
|
||||
resourceNodes={this.state.filteredGraph}
|
||||
/>
|
||||
)}
|
||||
</DataLoader>
|
||||
{(filteredRes.length > 0 && (
|
||||
<Paginate
|
||||
page={this.state.page}
|
||||
data={filteredRes}
|
||||
onPageChange={page => this.setState({page})}
|
||||
preferencesKey='application-details'>
|
||||
{data => (
|
||||
<ApplicationResourceList
|
||||
onNodeClick={fullName => this.selectNode(fullName)}
|
||||
resources={data}
|
||||
nodeMenu={node =>
|
||||
AppUtils.renderResourceMenu(
|
||||
{...node, root: node},
|
||||
application,
|
||||
tree,
|
||||
this.appContext.apis,
|
||||
this.appChanged,
|
||||
() => this.getApplicationActionMenu(application, false)
|
||||
)
|
||||
}
|
||||
<div className='graph-options-panel'>
|
||||
<a
|
||||
className={`group-nodes-button`}
|
||||
onClick={() => {
|
||||
toggleNameDirection();
|
||||
}}
|
||||
title={this.state.truncateNameOnRight ? 'Truncate resource name right' : 'Truncate resource name left'}>
|
||||
<i
|
||||
className={classNames({
|
||||
'fa fa-align-right': this.state.truncateNameOnRight,
|
||||
'fa fa-align-left': !this.state.truncateNameOnRight
|
||||
})}
|
||||
/>
|
||||
</a>
|
||||
{(pref.view === 'tree' || pref.view === 'network') && (
|
||||
<Tooltip
|
||||
content={AppUtils.userMsgsList[showToolTip?.msgKey] || 'Group Nodes'}
|
||||
visible={pref.groupNodes && showToolTip !== undefined && !showToolTip?.display}
|
||||
duration={showToolTip?.duration}
|
||||
zIndex={1}>
|
||||
<a
|
||||
className={`group-nodes-button group-nodes-button${!pref.groupNodes ? '' : '-on'}`}
|
||||
title={pref.view === 'tree' ? 'Group Nodes' : 'Collapse Pods'}
|
||||
onClick={() => this.toggleCompactView(application.metadata.name, pref)}>
|
||||
<i className={classNames('fa fa-object-group fa-fw')} />
|
||||
</a>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<span className={`separator`} />
|
||||
<a className={`group-nodes-button`} onClick={() => expandAll()} title='Expand all child nodes of all parent nodes'>
|
||||
<i className='fa fa-plus fa-fw' />
|
||||
</a>
|
||||
<a className={`group-nodes-button`} onClick={() => collapseAll()} title='Collapse all child nodes of all parent nodes'>
|
||||
<i className='fa fa-minus fa-fw' />
|
||||
</a>
|
||||
<span className={`separator`} />
|
||||
<span>
|
||||
<a className={`group-nodes-button`} onClick={() => setZoom(0.1)} title='Zoom in'>
|
||||
<i className='fa fa-search-plus fa-fw' />
|
||||
</a>
|
||||
<a className={`group-nodes-button`} onClick={() => setZoom(-0.1)} title='Zoom out'>
|
||||
<i className='fa fa-search-minus fa-fw' />
|
||||
</a>
|
||||
<div className={`zoom-value`}>{zoomNum}%</div>
|
||||
</span>
|
||||
</div>
|
||||
<ApplicationResourceTree
|
||||
nodeFilter={node => this.filterTreeNode(node, treeFilter)}
|
||||
selectedNodeFullName={this.selectedNodeKey}
|
||||
onNodeClick={fullName => this.selectNode(fullName)}
|
||||
nodeMenu={node =>
|
||||
AppUtils.renderResourceMenu(node, application, tree, this.appContext.apis, this.appChanged, () =>
|
||||
this.getApplicationActionMenu(application, false)
|
||||
)
|
||||
}
|
||||
showCompactNodes={pref.groupNodes}
|
||||
userMsgs={pref.userHelpTipMsgs}
|
||||
tree={tree}
|
||||
app={application}
|
||||
showOrphanedResources={pref.orphanedResources}
|
||||
useNetworkingHierarchy={pref.view === 'network'}
|
||||
onClearFilter={clearFilter}
|
||||
onGroupdNodeClick={groupdedNodeIds => openGroupNodeDetails(groupdedNodeIds)}
|
||||
zoom={pref.zoom}
|
||||
podGroupCount={pref.podGroupCount}
|
||||
appContext={this.appContext}
|
||||
nameDirection={this.state.truncateNameOnRight}
|
||||
filters={pref.resourceFilter}
|
||||
setTreeFilterGraph={setFilterGraph}
|
||||
updateUsrHelpTipMsgs={updateHelpTipState}
|
||||
setShowCompactNodes={setShowCompactNodes}
|
||||
setNodeExpansion={(node, isExpanded) => this.setNodeExpansion(node, isExpanded)}
|
||||
getNodeExpansion={node => this.getNodeExpansion(node)}
|
||||
/>
|
||||
</>
|
||||
)) ||
|
||||
(pref.view === 'pods' && (
|
||||
<PodView
|
||||
tree={tree}
|
||||
app={application}
|
||||
onItemClick={fullName => this.selectNode(fullName)}
|
||||
nodeMenu={node =>
|
||||
AppUtils.renderResourceMenu(node, application, tree, this.appContext.apis, this.appChanged, () =>
|
||||
this.getApplicationActionMenu(application, false)
|
||||
)
|
||||
}
|
||||
quickStarts={node => AppUtils.renderResourceButtons(node, application, tree, this.appContext.apis, this.appChanged)}
|
||||
/>
|
||||
)) ||
|
||||
(this.state.extensionsMap[pref.view] != null && (
|
||||
<ExtensionView extension={this.state.extensionsMap[pref.view]} application={application} tree={tree} />
|
||||
)) || (
|
||||
<div>
|
||||
<DataLoader load={() => services.viewPreferences.getPreferences()}>
|
||||
{viewPref => (
|
||||
<ApplicationDetailsFilters
|
||||
pref={pref}
|
||||
tree={tree}
|
||||
onSetFilter={setFilter}
|
||||
onClearFilter={clearFilter}
|
||||
collapsed={viewPref.hideSidebar}
|
||||
resourceNodes={filteredRes}
|
||||
/>
|
||||
)}
|
||||
</Paginate>
|
||||
)) || (
|
||||
<EmptyState icon='fa fa-search'>
|
||||
<h4>No resources found</h4>
|
||||
<h5>Try to change filter criteria</h5>
|
||||
</EmptyState>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</DataLoader>
|
||||
{(filteredRes.length > 0 && (
|
||||
<Paginate
|
||||
page={this.state.page}
|
||||
data={filteredRes}
|
||||
onPageChange={page => this.setState({page})}
|
||||
preferencesKey='application-details'>
|
||||
{data => (
|
||||
<ApplicationResourceList
|
||||
onNodeClick={fullName => this.selectNode(fullName)}
|
||||
resources={data}
|
||||
nodeMenu={node =>
|
||||
AppUtils.renderResourceMenu(
|
||||
{...node, root: node},
|
||||
application,
|
||||
tree,
|
||||
this.appContext.apis,
|
||||
this.appChanged,
|
||||
() => this.getApplicationActionMenu(application, false)
|
||||
)
|
||||
}
|
||||
tree={tree}
|
||||
/>
|
||||
)}
|
||||
</Paginate>
|
||||
)) || (
|
||||
<EmptyState icon='fa fa-search'>
|
||||
<h4>No resources found</h4>
|
||||
<h5>Try to change filter criteria</h5>
|
||||
</EmptyState>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<SlidingPanel isShown={this.state.groupedResources.length > 0} onClose={() => this.closeGroupedNodesPanel()}>
|
||||
<div className='application-details__sliding-panel-pagination-wrap'>
|
||||
@@ -748,12 +750,12 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{app
|
||||
return [
|
||||
{
|
||||
iconClassName: 'fa fa-info-circle',
|
||||
title: <ActionMenuItem actionLabel='App Details' />,
|
||||
title: <ActionMenuItem actionLabel='Details' />,
|
||||
action: () => this.selectNode(fullName)
|
||||
},
|
||||
{
|
||||
iconClassName: 'fa fa-file-medical',
|
||||
title: <ActionMenuItem actionLabel='App Diff' />,
|
||||
title: <ActionMenuItem actionLabel='Diff' />,
|
||||
action: () => this.selectNode(fullName, 0, 'diff'),
|
||||
disabled: app.status.sync.status === appModels.SyncStatuses.Synced
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
RevisionHelpIcon
|
||||
} from '../../../shared/components';
|
||||
import {BadgePanel, Spinner} from '../../../shared/components';
|
||||
import {Consumer, ContextApis} from '../../../shared/context';
|
||||
import {AuthSettingsCtx, Consumer, ContextApis} from '../../../shared/context';
|
||||
import * as models from '../../../shared/models';
|
||||
import {services} from '../../../shared/services';
|
||||
|
||||
@@ -30,6 +30,7 @@ import {EditAnnotations} from './edit-annotations';
|
||||
|
||||
import './application-summary.scss';
|
||||
import {DeepLinks} from '../../../shared/components/deep-links';
|
||||
import {ExternalLinks} from '../application-urls';
|
||||
|
||||
function swap(array: any[], a: number, b: number) {
|
||||
array = array.slice();
|
||||
@@ -47,6 +48,7 @@ export const ApplicationSummary = (props: ApplicationSummaryProps) => {
|
||||
const source = getAppDefaultSource(app);
|
||||
const isHelm = source.hasOwnProperty('chart');
|
||||
const initialState = app.spec.destination.server === undefined ? 'NAME' : 'URL';
|
||||
const useAuthSettingsCtx = React.useContext(AuthSettingsCtx);
|
||||
const [destFormat, setDestFormat] = React.useState(initialState);
|
||||
const [changeSync, setChangeSync] = React.useState(false);
|
||||
|
||||
@@ -325,20 +327,19 @@ export const ApplicationSummary = (props: ApplicationSummaryProps) => {
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
const urls = app.status.summary.externalURLs || [];
|
||||
const urls = ExternalLinks(app.status.summary.externalURLs);
|
||||
if (urls.length > 0) {
|
||||
attributes.push({
|
||||
title: 'URLs',
|
||||
view: (
|
||||
<React.Fragment>
|
||||
{urls
|
||||
.map(item => item.split('|'))
|
||||
.map((parts, i) => (
|
||||
<a key={i} href={parts.length > 1 ? parts[1] : parts[0]} target='__blank'>
|
||||
{parts[0]}
|
||||
{urls.map((url, i) => {
|
||||
return (
|
||||
<a key={i} href={url.ref} target='__blank'>
|
||||
{url.title}
|
||||
</a>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
)
|
||||
});
|
||||
@@ -589,7 +590,7 @@ export const ApplicationSummary = (props: ApplicationSummaryProps) => {
|
||||
</div>
|
||||
)}
|
||||
</Consumer>
|
||||
<BadgePanel app={props.app.metadata.name} />
|
||||
<BadgePanel app={props.app.metadata.name} appNamespace={props.app.metadata.namespace} nsEnabled={useAuthSettingsCtx?.appsInAnyNamespaceEnabled} />
|
||||
<EditablePanel
|
||||
save={updateApp}
|
||||
values={app}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ExternalLink, InvalidExternalLinkError} from './application-urls';
|
||||
import { ExternalLink, ExternalLinks, InvalidExternalLinkError } from './application-urls';
|
||||
|
||||
test('rejects malicious URLs', () => {
|
||||
expect(() => {
|
||||
@@ -7,6 +7,16 @@ test('rejects malicious URLs', () => {
|
||||
expect(() => {
|
||||
const _ = new ExternalLink('data:text/html;<h1>hi</h1>');
|
||||
}).toThrowError(InvalidExternalLinkError);
|
||||
expect(() => {
|
||||
const _ = new ExternalLink('title|data:text/html;<h1>hi</h1>');
|
||||
}).toThrowError(InvalidExternalLinkError);
|
||||
expect(() => {
|
||||
const _ = new ExternalLink('data:title|data:text/html;<h1>hi</h1>');
|
||||
}).toThrowError(InvalidExternalLinkError);
|
||||
|
||||
expect(() => {
|
||||
const _ = new ExternalLink('data:title|https://localhost:8080/applications');
|
||||
}).not.toThrowError(InvalidExternalLinkError);
|
||||
});
|
||||
|
||||
test('allows absolute URLs', () => {
|
||||
@@ -18,3 +28,59 @@ test('allows relative URLs', () => {
|
||||
window.location = new URL('https://localhost:8080/applications');
|
||||
expect(new ExternalLink('/applications').ref).toEqual('/applications');
|
||||
});
|
||||
|
||||
|
||||
test('URLs format', () => {
|
||||
expect(new ExternalLink('https://localhost:8080/applications')).toEqual({
|
||||
ref: 'https://localhost:8080/applications',
|
||||
title: 'https://localhost:8080/applications',
|
||||
})
|
||||
expect(new ExternalLink('title|https://localhost:8080/applications')).toEqual({
|
||||
ref: 'https://localhost:8080/applications',
|
||||
title: 'title',
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
test('malicious URLs from list to be removed', () => {
|
||||
const urls: string[] = [
|
||||
'javascript:alert("hi")',
|
||||
'https://localhost:8080/applications',
|
||||
]
|
||||
const links = ExternalLinks(urls);
|
||||
|
||||
expect(links).toHaveLength(1);
|
||||
expect(links).toContainEqual({
|
||||
ref: 'https://localhost:8080/applications',
|
||||
title: 'https://localhost:8080/applications',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('list to be sorted', () => {
|
||||
const urls: string[] = [
|
||||
'https://a',
|
||||
'https://b',
|
||||
'a|https://c',
|
||||
'z|https://c',
|
||||
'x|https://d',
|
||||
'x|https://c',
|
||||
]
|
||||
const links = ExternalLinks(urls);
|
||||
|
||||
// 'a|https://c',
|
||||
// 'x|https://c',
|
||||
// 'x|https://d',
|
||||
// 'z|https://c',
|
||||
// 'https://a',
|
||||
// 'https://b',
|
||||
expect(links).toHaveLength(6);
|
||||
expect(links[0].title).toEqual('a')
|
||||
expect(links[1].title).toEqual('x')
|
||||
expect(links[1].ref).toEqual('https://c')
|
||||
expect(links[2].title).toEqual('x')
|
||||
expect(links[2].ref).toEqual('https://d')
|
||||
expect(links[3].title).toEqual('z')
|
||||
expect(links[4].title).toEqual('https://a')
|
||||
expect(links[5].title).toEqual('https://b')
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user