mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-22 18:48:46 +01:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93d588c86e | ||
|
|
377eb799ff | ||
|
|
ff11b58816 | ||
|
|
b1625eb8cc | ||
|
|
b8e154f767 | ||
|
|
c4ab0938f9 | ||
|
|
3fe5753f33 | ||
|
|
2e550c3f07 | ||
|
|
d841aae433 | ||
|
|
b570ab8b17 | ||
|
|
8c82655c66 | ||
|
|
a9e1040314 | ||
|
|
6f4bbb5a55 | ||
|
|
8f981ccfcf | ||
|
|
dbf043e6f1 | ||
|
|
f6501652c4 | ||
|
|
78d749ec88 | ||
|
|
8217d70085 | ||
|
|
02e61797b3 | ||
|
|
998f063a80 | ||
|
|
987f6659b8 | ||
|
|
e099a6a851 | ||
|
|
afbd59ba63 | ||
|
|
b1e3a07d92 | ||
|
|
c3144c0059 | ||
|
|
33547f149b | ||
|
|
06dc9aa836 |
3
.github/workflows/ci-build.yaml
vendored
3
.github/workflows/ci-build.yaml
vendored
@@ -9,6 +9,7 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release-*'
|
||||
|
||||
env:
|
||||
# Golang version to use across CI steps
|
||||
@@ -400,7 +401,7 @@ jobs:
|
||||
run: |
|
||||
docker pull quay.io/dexidp/dex:v2.25.0
|
||||
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
||||
docker pull redis:6.2.4-alpine
|
||||
docker pull redis:6.2.6-alpine
|
||||
- name: Create target directory for binaries in the build-process
|
||||
run: |
|
||||
mkdir -p dist
|
||||
|
||||
4
Procfile
4
Procfile
@@ -1,7 +1,7 @@
|
||||
controller: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
|
||||
api-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} "
|
||||
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.30.0 serve /dex.yaml"
|
||||
redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" == 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:6.2.4-alpine --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi"
|
||||
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.30.2 dex serve /dex.yaml"
|
||||
redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" == 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:6.2.6-alpine --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi"
|
||||
repo-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-/tmp/argo-e2e/app/config/plugin} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} go run ./cmd/main.go --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}"
|
||||
ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start'
|
||||
git-server: test/fixture/testrepos/start-git.sh
|
||||
|
||||
@@ -2702,6 +2702,16 @@
|
||||
"type": "string",
|
||||
"name": "revision",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "appName",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "appProject",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -4042,6 +4052,9 @@
|
||||
"appName": {
|
||||
"type": "string"
|
||||
},
|
||||
"appProject": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/v1alpha1ApplicationSource"
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
apiruntime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
@@ -408,8 +409,12 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
|
||||
},
|
||||
})
|
||||
} else {
|
||||
err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, kube.GetResourceKey(live), func(child appv1.ResourceNode, appName string) {
|
||||
err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, kube.GetResourceKey(live), func(child appv1.ResourceNode, appName string) bool {
|
||||
if !proj.IsResourcePermitted(schema.GroupKind{Group: child.ResourceRef.Group, Kind: child.ResourceRef.Kind}, child.Namespace, a.Spec.Destination) {
|
||||
return false
|
||||
}
|
||||
nodes = append(nodes, child)
|
||||
return true
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -419,16 +424,18 @@ func (ctrl *ApplicationController) getResourceTree(a *appv1.Application, managed
|
||||
orphanedNodes := make([]appv1.ResourceNode, 0)
|
||||
for k := range orphanedNodesMap {
|
||||
if k.Namespace != "" && proj.IsGroupKindPermitted(k.GroupKind(), true) && !isKnownOrphanedResourceExclusion(k, proj) {
|
||||
err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, k, func(child appv1.ResourceNode, appName string) {
|
||||
err := ctrl.stateCache.IterateHierarchy(a.Spec.Destination.Server, k, func(child appv1.ResourceNode, appName string) bool {
|
||||
belongToAnotherApp := false
|
||||
if appName != "" {
|
||||
if _, exists, err := ctrl.appInformer.GetIndexer().GetByKey(ctrl.namespace + "/" + appName); exists && err == nil {
|
||||
belongToAnotherApp = true
|
||||
}
|
||||
}
|
||||
if !belongToAnotherApp {
|
||||
orphanedNodes = append(orphanedNodes, child)
|
||||
if belongToAnotherApp || !proj.IsResourcePermitted(schema.GroupKind{Group: child.ResourceRef.Group, Kind: child.ResourceRef.Kind}, child.Namespace, a.Spec.Destination) {
|
||||
return false
|
||||
}
|
||||
orphanedNodes = append(orphanedNodes, child)
|
||||
return true
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1258,6 +1265,13 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
|
||||
app.Status.Sync.Status = appv1.SyncStatusCodeUnknown
|
||||
app.Status.Health.Status = health.HealthStatusUnknown
|
||||
ctrl.persistAppStatus(origApp, &app.Status)
|
||||
|
||||
if err := ctrl.cache.SetAppResourcesTree(app.Name, &appv1.ApplicationTree{}); err != nil {
|
||||
log.Warnf("failed to set app resource tree: %v", err)
|
||||
}
|
||||
if err := ctrl.cache.SetAppManagedResources(app.Name, nil); err != nil {
|
||||
log.Warnf("failed to set app managed resources tree: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -136,12 +136,12 @@ func newFakeController(data *fakeData) *ApplicationController {
|
||||
mockStateCache.On("GetClusterCache", mock.Anything).Return(&clusterCacheMock, nil)
|
||||
mockStateCache.On("IterateHierarchy", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
||||
key := args[1].(kube.ResourceKey)
|
||||
action := args[2].(func(child argoappv1.ResourceNode, appName string))
|
||||
action := args[2].(func(child argoappv1.ResourceNode, appName string) bool)
|
||||
appName := ""
|
||||
if res, ok := data.namespacedResources[key]; ok {
|
||||
appName = res.AppName
|
||||
}
|
||||
action(argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: key.Kind, Group: key.Group, Namespace: key.Namespace, Name: key.Name}}, appName)
|
||||
_ = action(argoappv1.ResourceNode{ResourceRef: argoappv1.ResourceRef{Kind: key.Kind, Group: key.Group, Namespace: key.Namespace, Name: key.Name}}, appName)
|
||||
}).Return(nil)
|
||||
return ctrl
|
||||
}
|
||||
|
||||
8
controller/cache/cache.go
vendored
8
controller/cache/cache.go
vendored
@@ -79,7 +79,7 @@ type LiveStateCache interface {
|
||||
// Returns synced cluster cache
|
||||
GetClusterCache(server string) (clustercache.ClusterCache, error)
|
||||
// Executes give callback against resource specified by the key and all its children
|
||||
IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string)) error
|
||||
IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string) bool) error
|
||||
// Returns state of live nodes which correspond for target nodes of specified application.
|
||||
GetManagedLiveObjs(a *appv1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error)
|
||||
// IterateResources iterates all resource stored in cache
|
||||
@@ -397,13 +397,13 @@ func (c *liveStateCache) IsNamespaced(server string, gk schema.GroupKind) (bool,
|
||||
return clusterInfo.IsNamespaced(gk)
|
||||
}
|
||||
|
||||
func (c *liveStateCache) IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string)) error {
|
||||
func (c *liveStateCache) IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string) bool) error {
|
||||
clusterInfo, err := c.getSyncedCluster(server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clusterInfo.IterateHierarchy(key, func(resource *clustercache.Resource, namespaceResources map[kube.ResourceKey]*clustercache.Resource) {
|
||||
action(asResourceNode(resource), getApp(resource, namespaceResources))
|
||||
clusterInfo.IterateHierarchy(key, func(resource *clustercache.Resource, namespaceResources map[kube.ResourceKey]*clustercache.Resource) bool {
|
||||
return action(asResourceNode(resource), getApp(resource, namespaceResources))
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
4
controller/cache/mocks/LiveStateCache.go
vendored
4
controller/cache/mocks/LiveStateCache.go
vendored
@@ -176,11 +176,11 @@ func (_m *LiveStateCache) IsNamespaced(server string, gk schema.GroupKind) (bool
|
||||
}
|
||||
|
||||
// IterateHierarchy provides a mock function with given fields: server, key, action
|
||||
func (_m *LiveStateCache) IterateHierarchy(server string, key kube.ResourceKey, action func(v1alpha1.ResourceNode, string)) error {
|
||||
func (_m *LiveStateCache) IterateHierarchy(server string, key kube.ResourceKey, action func(v1alpha1.ResourceNode, string) bool) error {
|
||||
ret := _m.Called(server, key, action)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, kube.ResourceKey, func(v1alpha1.ResourceNode, string)) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(string, kube.ResourceKey, func(v1alpha1.ResourceNode, string) bool) error); ok {
|
||||
r0 = rf(server, key, action)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
|
||||
@@ -155,6 +155,11 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
helmOptions, err := m.settingsMgr.GetHelmSettings()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ts.AddCheckpoint("build_options_ms")
|
||||
serverVersion, apiResources, err := m.liveStateCache.GetVersionsInfo(app.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
@@ -178,6 +183,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
|
||||
VerifySignature: verifySignature,
|
||||
HelmRepoCreds: permittedHelmCredentials,
|
||||
TrackingMethod: string(argo.GetTrackingMethod(m.settingsMgr)),
|
||||
HelmOptions: helmOptions,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
@@ -194,6 +194,10 @@ data:
|
||||
kustomize.version.v3.5.1: /custom-tools/kustomize_3_5_1
|
||||
kustomize.version.v3.5.4: /custom-tools/kustomize_3_5_4
|
||||
|
||||
# Comma delimited list of additional custom remote values file schemes (http are https are allowed by default).
|
||||
# Change to empty value if you want to disable remote values files altogether.
|
||||
helm.valuesFileSchemes: http, https
|
||||
|
||||
# The metadata.label key name where Argo CD injects the app name as a tracking label (optional).
|
||||
# Tracking labels are used to determine which resources need to be deleted when pruning.
|
||||
# If omitted, Argo CD injects the app name into the label: 'app.kubernetes.io/instance'
|
||||
|
||||
@@ -40,6 +40,48 @@ the three components (argocd-server, argocd-repo-server, argocd-application-cont
|
||||
API server can enforce the use of TLS 1.2 using the flag: `--tlsminversion 1.2`.
|
||||
Communication with Redis is performed over plain HTTP by default. TLS can be setup with command line arguments.
|
||||
|
||||
## Git & Helm Repositories
|
||||
|
||||
Git and helm repositories are managed by a stand-alone service, called the repo-server. The
|
||||
repo-server does not carry any Kubernetes privileges and does not store credentials to any services
|
||||
(including git). The repo-server is responsible for cloning repositories which have been permitted
|
||||
and trusted by Argo CD operators, and generating kubernetes manifests at a given path in the
|
||||
repository. For performance and bandwidth efficiency, the repo-server maintains local clones of
|
||||
these repositories so that subsequent commits to the repository are efficiently downloaded.
|
||||
|
||||
There are security considerations when configuring git repositories that Argo CD is permitted to
|
||||
deploy from. In short, gaining unauthorized write access to a git repository trusted by Argo CD
|
||||
will have serious security implications outlined below.
|
||||
|
||||
### Unauthorized Deployments
|
||||
|
||||
Since Argo CD deploys the Kubernetes resources defined in git, an attacker with access to a trusted
|
||||
git repo would be able to affect the Kubernetes resources which are deployed. For example, an
|
||||
attacker could update the deployment manifest deploy malicious container images to the environment,
|
||||
or delete resources in git causing them to be pruned in the live environment.
|
||||
|
||||
### Tool command invocation
|
||||
|
||||
In addition to raw YAML, Argo CD natively supports two popular Kubernetes config management tools,
|
||||
helm and kustomize. When rendering manifests, Argo CD executes these config management tools
|
||||
(i.e. `helm template`, `kustomize build`) to generate the manifests. It is possible that an attacker
|
||||
with write access to a trusted git repository may construct malicious helm charts or kustomizations
|
||||
that attempt to read files out-of-tree. This includes adjacent git repos, as well as files on the
|
||||
repo-server itself. Whether or not this is a risk to your organization depends on if the contents
|
||||
in the git repos are sensitive in nature. By default, the repo-server itself does not contain
|
||||
sensitive information, but might be configured with Config Management Plugins which do
|
||||
(e.g. decryption keys). If such plugins are used, extreme care must be taken to ensure the
|
||||
repository contents can be trusted at all times.
|
||||
|
||||
### Remote bases and helm chart dependencies
|
||||
|
||||
Argo CD's repository allow-list only restricts the initial repository which is cloned. However, both
|
||||
kustomize and helm contain features to reference and follow *additional* repositories
|
||||
(e.g. kustomize remote bases, helm chart dependencies), of which might not be in the repository
|
||||
allow-list. Argo CD operators must understand that users with write access to trusted git
|
||||
repositories could reference other remote git repositories containing Kubernetes resources not
|
||||
easily searchable or auditable in the configured git repositories.
|
||||
|
||||
## Sensitive Information
|
||||
|
||||
### Secrets
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
# Roadmap
|
||||
|
||||
- [Roadmap](#roadmap)
|
||||
- [v2.2](#v22)
|
||||
- [Config Management Tools Integrations (proposal)](#config-management-tools-integrations-proposal)
|
||||
- [Argo CD Extensions (proposal)](#argo-cd-extensions-proposal)
|
||||
- [Project scoped repository and clusters (proposal)](#project-scoped-repository-and-clusters-proposal)
|
||||
- [v2.3 and beyond](#v23-and-beyond)
|
||||
- [Input Forms UI Refresh](#input-forms-ui-refresh)
|
||||
- [Merge ApplicationSet controller into Argo CD](#merge-applicationset-controller-into-argo-cd)
|
||||
- [v2.3](#v23)
|
||||
- [Merge Argo CD Notifications into Argo CD](#merge-argo-cd-notifications-into-argo-cd)
|
||||
- [Merge Argo CD Image Updater into Argo CD](#merge-argo-cd-image-updater-into-argo-cd)
|
||||
- [Compact Resources Tree](#compact-resources-tree)
|
||||
- [Input Forms UI Refresh](#input-forms-ui-refresh)
|
||||
- [Compact resources tree](#compact-resources-tree)
|
||||
- [Maintain difference in cluster and git values for specific fields](#maintain-difference-in-cluster-and-git-values-for-specific-fields)
|
||||
- [Web Shell](#web-shell)
|
||||
- [Helm values from external repo](#helm-values-from-external-repo)
|
||||
- [v2.4 and beyond](#v24-and-beyond)
|
||||
- [Merge ApplicationSet controller into Argo CD](#merge-applicationset-controller-into-argo-cd)
|
||||
- [Merge Argo CD Image Updater into Argo CD](#merge-argo-cd-image-updater-into-argo-cd)
|
||||
- [Config Management Tools Integrations UI/CLI](#config-management-tools-integrations-uicli)
|
||||
- [Allow specifying parent/child relationships in config](#allow-specifying-parentchild-relationships-in-config)
|
||||
- [Dependencies between applications](#dependencies-between-applications)
|
||||
- [Maintain difference in cluster and git values for specific fields](#maintain-difference-in-cluster-and-git-values-for-specific-fields)
|
||||
- [Multi-tenancy improvements](#multi-tenancy-improvements)
|
||||
- [GitOps Engine Enhancements](#gitops-engine-enhancements)
|
||||
- [Completed](#completed)
|
||||
- [✅ Core Argo CD (proposal)](#core-argo-cd-aka-gitops-agent-proposal)
|
||||
- [✅ Config Management Tools Integrations (proposal)](#-config-management-tools-integrations-proposal)
|
||||
- [✅ Argo CD Extensions (proposal)](#-argo-cd-extensions-proposal)
|
||||
- [✅ Project scoped repository and clusters (proposal)](#-project-scoped-repository-and-clusters-proposal)
|
||||
- [✅ Core Argo CD (proposal)](#-core-argo-cd-proposal)
|
||||
- [✅ Core Functionality Bug Fixes](#-core-functionality-bug-fixes)
|
||||
- [✅ Performance](#-performance)
|
||||
- [✅ ApplicationSet](#-applicationset)
|
||||
@@ -30,50 +30,24 @@
|
||||
- [✅ Automated Registry Monitoring](#-automated-registry-monitoring)
|
||||
- [✅ Projects Enhancements](#-projects-enhancements)
|
||||
|
||||
## v2.2
|
||||
|
||||
### Config Management Tools Integrations ([proposal](https://github.com/argoproj/argo-cd/pull/5927))
|
||||
|
||||
The community likes the first class support of Helm, Kustomize and keeps requesting support for more tools.
|
||||
Argo CD provides a mechanism to integrate with any config management tool. We need to investigate why
|
||||
it is not enough and implement missing features.
|
||||
|
||||
|
||||
### Argo CD Extensions ([proposal](https://github.com/argoproj/argo-cd/pull/6240))
|
||||
|
||||
Argo CD supports customizing handling of Kubernetes resources via diffing customizations,
|
||||
health checks, and custom actions. The Argo CD Extensions proposal takes it to next
|
||||
level and allows to deliver the resource customizations along with custom visualization in Argo CD
|
||||
via Git repository.
|
||||
|
||||
### Project scoped repository and clusters ([proposal](https://github.com/argoproj/argo-cd/blob/master/docs/proposals/project-repos-and-clusters.md))
|
||||
|
||||
The feature streamlines the process of adding repositories and clusters to the project and makes it self-service.
|
||||
Instead of asking an administrator to change Argo CD settings end users can perform the change independently.
|
||||
|
||||
## v2.3 and beyond
|
||||
|
||||
### Input Forms UI Refresh
|
||||
|
||||
Improved design of the input forms in Argo CD Web UI: https://www.figma.com/file/IIlsFqqmM5UhqMVul9fQNq/Argo-CD?node-id=0%3A1
|
||||
|
||||
### Merge ApplicationSet controller into Argo CD
|
||||
|
||||
The ApplicationSet functionality is available in Argo CD out-of-the-box ([#7351](https://github.com/argoproj/argo-cd/issues/7351)).
|
||||
The Argo CD UI/CLI/API allows to manage ApplicationSet resources same as Argo CD Applications ([#7352](https://github.com/argoproj/argo-cd/issues/7352)).
|
||||
## v2.3
|
||||
|
||||
### Merge Argo CD Notifications into Argo CD
|
||||
|
||||
The [Argo CD Notifications](https://github.com/argoproj-labs/argocd-notifications) should be merged into Argo CD and available out-of-the-box: [#7350](https://github.com/argoproj/argo-cd/issues/7350)
|
||||
|
||||
### Merge Argo CD Image Updater into Argo CD
|
||||
### Input Forms UI Refresh
|
||||
|
||||
The [Argo CD Image Updater](https://github.com/argoproj-labs/argocd-image-updater) should be merged into Argo CD and available out-of-the-box: [#7385](https://github.com/argoproj/argo-cd/issues/7385)
|
||||
Improved design of the input forms in Argo CD Web UI: https://www.figma.com/file/IIlsFqqmM5UhqMVul9fQNq/Argo-CD?node-id=0%3A1
|
||||
|
||||
### Compact resources tree
|
||||
|
||||
An ability to collaps leaf resources tree to improve visualization of very large applications: [#7349](https://github.com/argoproj/argo-cd/issues/7349)
|
||||
|
||||
### Maintain difference in cluster and git values for specific fields
|
||||
|
||||
The feature allows to avoid updating fields excluded from diffing ([#2913](https://github.com/argoproj/argo-cd/issues/2913)).
|
||||
|
||||
### Web Shell
|
||||
|
||||
Exec into the Kubernetes Pod right from Argo CD Web UI! [#4351](https://github.com/argoproj/argo-cd/issues/4351)
|
||||
@@ -82,6 +56,21 @@ Exec into the Kubernetes Pod right from Argo CD Web UI! [#4351](https://github.c
|
||||
|
||||
The feature allows combining of-the-shelf Helm chart and value file in Git repository ([#2789](https://github.com/argoproj/argo-cd/issues/2789))
|
||||
|
||||
|
||||
## v2.4 and beyond
|
||||
|
||||
|
||||
### Merge ApplicationSet controller into Argo CD
|
||||
|
||||
The ApplicationSet functionality is available in Argo CD out-of-the-box ([#7351](https://github.com/argoproj/argo-cd/issues/7351)).
|
||||
The Argo CD UI/CLI/API allows to manage ApplicationSet resources same as Argo CD Applications ([#7352](https://github.com/argoproj/argo-cd/issues/7352)).
|
||||
|
||||
|
||||
### Merge Argo CD Image Updater into Argo CD
|
||||
|
||||
The [Argo CD Image Updater](https://github.com/argoproj-labs/argocd-image-updater) should be merged into Argo CD and available out-of-the-box: [#7385](https://github.com/argoproj/argo-cd/issues/7385)
|
||||
|
||||
|
||||
### Config Management Tools Integrations UI/CLI
|
||||
|
||||
The continuation of the Config Management Tools of [proposal](https://github.com/argoproj/argo-cd/pull/5927). The Argo CD UI/CLI
|
||||
@@ -96,9 +85,6 @@ visualize custom resources that don't have owner references.
|
||||
|
||||
The feature allows specifying dependencies between applications that allow orchestrating synchronization of multiple applications. [#3517](https://github.com/argoproj/argo-cd/issues/3517)
|
||||
|
||||
### Maintain difference in cluster and git values for specific fields
|
||||
|
||||
The feature allows to avoid updating fields excluded from diffing ([#2913](https://github.com/argoproj/argo-cd/issues/2913)).
|
||||
|
||||
### Multi-tenancy improvements
|
||||
|
||||
@@ -119,6 +105,25 @@ A lot of Argo CD features are still not available in GitOps engine. The followin
|
||||
|
||||
## Completed
|
||||
|
||||
### ✅ Config Management Tools Integrations ([proposal](https://github.com/argoproj/argo-cd/pull/5927))
|
||||
|
||||
The community likes the first class support of Helm, Kustomize and keeps requesting support for more tools.
|
||||
Argo CD provides a mechanism to integrate with any config management tool. We need to investigate why
|
||||
it is not enough and implement missing features.
|
||||
|
||||
|
||||
### ✅ Argo CD Extensions ([proposal](https://github.com/argoproj/argo-cd/pull/6240))
|
||||
|
||||
Argo CD supports customizing handling of Kubernetes resources via diffing customizations,
|
||||
health checks, and custom actions. The Argo CD Extensions proposal takes it to next
|
||||
level and allows to deliver the resource customizations along with custom visualization in Argo CD
|
||||
via Git repository.
|
||||
|
||||
### ✅ Project scoped repository and clusters ([proposal](https://github.com/argoproj/argo-cd/blob/master/docs/proposals/project-repos-and-clusters.md))
|
||||
|
||||
The feature streamlines the process of adding repositories and clusters to the project and makes it self-service.
|
||||
Instead of asking an administrator to change Argo CD settings end users can perform the change independently.
|
||||
|
||||
### ✅ Core Argo CD ([proposal](https://github.com/argoproj/argo-cd/pull/6385))
|
||||
|
||||
Core Argo CD allows to installation and use of lightweight Argo CD that includes only the backend without exposing the API or UI.
|
||||
|
||||
2
go.mod
2
go.mod
@@ -8,7 +8,7 @@ require (
|
||||
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible
|
||||
github.com/alicebob/miniredis/v2 v2.14.2
|
||||
github.com/argoproj/gitops-engine v0.5.2
|
||||
github.com/argoproj/gitops-engine v0.5.5
|
||||
github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0
|
||||
github.com/bombsimon/logrusr v1.0.0
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.0.2
|
||||
|
||||
4
go.sum
4
go.sum
@@ -103,8 +103,8 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYU
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/argoproj/gitops-engine v0.5.2 h1:UQ2ajVyUPCSgFyqidzlTXddh/Xf6cE3I0s9uu92BoJg=
|
||||
github.com/argoproj/gitops-engine v0.5.2/go.mod h1:K2RYpGXh11VdFwDksS23SyFTOJaPcsF+MVJ/FHlqEOE=
|
||||
github.com/argoproj/gitops-engine v0.5.5 h1:ac6mKIncPzT/f3CH9+55ETqEsC+Z2lVDDz2Gbtvt8KE=
|
||||
github.com/argoproj/gitops-engine v0.5.5/go.mod h1:K2RYpGXh11VdFwDksS23SyFTOJaPcsF+MVJ/FHlqEOE=
|
||||
github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0 h1:Cfp7rO/HpVxnwlRqJe0jHiBbZ77ZgXhB6HWlYD02Xdc=
|
||||
github.com/argoproj/pkg v0.11.1-0.20211203175135-36c59d8fafe0/go.mod h1:ra+bQPmbVAoEL+gYSKesuigt4m49i3Qa3mE/xQcjCiA=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
|
||||
@@ -28,7 +28,7 @@ spec:
|
||||
name: dexconfig
|
||||
containers:
|
||||
- name: dex
|
||||
image: ghcr.io/dexidp/dex:v2.30.0
|
||||
image: ghcr.io/dexidp/dex:v2.30.2
|
||||
imagePullPolicy: Always
|
||||
command: [/shared/argocd-dex, rundex]
|
||||
securityContext:
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.2.2
|
||||
newTag: v2.2.8
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -21,7 +21,7 @@ spec:
|
||||
serviceAccountName: argocd-redis
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:6.2.4-alpine
|
||||
image: redis:6.2.6-alpine
|
||||
imagePullPolicy: Always
|
||||
args:
|
||||
- "--save"
|
||||
|
||||
@@ -2890,7 +2890,7 @@ spec:
|
||||
- ""
|
||||
- --appendonly
|
||||
- "no"
|
||||
image: redis:6.2.4-alpine
|
||||
image: redis:6.2.6-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -3018,7 +3018,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -3067,7 +3067,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
@@ -3232,7 +3232,7 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -11,4 +11,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.2.2
|
||||
newTag: v2.2.8
|
||||
|
||||
@@ -11,7 +11,7 @@ patchesStrategicMerge:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.2.2
|
||||
newTag: v2.2.8
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/dex
|
||||
|
||||
@@ -878,7 +878,7 @@ spec:
|
||||
automountServiceAccountToken: false
|
||||
initContainers:
|
||||
- name: config-init
|
||||
image: redis:6.2.4-alpine
|
||||
image: redis:6.2.6-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
resources:
|
||||
{}
|
||||
@@ -906,7 +906,7 @@ spec:
|
||||
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:6.2.4-alpine
|
||||
image: redis:6.2.6-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- redis-server
|
||||
@@ -947,7 +947,7 @@ spec:
|
||||
lifecycle:
|
||||
{}
|
||||
- name: sentinel
|
||||
image: redis:6.2.4-alpine
|
||||
image: redis:6.2.6-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- redis-sentinel
|
||||
|
||||
@@ -15,6 +15,6 @@ redis-ha:
|
||||
client: 6m
|
||||
checkInterval: 3s
|
||||
image:
|
||||
tag: 6.2.4-alpine
|
||||
tag: 6.2.6-alpine
|
||||
sentinel:
|
||||
bind: "0.0.0.0"
|
||||
|
||||
@@ -3687,7 +3687,7 @@ spec:
|
||||
- command:
|
||||
- /shared/argocd-dex
|
||||
- rundex
|
||||
image: ghcr.io/dexidp/dex:v2.30.0
|
||||
image: ghcr.io/dexidp/dex:v2.30.2
|
||||
imagePullPolicy: Always
|
||||
name: dex
|
||||
ports:
|
||||
@@ -3709,7 +3709,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -3926,7 +3926,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -3975,7 +3975,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
@@ -4202,7 +4202,7 @@ spec:
|
||||
key: server.http.cookie.maxnumber
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -4398,7 +4398,7 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -4480,7 +4480,7 @@ spec:
|
||||
- /data/conf/redis.conf
|
||||
command:
|
||||
- redis-server
|
||||
image: redis:6.2.4-alpine
|
||||
image: redis:6.2.6-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle: {}
|
||||
livenessProbe:
|
||||
@@ -4518,7 +4518,7 @@ spec:
|
||||
- /data/conf/sentinel.conf
|
||||
command:
|
||||
- redis-sentinel
|
||||
image: redis:6.2.4-alpine
|
||||
image: redis:6.2.6-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle: {}
|
||||
livenessProbe:
|
||||
@@ -4564,7 +4564,7 @@ spec:
|
||||
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
|
||||
- name: SENTINEL_ID_2
|
||||
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
|
||||
image: redis:6.2.4-alpine
|
||||
image: redis:6.2.6-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: config-init
|
||||
volumeMounts:
|
||||
|
||||
@@ -1046,7 +1046,7 @@ spec:
|
||||
- command:
|
||||
- /shared/argocd-dex
|
||||
- rundex
|
||||
image: ghcr.io/dexidp/dex:v2.30.0
|
||||
image: ghcr.io/dexidp/dex:v2.30.2
|
||||
imagePullPolicy: Always
|
||||
name: dex
|
||||
ports:
|
||||
@@ -1068,7 +1068,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -1285,7 +1285,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1334,7 +1334,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
@@ -1561,7 +1561,7 @@ spec:
|
||||
key: server.http.cookie.maxnumber
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -1757,7 +1757,7 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -1839,7 +1839,7 @@ spec:
|
||||
- /data/conf/redis.conf
|
||||
command:
|
||||
- redis-server
|
||||
image: redis:6.2.4-alpine
|
||||
image: redis:6.2.6-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle: {}
|
||||
livenessProbe:
|
||||
@@ -1877,7 +1877,7 @@ spec:
|
||||
- /data/conf/sentinel.conf
|
||||
command:
|
||||
- redis-sentinel
|
||||
image: redis:6.2.4-alpine
|
||||
image: redis:6.2.6-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle: {}
|
||||
livenessProbe:
|
||||
@@ -1923,7 +1923,7 @@ spec:
|
||||
value: 40000915ab58c3fa8fd888fb8b24711944e6cbb4
|
||||
- name: SENTINEL_ID_2
|
||||
value: 2bbec7894d954a8af3bb54d13eaec53cb024e2ca
|
||||
image: redis:6.2.4-alpine
|
||||
image: redis:6.2.6-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: config-init
|
||||
volumeMounts:
|
||||
|
||||
@@ -3057,7 +3057,7 @@ spec:
|
||||
- command:
|
||||
- /shared/argocd-dex
|
||||
- rundex
|
||||
image: ghcr.io/dexidp/dex:v2.30.0
|
||||
image: ghcr.io/dexidp/dex:v2.30.2
|
||||
imagePullPolicy: Always
|
||||
name: dex
|
||||
ports:
|
||||
@@ -3079,7 +3079,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -3132,7 +3132,7 @@ spec:
|
||||
- ""
|
||||
- --appendonly
|
||||
- "no"
|
||||
image: redis:6.2.4-alpine
|
||||
image: redis:6.2.6-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -3260,7 +3260,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -3309,7 +3309,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
@@ -3532,7 +3532,7 @@ spec:
|
||||
key: server.http.cookie.maxnumber
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -3722,7 +3722,7 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -416,7 +416,7 @@ spec:
|
||||
- command:
|
||||
- /shared/argocd-dex
|
||||
- rundex
|
||||
image: ghcr.io/dexidp/dex:v2.30.0
|
||||
image: ghcr.io/dexidp/dex:v2.30.2
|
||||
imagePullPolicy: Always
|
||||
name: dex
|
||||
ports:
|
||||
@@ -438,7 +438,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -491,7 +491,7 @@ spec:
|
||||
- ""
|
||||
- --appendonly
|
||||
- "no"
|
||||
image: redis:6.2.4-alpine
|
||||
image: redis:6.2.6-alpine
|
||||
imagePullPolicy: Always
|
||||
name: redis
|
||||
ports:
|
||||
@@ -619,7 +619,7 @@ spec:
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -668,7 +668,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
- mountPath: /var/run/argocd
|
||||
@@ -891,7 +891,7 @@ spec:
|
||||
key: server.http.cookie.maxnumber
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -1081,7 +1081,7 @@ spec:
|
||||
key: controller.default.cache.expiration
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.2.2
|
||||
image: quay.io/argoproj/argocd:v2.2.8
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -39,6 +39,8 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
type RepoAppsQuery struct {
|
||||
Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
|
||||
Revision string `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"`
|
||||
AppName string `protobuf:"bytes,3,opt,name=appName,proto3" json:"appName,omitempty"`
|
||||
AppProject string `protobuf:"bytes,4,opt,name=appProject,proto3" json:"appProject,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -91,6 +93,20 @@ func (m *RepoAppsQuery) GetRevision() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *RepoAppsQuery) GetAppName() string {
|
||||
if m != nil {
|
||||
return m.AppName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *RepoAppsQuery) GetAppProject() string {
|
||||
if m != nil {
|
||||
return m.AppProject
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// AppInfo contains application type and app file path
|
||||
type AppInfo struct {
|
||||
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
|
||||
@@ -151,6 +167,7 @@ func (m *AppInfo) GetPath() string {
|
||||
type RepoAppDetailsQuery struct {
|
||||
Source *v1alpha1.ApplicationSource `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"`
|
||||
AppName string `protobuf:"bytes,2,opt,name=appName,proto3" json:"appName,omitempty"`
|
||||
AppProject string `protobuf:"bytes,3,opt,name=appProject,proto3" json:"appProject,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -203,6 +220,13 @@ func (m *RepoAppDetailsQuery) GetAppName() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *RepoAppDetailsQuery) GetAppProject() string {
|
||||
if m != nil {
|
||||
return m.AppProject
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// RepoAppsResponse contains applications of specified repository
|
||||
type RepoAppsResponse struct {
|
||||
Items []*AppInfo `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
|
||||
@@ -663,77 +687,78 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_8d38260443475705 = []byte{
|
||||
// 1105 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x5f, 0x6f, 0x1b, 0x45,
|
||||
0x10, 0xd7, 0xe5, 0x8f, 0x93, 0x6c, 0xfe, 0xd4, 0xd9, 0x84, 0x72, 0xb8, 0x69, 0x1a, 0x6d, 0x4b,
|
||||
0x15, 0xa2, 0x72, 0xd7, 0x18, 0x21, 0xaa, 0x22, 0x40, 0x69, 0x12, 0xb5, 0x11, 0x11, 0x81, 0xab,
|
||||
0xc2, 0x03, 0x02, 0xa1, 0xcd, 0x79, 0x62, 0x1f, 0x39, 0xdf, 0x6e, 0x77, 0xd7, 0x06, 0xab, 0xea,
|
||||
0x0b, 0x4f, 0x48, 0xf0, 0x82, 0x10, 0x52, 0xdf, 0x78, 0x41, 0xe2, 0x81, 0xcf, 0xc0, 0x3b, 0x8f,
|
||||
0x48, 0x7c, 0x01, 0x14, 0xf1, 0x39, 0x10, 0xda, 0xdd, 0xf3, 0xdd, 0x39, 0xb1, 0x9d, 0x54, 0x84,
|
||||
0xbc, 0xed, 0xfc, 0x66, 0x76, 0xe6, 0x37, 0xe3, 0x99, 0x59, 0x1f, 0x22, 0x12, 0x44, 0x1b, 0x84,
|
||||
0x2f, 0x80, 0x33, 0x19, 0x29, 0x26, 0x3a, 0x85, 0xa3, 0xc7, 0x05, 0x53, 0x0c, 0xa3, 0x1c, 0xa9,
|
||||
0x2c, 0xd6, 0x59, 0x9d, 0x19, 0xd8, 0xd7, 0x27, 0x6b, 0x51, 0x59, 0xaa, 0x33, 0x56, 0x8f, 0xc1,
|
||||
0xa7, 0x3c, 0xf2, 0x69, 0x92, 0x30, 0x45, 0x55, 0xc4, 0x12, 0x99, 0x6a, 0xc9, 0xd1, 0x3d, 0xe9,
|
||||
0x45, 0xcc, 0x68, 0x43, 0x26, 0xc0, 0x6f, 0xaf, 0xfb, 0x75, 0x48, 0x40, 0x50, 0x05, 0xb5, 0xd4,
|
||||
0x66, 0xb7, 0x1e, 0xa9, 0x46, 0xeb, 0xc0, 0x0b, 0x59, 0xd3, 0xa7, 0xc2, 0x84, 0xf8, 0xc2, 0x1c,
|
||||
0x5e, 0x0f, 0x6b, 0x7e, 0xbb, 0xea, 0xf3, 0xa3, 0xba, 0xbe, 0x2f, 0x7d, 0xca, 0x79, 0x1c, 0x85,
|
||||
0xc6, 0xbf, 0xdf, 0x5e, 0xa7, 0x31, 0x6f, 0xd0, 0xd3, 0xde, 0xb6, 0xcf, 0xf0, 0x66, 0x12, 0x3a,
|
||||
0x33, 0x71, 0xf2, 0x1e, 0x9a, 0x0d, 0x80, 0xb3, 0x0d, 0xce, 0xe5, 0x47, 0x2d, 0x10, 0x1d, 0x8c,
|
||||
0xd1, 0x98, 0x36, 0x72, 0x9d, 0x15, 0x67, 0x75, 0x2a, 0x30, 0x67, 0x5c, 0x41, 0x93, 0x02, 0xda,
|
||||
0x91, 0x8c, 0x58, 0xe2, 0x8e, 0x18, 0x3c, 0x93, 0xc9, 0x3a, 0x9a, 0xd8, 0xe0, 0x7c, 0x27, 0x39,
|
||||
0x64, 0xfa, 0xaa, 0xea, 0x70, 0xe8, 0x5e, 0xd5, 0x67, 0x8d, 0x71, 0xaa, 0x1a, 0xe9, 0x35, 0x73,
|
||||
0x26, 0xcf, 0x1d, 0xb4, 0x90, 0x06, 0xdd, 0x02, 0x45, 0xa3, 0x38, 0x0d, 0x5d, 0x47, 0x25, 0xc9,
|
||||
0x5a, 0x22, 0xb4, 0x1e, 0xa6, 0xab, 0x7b, 0x5e, 0x9e, 0xa3, 0xd7, 0xcd, 0xd1, 0x1c, 0x3e, 0x0f,
|
||||
0x6b, 0x5e, 0xbb, 0xea, 0xf1, 0xa3, 0xba, 0xa7, 0x2b, 0xe6, 0x15, 0x2a, 0xe6, 0x75, 0x2b, 0xe6,
|
||||
0x6d, 0xe4, 0xe0, 0x63, 0xe3, 0x36, 0x48, 0xdd, 0x63, 0x17, 0x4d, 0x50, 0xce, 0x3f, 0xa0, 0x4d,
|
||||
0x48, 0x79, 0x75, 0x45, 0xf2, 0x0e, 0x2a, 0x77, 0xcb, 0x11, 0x80, 0xe4, 0x2c, 0x91, 0x80, 0x5f,
|
||||
0x43, 0xe3, 0x91, 0x82, 0xa6, 0x74, 0x9d, 0x95, 0xd1, 0xd5, 0xe9, 0xea, 0x82, 0x57, 0x28, 0x62,
|
||||
0x9a, 0x7a, 0x60, 0x2d, 0xc8, 0x26, 0x9a, 0xd2, 0xd7, 0x07, 0x57, 0x92, 0xa0, 0x99, 0x43, 0xa6,
|
||||
0xa9, 0xc0, 0xa1, 0x00, 0x69, 0xcb, 0x32, 0x19, 0xf4, 0x60, 0xe4, 0xb7, 0x31, 0x74, 0xc5, 0x90,
|
||||
0x08, 0x43, 0x90, 0xc3, 0x7f, 0x95, 0x96, 0x04, 0x91, 0xe4, 0x69, 0x64, 0xb2, 0xd6, 0x71, 0x2a,
|
||||
0xe5, 0x97, 0x4c, 0xd4, 0xdc, 0x51, 0xab, 0xeb, 0xca, 0xf8, 0x16, 0x9a, 0x95, 0xb2, 0xf1, 0xa1,
|
||||
0x88, 0xda, 0x54, 0xc1, 0xfb, 0xd0, 0x71, 0xc7, 0x8c, 0x41, 0x2f, 0xa8, 0x3d, 0x44, 0x89, 0x84,
|
||||
0xb0, 0x25, 0xc0, 0x1d, 0x37, 0x2c, 0x33, 0x19, 0xdf, 0x41, 0xf3, 0x2a, 0x96, 0x9b, 0x71, 0x04,
|
||||
0x89, 0xda, 0x04, 0xa1, 0xb6, 0xa8, 0xa2, 0x6e, 0xc9, 0x78, 0x39, 0xad, 0xc0, 0x6b, 0xa8, 0xdc,
|
||||
0x03, 0xea, 0x90, 0x13, 0xc6, 0xf8, 0x14, 0x9e, 0xb5, 0xd0, 0x54, 0x6f, 0x0b, 0x99, 0x1c, 0x91,
|
||||
0xc5, 0x4c, 0x7e, 0x4b, 0x68, 0x0a, 0x12, 0x7a, 0x10, 0xc3, 0x5e, 0x18, 0xb9, 0xd3, 0x86, 0x5e,
|
||||
0x0e, 0xe0, 0xbb, 0x68, 0xc1, 0x76, 0xce, 0x06, 0xe7, 0x85, 0x3c, 0x67, 0x8c, 0x83, 0x7e, 0x2a,
|
||||
0xbc, 0x82, 0xa6, 0x33, 0x78, 0x67, 0xcb, 0x9d, 0x5d, 0x71, 0x56, 0x47, 0x83, 0x22, 0x84, 0xef,
|
||||
0xa1, 0x97, 0x73, 0x31, 0x91, 0x8a, 0xc6, 0xb1, 0x69, 0xad, 0x9d, 0x2d, 0x77, 0xce, 0x58, 0x0f,
|
||||
0x52, 0xe3, 0x77, 0x51, 0x25, 0x53, 0x6d, 0x27, 0x0a, 0x04, 0x17, 0x91, 0x84, 0x07, 0x54, 0xc2,
|
||||
0xbe, 0x88, 0xdd, 0x2b, 0x86, 0xd4, 0x10, 0x0b, 0xbc, 0x88, 0xc6, 0xb9, 0x60, 0x5f, 0x75, 0xdc,
|
||||
0xb2, 0x31, 0xb5, 0x82, 0xee, 0x61, 0x3d, 0x0e, 0x10, 0x2a, 0x77, 0xde, 0xf6, 0x70, 0x2a, 0x92,
|
||||
0x39, 0x34, 0xa3, 0xdb, 0xa7, 0xdb, 0xbf, 0xe4, 0x17, 0x07, 0xcd, 0x6b, 0x60, 0x53, 0x00, 0x55,
|
||||
0x10, 0xc0, 0x93, 0x16, 0x48, 0x85, 0x3f, 0x2d, 0x74, 0xd4, 0x74, 0xf5, 0xd1, 0x7f, 0x1b, 0xb5,
|
||||
0x20, 0x9b, 0x88, 0xb4, 0x37, 0xaf, 0xa2, 0x52, 0x8b, 0x4b, 0x10, 0x2a, 0xed, 0xf0, 0x54, 0xd2,
|
||||
0xbf, 0x5b, 0x28, 0xa0, 0x26, 0xf7, 0x92, 0xb8, 0x63, 0x1a, 0x73, 0x32, 0xc8, 0x01, 0xf2, 0xc4,
|
||||
0x12, 0xdd, 0xe7, 0xb5, 0xcb, 0x22, 0x5a, 0xfd, 0x67, 0xce, 0xc6, 0xb4, 0xe0, 0x63, 0x10, 0xed,
|
||||
0x28, 0x04, 0xfc, 0x9d, 0x83, 0xc6, 0x76, 0x23, 0xa9, 0xf0, 0x4b, 0xc5, 0x61, 0xcf, 0x46, 0xbb,
|
||||
0xb2, 0x7b, 0x51, 0x2c, 0x74, 0x10, 0x72, 0xe3, 0xeb, 0x3f, 0xff, 0xfe, 0x61, 0xe4, 0x2a, 0x5e,
|
||||
0x34, 0xcf, 0x47, 0x7b, 0x3d, 0xdf, 0xd2, 0x11, 0xc8, 0x6f, 0x46, 0x1c, 0xfc, 0xad, 0x83, 0x46,
|
||||
0x1f, 0xc2, 0x40, 0x36, 0x17, 0x56, 0x13, 0x72, 0xd3, 0x30, 0xb9, 0x8e, 0xaf, 0xf5, 0x63, 0xe2,
|
||||
0x3f, 0xd5, 0xd2, 0x33, 0xfc, 0xa3, 0x83, 0xca, 0x9a, 0x77, 0x50, 0xd0, 0x5d, 0x4e, 0xa1, 0x96,
|
||||
0x86, 0x15, 0x0a, 0x7f, 0x86, 0x26, 0x2d, 0xad, 0xc3, 0x81, 0x74, 0xca, 0xbd, 0xf0, 0xa1, 0x24,
|
||||
0xab, 0xc6, 0x25, 0xc1, 0x2b, 0x43, 0x32, 0xf6, 0x85, 0x76, 0xd9, 0xb4, 0xee, 0xf5, 0xd3, 0x80,
|
||||
0x5f, 0x39, 0xe9, 0x3e, 0x7b, 0x3f, 0x2b, 0x4b, 0xfd, 0x54, 0xd9, 0x2c, 0x9e, 0x2b, 0x1c, 0xd5,
|
||||
0x21, 0xbe, 0x77, 0xd0, 0xec, 0x43, 0x50, 0xf9, 0x1b, 0x89, 0x6f, 0xf4, 0xf1, 0x5c, 0x7c, 0x3f,
|
||||
0x2b, 0x64, 0xb0, 0x41, 0x46, 0xe0, 0x6d, 0x43, 0xe0, 0x4d, 0x72, 0xb7, 0x3f, 0x01, 0xfb, 0x40,
|
||||
0x1a, 0x3f, 0xfb, 0xc1, 0xae, 0xa1, 0x52, 0xb3, 0x1e, 0xee, 0x3b, 0x6b, 0xb8, 0x6d, 0x28, 0x3d,
|
||||
0x82, 0xb8, 0xb9, 0xd9, 0xa0, 0x42, 0x0d, 0x2c, 0xf3, 0x72, 0x11, 0xce, 0xcd, 0x33, 0x12, 0x9e,
|
||||
0x21, 0xb1, 0x8a, 0x6f, 0x0f, 0xab, 0x42, 0x03, 0xe2, 0x66, 0x68, 0xc3, 0x3c, 0x77, 0x50, 0xc9,
|
||||
0x6e, 0x2f, 0x7c, 0xfd, 0x64, 0xc4, 0x9e, 0xad, 0x76, 0x81, 0xa3, 0xf0, 0xaa, 0xe1, 0xb8, 0x44,
|
||||
0xfa, 0xf6, 0xda, 0x7d, 0xb3, 0x3c, 0xf4, 0x68, 0xfe, 0xe4, 0xa0, 0x72, 0x97, 0x42, 0xf7, 0xee,
|
||||
0xe5, 0x91, 0x24, 0x67, 0x93, 0xc4, 0x3f, 0x3b, 0xa8, 0x64, 0x37, 0xea, 0x69, 0x5e, 0x3d, 0x9b,
|
||||
0xf6, 0x02, 0x79, 0xad, 0xdb, 0x1f, 0xb8, 0x32, 0xa4, 0xcd, 0x0d, 0x95, 0x67, 0x79, 0x21, 0x7f,
|
||||
0x75, 0x50, 0xb9, 0x4b, 0x67, 0x70, 0x21, 0xff, 0x2f, 0xc2, 0xde, 0x8b, 0x11, 0xc6, 0x14, 0x95,
|
||||
0xb6, 0x20, 0x06, 0x05, 0x83, 0x46, 0xc0, 0x3d, 0x09, 0x67, 0xcd, 0x7f, 0xdb, 0xee, 0xd8, 0xb5,
|
||||
0x61, 0x3b, 0x56, 0x17, 0xa4, 0x81, 0xca, 0x36, 0x44, 0xa1, 0x1e, 0x2f, 0x1c, 0xec, 0xe6, 0x39,
|
||||
0x82, 0xe1, 0xa7, 0x68, 0xee, 0x63, 0x1a, 0x47, 0xba, 0xb2, 0xf6, 0x3f, 0x27, 0xbe, 0x76, 0x6a,
|
||||
0x93, 0xe4, 0xff, 0x45, 0x87, 0x44, 0xab, 0x9a, 0x68, 0x77, 0xc8, 0xad, 0x61, 0x73, 0xdd, 0x4e,
|
||||
0x43, 0xd9, 0x4a, 0x3e, 0xd8, 0xfe, 0xfd, 0x78, 0xd9, 0xf9, 0xe3, 0x78, 0xd9, 0xf9, 0xeb, 0x78,
|
||||
0xd9, 0xf9, 0xe4, 0xad, 0xf3, 0x7d, 0x23, 0x85, 0xe6, 0x4f, 0x63, 0xe1, 0x6b, 0xe6, 0xa0, 0x64,
|
||||
0x3e, 0x67, 0xde, 0xf8, 0x37, 0x00, 0x00, 0xff, 0xff, 0x38, 0x35, 0xe9, 0x0a, 0xed, 0x0d, 0x00,
|
||||
0x00,
|
||||
// 1129 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x4d, 0x6f, 0x1c, 0x45,
|
||||
0x13, 0xd6, 0xf8, 0x63, 0x6d, 0xb7, 0x3f, 0xb2, 0x6e, 0xfb, 0xcd, 0x3b, 0x6c, 0x1c, 0xc7, 0x9a,
|
||||
0x84, 0xc8, 0x58, 0x61, 0x26, 0x5e, 0x84, 0x88, 0x82, 0x40, 0x72, 0x6c, 0x2b, 0xb1, 0xb0, 0x70,
|
||||
0x98, 0xc8, 0x1c, 0x10, 0x08, 0xb5, 0x67, 0x6b, 0x77, 0x27, 0x9e, 0x9d, 0xee, 0x74, 0xf7, 0x0e,
|
||||
0xac, 0xa2, 0x5c, 0x38, 0x21, 0xc1, 0x05, 0x21, 0x24, 0x6e, 0x5c, 0x90, 0x38, 0xf0, 0x07, 0xb8,
|
||||
0x70, 0xe7, 0x88, 0xc4, 0x1f, 0x40, 0x16, 0xbf, 0x03, 0xa1, 0xee, 0x9e, 0x9d, 0x99, 0xf5, 0x7e,
|
||||
0xd8, 0x11, 0xc6, 0xb7, 0xae, 0xa7, 0x6a, 0xab, 0x9e, 0x7a, 0xba, 0xba, 0x7b, 0x07, 0x39, 0x02,
|
||||
0x78, 0x02, 0xdc, 0xe3, 0xc0, 0xa8, 0x08, 0x25, 0xe5, 0x9d, 0xc2, 0xd2, 0x65, 0x9c, 0x4a, 0x8a,
|
||||
0x51, 0x8e, 0x54, 0x96, 0x1b, 0xb4, 0x41, 0x35, 0xec, 0xa9, 0x95, 0x89, 0xa8, 0xac, 0x34, 0x28,
|
||||
0x6d, 0x44, 0xe0, 0x11, 0x16, 0x7a, 0x24, 0x8e, 0xa9, 0x24, 0x32, 0xa4, 0xb1, 0x48, 0xbd, 0xce,
|
||||
0xf1, 0x3d, 0xe1, 0x86, 0x54, 0x7b, 0x03, 0xca, 0xc1, 0x4b, 0x36, 0xbd, 0x06, 0xc4, 0xc0, 0x89,
|
||||
0x84, 0x5a, 0x1a, 0xb3, 0xdf, 0x08, 0x65, 0xb3, 0x7d, 0xe4, 0x06, 0xb4, 0xe5, 0x11, 0xae, 0x4b,
|
||||
0x3c, 0xd5, 0x8b, 0xd7, 0x83, 0x9a, 0x97, 0x54, 0x3d, 0x76, 0xdc, 0x50, 0xbf, 0x17, 0x1e, 0x61,
|
||||
0x2c, 0x0a, 0x03, 0x9d, 0xdf, 0x4b, 0x36, 0x49, 0xc4, 0x9a, 0xa4, 0x3f, 0xdb, 0xee, 0x19, 0xd9,
|
||||
0x74, 0x43, 0x67, 0x36, 0xee, 0x74, 0xd0, 0xbc, 0x0f, 0x8c, 0x6e, 0x31, 0x26, 0x3e, 0x68, 0x03,
|
||||
0xef, 0x60, 0x8c, 0x26, 0x54, 0x90, 0x6d, 0xad, 0x59, 0xeb, 0x33, 0xbe, 0x5e, 0xe3, 0x0a, 0x9a,
|
||||
0xe6, 0x90, 0x84, 0x22, 0xa4, 0xb1, 0x3d, 0xa6, 0xf1, 0xcc, 0xc6, 0x36, 0x9a, 0x22, 0x8c, 0xbd,
|
||||
0x4f, 0x5a, 0x60, 0x8f, 0x6b, 0x57, 0xd7, 0xc4, 0xab, 0x08, 0x11, 0xc6, 0x1e, 0x73, 0xfa, 0x14,
|
||||
0x02, 0x69, 0x4f, 0x68, 0x67, 0x01, 0x71, 0x36, 0xd1, 0xd4, 0x16, 0x63, 0x7b, 0x71, 0x9d, 0xaa,
|
||||
0xa2, 0xb2, 0xc3, 0xa0, 0x5b, 0x54, 0xad, 0x15, 0xc6, 0x88, 0x6c, 0xa6, 0x05, 0xf5, 0xda, 0xf9,
|
||||
0xc5, 0x42, 0x4b, 0x29, 0xdd, 0x1d, 0x90, 0x24, 0x8c, 0x52, 0xd2, 0x0d, 0x54, 0x12, 0xb4, 0xcd,
|
||||
0x03, 0x93, 0x61, 0xb6, 0x7a, 0xe0, 0xe6, 0xea, 0xb8, 0x5d, 0x75, 0xf4, 0xe2, 0xd3, 0xa0, 0xe6,
|
||||
0x26, 0x55, 0x97, 0x1d, 0x37, 0x5c, 0xa5, 0xb5, 0x5b, 0xd0, 0xda, 0xed, 0x6a, 0xed, 0x6e, 0xe5,
|
||||
0xe0, 0x13, 0x9d, 0xd6, 0x4f, 0xd3, 0x17, 0xbb, 0x1d, 0x1b, 0xd5, 0xed, 0x78, 0x5f, 0xb7, 0xef,
|
||||
0xa0, 0x72, 0x57, 0x68, 0x1f, 0x04, 0xa3, 0xb1, 0x00, 0xfc, 0x1a, 0x9a, 0x0c, 0x25, 0xb4, 0x84,
|
||||
0x6d, 0xad, 0x8d, 0xaf, 0xcf, 0x56, 0x97, 0xdc, 0xc2, 0xf6, 0xa4, 0xd2, 0xf8, 0x26, 0xc2, 0xd9,
|
||||
0x46, 0x33, 0xea, 0xe7, 0xc3, 0xf7, 0xc8, 0x41, 0x73, 0x75, 0xaa, 0xa8, 0x42, 0x9d, 0x83, 0x30,
|
||||
0xb2, 0x4d, 0xfb, 0x3d, 0x98, 0xf3, 0xeb, 0x04, 0xba, 0xa2, 0x49, 0x04, 0x01, 0x88, 0xd1, 0xfb,
|
||||
0xdd, 0x16, 0xc0, 0xe3, 0xbc, 0xcd, 0xcc, 0x56, 0x3e, 0x46, 0x84, 0xf8, 0x8c, 0xf2, 0x5a, 0xda,
|
||||
0x65, 0x66, 0xe3, 0x5b, 0x68, 0x5e, 0x88, 0xe6, 0x63, 0x1e, 0x26, 0x44, 0xc2, 0x7b, 0xd0, 0x49,
|
||||
0x37, 0xbd, 0x17, 0x54, 0x19, 0xc2, 0x58, 0x40, 0xd0, 0xe6, 0x60, 0x4f, 0x6a, 0x96, 0x99, 0x8d,
|
||||
0xef, 0xa0, 0x45, 0x19, 0x89, 0xed, 0x28, 0x84, 0x58, 0x6e, 0x03, 0x97, 0x3b, 0x44, 0x12, 0xbb,
|
||||
0xa4, 0xb3, 0xf4, 0x3b, 0xf0, 0x06, 0x2a, 0xf7, 0x80, 0xaa, 0xe4, 0x94, 0x0e, 0xee, 0xc3, 0xb3,
|
||||
0x11, 0x9b, 0xe9, 0x1d, 0x31, 0xdd, 0x23, 0x32, 0x98, 0xee, 0x6f, 0x05, 0xcd, 0x40, 0x4c, 0x8e,
|
||||
0x22, 0x38, 0x08, 0x42, 0x7b, 0x56, 0xd3, 0xcb, 0x01, 0x7c, 0x17, 0x2d, 0x99, 0xc9, 0xda, 0x52,
|
||||
0x3b, 0x9b, 0xf5, 0x39, 0xa7, 0x13, 0x0c, 0x72, 0xe1, 0x35, 0x34, 0x9b, 0xc1, 0x7b, 0x3b, 0xf6,
|
||||
0xfc, 0x9a, 0xb5, 0x3e, 0xee, 0x17, 0x21, 0x7c, 0x0f, 0xfd, 0x3f, 0x37, 0x63, 0x21, 0x49, 0x14,
|
||||
0xe9, 0xd1, 0xdb, 0xdb, 0xb1, 0x17, 0x74, 0xf4, 0x30, 0x37, 0x7e, 0x17, 0x55, 0x32, 0xd7, 0x6e,
|
||||
0x2c, 0x81, 0x33, 0x1e, 0x0a, 0x78, 0x40, 0x04, 0x1c, 0xf2, 0xc8, 0xbe, 0xa2, 0x49, 0x8d, 0x88,
|
||||
0xc0, 0xcb, 0x68, 0x92, 0x71, 0xfa, 0x79, 0xc7, 0x2e, 0xeb, 0x50, 0x63, 0xa8, 0x19, 0x67, 0xe9,
|
||||
0x18, 0x2f, 0x9a, 0x19, 0x4f, 0x4d, 0x67, 0x01, 0xcd, 0xa9, 0xf1, 0xe9, 0xce, 0xaf, 0xf3, 0x93,
|
||||
0x85, 0x16, 0x15, 0xb0, 0xcd, 0x81, 0x48, 0xf0, 0xe1, 0x59, 0x1b, 0x84, 0xc4, 0x1f, 0x17, 0x26,
|
||||
0x6a, 0xb6, 0xfa, 0xe8, 0xdf, 0x1d, 0x45, 0x3f, 0x3b, 0x11, 0xe9, 0x6c, 0x5e, 0x45, 0xa5, 0x36,
|
||||
0x13, 0xc0, 0x65, 0x3a, 0xe1, 0xa9, 0xa5, 0xf6, 0x2d, 0xe0, 0x50, 0x13, 0x07, 0x71, 0xd4, 0xd1,
|
||||
0x83, 0x39, 0xed, 0xe7, 0x80, 0xf3, 0xcc, 0x10, 0x3d, 0x64, 0xb5, 0xcb, 0x22, 0x5a, 0xfd, 0x7b,
|
||||
0xc1, 0xd4, 0x34, 0xe0, 0x13, 0xe0, 0x49, 0x18, 0x00, 0xfe, 0xda, 0x42, 0x13, 0xfb, 0xa1, 0x90,
|
||||
0xf8, 0x7f, 0xc5, 0xc3, 0x9e, 0x1d, 0xed, 0xca, 0xfe, 0x45, 0xb1, 0x50, 0x45, 0x9c, 0x1b, 0x5f,
|
||||
0xfc, 0xf1, 0xd7, 0xb7, 0x63, 0x57, 0xf1, 0xb2, 0x7e, 0x98, 0x92, 0xcd, 0xfc, 0xfe, 0x0f, 0x41,
|
||||
0x7c, 0x39, 0x66, 0xe1, 0xaf, 0x2c, 0x34, 0xfe, 0x10, 0x86, 0xb2, 0xb9, 0x30, 0x4d, 0x9c, 0x9b,
|
||||
0x9a, 0xc9, 0x75, 0x7c, 0x6d, 0x10, 0x13, 0xef, 0xb9, 0xb2, 0x5e, 0xe0, 0xef, 0x2c, 0x54, 0x56,
|
||||
0xbc, 0xfd, 0x82, 0xef, 0x72, 0x84, 0x5a, 0x19, 0x25, 0x14, 0xfe, 0x04, 0x4d, 0x1b, 0x5a, 0xf5,
|
||||
0xa1, 0x74, 0xca, 0xbd, 0x70, 0x5d, 0x38, 0xeb, 0x3a, 0xa5, 0x83, 0xd7, 0x46, 0x74, 0xec, 0x71,
|
||||
0x95, 0xb2, 0x65, 0xd2, 0xab, 0xa7, 0x01, 0xbf, 0x72, 0x3a, 0x7d, 0xf6, 0x32, 0x57, 0x56, 0x06,
|
||||
0xb9, 0xb2, 0xb3, 0x78, 0xae, 0x72, 0x44, 0x95, 0xf8, 0xc6, 0x42, 0xf3, 0x0f, 0x41, 0xe6, 0x6f,
|
||||
0x28, 0xbe, 0x31, 0x20, 0x73, 0xf1, 0x7d, 0xad, 0x38, 0xc3, 0x03, 0x32, 0x02, 0x6f, 0x6b, 0x02,
|
||||
0x6f, 0x3a, 0x77, 0x07, 0x13, 0x30, 0x0f, 0xa8, 0xce, 0x73, 0xe8, 0xef, 0x6b, 0x2a, 0x35, 0x93,
|
||||
0xe1, 0xbe, 0xb5, 0x81, 0x13, 0x4d, 0xe9, 0x11, 0x44, 0xad, 0xed, 0x26, 0xe1, 0x72, 0xa8, 0xcc,
|
||||
0xab, 0x45, 0x38, 0x0f, 0xcf, 0x48, 0xb8, 0x9a, 0xc4, 0x3a, 0xbe, 0x3d, 0x4a, 0x85, 0x26, 0x44,
|
||||
0xad, 0xc0, 0x94, 0xf9, 0xde, 0x42, 0x25, 0x73, 0x7b, 0xe1, 0xeb, 0xa7, 0x2b, 0xf6, 0xdc, 0x6a,
|
||||
0x17, 0x78, 0x14, 0x5e, 0xd5, 0x1c, 0x57, 0x9c, 0x81, 0xb3, 0x76, 0x5f, 0x5f, 0x1e, 0xea, 0x68,
|
||||
0xfe, 0x60, 0xa1, 0x72, 0x97, 0x42, 0xf7, 0xb7, 0x97, 0x47, 0xd2, 0x39, 0x9b, 0x24, 0xfe, 0xd1,
|
||||
0x42, 0x25, 0x73, 0xa3, 0xf6, 0xf3, 0xea, 0xb9, 0x69, 0x2f, 0x90, 0xd7, 0xa6, 0xd9, 0xe0, 0xca,
|
||||
0x88, 0x31, 0xd7, 0x54, 0x5e, 0xe4, 0x42, 0xfe, 0x6c, 0xa1, 0x72, 0x97, 0xce, 0x70, 0x21, 0xff,
|
||||
0x2b, 0xc2, 0xee, 0xcb, 0x11, 0xc6, 0x04, 0x95, 0x76, 0x20, 0x02, 0x09, 0xc3, 0x8e, 0x80, 0x7d,
|
||||
0x1a, 0xce, 0x86, 0xff, 0xb6, 0xb9, 0x63, 0x37, 0x46, 0xdd, 0xb1, 0x4a, 0x90, 0x26, 0x2a, 0x9b,
|
||||
0x12, 0x05, 0x3d, 0x5e, 0xba, 0xd8, 0xcd, 0x73, 0x14, 0xc3, 0xcf, 0xd1, 0xc2, 0x87, 0x24, 0x0a,
|
||||
0x95, 0xb2, 0xe6, 0x3f, 0x27, 0xbe, 0xd6, 0x77, 0x93, 0xe4, 0xff, 0x45, 0x47, 0x54, 0xab, 0xea,
|
||||
0x6a, 0x77, 0x9c, 0x5b, 0xa3, 0xce, 0x75, 0x92, 0x96, 0x32, 0x4a, 0x3e, 0xd8, 0xfd, 0xed, 0x64,
|
||||
0xd5, 0xfa, 0xfd, 0x64, 0xd5, 0xfa, 0xf3, 0x64, 0xd5, 0xfa, 0xe8, 0xad, 0xf3, 0x7d, 0x7d, 0x05,
|
||||
0xfa, 0x4f, 0x63, 0xe1, 0x3b, 0xe9, 0xa8, 0xa4, 0x3f, 0x94, 0xde, 0xf8, 0x27, 0x00, 0x00, 0xff,
|
||||
0xff, 0x52, 0x69, 0xb3, 0xbe, 0x47, 0x0e, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@@ -1338,6 +1363,20 @@ func (m *RepoAppsQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if len(m.AppProject) > 0 {
|
||||
i -= len(m.AppProject)
|
||||
copy(dAtA[i:], m.AppProject)
|
||||
i = encodeVarintRepository(dAtA, i, uint64(len(m.AppProject)))
|
||||
i--
|
||||
dAtA[i] = 0x22
|
||||
}
|
||||
if len(m.AppName) > 0 {
|
||||
i -= len(m.AppName)
|
||||
copy(dAtA[i:], m.AppName)
|
||||
i = encodeVarintRepository(dAtA, i, uint64(len(m.AppName)))
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
}
|
||||
if len(m.Revision) > 0 {
|
||||
i -= len(m.Revision)
|
||||
copy(dAtA[i:], m.Revision)
|
||||
@@ -1420,6 +1459,13 @@ func (m *RepoAppDetailsQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if len(m.AppProject) > 0 {
|
||||
i -= len(m.AppProject)
|
||||
copy(dAtA[i:], m.AppProject)
|
||||
i = encodeVarintRepository(dAtA, i, uint64(len(m.AppProject)))
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
}
|
||||
if len(m.AppName) > 0 {
|
||||
i -= len(m.AppName)
|
||||
copy(dAtA[i:], m.AppName)
|
||||
@@ -1822,6 +1868,14 @@ func (m *RepoAppsQuery) Size() (n int) {
|
||||
if l > 0 {
|
||||
n += 1 + l + sovRepository(uint64(l))
|
||||
}
|
||||
l = len(m.AppName)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovRepository(uint64(l))
|
||||
}
|
||||
l = len(m.AppProject)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovRepository(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
@@ -1862,6 +1916,10 @@ func (m *RepoAppDetailsQuery) Size() (n int) {
|
||||
if l > 0 {
|
||||
n += 1 + l + sovRepository(uint64(l))
|
||||
}
|
||||
l = len(m.AppProject)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovRepository(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
@@ -2126,6 +2184,70 @@ func (m *RepoAppsQuery) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
m.Revision = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field AppName", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowRepository
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.AppName = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field AppProject", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowRepository
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.AppProject = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipRepository(dAtA[iNdEx:])
|
||||
@@ -2360,6 +2482,38 @@ func (m *RepoAppDetailsQuery) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
m.AppName = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field AppProject", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowRepository
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.AppProject = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipRepository(dAtA[iNdEx:])
|
||||
|
||||
@@ -27,6 +27,7 @@ API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/ap
|
||||
API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,Command,Args
|
||||
API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,Command,Command
|
||||
API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ExecProviderConfig,Args
|
||||
API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,HelmOptions,ValuesFileSchemes
|
||||
API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,HostInfo,ResourcesInfo
|
||||
API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,JWTTokens,Items
|
||||
API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,Operation,Info
|
||||
@@ -61,6 +62,7 @@ API rule violation: list_type_missing,github.com/argoproj/argo-cd/v2/pkg/apis/ap
|
||||
API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,ApplicationSourceJsonnet,TLAs
|
||||
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,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
|
||||
API rule violation: names_match,github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1,KustomizeOptions,BinaryPath
|
||||
|
||||
@@ -313,11 +313,15 @@ func (proj AppProject) IsGroupKindPermitted(gk schema.GroupKind, namespaced bool
|
||||
|
||||
// IsLiveResourcePermitted returns whether a live resource found in the cluster is permitted by an AppProject
|
||||
func (proj AppProject) IsLiveResourcePermitted(un *unstructured.Unstructured, server string, name string) bool {
|
||||
if !proj.IsGroupKindPermitted(un.GroupVersionKind().GroupKind(), un.GetNamespace() != "") {
|
||||
return proj.IsResourcePermitted(un.GroupVersionKind().GroupKind(), un.GetNamespace(), ApplicationDestination{Server: server, Name: name})
|
||||
}
|
||||
|
||||
func (proj AppProject) IsResourcePermitted(groupKind schema.GroupKind, namespace string, dest ApplicationDestination) bool {
|
||||
if !proj.IsGroupKindPermitted(groupKind, namespace != "") {
|
||||
return false
|
||||
}
|
||||
if un.GetNamespace() != "" {
|
||||
return proj.IsDestinationPermitted(ApplicationDestination{Server: server, Namespace: un.GetNamespace(), Name: name})
|
||||
if namespace != "" {
|
||||
return proj.IsDestinationPermitted(ApplicationDestination{Server: dest.Server, Name: dest.Name, Namespace: namespace})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -602,6 +602,11 @@ message HelmFileParameter {
|
||||
optional string path = 2;
|
||||
}
|
||||
|
||||
// HelmOptions holds helm options
|
||||
message HelmOptions {
|
||||
repeated string valuesFileSchemes = 1;
|
||||
}
|
||||
|
||||
// HelmParameter is a parameter that's passed to helm template during manifest generation
|
||||
message HelmParameter {
|
||||
// Name is the name of the Helm parameter
|
||||
|
||||
@@ -51,6 +51,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.GnuPGPublicKeyList": schema_pkg_apis_application_v1alpha1_GnuPGPublicKeyList(ref),
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HealthStatus": schema_pkg_apis_application_v1alpha1_HealthStatus(ref),
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HelmFileParameter": schema_pkg_apis_application_v1alpha1_HelmFileParameter(ref),
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HelmOptions": schema_pkg_apis_application_v1alpha1_HelmOptions(ref),
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HelmParameter": schema_pkg_apis_application_v1alpha1_HelmParameter(ref),
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HostInfo": schema_pkg_apis_application_v1alpha1_HostInfo(ref),
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1.HostResourceInfo": schema_pkg_apis_application_v1alpha1_HostResourceInfo(ref),
|
||||
@@ -2104,6 +2105,34 @@ func schema_pkg_apis_application_v1alpha1_HelmFileParameter(ref common.Reference
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_application_v1alpha1_HelmOptions(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "HelmOptions holds helm options",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"ValuesFileSchemes": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"ValuesFileSchemes"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_application_v1alpha1_HelmParameter(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
|
||||
@@ -2067,6 +2067,11 @@ type ConfigManagementPlugin struct {
|
||||
LockRepo bool `json:"lockRepo,omitempty" protobuf:"bytes,4,name=lockRepo"`
|
||||
}
|
||||
|
||||
// HelmOptions holds helm options
|
||||
type HelmOptions struct {
|
||||
ValuesFileSchemes []string `protobuf:"bytes,1,opt,name=valuesFileSchemes"`
|
||||
}
|
||||
|
||||
// KustomizeOptions are options for kustomize to use when building manifests
|
||||
type KustomizeOptions struct {
|
||||
// BuildOptions is a string of build parameters to use when calling `kustomize build`
|
||||
|
||||
@@ -1066,6 +1066,27 @@ func (in *HelmFileParameter) DeepCopy() *HelmFileParameter {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmOptions) DeepCopyInto(out *HelmOptions) {
|
||||
*out = *in
|
||||
if in.ValuesFileSchemes != nil {
|
||||
in, out := &in.ValuesFileSchemes, &out.ValuesFileSchemes
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmOptions.
|
||||
func (in *HelmOptions) DeepCopy() *HelmOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmParameter) DeepCopyInto(out *HelmParameter) {
|
||||
*out = *in
|
||||
|
||||
15
reposerver/apiclient/repository.go
Normal file
15
reposerver/apiclient/repository.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package apiclient
|
||||
|
||||
func (q *ManifestRequest) GetValuesFileSchemes() []string {
|
||||
if q.HelmOptions == nil {
|
||||
return nil
|
||||
}
|
||||
return q.HelmOptions.ValuesFileSchemes
|
||||
}
|
||||
|
||||
func (q *RepoServerAppDetailsQuery) GetValuesFileSchemes() []string {
|
||||
if q.HelmOptions == nil {
|
||||
return nil
|
||||
}
|
||||
return q.HelmOptions.ValuesFileSchemes
|
||||
}
|
||||
@@ -51,6 +51,7 @@ type ManifestRequest struct {
|
||||
HelmRepoCreds []*v1alpha1.RepoCreds `protobuf:"bytes,17,rep,name=helmRepoCreds,proto3" json:"helmRepoCreds,omitempty"`
|
||||
NoRevisionCache bool `protobuf:"varint,18,opt,name=noRevisionCache,proto3" json:"noRevisionCache,omitempty"`
|
||||
TrackingMethod string `protobuf:"bytes,19,opt,name=trackingMethod,proto3" json:"trackingMethod,omitempty"`
|
||||
HelmOptions *v1alpha1.HelmOptions `protobuf:"bytes,21,opt,name=helmOptions,proto3" json:"helmOptions,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -201,6 +202,13 @@ func (m *ManifestRequest) GetTrackingMethod() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ManifestRequest) GetHelmOptions() *v1alpha1.HelmOptions {
|
||||
if m != nil {
|
||||
return m.HelmOptions
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestRepositoryRequest is a query to test repository is valid or not and has valid access.
|
||||
type TestRepositoryRequest struct {
|
||||
Repo *v1alpha1.Repository `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
|
||||
@@ -604,6 +612,7 @@ type RepoServerAppDetailsQuery struct {
|
||||
NoCache bool `protobuf:"varint,6,opt,name=noCache,proto3" json:"noCache,omitempty"`
|
||||
NoRevisionCache bool `protobuf:"varint,7,opt,name=noRevisionCache,proto3" json:"noRevisionCache,omitempty"`
|
||||
TrackingMethod string `protobuf:"bytes,8,opt,name=trackingMethod,proto3" json:"trackingMethod,omitempty"`
|
||||
HelmOptions *v1alpha1.HelmOptions `protobuf:"bytes,10,opt,name=helmOptions,proto3" json:"helmOptions,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -698,6 +707,13 @@ func (m *RepoServerAppDetailsQuery) GetTrackingMethod() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *RepoServerAppDetailsQuery) GetHelmOptions() *v1alpha1.HelmOptions {
|
||||
if m != nil {
|
||||
return m.HelmOptions
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RepoAppDetailsResponse application details
|
||||
type RepoAppDetailsResponse struct {
|
||||
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
|
||||
@@ -1383,96 +1399,98 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_dd8723cfcc820480 = []byte{
|
||||
// 1423 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0x5b, 0x6f, 0x1b, 0xc5,
|
||||
0x17, 0xcf, 0x26, 0x4e, 0x62, 0x1f, 0xb7, 0x89, 0x33, 0xbd, 0xfc, 0xf7, 0x6f, 0x52, 0x2b, 0x5d,
|
||||
0x89, 0x2a, 0x50, 0xba, 0x56, 0xdd, 0x0a, 0xaa, 0x56, 0x42, 0x32, 0x69, 0x9b, 0x4a, 0x69, 0x9a,
|
||||
0xb0, 0x29, 0x48, 0xa0, 0x8a, 0x6a, 0xb2, 0x3e, 0x59, 0x0f, 0xb6, 0x77, 0xa7, 0xbb, 0x6b, 0xa3,
|
||||
0x54, 0xe2, 0x11, 0xf1, 0xc0, 0x33, 0x7c, 0x1d, 0x5e, 0xb8, 0x3d, 0xf2, 0x11, 0x50, 0xbf, 0x05,
|
||||
0x6f, 0x68, 0x66, 0x6f, 0xb3, 0xeb, 0x4d, 0x40, 0x72, 0x9b, 0xbe, 0x24, 0x33, 0xe7, 0x3e, 0x67,
|
||||
0xce, 0xfc, 0xce, 0xf1, 0xc2, 0x35, 0x1f, 0xb9, 0x17, 0xa0, 0x3f, 0x41, 0xbf, 0x2d, 0x97, 0x2c,
|
||||
0xf4, 0xfc, 0x63, 0x65, 0x69, 0x72, 0xdf, 0x0b, 0x3d, 0x02, 0x19, 0xa5, 0x79, 0xd1, 0xf1, 0x1c,
|
||||
0x4f, 0x92, 0xdb, 0x62, 0x15, 0x49, 0x34, 0xd7, 0x1d, 0xcf, 0x73, 0x86, 0xd8, 0xa6, 0x9c, 0xb5,
|
||||
0xa9, 0xeb, 0x7a, 0x21, 0x0d, 0x99, 0xe7, 0x06, 0x31, 0xd7, 0x18, 0xdc, 0x09, 0x4c, 0xe6, 0x49,
|
||||
0xae, 0xed, 0xf9, 0xd8, 0x9e, 0xdc, 0x6c, 0x3b, 0xe8, 0xa2, 0x4f, 0x43, 0xec, 0xc5, 0x32, 0x8f,
|
||||
0x1d, 0x16, 0xf6, 0xc7, 0x87, 0xa6, 0xed, 0x8d, 0xda, 0xd4, 0x97, 0x2e, 0xbe, 0x96, 0x8b, 0x1b,
|
||||
0x76, 0xaf, 0x3d, 0xe9, 0xb4, 0xf9, 0xc0, 0x11, 0xfa, 0x41, 0x9b, 0x72, 0x3e, 0x64, 0xb6, 0xb4,
|
||||
0xdf, 0x9e, 0xdc, 0xa4, 0x43, 0xde, 0xa7, 0x53, 0xd6, 0x8c, 0xbf, 0x97, 0x61, 0x75, 0x97, 0xba,
|
||||
0xec, 0x08, 0x83, 0xd0, 0xc2, 0x17, 0x63, 0x0c, 0x42, 0xf2, 0x0c, 0x2a, 0xe2, 0x1c, 0xba, 0xb6,
|
||||
0xa1, 0x6d, 0xd6, 0x3b, 0x8f, 0xcc, 0xcc, 0xa1, 0x99, 0x38, 0x94, 0x8b, 0xe7, 0x76, 0xcf, 0x9c,
|
||||
0x74, 0x4c, 0x3e, 0x70, 0x4c, 0xe1, 0xd0, 0x54, 0x1c, 0x9a, 0x89, 0x43, 0xd3, 0x4a, 0x33, 0x62,
|
||||
0x49, 0xab, 0xa4, 0x09, 0x55, 0x1f, 0x27, 0x2c, 0x60, 0x9e, 0xab, 0xcf, 0x6f, 0x68, 0x9b, 0x35,
|
||||
0x2b, 0xdd, 0x13, 0x1d, 0x96, 0x5d, 0x6f, 0x8b, 0xda, 0x7d, 0xd4, 0x17, 0x36, 0xb4, 0xcd, 0xaa,
|
||||
0x95, 0x6c, 0xc9, 0x06, 0xd4, 0x29, 0xe7, 0x8f, 0xe9, 0x21, 0x0e, 0x77, 0xf0, 0x58, 0xaf, 0x48,
|
||||
0x45, 0x95, 0x24, 0x74, 0x29, 0xe7, 0x4f, 0xe8, 0x08, 0xf5, 0x45, 0xc9, 0x4d, 0xb6, 0x64, 0x1d,
|
||||
0x6a, 0x2e, 0x1d, 0x61, 0xc0, 0xa9, 0x8d, 0x7a, 0x55, 0xf2, 0x32, 0x02, 0xf9, 0x16, 0xd6, 0x94,
|
||||
0xc0, 0x0f, 0xbc, 0xb1, 0x6f, 0xa3, 0x0e, 0xf2, 0xe8, 0x7b, 0xb3, 0x1d, 0xbd, 0x5b, 0x34, 0x6b,
|
||||
0x4d, 0x7b, 0x22, 0x5f, 0xc1, 0xa2, 0x2c, 0x1a, 0xbd, 0xbe, 0xb1, 0xf0, 0x5a, 0xb3, 0x1d, 0x99,
|
||||
0x25, 0x2e, 0x2c, 0xf3, 0xe1, 0xd8, 0x61, 0x6e, 0xa0, 0x9f, 0x93, 0x1e, 0x9e, 0xce, 0xe6, 0x61,
|
||||
0xcb, 0x73, 0x8f, 0x98, 0xb3, 0x4b, 0x5d, 0xea, 0xe0, 0x08, 0xdd, 0x70, 0x5f, 0x1a, 0xb7, 0x12,
|
||||
0x27, 0xe4, 0x25, 0x34, 0x06, 0xe3, 0x20, 0xf4, 0x46, 0xec, 0x25, 0xee, 0x71, 0x59, 0xdc, 0xfa,
|
||||
0x79, 0x99, 0xcd, 0x27, 0xb3, 0x39, 0xde, 0x29, 0x58, 0xb5, 0xa6, 0xfc, 0x88, 0x22, 0x19, 0x8c,
|
||||
0x0f, 0xf1, 0x73, 0xf4, 0x65, 0x75, 0xad, 0x44, 0x45, 0xa2, 0x90, 0xa2, 0x32, 0x62, 0xf1, 0x2e,
|
||||
0xd0, 0x57, 0x37, 0x16, 0xa2, 0x32, 0x4a, 0x49, 0x64, 0x13, 0x56, 0x27, 0xe8, 0xb3, 0xa3, 0xe3,
|
||||
0x03, 0xe6, 0xb8, 0x34, 0x1c, 0xfb, 0xa8, 0x37, 0x64, 0x29, 0x16, 0xc9, 0x64, 0x04, 0xe7, 0xfb,
|
||||
0x38, 0x1c, 0x89, 0x94, 0x6f, 0xf9, 0xd8, 0x0b, 0xf4, 0x35, 0x99, 0xdf, 0xed, 0xd9, 0x6f, 0x50,
|
||||
0x9a, 0xb3, 0xf2, 0xd6, 0x45, 0x60, 0xae, 0x67, 0xc5, 0x2f, 0x25, 0x7a, 0x23, 0x24, 0x0a, 0xac,
|
||||
0x40, 0x26, 0xd7, 0x60, 0x25, 0xf4, 0xa9, 0x3d, 0x60, 0xae, 0xb3, 0x8b, 0x61, 0xdf, 0xeb, 0xe9,
|
||||
0x17, 0x64, 0x26, 0x0a, 0x54, 0x63, 0x0c, 0x97, 0x9e, 0xca, 0x67, 0x9f, 0xd6, 0xcc, 0x59, 0x00,
|
||||
0x80, 0xf1, 0x08, 0x2e, 0x17, 0xdd, 0x06, 0xdc, 0x73, 0x03, 0x24, 0x26, 0x10, 0x99, 0x64, 0x86,
|
||||
0xbd, 0x8c, 0x2b, 0xa3, 0xa8, 0x5a, 0x25, 0x1c, 0xe3, 0x37, 0x0d, 0x1a, 0x19, 0x78, 0xc5, 0x46,
|
||||
0xd6, 0xa1, 0x36, 0x8a, 0x69, 0x81, 0xae, 0xc9, 0x0b, 0xce, 0x08, 0x79, 0x2c, 0x98, 0x2f, 0x62,
|
||||
0xc1, 0x65, 0x58, 0x8a, 0x50, 0x5e, 0xc2, 0x4f, 0xcd, 0x8a, 0x77, 0x39, 0xcc, 0xaa, 0x14, 0x30,
|
||||
0xab, 0x05, 0x10, 0xc8, 0xa7, 0xfc, 0xf4, 0x98, 0xa3, 0xbe, 0x24, 0xb9, 0x0a, 0x85, 0x18, 0x70,
|
||||
0x2e, 0xaa, 0x1c, 0x0b, 0x83, 0xf1, 0x30, 0xd4, 0x97, 0xa5, 0x44, 0x8e, 0x66, 0x78, 0xb0, 0xfa,
|
||||
0x98, 0x89, 0x33, 0x1c, 0x05, 0x67, 0x73, 0x07, 0x1f, 0x42, 0x45, 0x38, 0x13, 0x07, 0x3b, 0xf4,
|
||||
0xa9, 0x6b, 0xf7, 0x31, 0xc9, 0x55, 0xba, 0x27, 0x04, 0x2a, 0x21, 0x75, 0x02, 0x7d, 0x5e, 0xd2,
|
||||
0xe5, 0xda, 0xf8, 0x41, 0x8b, 0x22, 0xed, 0x72, 0x1e, 0xbc, 0xf5, 0x76, 0x61, 0x8c, 0x61, 0xb9,
|
||||
0xcb, 0xb9, 0x88, 0x87, 0xdc, 0x84, 0x0a, 0xe5, 0x3c, 0x3a, 0x44, 0xbd, 0x73, 0xc5, 0x54, 0x5a,
|
||||
0x73, 0x2c, 0x22, 0xfe, 0x07, 0x0f, 0xdc, 0x50, 0x58, 0x16, 0xa2, 0xcd, 0x8f, 0xa0, 0x96, 0x92,
|
||||
0x48, 0x03, 0x16, 0x06, 0x18, 0xd5, 0x5a, 0xcd, 0x12, 0x4b, 0x72, 0x11, 0x16, 0x27, 0x74, 0x38,
|
||||
0x4e, 0xaa, 0x24, 0xda, 0xdc, 0x9d, 0xbf, 0xa3, 0x19, 0xbf, 0x54, 0xe0, 0xff, 0x22, 0xce, 0x03,
|
||||
0x59, 0x1c, 0x5d, 0xce, 0xef, 0x63, 0x48, 0xd9, 0x30, 0xf8, 0x74, 0x8c, 0xfe, 0xf1, 0x1b, 0x4e,
|
||||
0x87, 0x03, 0x4b, 0x51, 0x6d, 0xc9, 0xb0, 0xde, 0x40, 0x8b, 0x8a, 0xcd, 0x67, 0x7d, 0x69, 0xe1,
|
||||
0xcd, 0xf4, 0xa5, 0xb2, 0x3e, 0x51, 0x39, 0xa3, 0x3e, 0x71, 0xf2, 0xa8, 0xa0, 0x0c, 0x20, 0x4b,
|
||||
0xf9, 0x01, 0xa4, 0x04, 0x7e, 0x97, 0xff, 0x2b, 0xfc, 0x56, 0x4b, 0xe1, 0xf7, 0xfb, 0x79, 0xb8,
|
||||
0x2c, 0xf2, 0x92, 0x15, 0x50, 0x8a, 0x61, 0xe2, 0xe9, 0x09, 0x34, 0x89, 0xca, 0x51, 0xae, 0xc9,
|
||||
0x6d, 0x58, 0x1e, 0x04, 0x9e, 0xeb, 0x62, 0x18, 0x5f, 0x7d, 0x53, 0x2d, 0xf2, 0x9d, 0x88, 0xd5,
|
||||
0xe5, 0xfc, 0x80, 0xa3, 0x6d, 0x25, 0xa2, 0xe4, 0x3a, 0x54, 0x44, 0x1b, 0x91, 0x78, 0x56, 0xef,
|
||||
0xfc, 0x4f, 0x55, 0x79, 0x84, 0xc3, 0x51, 0x22, 0x2f, 0x85, 0xc8, 0x5d, 0xa8, 0xa5, 0xb9, 0x8a,
|
||||
0x2f, 0x63, 0x3d, 0xe7, 0x24, 0x61, 0x26, 0x6a, 0x99, 0xb8, 0xd0, 0xed, 0x31, 0x1f, 0x6d, 0x09,
|
||||
0xd9, 0x8b, 0xd3, 0xba, 0xf7, 0x13, 0x66, 0xaa, 0x9b, 0x8a, 0x1b, 0xbf, 0x6a, 0x70, 0x35, 0x7b,
|
||||
0x50, 0x49, 0x36, 0x77, 0x31, 0xa4, 0x3d, 0x1a, 0xd2, 0xb7, 0x3f, 0x96, 0x5e, 0x83, 0x15, 0xbb,
|
||||
0x8f, 0xf6, 0x20, 0x1b, 0x09, 0xa2, 0xe9, 0xb4, 0x40, 0x35, 0x7e, 0x9f, 0x87, 0x95, 0xfc, 0x45,
|
||||
0x88, 0x9b, 0x14, 0xed, 0x25, 0xb9, 0x49, 0xb1, 0x26, 0xfb, 0x70, 0x0e, 0xdd, 0x09, 0xf3, 0x3d,
|
||||
0x57, 0x0c, 0x50, 0xc9, 0x0b, 0xfb, 0xe0, 0xe4, 0xeb, 0x34, 0x1f, 0x28, 0xe2, 0x11, 0x84, 0xe5,
|
||||
0x2c, 0x10, 0x17, 0x80, 0x53, 0x9f, 0x8e, 0x30, 0x44, 0x5f, 0x3c, 0xa3, 0x85, 0xd7, 0xf0, 0x8c,
|
||||
0xa2, 0x08, 0xf6, 0x13, 0xb3, 0x96, 0xe2, 0xa1, 0xf9, 0x1c, 0xd6, 0xa6, 0x42, 0x2a, 0x81, 0xd0,
|
||||
0xdb, 0x2a, 0x84, 0xd6, 0x3b, 0xad, 0x92, 0x13, 0x2a, 0x66, 0x54, 0x88, 0xfd, 0x79, 0x1e, 0xea,
|
||||
0x4a, 0x7d, 0x96, 0xa6, 0xb1, 0x05, 0x20, 0x15, 0x1e, 0xb2, 0x21, 0x46, 0x49, 0xac, 0x59, 0x0a,
|
||||
0x85, 0x0c, 0x4a, 0x92, 0xb2, 0x33, 0x5b, 0x52, 0x44, 0x48, 0xa5, 0x19, 0x11, 0x93, 0x83, 0x74,
|
||||
0x1d, 0xc4, 0x88, 0x12, 0xef, 0xc8, 0x37, 0xb0, 0x72, 0xc4, 0x86, 0xb8, 0x9f, 0x05, 0xb2, 0x24,
|
||||
0x03, 0xd9, 0x9b, 0x3d, 0x90, 0x87, 0xaa, 0x5d, 0xab, 0xe0, 0xc6, 0x78, 0x1f, 0x1a, 0xc5, 0xe7,
|
||||
0x2a, 0x82, 0x64, 0x23, 0xea, 0xa4, 0xd9, 0x8a, 0x77, 0xc6, 0x8f, 0x1a, 0x90, 0xe9, 0xfb, 0x38,
|
||||
0x29, 0xe9, 0x83, 0x3b, 0x41, 0x32, 0x61, 0x47, 0x0f, 0x45, 0xa1, 0x90, 0x1d, 0xa8, 0xf7, 0x30,
|
||||
0x08, 0x99, 0x2b, 0x03, 0x8e, 0x41, 0xe4, 0xbd, 0xd3, 0x2f, 0xfe, 0x7e, 0xa6, 0x60, 0xa9, 0xda,
|
||||
0xc6, 0x67, 0x70, 0xe5, 0x54, 0x69, 0x65, 0x5e, 0xd3, 0x72, 0xf3, 0xda, 0xa9, 0x53, 0x9e, 0x41,
|
||||
0xa0, 0x51, 0x44, 0x23, 0xe3, 0x05, 0xac, 0x89, 0x9c, 0x6e, 0xf5, 0xa9, 0x1f, 0x9e, 0xd1, 0x0c,
|
||||
0x76, 0x0f, 0x6a, 0xa9, 0xcb, 0xd2, 0x5c, 0x37, 0xa1, 0x3a, 0x49, 0x7e, 0xa9, 0x44, 0x43, 0x58,
|
||||
0xba, 0x37, 0xba, 0x40, 0xd4, 0x78, 0xe3, 0xbe, 0x71, 0x1d, 0x16, 0x59, 0x88, 0xa3, 0x64, 0x0c,
|
||||
0xba, 0x54, 0x84, 0x7b, 0x29, 0x6e, 0x45, 0x32, 0x9d, 0xef, 0x16, 0x61, 0x2d, 0x43, 0x5d, 0xf1,
|
||||
0x97, 0xd9, 0x48, 0xf6, 0xa0, 0xb1, 0x1d, 0x7f, 0x23, 0x48, 0x46, 0x6b, 0xf2, 0x8e, 0x6a, 0xa7,
|
||||
0xf0, 0xb5, 0xa0, 0xb9, 0x5e, 0xce, 0x8c, 0x22, 0x32, 0xe6, 0xc8, 0x17, 0xb0, 0x92, 0x1f, 0xf7,
|
||||
0xc9, 0x55, 0x55, 0xa3, 0xf4, 0x17, 0x48, 0xd3, 0x38, 0x4d, 0x24, 0x35, 0x7d, 0x0f, 0xaa, 0xc9,
|
||||
0xd8, 0x9c, 0x8f, 0xb1, 0x30, 0x4c, 0x37, 0x1b, 0x2a, 0x53, 0x30, 0x8c, 0x39, 0xf2, 0x71, 0xa4,
|
||||
0x2c, 0x46, 0xc0, 0x69, 0x65, 0x65, 0xbe, 0x6d, 0x5e, 0x28, 0x19, 0x26, 0x8d, 0x39, 0xf2, 0x0c,
|
||||
0xce, 0x6f, 0x4b, 0x84, 0x8e, 0x9b, 0x37, 0x79, 0x37, 0xef, 0xe4, 0x84, 0xf9, 0x30, 0x7f, 0xb4,
|
||||
0xf2, 0xfe, 0x6f, 0xcc, 0x91, 0x9f, 0x34, 0xb8, 0xb0, 0x8d, 0x61, 0xb1, 0x17, 0x92, 0x1b, 0xe5,
|
||||
0x4e, 0x4e, 0xe8, 0x99, 0xcd, 0x27, 0xb3, 0xd6, 0x6c, 0xde, 0xac, 0x31, 0x47, 0xf6, 0xe5, 0xb1,
|
||||
0xb3, 0xda, 0x23, 0x57, 0x4a, 0x8b, 0x2c, 0xcd, 0x5e, 0xeb, 0x24, 0x76, 0x72, 0xd4, 0x4f, 0xba,
|
||||
0x7f, 0xbc, 0x6a, 0x69, 0x7f, 0xbe, 0x6a, 0x69, 0x7f, 0xbd, 0x6a, 0x69, 0x5f, 0xde, 0xfa, 0x97,
|
||||
0xcf, 0x5b, 0xca, 0x97, 0x38, 0xca, 0x99, 0x3d, 0x64, 0xe8, 0x86, 0x87, 0x4b, 0xf2, 0x63, 0xd6,
|
||||
0xad, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x25, 0x4e, 0x84, 0xa1, 0xa8, 0x13, 0x00, 0x00,
|
||||
// 1447 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0xdd, 0x6e, 0x1b, 0xc5,
|
||||
0x17, 0xcf, 0x3a, 0x76, 0x62, 0x1f, 0xb7, 0x89, 0x33, 0xfd, 0xf8, 0xef, 0xdf, 0xa4, 0x56, 0xba,
|
||||
0x12, 0x55, 0xa0, 0x74, 0xad, 0xba, 0x15, 0x54, 0xad, 0x84, 0x64, 0xd2, 0x36, 0x45, 0x69, 0x9a,
|
||||
0xb0, 0x29, 0x48, 0xa0, 0x8a, 0x6a, 0xb2, 0x9e, 0xac, 0x07, 0xdb, 0xbb, 0xd3, 0xdd, 0xb1, 0x51,
|
||||
0x2a, 0x71, 0x89, 0xb8, 0xe0, 0x1a, 0xde, 0x83, 0x27, 0xe0, 0x8a, 0x8f, 0x4b, 0x1e, 0x01, 0xf5,
|
||||
0x82, 0xe7, 0x40, 0x33, 0xfb, 0x35, 0xbb, 0x5e, 0x87, 0x4a, 0x6e, 0xd2, 0x9b, 0x64, 0xe6, 0xcc,
|
||||
0xf9, 0x9a, 0x73, 0xce, 0xfc, 0xce, 0xf1, 0xc2, 0x35, 0x9f, 0x30, 0x2f, 0x20, 0xfe, 0x84, 0xf8,
|
||||
0x6d, 0xb9, 0xa4, 0xdc, 0xf3, 0x8f, 0x95, 0xa5, 0xc9, 0x7c, 0x8f, 0x7b, 0x08, 0x52, 0x4a, 0xf3,
|
||||
0xa2, 0xe3, 0x39, 0x9e, 0x24, 0xb7, 0xc5, 0x2a, 0xe4, 0x68, 0xae, 0x3b, 0x9e, 0xe7, 0x0c, 0x49,
|
||||
0x1b, 0x33, 0xda, 0xc6, 0xae, 0xeb, 0x71, 0xcc, 0xa9, 0xe7, 0x06, 0xd1, 0xa9, 0x31, 0xb8, 0x13,
|
||||
0x98, 0xd4, 0x93, 0xa7, 0xb6, 0xe7, 0x93, 0xf6, 0xe4, 0x66, 0xdb, 0x21, 0x2e, 0xf1, 0x31, 0x27,
|
||||
0xbd, 0x88, 0xe7, 0xb1, 0x43, 0x79, 0x7f, 0x7c, 0x68, 0xda, 0xde, 0xa8, 0x8d, 0x7d, 0x69, 0xe2,
|
||||
0x1b, 0xb9, 0xb8, 0x61, 0xf7, 0xda, 0x93, 0x4e, 0x9b, 0x0d, 0x1c, 0x21, 0x1f, 0xb4, 0x31, 0x63,
|
||||
0x43, 0x6a, 0x4b, 0xfd, 0xed, 0xc9, 0x4d, 0x3c, 0x64, 0x7d, 0x3c, 0xa5, 0xcd, 0xf8, 0xa7, 0x0a,
|
||||
0xab, 0xbb, 0xd8, 0xa5, 0x47, 0x24, 0xe0, 0x16, 0x79, 0x31, 0x26, 0x01, 0x47, 0xcf, 0xa0, 0x2c,
|
||||
0xee, 0xa1, 0x6b, 0x1b, 0xda, 0x66, 0xbd, 0xf3, 0xc8, 0x4c, 0x0d, 0x9a, 0xb1, 0x41, 0xb9, 0x78,
|
||||
0x6e, 0xf7, 0xcc, 0x49, 0xc7, 0x64, 0x03, 0xc7, 0x14, 0x06, 0x4d, 0xc5, 0xa0, 0x19, 0x1b, 0x34,
|
||||
0xad, 0x24, 0x22, 0x96, 0xd4, 0x8a, 0x9a, 0x50, 0xf5, 0xc9, 0x84, 0x06, 0xd4, 0x73, 0xf5, 0xd2,
|
||||
0x86, 0xb6, 0x59, 0xb3, 0x92, 0x3d, 0xd2, 0x61, 0xd9, 0xf5, 0xb6, 0xb0, 0xdd, 0x27, 0xfa, 0xe2,
|
||||
0x86, 0xb6, 0x59, 0xb5, 0xe2, 0x2d, 0xda, 0x80, 0x3a, 0x66, 0xec, 0x31, 0x3e, 0x24, 0xc3, 0x1d,
|
||||
0x72, 0xac, 0x97, 0xa5, 0xa0, 0x4a, 0x12, 0xb2, 0x98, 0xb1, 0x27, 0x78, 0x44, 0xf4, 0x8a, 0x3c,
|
||||
0x8d, 0xb7, 0x68, 0x1d, 0x6a, 0x2e, 0x1e, 0x91, 0x80, 0x61, 0x9b, 0xe8, 0x55, 0x79, 0x96, 0x12,
|
||||
0xd0, 0x77, 0xb0, 0xa6, 0x38, 0x7e, 0xe0, 0x8d, 0x7d, 0x9b, 0xe8, 0x20, 0xaf, 0xbe, 0x37, 0xdf,
|
||||
0xd5, 0xbb, 0x79, 0xb5, 0xd6, 0xb4, 0x25, 0xf4, 0x35, 0x54, 0x64, 0xd1, 0xe8, 0xf5, 0x8d, 0xc5,
|
||||
0x37, 0x1a, 0xed, 0x50, 0x2d, 0x72, 0x61, 0x99, 0x0d, 0xc7, 0x0e, 0x75, 0x03, 0xfd, 0x9c, 0xb4,
|
||||
0xf0, 0x74, 0x3e, 0x0b, 0x5b, 0x9e, 0x7b, 0x44, 0x9d, 0x5d, 0xec, 0x62, 0x87, 0x8c, 0x88, 0xcb,
|
||||
0xf7, 0xa5, 0x72, 0x2b, 0x36, 0x82, 0x5e, 0x42, 0x63, 0x30, 0x0e, 0xb8, 0x37, 0xa2, 0x2f, 0xc9,
|
||||
0x1e, 0x93, 0xc5, 0xad, 0x9f, 0x97, 0xd1, 0x7c, 0x32, 0x9f, 0xe1, 0x9d, 0x9c, 0x56, 0x6b, 0xca,
|
||||
0x8e, 0x28, 0x92, 0xc1, 0xf8, 0x90, 0x7c, 0x41, 0x7c, 0x59, 0x5d, 0x2b, 0x61, 0x91, 0x28, 0xa4,
|
||||
0xb0, 0x8c, 0x68, 0xb4, 0x0b, 0xf4, 0xd5, 0x8d, 0xc5, 0xb0, 0x8c, 0x12, 0x12, 0xda, 0x84, 0xd5,
|
||||
0x09, 0xf1, 0xe9, 0xd1, 0xf1, 0x01, 0x75, 0x5c, 0xcc, 0xc7, 0x3e, 0xd1, 0x1b, 0xb2, 0x14, 0xf3,
|
||||
0x64, 0x34, 0x82, 0xf3, 0x7d, 0x32, 0x1c, 0x89, 0x90, 0x6f, 0xf9, 0xa4, 0x17, 0xe8, 0x6b, 0x32,
|
||||
0xbe, 0xdb, 0xf3, 0x67, 0x50, 0xaa, 0xb3, 0xb2, 0xda, 0x85, 0x63, 0xae, 0x67, 0x45, 0x2f, 0x25,
|
||||
0x7c, 0x23, 0x28, 0x74, 0x2c, 0x47, 0x46, 0xd7, 0x60, 0x85, 0xfb, 0xd8, 0x1e, 0x50, 0xd7, 0xd9,
|
||||
0x25, 0xbc, 0xef, 0xf5, 0xf4, 0x0b, 0x32, 0x12, 0x39, 0x2a, 0x1a, 0x40, 0x5d, 0x98, 0x88, 0xb3,
|
||||
0x74, 0x49, 0x66, 0xe9, 0xd3, 0xf9, 0xdc, 0x7f, 0x94, 0x2a, 0xb4, 0x54, 0xed, 0xc6, 0x18, 0x2e,
|
||||
0x3d, 0x95, 0x18, 0x93, 0x14, 0xe8, 0x59, 0xa0, 0x8d, 0xf1, 0x08, 0x2e, 0xe7, 0xcd, 0x06, 0xcc,
|
||||
0x73, 0x03, 0x82, 0x4c, 0x40, 0x32, 0xa3, 0x94, 0xf4, 0xd2, 0x53, 0xe9, 0x45, 0xd5, 0x2a, 0x38,
|
||||
0x31, 0x7e, 0xd7, 0xa0, 0x91, 0x22, 0x65, 0xa4, 0x64, 0x1d, 0x6a, 0xa3, 0x88, 0x16, 0xe8, 0x9a,
|
||||
0xac, 0xa6, 0x94, 0x90, 0x05, 0x9e, 0x52, 0x1e, 0x78, 0x2e, 0xc3, 0x52, 0xd8, 0x52, 0x24, 0xd6,
|
||||
0xd5, 0xac, 0x68, 0x97, 0x01, 0xc8, 0x72, 0x0e, 0x20, 0x5b, 0x00, 0x81, 0xc4, 0x8d, 0xa7, 0xc7,
|
||||
0x8c, 0xe8, 0x4b, 0xf2, 0x54, 0xa1, 0x20, 0x03, 0xce, 0x85, 0x65, 0x6a, 0x91, 0x60, 0x3c, 0xe4,
|
||||
0xfa, 0xb2, 0xe4, 0xc8, 0xd0, 0x0c, 0x0f, 0x56, 0x1f, 0x53, 0x71, 0x87, 0xa3, 0xe0, 0x6c, 0x72,
|
||||
0xf0, 0x21, 0x94, 0x85, 0x31, 0x71, 0xb1, 0x43, 0x1f, 0xbb, 0x76, 0x9f, 0xc4, 0xb1, 0x4a, 0xf6,
|
||||
0x08, 0x41, 0x99, 0x63, 0x27, 0xd0, 0x4b, 0x92, 0x2e, 0xd7, 0xc6, 0x8f, 0x5a, 0xe8, 0x69, 0x97,
|
||||
0xb1, 0xe0, 0xad, 0xf7, 0x26, 0x63, 0x0c, 0xcb, 0x5d, 0xc6, 0x84, 0x3f, 0xe8, 0x26, 0x94, 0x31,
|
||||
0x63, 0xe1, 0x25, 0xea, 0x9d, 0x2b, 0xa6, 0x32, 0x07, 0x44, 0x2c, 0xe2, 0x7f, 0xf0, 0xc0, 0xe5,
|
||||
0x42, 0xb3, 0x60, 0x6d, 0x7e, 0x04, 0xb5, 0x84, 0x84, 0x1a, 0xb0, 0x38, 0x20, 0x61, 0xad, 0xd5,
|
||||
0x2c, 0xb1, 0x44, 0x17, 0xa1, 0x32, 0xc1, 0xc3, 0x71, 0x5c, 0x25, 0xe1, 0xe6, 0x6e, 0xe9, 0x8e,
|
||||
0x66, 0xfc, 0x52, 0x81, 0xff, 0x0b, 0x3f, 0x0f, 0x64, 0x71, 0x74, 0x19, 0xbb, 0x4f, 0x38, 0xa6,
|
||||
0xc3, 0xe0, 0xb3, 0x31, 0xf1, 0x8f, 0x4f, 0x39, 0x1c, 0x0e, 0x2c, 0x85, 0xb5, 0x25, 0xdd, 0x3a,
|
||||
0x85, 0x7e, 0x18, 0xa9, 0x4f, 0x9b, 0xe0, 0xe2, 0xe9, 0x34, 0xc1, 0xa2, 0xa6, 0x54, 0x3e, 0xa3,
|
||||
0xa6, 0x34, 0x7b, 0x2e, 0x51, 0xa6, 0x9d, 0xa5, 0xec, 0xb4, 0x53, 0x80, 0xf5, 0xcb, 0xaf, 0x8b,
|
||||
0xf5, 0xd5, 0xd7, 0xc1, 0x7a, 0x38, 0x55, 0xac, 0xff, 0xa1, 0x04, 0x97, 0x45, 0x12, 0xd2, 0x6a,
|
||||
0x4d, 0x00, 0x53, 0xbc, 0x73, 0x01, 0x5d, 0x61, 0xed, 0xcb, 0x35, 0xba, 0x0d, 0xcb, 0x83, 0xc0,
|
||||
0x73, 0x5d, 0xc2, 0xa3, 0x3a, 0x6b, 0xaa, 0x2f, 0x6a, 0x27, 0x3c, 0xea, 0x32, 0x76, 0xc0, 0x88,
|
||||
0x6d, 0xc5, 0xac, 0xe8, 0x3a, 0x94, 0x85, 0x4d, 0x09, 0x9e, 0xf5, 0xce, 0xff, 0x54, 0x11, 0xe1,
|
||||
0x58, 0xcc, 0x2f, 0x99, 0xd0, 0x5d, 0xa8, 0x25, 0x89, 0x89, 0x32, 0xbf, 0x9e, 0x31, 0x12, 0x1f,
|
||||
0xc6, 0x62, 0x29, 0xbb, 0x90, 0xed, 0x51, 0x9f, 0xd8, 0xb2, 0x3f, 0x54, 0xa6, 0x65, 0xef, 0xc7,
|
||||
0x87, 0x89, 0x6c, 0xc2, 0x6e, 0xfc, 0xa6, 0xc1, 0xd5, 0xf4, 0xf5, 0xc6, 0xa9, 0xdb, 0x25, 0x1c,
|
||||
0xf7, 0x30, 0xc7, 0x6f, 0x7f, 0xe0, 0xbe, 0x06, 0x2b, 0x76, 0x9f, 0xd8, 0x83, 0x74, 0xd8, 0x09,
|
||||
0xe7, 0xee, 0x1c, 0xd5, 0xf8, 0xa3, 0x04, 0x2b, 0xd9, 0x44, 0x88, 0x4c, 0x8a, 0x5e, 0x16, 0x67,
|
||||
0x52, 0xac, 0xd1, 0x3e, 0x9c, 0x23, 0xee, 0x84, 0xfa, 0x9e, 0x2b, 0x46, 0xc3, 0xf8, 0x39, 0x7f,
|
||||
0x30, 0x3b, 0x9d, 0xe6, 0x03, 0x85, 0x3d, 0xc4, 0xcb, 0x8c, 0x06, 0xe4, 0x02, 0x30, 0xec, 0xe3,
|
||||
0x11, 0xe1, 0xc4, 0x17, 0x6f, 0x76, 0xf1, 0x0d, 0xbc, 0xd9, 0xd0, 0x83, 0xfd, 0x58, 0xad, 0xa5,
|
||||
0x58, 0x68, 0x3e, 0x87, 0xb5, 0x29, 0x97, 0x0a, 0xf0, 0xfa, 0xb6, 0x8a, 0xd7, 0xf5, 0x4e, 0xab,
|
||||
0xe0, 0x86, 0x8a, 0x1a, 0x15, 0xcf, 0x7f, 0x2d, 0x41, 0x5d, 0xa9, 0xcf, 0xc2, 0x30, 0xb6, 0x00,
|
||||
0xa4, 0xc0, 0x43, 0x3a, 0x24, 0x61, 0x10, 0x6b, 0x96, 0x42, 0x41, 0x83, 0x82, 0xa0, 0xec, 0xcc,
|
||||
0xff, 0x96, 0x0b, 0x23, 0x22, 0xc6, 0x14, 0x69, 0x3a, 0x88, 0xe0, 0x2b, 0xda, 0xa1, 0x6f, 0x61,
|
||||
0xe5, 0x88, 0x0e, 0xc9, 0x7e, 0xea, 0xc8, 0x92, 0x74, 0x64, 0x6f, 0x7e, 0x47, 0x1e, 0xaa, 0x7a,
|
||||
0xad, 0x9c, 0x19, 0xe3, 0x7d, 0x68, 0xe4, 0x9f, 0xab, 0x70, 0x92, 0x8e, 0xb0, 0x93, 0x44, 0x2b,
|
||||
0xda, 0x19, 0x3f, 0x69, 0x80, 0xa6, 0xf3, 0x31, 0x2b, 0xe8, 0x83, 0x3b, 0x41, 0xfc, 0xdb, 0x21,
|
||||
0x7c, 0x28, 0x0a, 0x05, 0xed, 0x40, 0xbd, 0x47, 0x02, 0x4e, 0x5d, 0xe9, 0x70, 0x04, 0x22, 0xef,
|
||||
0x9d, 0x9c, 0xf8, 0xfb, 0xa9, 0x80, 0xa5, 0x4a, 0x1b, 0x9f, 0xc3, 0x95, 0x13, 0xb9, 0x95, 0xe1,
|
||||
0x50, 0xcb, 0x0c, 0x87, 0x27, 0x8e, 0x94, 0x06, 0x82, 0x46, 0x1e, 0x8d, 0x8c, 0x17, 0xb0, 0x26,
|
||||
0x62, 0xba, 0xd5, 0xc7, 0x3e, 0x3f, 0xa3, 0x81, 0xef, 0x1e, 0xd4, 0x12, 0x93, 0x85, 0xb1, 0x6e,
|
||||
0x42, 0x75, 0x12, 0xff, 0x06, 0x0b, 0x27, 0xbe, 0x64, 0x6f, 0x74, 0x01, 0xa9, 0xfe, 0x46, 0x7d,
|
||||
0xe3, 0x3a, 0x54, 0x28, 0x27, 0xa3, 0x78, 0xe6, 0xba, 0x94, 0x87, 0x7b, 0xc9, 0x6e, 0x85, 0x3c,
|
||||
0x9d, 0xef, 0x2b, 0xb0, 0x96, 0xa2, 0xae, 0xf8, 0x4b, 0x6d, 0x82, 0xf6, 0xa0, 0xb1, 0x1d, 0x7d,
|
||||
0xfd, 0x88, 0xe7, 0x78, 0xf4, 0x8e, 0xaa, 0x27, 0xf7, 0x1d, 0xa4, 0xb9, 0x5e, 0x7c, 0x18, 0x7a,
|
||||
0x64, 0x2c, 0xa0, 0x2f, 0x61, 0x25, 0xfb, 0xdb, 0x02, 0x5d, 0x55, 0x25, 0x0a, 0x7f, 0xee, 0x34,
|
||||
0x8d, 0x93, 0x58, 0x12, 0xd5, 0xf7, 0xa0, 0x1a, 0xcf, 0xe8, 0x59, 0x1f, 0x73, 0x93, 0x7b, 0xb3,
|
||||
0xa1, 0x1e, 0x8a, 0x03, 0x63, 0x01, 0x7d, 0x1c, 0x0a, 0x8b, 0x79, 0x73, 0x5a, 0x58, 0x19, 0xa6,
|
||||
0x9b, 0x17, 0x0a, 0x26, 0x57, 0x63, 0x01, 0x3d, 0x83, 0xf3, 0xdb, 0x12, 0xa1, 0xa3, 0xe6, 0x8d,
|
||||
0xde, 0xcd, 0x1a, 0x99, 0x31, 0x8c, 0x66, 0xaf, 0x56, 0xdc, 0xff, 0x8d, 0x05, 0xf4, 0xb3, 0x06,
|
||||
0x17, 0xb6, 0x09, 0xcf, 0xf7, 0x42, 0x74, 0xa3, 0xd8, 0xc8, 0x8c, 0x9e, 0xd9, 0x7c, 0x32, 0x6f,
|
||||
0xcd, 0x66, 0xd5, 0x1a, 0x0b, 0x68, 0x5f, 0x5e, 0x3b, 0xad, 0x3d, 0x74, 0xa5, 0xb0, 0xc8, 0x92,
|
||||
0xe8, 0xb5, 0x66, 0x1d, 0xc7, 0x57, 0xfd, 0xa4, 0xfb, 0xe7, 0xab, 0x96, 0xf6, 0xd7, 0xab, 0x96,
|
||||
0xf6, 0xf7, 0xab, 0x96, 0xf6, 0xd5, 0xad, 0xff, 0xf8, 0x70, 0xa7, 0x7c, 0x63, 0xc4, 0x8c, 0xda,
|
||||
0x43, 0x4a, 0x5c, 0x7e, 0xb8, 0x24, 0x3f, 0xd3, 0xdd, 0xfa, 0x37, 0x00, 0x00, 0xff, 0xff, 0x98,
|
||||
0x26, 0x41, 0x78, 0x82, 0x14, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@@ -1809,6 +1827,20 @@ func (m *ManifestRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if m.HelmOptions != nil {
|
||||
{
|
||||
size, err := m.HelmOptions.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintRepository(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x1
|
||||
i--
|
||||
dAtA[i] = 0xaa
|
||||
}
|
||||
if len(m.TrackingMethod) > 0 {
|
||||
i -= len(m.TrackingMethod)
|
||||
copy(dAtA[i:], m.TrackingMethod)
|
||||
@@ -2326,6 +2358,18 @@ func (m *RepoServerAppDetailsQuery) MarshalToSizedBuffer(dAtA []byte) (int, erro
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if m.HelmOptions != nil {
|
||||
{
|
||||
size, err := m.HelmOptions.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintRepository(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x52
|
||||
}
|
||||
if len(m.TrackingMethod) > 0 {
|
||||
i -= len(m.TrackingMethod)
|
||||
copy(dAtA[i:], m.TrackingMethod)
|
||||
@@ -3069,6 +3113,10 @@ func (m *ManifestRequest) Size() (n int) {
|
||||
if l > 0 {
|
||||
n += 2 + l + sovRepository(uint64(l))
|
||||
}
|
||||
if m.HelmOptions != nil {
|
||||
l = m.HelmOptions.Size()
|
||||
n += 2 + l + sovRepository(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
@@ -3262,6 +3310,10 @@ func (m *RepoServerAppDetailsQuery) Size() (n int) {
|
||||
if l > 0 {
|
||||
n += 1 + l + sovRepository(uint64(l))
|
||||
}
|
||||
if m.HelmOptions != nil {
|
||||
l = m.HelmOptions.Size()
|
||||
n += 1 + l + sovRepository(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
@@ -4055,6 +4107,42 @@ func (m *ManifestRequest) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
m.TrackingMethod = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 21:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field HelmOptions", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowRepository
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.HelmOptions == nil {
|
||||
m.HelmOptions = &v1alpha1.HelmOptions{}
|
||||
}
|
||||
if err := m.HelmOptions.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipRepository(dAtA[iNdEx:])
|
||||
@@ -5252,6 +5340,42 @@ func (m *RepoServerAppDetailsQuery) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
m.TrackingMethod = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 10:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field HelmOptions", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowRepository
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthRepository
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.HelmOptions == nil {
|
||||
m.HelmOptions = &v1alpha1.HelmOptions{}
|
||||
}
|
||||
if err := m.HelmOptions.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipRepository(dAtA[iNdEx:])
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/google/go-jsonnet"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/semaphore"
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -49,9 +50,9 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/util/gpg"
|
||||
"github.com/argoproj/argo-cd/v2/util/helm"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
pathutil "github.com/argoproj/argo-cd/v2/util/io/path"
|
||||
"github.com/argoproj/argo-cd/v2/util/ksonnet"
|
||||
"github.com/argoproj/argo-cd/v2/util/kustomize"
|
||||
"github.com/argoproj/argo-cd/v2/util/security"
|
||||
"github.com/argoproj/argo-cd/v2/util/text"
|
||||
)
|
||||
|
||||
@@ -567,7 +568,7 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie
|
||||
APIVersions: q.ApiVersions,
|
||||
Set: map[string]string{},
|
||||
SetString: map[string]string{},
|
||||
SetFile: map[string]string{},
|
||||
SetFile: map[string]pathutil.ResolvedFilePath{},
|
||||
}
|
||||
|
||||
appHelm := q.ApplicationSource.Helm
|
||||
@@ -582,46 +583,28 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie
|
||||
}
|
||||
|
||||
for _, val := range appHelm.ValueFiles {
|
||||
// If val is not a URL, run it against the directory enforcer. If it is a URL, use it without checking
|
||||
if _, err := url.ParseRequestURI(val); err != nil {
|
||||
|
||||
// Ensure that the repo root provided is absolute
|
||||
absRepoPath, err := filepath.Abs(repoRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the path to the file is relative, join it with the current working directory (appPath)
|
||||
path := val
|
||||
if !filepath.IsAbs(path) {
|
||||
absWorkDir, err := filepath.Abs(appPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path = filepath.Join(absWorkDir, path)
|
||||
}
|
||||
|
||||
_, err = security.EnforceToCurrentRoot(absRepoPath, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
templateOpts.Values = append(templateOpts.Values, val)
|
||||
}
|
||||
|
||||
if appHelm.Values != "" {
|
||||
file, err := ioutil.TempFile("", "values-*.yaml")
|
||||
// This will resolve val to an absolute path (or an URL)
|
||||
path, _, err := pathutil.ResolveFilePath(appPath, repoRoot, val, q.GetValuesFileSchemes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := file.Name()
|
||||
|
||||
templateOpts.Values = append(templateOpts.Values, path)
|
||||
}
|
||||
|
||||
if appHelm.Values != "" {
|
||||
rand, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := path.Join(os.TempDir(), rand.String())
|
||||
defer func() { _ = os.RemoveAll(p) }()
|
||||
err = ioutil.WriteFile(p, []byte(appHelm.Values), 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
templateOpts.Values = append(templateOpts.Values, p)
|
||||
templateOpts.Values = append(templateOpts.Values, pathutil.ResolvedFilePath(p))
|
||||
}
|
||||
|
||||
for _, p := range appHelm.Parameters {
|
||||
@@ -632,7 +615,11 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie
|
||||
}
|
||||
}
|
||||
for _, p := range appHelm.FileParameters {
|
||||
templateOpts.SetFile[p.Name] = p.Path
|
||||
resolvedPath, _, err := pathutil.ResolveFilePath(appPath, repoRoot, env.Envsubst(p.Path), q.GetValuesFileSchemes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
templateOpts.SetFile[p.Name] = resolvedPath
|
||||
}
|
||||
passCredentials = appHelm.PassCredentials
|
||||
}
|
||||
@@ -645,9 +632,6 @@ func helmTemplate(appPath string, repoRoot string, env *v1alpha1.Env, q *apiclie
|
||||
for i, j := range templateOpts.SetString {
|
||||
templateOpts.SetString[i] = env.Envsubst(j)
|
||||
}
|
||||
for i, j := range templateOpts.SetFile {
|
||||
templateOpts.SetFile[i] = env.Envsubst(j)
|
||||
}
|
||||
|
||||
repos, err := getHelmDependencyRepos(appPath)
|
||||
if err != nil {
|
||||
@@ -1087,11 +1071,12 @@ func makeJsonnetVm(appPath string, repoRoot string, sourceJsonnet v1alpha1.Appli
|
||||
// Jsonnet Imports relative to the repository path
|
||||
jpaths := []string{appPath}
|
||||
for _, p := range sourceJsonnet.Libs {
|
||||
jpath := path.Join(repoRoot, p)
|
||||
if !strings.HasPrefix(jpath, repoRoot) {
|
||||
return nil, status.Errorf(codes.FailedPrecondition, "%s: referenced library points outside the repository", p)
|
||||
// the jsonnet library path is relative to the repository root, not application path
|
||||
jpath, _, err := pathutil.ResolveFilePath(repoRoot, repoRoot, p, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jpaths = append(jpaths, jpath)
|
||||
jpaths = append(jpaths, string(jpath))
|
||||
}
|
||||
|
||||
vm.Importer(&jsonnet.FileImporter{
|
||||
@@ -1259,7 +1244,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD
|
||||
return err
|
||||
}
|
||||
case v1alpha1.ApplicationSourceTypeHelm:
|
||||
if err := populateHelmAppDetails(res, ctx.appPath, q); err != nil {
|
||||
if err := populateHelmAppDetails(res, ctx.appPath, repoRoot, q); err != nil {
|
||||
return err
|
||||
}
|
||||
case v1alpha1.ApplicationSourceTypeKustomize:
|
||||
@@ -1321,7 +1306,7 @@ func populateKsonnetAppDetails(res *apiclient.RepoAppDetailsResponse, appPath st
|
||||
return nil
|
||||
}
|
||||
|
||||
func populateHelmAppDetails(res *apiclient.RepoAppDetailsResponse, appPath string, q *apiclient.RepoServerAppDetailsQuery) error {
|
||||
func populateHelmAppDetails(res *apiclient.RepoAppDetailsResponse, appPath string, repoRoot string, q *apiclient.RepoServerAppDetailsQuery) error {
|
||||
var selectedValueFiles []string
|
||||
|
||||
if q.Source.Helm != nil {
|
||||
@@ -1355,7 +1340,16 @@ func populateHelmAppDetails(res *apiclient.RepoAppDetailsResponse, appPath strin
|
||||
if err := loadFileIntoIfExists(filepath.Join(appPath, "values.yaml"), &res.Helm.Values); err != nil {
|
||||
return err
|
||||
}
|
||||
params, err := h.GetParameters(selectedValueFiles)
|
||||
var resolvedSelectedValueFiles []pathutil.ResolvedFilePath
|
||||
// drop not allowed values files
|
||||
for _, file := range selectedValueFiles {
|
||||
if resolvedFile, _, err := pathutil.ResolveFilePath(appPath, repoRoot, file, q.GetValuesFileSchemes()); err == nil {
|
||||
resolvedSelectedValueFiles = append(resolvedSelectedValueFiles, resolvedFile)
|
||||
} else {
|
||||
log.Debugf("Values file %s is not allowed: %v", file, err)
|
||||
}
|
||||
}
|
||||
params, err := h.GetParameters(resolvedSelectedValueFiles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ message ManifestRequest {
|
||||
repeated github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.RepoCreds helmRepoCreds = 17;
|
||||
bool noRevisionCache = 18;
|
||||
string trackingMethod = 19;
|
||||
github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.HelmOptions helmOptions = 21;
|
||||
}
|
||||
|
||||
// TestRepositoryRequest is a query to test repository is valid or not and has valid access.
|
||||
@@ -84,6 +85,7 @@ message RepoServerAppDetailsQuery {
|
||||
bool noCache = 6;
|
||||
bool noRevisionCache = 7;
|
||||
string trackingMethod = 8;
|
||||
github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.HelmOptions helmOptions = 10;
|
||||
}
|
||||
|
||||
// RepoAppDetailsResponse application details
|
||||
|
||||
@@ -260,6 +260,25 @@ func TestGenerateJsonnetManifestInDir(t *testing.T) {
|
||||
assert.Equal(t, 2, len(res1.Manifests))
|
||||
}
|
||||
|
||||
func TestGenerateJsonnetLibOutside(t *testing.T) {
|
||||
service := newService(".")
|
||||
|
||||
q := apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: "./testdata/jsonnet",
|
||||
Directory: &argoappv1.ApplicationSourceDirectory{
|
||||
Jsonnet: argoappv1.ApplicationSourceJsonnet{
|
||||
Libs: []string{"../../../testdata/jsonnet/vendor"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := service.GenerateManifest(context.Background(), &q)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "value file '../../../testdata/jsonnet/vendor' resolved to outside repository root")
|
||||
}
|
||||
|
||||
func TestGenerateKsonnetManifest(t *testing.T) {
|
||||
service := newService("../..")
|
||||
|
||||
@@ -728,7 +747,37 @@ func TestHelmManifestFromChartRepoWithValueFileOutsideRepo(t *testing.T) {
|
||||
}
|
||||
request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true}
|
||||
_, err := service.GenerateManifest(context.Background(), request)
|
||||
assert.Error(t, err, "should be on or under current directory")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestHelmManifestFromChartRepoWithValueFileLinks(t *testing.T) {
|
||||
t.Run("Valid symlink", func(t *testing.T) {
|
||||
service := newService("../..")
|
||||
source := &argoappv1.ApplicationSource{
|
||||
Chart: "my-chart",
|
||||
TargetRevision: ">= 1.0.0",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"my-chart-link.yaml"},
|
||||
},
|
||||
}
|
||||
request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true}
|
||||
_, err := service.GenerateManifest(context.Background(), request)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("Symlink pointing to outside", func(t *testing.T) {
|
||||
service := newService("../..")
|
||||
source := &argoappv1.ApplicationSource{
|
||||
Chart: "my-chart",
|
||||
TargetRevision: ">= 1.0.0",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"my-chart-outside-link.yaml"},
|
||||
},
|
||||
}
|
||||
request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true}
|
||||
_, err := service.GenerateManifest(context.Background(), request)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "outside repository root")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGenerateHelmWithURL(t *testing.T) {
|
||||
@@ -744,6 +793,7 @@ func TestGenerateHelmWithURL(t *testing.T) {
|
||||
Values: `cluster: {slaveCount: 2}`,
|
||||
},
|
||||
},
|
||||
HelmOptions: &argoappv1.HelmOptions{ValuesFileSchemes: []string{"https"}},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -751,38 +801,108 @@ func TestGenerateHelmWithURL(t *testing.T) {
|
||||
// The requested value file (`../../../../../minio/values.yaml`) is outside the repo directory
|
||||
// (`~/go/src/github.com/argoproj/argo-cd`), so it is blocked
|
||||
func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) {
|
||||
service := newService("../..")
|
||||
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppName: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: "./util/helm/testdata/redis",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"../../../../../minio/values.yaml"},
|
||||
Values: `cluster: {slaveCount: 2}`,
|
||||
t.Run("Values file with relative path pointing outside repo root", func(t *testing.T) {
|
||||
service := newService("../..")
|
||||
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppName: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: "./util/helm/testdata/redis",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"../../../../../minio/values.yaml"},
|
||||
Values: `cluster: {slaveCount: 2}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "outside repository root")
|
||||
})
|
||||
assert.Error(t, err, "should be on or under current directory")
|
||||
|
||||
service = newService("./testdata/my-chart")
|
||||
_, err = service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppName: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: ".",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"../my-chart-2/values.yaml"},
|
||||
Values: `cluster: {slaveCount: 2}`,
|
||||
t.Run("Values file with relative path pointing inside repo root", func(t *testing.T) {
|
||||
service := newService("./testdata/my-chart")
|
||||
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppName: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: ".",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"../my-chart/my-chart-values.yaml"},
|
||||
Values: `cluster: {slaveCount: 2}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Values file with absolute path stays within repo root", func(t *testing.T) {
|
||||
service := newService("./testdata/my-chart")
|
||||
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppName: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: ".",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"/my-chart-values.yaml"},
|
||||
Values: `cluster: {slaveCount: 2}`,
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Values file with absolute path using back-references outside repo root", func(t *testing.T) {
|
||||
service := newService("./testdata/my-chart")
|
||||
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppName: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: ".",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"/../../../my-chart-values.yaml"},
|
||||
Values: `cluster: {slaveCount: 2}`,
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "outside repository root")
|
||||
})
|
||||
|
||||
t.Run("Remote values file from forbidden protocol", func(t *testing.T) {
|
||||
service := newService("./testdata/my-chart")
|
||||
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppName: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: ".",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"file://../../../../my-chart-values.yaml"},
|
||||
Values: `cluster: {slaveCount: 2}`,
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "is not allowed")
|
||||
})
|
||||
|
||||
t.Run("Remote values file from custom allowed protocol", func(t *testing.T) {
|
||||
service := newService("./testdata/my-chart")
|
||||
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppName: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: ".",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"s3://my-bucket/my-chart-values.yaml"},
|
||||
},
|
||||
},
|
||||
HelmOptions: &argoappv1.HelmOptions{ValuesFileSchemes: []string{"s3"}},
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "s3://my-bucket/my-chart-values.yaml: no such file or directory")
|
||||
})
|
||||
assert.Error(t, err, "should be on or under current directory")
|
||||
}
|
||||
|
||||
// The requested file parameter (`/tmp/external-secret.txt`) is outside the app path
|
||||
// (`./util/helm/testdata/redis`), and outside the repo directory. It is used as a means
|
||||
// of providing direct content to a helm chart via a specific key.
|
||||
// File parameter should not allow traversal outside of the repository root
|
||||
func TestGenerateHelmWithAbsoluteFileParameter(t *testing.T) {
|
||||
service := newService("../..")
|
||||
|
||||
@@ -804,16 +924,14 @@ func TestGenerateHelmWithAbsoluteFileParameter(t *testing.T) {
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"values-production.yaml"},
|
||||
Values: `cluster: {slaveCount: 2}`,
|
||||
FileParameters: []argoappv1.HelmFileParameter{
|
||||
argoappv1.HelmFileParameter{
|
||||
Name: "passwordContent",
|
||||
Path: externalSecretPath,
|
||||
},
|
||||
},
|
||||
FileParameters: []argoappv1.HelmFileParameter{{
|
||||
Name: "passwordContent",
|
||||
Path: externalSecretPath,
|
||||
}},
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// The requested file parameter (`../external/external-secret.txt`) is outside the app path
|
||||
|
||||
1
reposerver/repository/testdata/my-chart/my-chart-link.yaml
vendored
Symbolic link
1
reposerver/repository/testdata/my-chart/my-chart-link.yaml
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
my-chart-values.yaml
|
||||
1
reposerver/repository/testdata/my-chart/my-chart-outside-link.yaml
vendored
Symbolic link
1
reposerver/repository/testdata/my-chart/my-chart-outside-link.yaml
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../my-chart-2/my-chart-2-values.yaml
|
||||
@@ -2,10 +2,10 @@ health_status = {}
|
||||
if obj.status ~= nil then
|
||||
if obj.status.ingress ~= nil then
|
||||
numIngressRules = 0
|
||||
numTrue = 0
|
||||
numFalse = 0
|
||||
for _, ingressRules in pairs(obj.status.ingress) do
|
||||
numIngressRules = numIngressRules + 1
|
||||
numTrue = 0
|
||||
numFalse = 0
|
||||
if obj.status.ingress ~= nil then
|
||||
for _, condition in pairs(ingressRules.conditions) do
|
||||
if condition.type == "Admitted" and condition.status == "True" then
|
||||
|
||||
@@ -213,7 +213,7 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq
|
||||
return existing, nil
|
||||
}
|
||||
if q.Upsert == nil || !*q.Upsert {
|
||||
return nil, status.Errorf(codes.InvalidArgument, argo.GenerateSpecIsDifferentErrorMessage("application", existing.Spec, a.Spec))
|
||||
return nil, status.Errorf(codes.InvalidArgument, "existing application spec is different, use upsert flag to force update")
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, appRBACName(a)); err != nil {
|
||||
return nil, err
|
||||
@@ -230,6 +230,7 @@ func (s *Server) queryRepoServer(ctx context.Context, a *v1alpha1.Application, a
|
||||
repo *appv1.Repository,
|
||||
helmRepos []*appv1.Repository,
|
||||
helmCreds []*v1alpha1.RepoCreds,
|
||||
helmOptions *v1alpha1.HelmOptions,
|
||||
kustomizeOptions *v1alpha1.KustomizeOptions,
|
||||
) error) error {
|
||||
|
||||
@@ -271,11 +272,16 @@ func (s *Server) queryRepoServer(ctx context.Context, a *v1alpha1.Application, a
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
helmOptions, err := s.settingsMgr.GetHelmSettings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
permittedHelmCredentials, err := argo.GetPermittedReposCredentials(proj, helmRepositoryCredentials)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return action(client, repo, permittedHelmRepos, permittedHelmCredentials, kustomizeOptions)
|
||||
|
||||
return action(client, repo, permittedHelmRepos, permittedHelmCredentials, helmOptions, kustomizeOptions)
|
||||
}
|
||||
|
||||
// GetManifests returns application manifests
|
||||
@@ -290,7 +296,7 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
|
||||
|
||||
var manifestInfo *apiclient.ManifestResponse
|
||||
err = s.queryRepoServer(ctx, a, func(
|
||||
client apiclient.RepoServerServiceClient, repo *appv1.Repository, helmRepos []*appv1.Repository, helmCreds []*appv1.RepoCreds, kustomizeOptions *appv1.KustomizeOptions) error {
|
||||
client apiclient.RepoServerServiceClient, repo *appv1.Repository, helmRepos []*appv1.Repository, helmCreds []*appv1.RepoCreds, helmOptions *appv1.HelmOptions, kustomizeOptions *appv1.KustomizeOptions) error {
|
||||
revision := a.Spec.Source.TargetRevision
|
||||
if q.Revision != "" {
|
||||
revision = q.Revision
|
||||
@@ -332,6 +338,7 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
|
||||
KubeVersion: serverVersion,
|
||||
ApiVersions: argo.APIResourcesToStrings(apiResources, true),
|
||||
HelmRepoCreds: helmCreds,
|
||||
HelmOptions: helmOptions,
|
||||
TrackingMethod: string(argoutil.GetTrackingMethod(s.settingsMgr)),
|
||||
})
|
||||
return err
|
||||
@@ -407,6 +414,7 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app
|
||||
repo *appv1.Repository,
|
||||
helmRepos []*appv1.Repository,
|
||||
_ []*appv1.RepoCreds,
|
||||
helmOptions *appv1.HelmOptions,
|
||||
kustomizeOptions *appv1.KustomizeOptions,
|
||||
) error {
|
||||
_, err := client.GetAppDetails(ctx, &apiclient.RepoServerAppDetailsQuery{
|
||||
@@ -417,6 +425,7 @@ func (s *Server) Get(ctx context.Context, q *application.ApplicationQuery) (*app
|
||||
Repos: helmRepos,
|
||||
NoCache: true,
|
||||
TrackingMethod: string(argoutil.GetTrackingMethod(s.settingsMgr)),
|
||||
HelmOptions: helmOptions,
|
||||
})
|
||||
return err
|
||||
}); err != nil {
|
||||
@@ -473,6 +482,21 @@ func (s *Server) ListResourceEvents(ctx context.Context, q *application.Applicat
|
||||
"involvedObject.namespace": a.Namespace,
|
||||
}).String()
|
||||
} else {
|
||||
tree, err := s.getAppResources(ctx, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
found := false
|
||||
for _, n := range append(tree.Nodes, tree.OrphanedNodes...) {
|
||||
if n.ResourceRef.UID == q.ResourceUID && n.ResourceRef.Name == q.ResourceName && n.ResourceRef.Namespace == q.ResourceNamespace {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "%s not found as part of application %s", q.ResourceName, *q.Name)
|
||||
}
|
||||
|
||||
namespace = q.ResourceNamespace
|
||||
var config *rest.Config
|
||||
config, err = s.getApplicationClusterConfig(ctx, a)
|
||||
@@ -914,7 +938,7 @@ func (s *Server) getAppResources(ctx context.Context, a *appv1.Application) (*ap
|
||||
return &tree, err
|
||||
}
|
||||
|
||||
func (s *Server) getAppResource(ctx context.Context, action string, q *application.ApplicationResourceRequest) (*appv1.ResourceNode, *rest.Config, *appv1.Application, error) {
|
||||
func (s *Server) getAppLiveResource(ctx context.Context, action string, q *application.ApplicationResourceRequest) (*appv1.ResourceNode, *rest.Config, *appv1.Application, error) {
|
||||
a, err := s.appLister.Get(*q.Name)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
@@ -929,7 +953,7 @@ func (s *Server) getAppResource(ctx context.Context, action string, q *applicati
|
||||
}
|
||||
|
||||
found := tree.FindNode(q.Group, q.Kind, q.Namespace, q.ResourceName)
|
||||
if found == nil {
|
||||
if found == nil || found.ResourceRef.UID == "" {
|
||||
return nil, nil, nil, status.Errorf(codes.InvalidArgument, "%s %s %s not found as part of application %s", q.Kind, q.Group, q.ResourceName, *q.Name)
|
||||
}
|
||||
config, err := s.getApplicationClusterConfig(ctx, a)
|
||||
@@ -940,7 +964,7 @@ func (s *Server) getAppResource(ctx context.Context, action string, q *applicati
|
||||
}
|
||||
|
||||
func (s *Server) GetResource(ctx context.Context, q *application.ApplicationResourceRequest) (*application.ApplicationResourceResponse, error) {
|
||||
res, config, _, err := s.getAppResource(ctx, rbacpolicy.ActionGet, q)
|
||||
res, config, _, err := s.getAppLiveResource(ctx, rbacpolicy.ActionGet, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -985,7 +1009,7 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe
|
||||
Version: q.Version,
|
||||
Group: q.Group,
|
||||
}
|
||||
res, config, a, err := s.getAppResource(ctx, rbacpolicy.ActionUpdate, resourceRequest)
|
||||
res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionUpdate, resourceRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1025,7 +1049,7 @@ func (s *Server) DeleteResource(ctx context.Context, q *application.ApplicationR
|
||||
Version: q.Version,
|
||||
Group: q.Group,
|
||||
}
|
||||
res, config, a, err := s.getAppResource(ctx, rbacpolicy.ActionDelete, resourceRequest)
|
||||
res, config, a, err := s.getAppLiveResource(ctx, rbacpolicy.ActionDelete, resourceRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1296,7 +1320,7 @@ func getSelectedPods(treeNodes []appv1.ResourceNode, q *application.ApplicationP
|
||||
var pods []appv1.ResourceNode
|
||||
isTheOneMap := make(map[string]bool)
|
||||
for _, treeNode := range treeNodes {
|
||||
if treeNode.Kind == kube.PodKind && treeNode.Group == "" {
|
||||
if treeNode.Kind == kube.PodKind && treeNode.Group == "" && treeNode.UID != "" {
|
||||
if isTheSelectedOne(&treeNode, q, treeNodes, isTheOneMap) {
|
||||
pods = append(pods, treeNode)
|
||||
}
|
||||
@@ -1606,7 +1630,7 @@ func (s *Server) logResourceEvent(res *appv1.ResourceNode, ctx context.Context,
|
||||
}
|
||||
|
||||
func (s *Server) ListResourceActions(ctx context.Context, q *application.ApplicationResourceRequest) (*application.ResourceActionsListResponse, error) {
|
||||
res, config, _, err := s.getAppResource(ctx, rbacpolicy.ActionGet, q)
|
||||
res, config, _, err := s.getAppLiveResource(ctx, rbacpolicy.ActionGet, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1657,7 +1681,7 @@ func (s *Server) RunResourceAction(ctx context.Context, q *application.ResourceA
|
||||
Group: q.Group,
|
||||
}
|
||||
actionRequest := fmt.Sprintf("%s/%s/%s/%s", rbacpolicy.ActionAction, q.Group, q.Kind, q.Action)
|
||||
res, config, a, err := s.getAppResource(ctx, actionRequest, resourceRequest)
|
||||
res, config, a, err := s.getAppLiveResource(ctx, actionRequest, resourceRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,22 +4,24 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/text"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
repositorypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
servercache "github.com/argoproj/argo-cd/v2/server/cache"
|
||||
"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
@@ -33,6 +35,8 @@ type Server struct {
|
||||
repoClientset apiclient.Clientset
|
||||
enf *rbac.Enforcer
|
||||
cache *servercache.Cache
|
||||
appLister applisters.ApplicationNamespaceLister
|
||||
projLister applisters.AppProjectNamespaceLister
|
||||
settings *settings.SettingsManager
|
||||
}
|
||||
|
||||
@@ -42,6 +46,8 @@ func NewServer(
|
||||
db db.ArgoDB,
|
||||
enf *rbac.Enforcer,
|
||||
cache *servercache.Cache,
|
||||
appLister applisters.ApplicationNamespaceLister,
|
||||
projLister applisters.AppProjectNamespaceLister,
|
||||
settings *settings.SettingsManager,
|
||||
) *Server {
|
||||
return &Server{
|
||||
@@ -49,14 +55,20 @@ func NewServer(
|
||||
repoClientset: repoClientset,
|
||||
enf: enf,
|
||||
cache: cache,
|
||||
appLister: appLister,
|
||||
projLister: projLister,
|
||||
settings: settings,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
errPermissionDenied = status.Error(codes.PermissionDenied, "permission denied")
|
||||
)
|
||||
|
||||
func (s *Server) getRepo(ctx context.Context, url string) (*appsv1.Repository, error) {
|
||||
repo, err := s.db.GetRepository(ctx, url)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.PermissionDenied, "permission denied")
|
||||
return nil, errPermissionDenied
|
||||
}
|
||||
return repo, nil
|
||||
}
|
||||
@@ -213,14 +225,29 @@ func (s *Server) ListRefs(ctx context.Context, q *repositorypkg.RepoQuery) (*api
|
||||
})
|
||||
}
|
||||
|
||||
// ListApps returns list of apps in the repo
|
||||
// ListApps performs discovery of a git repository for potential sources of applications. Used
|
||||
// as a convenience to the UI for auto-complete.
|
||||
func (s *Server) ListApps(ctx context.Context, q *repositorypkg.RepoAppsQuery) (*repositorypkg.RepoAppsResponse, error) {
|
||||
repo, err := s.getRepo(ctx, q.Repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil {
|
||||
claims := ctx.Value("claims")
|
||||
if err := s.enf.EnforceErr(claims, rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// This endpoint causes us to clone git repos & invoke config management tooling for the purposes
|
||||
// of app discovery. Only allow this to happen if user has privileges to create or update the
|
||||
// application which it wants to retrieve these details for.
|
||||
appRBACresource := fmt.Sprintf("%s/%s", q.AppProject, q.AppName)
|
||||
if !s.enf.Enforce(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionCreate, appRBACresource) &&
|
||||
!s.enf.Enforce(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, appRBACresource) {
|
||||
return nil, errPermissionDenied
|
||||
}
|
||||
// Also ensure the repo is actually allowed in the project in question
|
||||
if err := s.isRepoPermittedInProject(q.Repo, q.AppProject); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -245,6 +272,9 @@ func (s *Server) ListApps(ctx context.Context, q *repositorypkg.RepoAppsQuery) (
|
||||
return &repositorypkg.RepoAppsResponse{Items: items}, nil
|
||||
}
|
||||
|
||||
// GetAppDetails shows parameter values to various config tools (e.g. helm/kustomize values)
|
||||
// This is used by UI for parameter form fields during app create & edit pages.
|
||||
// It is also used when showing history of parameters used in previous syncs in the app history.
|
||||
func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDetailsQuery) (*apiclient.RepoAppDetailsResponse, error) {
|
||||
if q.Source == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "missing payload in request")
|
||||
@@ -253,9 +283,38 @@ func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDeta
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil {
|
||||
claims := ctx.Value("claims")
|
||||
if err := s.enf.EnforceErr(claims, rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
app, err := s.appLister.Get(q.AppName)
|
||||
appRBACObj := createRBACObject(q.AppProject, q.AppName)
|
||||
// ensure caller has read privileges to app
|
||||
if err := s.enf.EnforceErr(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, appRBACObj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if apierr.IsNotFound(err) {
|
||||
// app doesn't exist since it still is being formulated. verify they can create the app
|
||||
// before we reveal repo details
|
||||
if err := s.enf.EnforceErr(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionCreate, appRBACObj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// if we get here we are returning repo details of an existing app
|
||||
if q.AppProject != app.Spec.Project {
|
||||
return nil, errPermissionDenied
|
||||
}
|
||||
// verify caller is not making a request with arbitrary source values which were not in our history
|
||||
if !isSourceInHistory(app, *q.Source) {
|
||||
return nil, errPermissionDenied
|
||||
}
|
||||
}
|
||||
// Ensure the repo is actually allowed in the project in question
|
||||
if err := s.isRepoPermittedInProject(q.Source.RepoURL, q.AppProject); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, repoClient, err := s.repoClientset.NewRepoServerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -273,11 +332,16 @@ func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDeta
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
helmOptions, err := s.settings.GetHelmSettings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repoClient.GetAppDetails(ctx, &apiclient.RepoServerAppDetailsQuery{
|
||||
Repo: repo,
|
||||
Source: q.Source,
|
||||
Repos: helmRepos,
|
||||
KustomizeOptions: kustomizeOptions,
|
||||
HelmOptions: helmOptions,
|
||||
AppName: q.AppName,
|
||||
})
|
||||
}
|
||||
@@ -473,3 +537,33 @@ func (s *Server) testRepo(ctx context.Context, repo *appsv1.Repository) error {
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Server) isRepoPermittedInProject(repo string, projName string) error {
|
||||
proj, err := s.projLister.Get(projName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !proj.IsSourcePermitted(appsv1.ApplicationSource{RepoURL: repo}) {
|
||||
return status.Errorf(codes.PermissionDenied, "repository '%s' not permitted in project '%s'", repo, projName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isSourceInHistory checks if the supplied application source is either our current application
|
||||
// source, or was something which we synced to previously.
|
||||
func isSourceInHistory(app *v1alpha1.Application, source v1alpha1.ApplicationSource) bool {
|
||||
if source.Equals(app.Spec.Source) {
|
||||
return true
|
||||
}
|
||||
// Iterate history. When comparing items in our history, use the actual synced revision to
|
||||
// compare with the supplied source.targetRevision in the request. This is because
|
||||
// history[].source.targetRevision is ambiguous (e.g. HEAD), whereas
|
||||
// history[].revision will contain the explicit SHA
|
||||
for _, h := range app.Status.History {
|
||||
h.Source.TargetRevision = h.Revision
|
||||
if source.Equals(h.Source) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ import "github.com/argoproj/argo-cd/v2/reposerver/repository/repository.proto";
|
||||
message RepoAppsQuery {
|
||||
string repo = 1;
|
||||
string revision = 2;
|
||||
string appName = 3;
|
||||
string appProject = 4;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +31,7 @@ message AppInfo {
|
||||
message RepoAppDetailsQuery {
|
||||
github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSource source = 1;
|
||||
string appName = 2;
|
||||
string appProject = 3;
|
||||
}
|
||||
|
||||
// RepoAppsResponse contains applications of specified repository
|
||||
|
||||
@@ -6,43 +6,143 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/server/cache"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient/repository"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/util/assets"
|
||||
"github.com/argoproj/argo-cd/v2/util/rbac"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient/repository"
|
||||
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
fakeapps "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake"
|
||||
appinformer "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions"
|
||||
applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/server/cache"
|
||||
"github.com/argoproj/argo-cd/v2/util/assets"
|
||||
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/db"
|
||||
dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/util/rbac"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
const testNamespace = "default"
|
||||
|
||||
var (
|
||||
argocdCM = corev1.ConfigMap{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: testNamespace,
|
||||
Name: "argocd-cm",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
}
|
||||
argocdSecret = corev1.Secret{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "argocd-secret",
|
||||
Namespace: testNamespace,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"admin.password": []byte("test"),
|
||||
"server.secretkey": []byte("test"),
|
||||
},
|
||||
}
|
||||
defaultProj = &appsv1.AppProject{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "AppProject",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default",
|
||||
Namespace: testNamespace,
|
||||
},
|
||||
Spec: appsv1.AppProjectSpec{
|
||||
SourceRepos: []string{"*"},
|
||||
Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
|
||||
},
|
||||
}
|
||||
|
||||
defaultProjNoSources = &appsv1.AppProject{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "AppProject",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default",
|
||||
Namespace: testNamespace,
|
||||
},
|
||||
Spec: appsv1.AppProjectSpec{
|
||||
SourceRepos: []string{},
|
||||
Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
|
||||
},
|
||||
}
|
||||
|
||||
guestbookApp = &appsv1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "guestbook",
|
||||
Namespace: testNamespace,
|
||||
},
|
||||
Spec: appsv1.ApplicationSpec{
|
||||
Project: "default",
|
||||
Source: appsv1.ApplicationSource{
|
||||
RepoURL: "https://test",
|
||||
TargetRevision: "HEAD",
|
||||
Helm: &appsv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"values.yaml"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: appsv1.ApplicationStatus{
|
||||
History: appsv1.RevisionHistories{
|
||||
{
|
||||
Revision: "abcdef123567",
|
||||
Source: appsv1.ApplicationSource{
|
||||
RepoURL: "https://test",
|
||||
TargetRevision: "HEAD",
|
||||
Helm: &appsv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"values-old.yaml"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func newAppAndProjLister(objects ...runtime.Object) (applisters.ApplicationNamespaceLister, applisters.AppProjectNamespaceLister) {
|
||||
fakeAppsClientset := fakeapps.NewSimpleClientset(objects...)
|
||||
factory := appinformer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, appinformer.WithNamespace(""), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {}))
|
||||
projInformer := factory.Argoproj().V1alpha1().AppProjects()
|
||||
appsInformer := factory.Argoproj().V1alpha1().Applications()
|
||||
for _, obj := range objects {
|
||||
switch obj.(type) {
|
||||
case *appsv1.AppProject:
|
||||
_ = projInformer.Informer().GetStore().Add(obj)
|
||||
case *appsv1.Application:
|
||||
_ = appsInformer.Informer().GetStore().Add(obj)
|
||||
}
|
||||
}
|
||||
appLister := appsInformer.Lister().Applications(testNamespace)
|
||||
projLister := projInformer.Lister().AppProjects(testNamespace)
|
||||
return appLister, projLister
|
||||
}
|
||||
|
||||
func Test_createRBACObject(t *testing.T) {
|
||||
object := createRBACObject("test-prj", "test-repo")
|
||||
assert.Equal(t, "test-prj/test-repo", object)
|
||||
@@ -51,34 +151,17 @@ func Test_createRBACObject(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRepositoryServer(t *testing.T) {
|
||||
kubeclientset := fake.NewSimpleClientset(&corev1.ConfigMap{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: testNamespace,
|
||||
Name: "argocd-cm",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
}, &corev1.Secret{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "argocd-secret",
|
||||
Namespace: testNamespace,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"admin.password": []byte("test"),
|
||||
"server.secretkey": []byte("test"),
|
||||
},
|
||||
})
|
||||
kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret)
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), kubeclientset, testNamespace)
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
|
||||
appLister, projLister := newAppAndProjLister(defaultProj)
|
||||
argoDB := db.NewDB("default", settingsMgr, kubeclientset)
|
||||
|
||||
t.Run("Test_getRepo", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
|
||||
s := NewServer(&repoServerClientset, argoDB, enforcer, nil, settingsMgr)
|
||||
s := NewServer(&repoServerClientset, argoDB, enforcer, nil, appLister, projLister, settingsMgr)
|
||||
url := "https://test"
|
||||
repo, _ := s.getRepo(context.TODO(), url)
|
||||
assert.Equal(t, repo.Repo, url)
|
||||
@@ -89,7 +172,7 @@ func TestRepositoryServer(t *testing.T) {
|
||||
repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
|
||||
s := NewServer(&repoServerClientset, argoDB, enforcer, nil, settingsMgr)
|
||||
s := NewServer(&repoServerClientset, argoDB, enforcer, nil, appLister, projLister, settingsMgr)
|
||||
url := "https://test"
|
||||
_, err := s.ValidateAccess(context.TODO(), &repository.RepoAccessQuery{
|
||||
Repo: url,
|
||||
@@ -104,10 +187,10 @@ func TestRepositoryServer(t *testing.T) {
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), url).Return(&v1alpha1.Repository{Repo: url}, nil)
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
db.On("RepositoryExists", context.TODO(), url).Return(true, nil)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, settingsMgr)
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
repo, err := s.Get(context.TODO(), &repository.RepoQuery{
|
||||
Repo: url,
|
||||
})
|
||||
@@ -124,12 +207,12 @@ func TestRepositoryServer(t *testing.T) {
|
||||
db.On("GetRepository", context.TODO(), url).Return(nil, errors.New("some error"))
|
||||
db.On("RepositoryExists", context.TODO(), url).Return(true, nil)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, settingsMgr)
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
repo, err := s.Get(context.TODO(), &repository.RepoQuery{
|
||||
Repo: url,
|
||||
})
|
||||
assert.Nil(t, repo)
|
||||
assert.Equal(t, err.Error(), "rpc error: code = PermissionDenied desc = permission denied")
|
||||
assert.Equal(t, err, errPermissionDenied)
|
||||
})
|
||||
|
||||
t.Run("Test_GetWithNotExistRepoShouldReturn404", func(t *testing.T) {
|
||||
@@ -138,15 +221,15 @@ func TestRepositoryServer(t *testing.T) {
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), url).Return(&v1alpha1.Repository{Repo: url}, nil)
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
db.On("RepositoryExists", context.TODO(), url).Return(false, nil)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, settingsMgr)
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
repo, err := s.Get(context.TODO(), &repository.RepoQuery{
|
||||
Repo: url,
|
||||
})
|
||||
assert.Nil(t, repo)
|
||||
assert.Equal(t, err.Error(), "rpc error: code = NotFound desc = repo 'https://test' not found")
|
||||
assert.Equal(t, "rpc error: code = NotFound desc = repo 'https://test' not found", err.Error())
|
||||
})
|
||||
|
||||
t.Run("Test_CreateRepositoryWithoutUpsert", func(t *testing.T) {
|
||||
@@ -156,14 +239,14 @@ func TestRepositoryServer(t *testing.T) {
|
||||
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), "test").Return(nil, errors.New("not found"))
|
||||
db.On("CreateRepository", context.TODO(), mock.Anything).Return(&apiclient.TestRepositoryResponse{}).Return(&v1alpha1.Repository{
|
||||
db.On("CreateRepository", context.TODO(), mock.Anything).Return(&apiclient.TestRepositoryResponse{}).Return(&appsv1.Repository{
|
||||
Repo: "repo",
|
||||
Project: "proj",
|
||||
}, nil)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, settingsMgr)
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
repo, err := s.CreateRepository(context.TODO(), &repository.RepoCreateRequest{
|
||||
Repo: &v1alpha1.Repository{
|
||||
Repo: &appsv1.Repository{
|
||||
Repo: "test",
|
||||
Username: "test",
|
||||
},
|
||||
@@ -178,16 +261,16 @@ func TestRepositoryServer(t *testing.T) {
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), "test").Return(&v1alpha1.Repository{
|
||||
db.On("GetRepository", context.TODO(), "test").Return(&appsv1.Repository{
|
||||
Repo: "test",
|
||||
Username: "test",
|
||||
}, nil)
|
||||
db.On("CreateRepository", context.TODO(), mock.Anything).Return(nil, status.Errorf(codes.AlreadyExists, "repository already exists"))
|
||||
db.On("UpdateRepository", context.TODO(), mock.Anything).Return(nil, nil)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, settingsMgr)
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
repo, err := s.CreateRepository(context.TODO(), &repository.RepoCreateRequest{
|
||||
Repo: &v1alpha1.Repository{
|
||||
Repo: &appsv1.Repository{
|
||||
Repo: "test",
|
||||
Username: "test",
|
||||
},
|
||||
@@ -200,6 +283,295 @@ func TestRepositoryServer(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestRepositoryServerListApps(t *testing.T) {
|
||||
kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret)
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), kubeclientset, testNamespace)
|
||||
|
||||
t.Run("Test_WithoutAppCreateUpdatePrivileges", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
enforcer.SetDefaultRole("role:readonly")
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
appLister, projLister := newAppAndProjLister(defaultProj)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
resp, err := s.ListApps(context.TODO(), &repository.RepoAppsQuery{
|
||||
Repo: "https://test",
|
||||
Revision: "HEAD",
|
||||
AppName: "foo",
|
||||
AppProject: "default",
|
||||
})
|
||||
assert.Nil(t, resp)
|
||||
assert.Equal(t, err, errPermissionDenied)
|
||||
})
|
||||
|
||||
t.Run("Test_WithAppCreateUpdatePrivileges", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
enforcer.SetDefaultRole("role:admin")
|
||||
appLister, projLister := newAppAndProjLister(defaultProj)
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
repoServerClient.On("ListApps", context.TODO(), mock.Anything).Return(&apiclient.AppList{
|
||||
Apps: map[string]string{
|
||||
"path/to/dir": "Kustomize",
|
||||
},
|
||||
}, nil)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
resp, err := s.ListApps(context.TODO(), &repository.RepoAppsQuery{
|
||||
Repo: "https://test",
|
||||
Revision: "HEAD",
|
||||
AppName: "foo",
|
||||
AppProject: "default",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, resp.Items, 1)
|
||||
assert.Equal(t, "path/to/dir", resp.Items[0].Path)
|
||||
assert.Equal(t, "Kustomize", resp.Items[0].Type)
|
||||
})
|
||||
|
||||
t.Run("Test_WithAppCreateUpdatePrivilegesRepoNotAllowed", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
enforcer.SetDefaultRole("role:admin")
|
||||
appLister, projLister := newAppAndProjLister(defaultProjNoSources)
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
repoServerClient.On("ListApps", context.TODO(), mock.Anything).Return(&apiclient.AppList{
|
||||
Apps: map[string]string{
|
||||
"path/to/dir": "Kustomize",
|
||||
},
|
||||
}, nil)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
resp, err := s.ListApps(context.TODO(), &repository.RepoAppsQuery{
|
||||
Repo: "https://test",
|
||||
Revision: "HEAD",
|
||||
AppName: "foo",
|
||||
AppProject: "default",
|
||||
})
|
||||
assert.Nil(t, resp)
|
||||
assert.Error(t, err, "repository 'https://test' not permitted in project 'default'")
|
||||
})
|
||||
}
|
||||
|
||||
func TestRepositoryServerGetAppDetails(t *testing.T) {
|
||||
kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret)
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), kubeclientset, testNamespace)
|
||||
|
||||
t.Run("Test_WithoutRepoReadPrivileges", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
enforcer.SetDefaultRole("")
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
appLister, projLister := newAppAndProjLister(defaultProj)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
|
||||
Source: &appsv1.ApplicationSource{
|
||||
RepoURL: url,
|
||||
},
|
||||
AppName: "newapp",
|
||||
AppProject: "default",
|
||||
})
|
||||
assert.Nil(t, resp)
|
||||
assert.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: repositories, get, https://test")
|
||||
})
|
||||
t.Run("Test_WithoutAppReadPrivileges", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
_ = enforcer.SetUserPolicy("p, role:readrepos, repositories, get, *, allow")
|
||||
enforcer.SetDefaultRole("role:readrepos")
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
appLister, projLister := newAppAndProjLister(defaultProj)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
|
||||
Source: &appsv1.ApplicationSource{
|
||||
RepoURL: url,
|
||||
},
|
||||
AppName: "newapp",
|
||||
AppProject: "default",
|
||||
})
|
||||
assert.Nil(t, resp)
|
||||
assert.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, get, default/newapp")
|
||||
})
|
||||
t.Run("Test_WithoutCreatePrivileges", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
enforcer.SetDefaultRole("role:readonly")
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
appLister, projLister := newAppAndProjLister(defaultProj)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
|
||||
Source: &appsv1.ApplicationSource{
|
||||
RepoURL: url,
|
||||
},
|
||||
AppName: "newapp",
|
||||
AppProject: "default",
|
||||
})
|
||||
assert.Nil(t, resp)
|
||||
assert.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, create, default/newapp")
|
||||
})
|
||||
t.Run("Test_WithCreatePrivileges", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil)
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"}
|
||||
repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil)
|
||||
appLister, projLister := newAppAndProjLister(defaultProj)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
|
||||
Source: &appsv1.ApplicationSource{
|
||||
RepoURL: url,
|
||||
},
|
||||
AppName: "newapp",
|
||||
AppProject: "default",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedResp, *resp)
|
||||
})
|
||||
t.Run("Test_RepoNotPermitted", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"}
|
||||
repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil)
|
||||
appLister, projLister := newAppAndProjLister(defaultProjNoSources)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
|
||||
Source: &appsv1.ApplicationSource{
|
||||
RepoURL: url,
|
||||
},
|
||||
AppName: "newapp",
|
||||
AppProject: "default",
|
||||
})
|
||||
assert.Error(t, err, "repository 'https://test' not permitted in project 'default'")
|
||||
assert.Nil(t, resp)
|
||||
})
|
||||
t.Run("Test_ExistingApp", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil)
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"}
|
||||
repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil)
|
||||
appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
|
||||
Source: &guestbookApp.Spec.Source,
|
||||
AppName: "guestbook",
|
||||
AppProject: "default",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedResp, *resp)
|
||||
})
|
||||
t.Run("Test_ExistingAppMismatchedProjectName", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp)
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
|
||||
Source: &guestbookApp.Spec.Source,
|
||||
AppName: "guestbook",
|
||||
AppProject: "mismatch",
|
||||
})
|
||||
assert.Equal(t, errPermissionDenied, err)
|
||||
assert.Nil(t, resp)
|
||||
})
|
||||
t.Run("Test_ExistingAppSourceNotInHistory", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp)
|
||||
differentSource := guestbookApp.Spec.Source.DeepCopy()
|
||||
differentSource.Helm.ValueFiles = []string{"/etc/passwd"}
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
|
||||
Source: differentSource,
|
||||
AppName: "guestbook",
|
||||
AppProject: "default",
|
||||
})
|
||||
assert.Equal(t, errPermissionDenied, err)
|
||||
assert.Nil(t, resp)
|
||||
})
|
||||
t.Run("Test_ExistingAppSourceInHistory", func(t *testing.T) {
|
||||
repoServerClient := mocks.RepoServerServiceClient{}
|
||||
repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
|
||||
enforcer := newEnforcer(kubeclientset)
|
||||
|
||||
url := "https://test"
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
|
||||
db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil)
|
||||
expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"}
|
||||
repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil)
|
||||
appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp)
|
||||
previousSource := guestbookApp.Status.History[0].Source.DeepCopy()
|
||||
previousSource.TargetRevision = guestbookApp.Status.History[0].Revision
|
||||
|
||||
s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
|
||||
resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
|
||||
Source: previousSource,
|
||||
AppName: "guestbook",
|
||||
AppProject: "default",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedResp, *resp)
|
||||
})
|
||||
}
|
||||
|
||||
type fixtures struct {
|
||||
*cache.Cache
|
||||
}
|
||||
|
||||
@@ -154,6 +154,7 @@ type ArgoCDServer struct {
|
||||
settingsMgr *settings_util.SettingsManager
|
||||
enf *rbac.Enforcer
|
||||
projInformer cache.SharedIndexInformer
|
||||
projLister applisters.AppProjectNamespaceLister
|
||||
policyEnforcer *rbacpolicy.RBACPolicyEnforcer
|
||||
appInformer cache.SharedIndexInformer
|
||||
appLister applisters.ApplicationNamespaceLister
|
||||
@@ -248,6 +249,7 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer {
|
||||
settingsMgr: settingsMgr,
|
||||
enf: enf,
|
||||
projInformer: projInformer,
|
||||
projLister: projLister,
|
||||
appInformer: appInformer,
|
||||
appLister: appLister,
|
||||
policyEnforcer: policyEnf,
|
||||
@@ -562,7 +564,7 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server {
|
||||
db := db.NewDB(a.Namespace, a.settingsMgr, a.KubeClientset)
|
||||
kubectl := kubeutil.NewKubectl()
|
||||
clusterService := cluster.NewServer(db, a.enf, a.Cache, kubectl)
|
||||
repoService := repository.NewServer(a.RepoClientset, db, a.enf, a.Cache, a.settingsMgr)
|
||||
repoService := repository.NewServer(a.RepoClientset, db, a.enf, a.Cache, a.appLister, a.projLister, a.settingsMgr)
|
||||
repoCredsService := repocreds.NewServer(a.RepoClientset, db, a.enf, a.settingsMgr)
|
||||
var loginRateLimiter func() (io.Closer, error)
|
||||
if maxConcurrentLoginRequestsCount > 0 {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM redis:6.2.4 as redis
|
||||
FROM redis:6.2.6-alpine as redis
|
||||
|
||||
FROM node:12.18.4 as node
|
||||
|
||||
|
||||
@@ -643,7 +643,9 @@ func TestKsonnetApp(t *testing.T) {
|
||||
defer io.Close(closer)
|
||||
|
||||
details, err := client.GetAppDetails(context.Background(), &repositorypkg.RepoAppDetailsQuery{
|
||||
Source: &app.Spec.Source,
|
||||
AppName: app.Name,
|
||||
AppProject: app.Spec.Project,
|
||||
Source: &app.Spec.Source,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -821,64 +823,125 @@ func TestSyncAsync(t *testing.T) {
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced))
|
||||
}
|
||||
|
||||
func TestPermissions(t *testing.T) {
|
||||
EnsureCleanState(t)
|
||||
appName := Name()
|
||||
_, err := RunCli("proj", "create", "test")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// make sure app cannot be created without permissions in project
|
||||
_, err = RunCli("app", "create", appName, "--repo", RepoURL(RepoURLTypeFile),
|
||||
"--path", guestbookPath, "--project", "test", "--dest-server", KubernetesInternalAPIServerAddr, "--dest-namespace", DeploymentNamespace())
|
||||
assert.Error(t, err)
|
||||
sourceError := fmt.Sprintf("application repo %s is not permitted in project 'test'", RepoURL(RepoURLTypeFile))
|
||||
destinationError := fmt.Sprintf("application destination {%s %s} is not permitted in project 'test'", KubernetesInternalAPIServerAddr, DeploymentNamespace())
|
||||
|
||||
assert.Contains(t, err.Error(), sourceError)
|
||||
assert.Contains(t, err.Error(), destinationError)
|
||||
|
||||
proj, err := AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Get(context.Background(), "test", metav1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
proj.Spec.Destinations = []ApplicationDestination{{Server: "*", Namespace: "*"}}
|
||||
proj.Spec.SourceRepos = []string{"*"}
|
||||
proj, err = AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Update(context.Background(), proj, metav1.UpdateOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// make sure controller report permissions issues in conditions
|
||||
_, err = RunCli("app", "create", appName, "--repo", RepoURL(RepoURLTypeFile),
|
||||
"--path", guestbookPath, "--project", "test", "--dest-server", KubernetesInternalAPIServerAddr, "--dest-namespace", DeploymentNamespace())
|
||||
assert.NoError(t, err)
|
||||
defer func() {
|
||||
err = AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Delete(context.Background(), appName, metav1.DeleteOptions{})
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
|
||||
proj.Spec.Destinations = []ApplicationDestination{}
|
||||
proj.Spec.SourceRepos = []string{}
|
||||
_, err = AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Update(context.Background(), proj, metav1.UpdateOptions{})
|
||||
assert.NoError(t, err)
|
||||
time.Sleep(1 * time.Second)
|
||||
closer, client, err := ArgoCDClientset.NewApplicationClient()
|
||||
assert.NoError(t, err)
|
||||
defer io.Close(closer)
|
||||
|
||||
refresh := string(RefreshTypeNormal)
|
||||
app, err := client.Get(context.Background(), &applicationpkg.ApplicationQuery{Name: &appName, Refresh: &refresh})
|
||||
assert.NoError(t, err)
|
||||
|
||||
destinationErrorExist := false
|
||||
sourceErrorExist := false
|
||||
for i := range app.Status.Conditions {
|
||||
if strings.Contains(app.Status.Conditions[i].Message, destinationError) {
|
||||
destinationErrorExist = true
|
||||
}
|
||||
if strings.Contains(app.Status.Conditions[i].Message, sourceError) {
|
||||
sourceErrorExist = true
|
||||
// assertResourceActions verifies if view/modify resource actions are successful/failing for given application
|
||||
func assertResourceActions(t *testing.T, appName string, successful bool) {
|
||||
assertError := func(err error, message string) {
|
||||
if successful {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), message)
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.True(t, destinationErrorExist)
|
||||
assert.True(t, sourceErrorExist)
|
||||
|
||||
closer, cdClient := ArgoCDClientset.NewApplicationClientOrDie()
|
||||
defer io.Close(closer)
|
||||
|
||||
deploymentResource, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
logs, err := cdClient.PodLogs(context.Background(), &applicationpkg.ApplicationPodLogsQuery{
|
||||
Group: pointer.String("apps"), Kind: pointer.String("Deployment"), Name: &appName, Namespace: DeploymentNamespace(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = logs.Recv()
|
||||
assertError(err, "EOF")
|
||||
|
||||
expectedError := fmt.Sprintf("Deployment apps guestbook-ui not found as part of application %s", appName)
|
||||
|
||||
_, err = cdClient.ListResourceEvents(context.Background(), &applicationpkg.ApplicationResourceEventsQuery{
|
||||
Name: &appName, ResourceName: "guestbook-ui", ResourceNamespace: DeploymentNamespace(), ResourceUID: string(deploymentResource.UID)})
|
||||
assertError(err, fmt.Sprintf("%s not found as part of application %s", "guestbook-ui", appName))
|
||||
|
||||
_, err = cdClient.GetResource(context.Background(), &applicationpkg.ApplicationResourceRequest{
|
||||
Name: &appName, ResourceName: "guestbook-ui", Namespace: DeploymentNamespace(), Version: "v1", Group: "apps", Kind: "Deployment"})
|
||||
assertError(err, expectedError)
|
||||
|
||||
_, err = cdClient.DeleteResource(context.Background(), &applicationpkg.ApplicationResourceDeleteRequest{
|
||||
Name: &appName, ResourceName: "guestbook-ui", Namespace: DeploymentNamespace(), Version: "v1", Group: "apps", Kind: "Deployment",
|
||||
})
|
||||
assertError(err, expectedError)
|
||||
|
||||
_, err = cdClient.RunResourceAction(context.Background(), &applicationpkg.ResourceActionRunRequest{
|
||||
Name: &appName, ResourceName: "guestbook-ui", Namespace: DeploymentNamespace(), Version: "v1", Group: "apps", Kind: "Deployment", Action: "restart",
|
||||
})
|
||||
assertError(err, expectedError)
|
||||
}
|
||||
|
||||
func TestPermissions(t *testing.T) {
|
||||
appCtx := Given(t)
|
||||
projName := "argo-project"
|
||||
projActions := projectFixture.
|
||||
Given(t).
|
||||
Name(projName).
|
||||
When().
|
||||
Create()
|
||||
|
||||
sourceError := fmt.Sprintf("application repo %s is not permitted in project 'argo-project'", RepoURL(RepoURLTypeFile))
|
||||
destinationError := fmt.Sprintf("application destination {%s %s} is not permitted in project 'argo-project'", KubernetesInternalAPIServerAddr, DeploymentNamespace())
|
||||
|
||||
appCtx.
|
||||
Path("guestbook-logs").
|
||||
Project(projName).
|
||||
When().
|
||||
IgnoreErrors().
|
||||
// ensure app is not created if project permissions are missing
|
||||
Create().
|
||||
Then().
|
||||
Expect(Error("", sourceError)).
|
||||
Expect(Error("", destinationError)).
|
||||
When().
|
||||
DoNotIgnoreErrors().
|
||||
// add missing permissions, create and sync app
|
||||
And(func() {
|
||||
projActions.AddDestination("*", "*")
|
||||
projActions.AddSource("*")
|
||||
}).
|
||||
Create().
|
||||
Sync().
|
||||
Then().
|
||||
// make sure application resource actiions are successful
|
||||
And(func(app *Application) {
|
||||
assertResourceActions(t, app.Name, true)
|
||||
}).
|
||||
When().
|
||||
// remove projet permissions and "refresh" app
|
||||
And(func() {
|
||||
projActions.UpdateProject(func(proj *AppProject) {
|
||||
proj.Spec.Destinations = nil
|
||||
proj.Spec.SourceRepos = nil
|
||||
})
|
||||
}).
|
||||
Refresh(RefreshTypeNormal).
|
||||
Then().
|
||||
// ensure app resource tree is empty when source/destination permissions are missing
|
||||
Expect(Condition(ApplicationConditionInvalidSpecError, destinationError)).
|
||||
Expect(Condition(ApplicationConditionInvalidSpecError, sourceError)).
|
||||
And(func(app *Application) {
|
||||
closer, cdClient := ArgoCDClientset.NewApplicationClientOrDie()
|
||||
defer io.Close(closer)
|
||||
tree, err := cdClient.ResourceTree(context.Background(), &applicationpkg.ResourcesQuery{ApplicationName: &app.Name})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, tree.Nodes, 0)
|
||||
assert.Len(t, tree.OrphanedNodes, 0)
|
||||
}).
|
||||
When().
|
||||
// add missing permissions but deny management of Deployment kind
|
||||
And(func() {
|
||||
projActions.
|
||||
AddDestination("*", "*").
|
||||
AddSource("*").
|
||||
UpdateProject(func(proj *AppProject) {
|
||||
proj.Spec.NamespaceResourceBlacklist = []metav1.GroupKind{{Group: "*", Kind: "Deployment"}}
|
||||
})
|
||||
}).
|
||||
Refresh(RefreshTypeNormal).
|
||||
Then().
|
||||
// make sure application resource actiions are failing
|
||||
And(func(app *Application) {
|
||||
assertResourceActions(t, "test-permissions", false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPermissionWithScopedRepo(t *testing.T) {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/test/e2e/fixture"
|
||||
)
|
||||
|
||||
@@ -34,6 +40,25 @@ func (a *Actions) Create(args ...string) *Actions {
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Actions) AddDestination(cluster string, namespace string) *Actions {
|
||||
a.runCli("proj", "add-destination", a.context.name, cluster, namespace)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Actions) AddSource(repo string) *Actions {
|
||||
a.runCli("proj", "add-source", a.context.name, repo)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Actions) UpdateProject(updater func(project *v1alpha1.AppProject)) *Actions {
|
||||
proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get(context.TODO(), a.context.name, v1.GetOptions{})
|
||||
require.NoError(a.context.t, err)
|
||||
updater(proj)
|
||||
_, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Update(context.TODO(), proj, v1.UpdateOptions{})
|
||||
require.NoError(a.context.t, err)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Actions) Name(name string) *Actions {
|
||||
a.context.name = name
|
||||
return a
|
||||
@@ -72,4 +97,7 @@ func (a *Actions) Then() *Consequences {
|
||||
func (a *Actions) runCli(args ...string) {
|
||||
a.context.t.Helper()
|
||||
a.lastOutput, a.lastError = fixture.RunCli(args...)
|
||||
if !a.ignoreErrors {
|
||||
require.Empty(a.context.t, a.lastError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ export const ApplicationCreatePanel = (props: {
|
||||
load={async src =>
|
||||
(src.repoURL &&
|
||||
services.repos
|
||||
.apps(src.repoURL, src.revision)
|
||||
.apps(src.repoURL, src.revision, app.metadata.name, app.spec.project)
|
||||
.then(apps => Array.from(new Set(apps.map(item => item.path))).sort())
|
||||
.catch(() => new Array<string>())) ||
|
||||
new Array<string>()
|
||||
@@ -421,7 +421,7 @@ export const ApplicationCreatePanel = (props: {
|
||||
}}
|
||||
load={async src => {
|
||||
if (src.repoURL && src.targetRevision && (src.path || src.chart)) {
|
||||
return services.repos.appDetails(src, src.appName).catch(() => ({
|
||||
return services.repos.appDetails(src, src.appName, app.spec.project).catch(() => ({
|
||||
type: 'Directory',
|
||||
details: {}
|
||||
}));
|
||||
|
||||
@@ -80,7 +80,7 @@ export const ApplicationDeploymentHistory = ({
|
||||
/>
|
||||
<DataLoader
|
||||
input={{...recentDeployments[index].source, targetRevision: recentDeployments[index].revision, appName: app.metadata.name}}
|
||||
load={src => services.repos.appDetails(src, src.appName)}>
|
||||
load={src => services.repos.appDetails(src, src.appName, app.spec.project)}>
|
||||
{(details: models.RepoAppDetails) => (
|
||||
<div>
|
||||
<ApplicationParameters
|
||||
|
||||
@@ -145,7 +145,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => {
|
||||
key='appDetails'
|
||||
input={application}
|
||||
load={app =>
|
||||
services.repos.appDetails(app.spec.source, app.metadata.name).catch(() => ({
|
||||
services.repos.appDetails(app.spec.source, app.metadata.name, app.spec.project).catch(() => ({
|
||||
type: 'Directory' as AppSourceType,
|
||||
path: application.spec.source.path
|
||||
}))
|
||||
|
||||
@@ -132,10 +132,12 @@ export class RepositoriesService {
|
||||
return requests.get(`/repositories/${encodeURIComponent(repo)}/refs`).then(res => res.body as models.RefsInfo);
|
||||
}
|
||||
|
||||
public apps(repo: string, revision: string): Promise<models.AppInfo[]> {
|
||||
public apps(repo: string, revision: string, appName: string, appProject: string): Promise<models.AppInfo[]> {
|
||||
return requests
|
||||
.get(`/repositories/${encodeURIComponent(repo)}/apps`)
|
||||
.query({revision})
|
||||
.query({appName})
|
||||
.query({appProject})
|
||||
.then(res => (res.body.items as models.AppInfo[]) || []);
|
||||
}
|
||||
|
||||
@@ -143,10 +145,10 @@ export class RepositoriesService {
|
||||
return requests.get(`/repositories/${encodeURIComponent(repo)}/helmcharts`).then(res => (res.body.items as models.HelmChart[]) || []);
|
||||
}
|
||||
|
||||
public appDetails(source: models.ApplicationSource, appName: string): Promise<models.RepoAppDetails> {
|
||||
public appDetails(source: models.ApplicationSource, appName: string, appProject: string): Promise<models.RepoAppDetails> {
|
||||
return requests
|
||||
.post(`/repositories/${encodeURIComponent(source.repoURL)}/appdetails`)
|
||||
.send({source, appName})
|
||||
.send({source, appName, appProject})
|
||||
.then(res => res.body as models.RepoAppDetails);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,6 +222,11 @@ func ValidateRepo(
|
||||
return conditions, nil
|
||||
}
|
||||
|
||||
helmOptions, err := settingsMgr.GetHelmSettings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
helmRepos, err := db.ListHelmRepositories(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -247,6 +252,7 @@ func ValidateRepo(
|
||||
// don't use case during application change to make sure to fetch latest git/helm revisions
|
||||
NoRevisionCache: true,
|
||||
TrackingMethod: string(GetTrackingMethod(settingsMgr)),
|
||||
HelmOptions: helmOptions,
|
||||
})
|
||||
if err != nil {
|
||||
conditions = append(conditions, argoappv1.ApplicationCondition{
|
||||
@@ -276,7 +282,18 @@ func ValidateRepo(
|
||||
return nil, err
|
||||
}
|
||||
conditions = append(conditions, verifyGenerateManifests(
|
||||
ctx, repo, permittedHelmRepos, app, repoClient, kustomizeOptions, plugins, cluster.ServerVersion, APIResourcesToStrings(apiGroups, true), permittedHelmCredentials, settingsMgr)...)
|
||||
ctx,
|
||||
repo,
|
||||
permittedHelmRepos,
|
||||
helmOptions,
|
||||
app,
|
||||
repoClient,
|
||||
kustomizeOptions,
|
||||
plugins,
|
||||
cluster.ServerVersion,
|
||||
APIResourcesToStrings(apiGroups, true),
|
||||
permittedHelmCredentials,
|
||||
settingsMgr)...)
|
||||
|
||||
return conditions, nil
|
||||
}
|
||||
@@ -457,19 +474,7 @@ func GetAppProject(spec *argoappv1.ApplicationSpec, projLister applicationsv1.Ap
|
||||
}
|
||||
|
||||
// verifyGenerateManifests verifies a repo path can generate manifests
|
||||
func verifyGenerateManifests(
|
||||
ctx context.Context,
|
||||
repoRes *argoappv1.Repository,
|
||||
helmRepos argoappv1.Repositories,
|
||||
app *argoappv1.Application,
|
||||
repoClient apiclient.RepoServerServiceClient,
|
||||
kustomizeOptions *argoappv1.KustomizeOptions,
|
||||
plugins []*argoappv1.ConfigManagementPlugin,
|
||||
kubeVersion string,
|
||||
apiVersions []string,
|
||||
repositoryCredentials []*argoappv1.RepoCreds,
|
||||
settingsMgr *settings.SettingsManager,
|
||||
) []argoappv1.ApplicationCondition {
|
||||
func verifyGenerateManifests(ctx context.Context, repoRes *argoappv1.Repository, helmRepos argoappv1.Repositories, helmOptions *argoappv1.HelmOptions, app *argoappv1.Application, repoClient apiclient.RepoServerServiceClient, kustomizeOptions *argoappv1.KustomizeOptions, plugins []*argoappv1.ConfigManagementPlugin, kubeVersion string, apiVersions []string, repositoryCredentials []*argoappv1.RepoCreds, settingsMgr *settings.SettingsManager) []argoappv1.ApplicationCondition {
|
||||
spec := &app.Spec
|
||||
var conditions []argoappv1.ApplicationCondition
|
||||
if spec.Destination.Server == "" {
|
||||
@@ -495,6 +500,7 @@ func verifyGenerateManifests(
|
||||
KustomizeOptions: kustomizeOptions,
|
||||
KubeVersion: kubeVersion,
|
||||
ApiVersions: apiVersions,
|
||||
HelmOptions: helmOptions,
|
||||
HelmRepoCreds: repositoryCredentials,
|
||||
TrackingMethod: string(GetTrackingMethod(settingsMgr)),
|
||||
NoRevisionCache: true,
|
||||
@@ -607,16 +613,10 @@ func GetPermittedRepos(proj *argoappv1.AppProject, repos []*argoappv1.Repository
|
||||
}
|
||||
|
||||
func getDestinationServer(ctx context.Context, db db.ArgoDB, clusterName string) (string, error) {
|
||||
clusterList, err := db.ListClusters(ctx)
|
||||
servers, err := db.GetClusterServersByName(ctx, clusterName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var servers []string
|
||||
for _, c := range clusterList.Items {
|
||||
if c.Name == clusterName {
|
||||
servers = append(servers, c.Server)
|
||||
}
|
||||
}
|
||||
if len(servers) > 1 {
|
||||
return "", fmt.Errorf("there are %d clusters with the same name: %v", len(servers), servers)
|
||||
} else if len(servers) == 0 {
|
||||
|
||||
@@ -268,6 +268,7 @@ func TestValidateRepo(t *testing.T) {
|
||||
Source: &app.Spec.Source,
|
||||
Repos: helmRepos,
|
||||
KustomizeOptions: kustomizeOptions,
|
||||
HelmOptions: &argoappv1.HelmOptions{ValuesFileSchemes: []string{"https", "http"}},
|
||||
NoRevisionCache: true,
|
||||
}).Return(&apiclient.RepoAppDetailsResponse{}, nil)
|
||||
|
||||
@@ -680,14 +681,7 @@ func TestValidateDestination(t *testing.T) {
|
||||
}
|
||||
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("ListClusters", context.Background()).Return(&argoappv1.ClusterList{
|
||||
Items: []argoappv1.Cluster{
|
||||
{
|
||||
Name: "minikube",
|
||||
Server: "https://127.0.0.1:6443",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
db.On("GetClusterServersByName", context.Background(), "minikube").Return([]string{"https://127.0.0.1:6443"}, nil)
|
||||
|
||||
appCond := ValidateDestination(context.Background(), &dest, db)
|
||||
assert.Nil(t, appCond)
|
||||
@@ -707,13 +701,13 @@ func TestValidateDestination(t *testing.T) {
|
||||
assert.False(t, dest.IsServerInferred())
|
||||
})
|
||||
|
||||
t.Run("List clusters fails", func(t *testing.T) {
|
||||
t.Run("GetClusterServersByName fails", func(t *testing.T) {
|
||||
dest := argoappv1.ApplicationDestination{
|
||||
Name: "minikube",
|
||||
}
|
||||
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("ListClusters", context.Background()).Return(nil, fmt.Errorf("an error occurred"))
|
||||
db.On("GetClusterServersByName", context.Background(), mock.Anything).Return(nil, fmt.Errorf("an error occurred"))
|
||||
|
||||
err := ValidateDestination(context.Background(), &dest, db)
|
||||
assert.Equal(t, "unable to find destination server: an error occurred", err.Error())
|
||||
@@ -726,14 +720,7 @@ func TestValidateDestination(t *testing.T) {
|
||||
}
|
||||
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("ListClusters", context.Background()).Return(&argoappv1.ClusterList{
|
||||
Items: []argoappv1.Cluster{
|
||||
{
|
||||
Name: "dind",
|
||||
Server: "https://127.0.0.1:6443",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
db.On("GetClusterServersByName", context.Background(), "minikube").Return(nil, nil)
|
||||
|
||||
err := ValidateDestination(context.Background(), &dest, db)
|
||||
assert.Equal(t, "unable to find destination server: there are no clusters with this name: minikube", err.Error())
|
||||
@@ -746,18 +733,7 @@ func TestValidateDestination(t *testing.T) {
|
||||
}
|
||||
|
||||
db := &dbmocks.ArgoDB{}
|
||||
db.On("ListClusters", context.Background()).Return(&argoappv1.ClusterList{
|
||||
Items: []argoappv1.Cluster{
|
||||
{
|
||||
Name: "dind",
|
||||
Server: "https://127.0.0.1:2443",
|
||||
},
|
||||
{
|
||||
Name: "dind",
|
||||
Server: "https://127.0.0.1:8443",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
db.On("GetClusterServersByName", context.Background(), "dind").Return([]string{"https://127.0.0.1:2443", "https://127.0.0.1:8443"}, nil)
|
||||
|
||||
err := ValidateDestination(context.Background(), &dest, db)
|
||||
assert.Equal(t, "unable to find destination server: there are 2 clusters with the same name: [https://127.0.0.1:2443 https://127.0.0.1:8443]", err.Error())
|
||||
|
||||
@@ -232,6 +232,34 @@ func (db *db) GetProjectClusters(ctx context.Context, project string) ([]*appv1.
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (db *db) GetClusterServersByName(ctx context.Context, name string) ([]string, error) {
|
||||
informer, err := db.settingsMgr.GetSecretsInformer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if local cluster name is not overridden and specified name is local cluster name, return local cluster server
|
||||
localClusterSecrets, err := informer.GetIndexer().ByIndex(settings.ByClusterURLIndexer, appv1.KubernetesInternalAPIServerAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(localClusterSecrets) == 0 && db.getLocalCluster().Name == name {
|
||||
return []string{appv1.KubernetesInternalAPIServerAddr}, nil
|
||||
}
|
||||
|
||||
secrets, err := informer.GetIndexer().ByIndex(settings.ByClusterNameIndexer, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []string
|
||||
for i := range secrets {
|
||||
s := secrets[i].(*apiv1.Secret)
|
||||
res = append(res, strings.TrimRight(string(s.Data["server"]), "/"))
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// UpdateCluster updates a cluster
|
||||
func (db *db) UpdateCluster(ctx context.Context, c *appv1.Cluster) (*appv1.Cluster, error) {
|
||||
clusterSecret, err := db.getClusterSecret(c.Server)
|
||||
|
||||
@@ -27,8 +27,10 @@ type ArgoDB interface {
|
||||
handleAddEvent func(cluster *appv1.Cluster),
|
||||
handleModEvent func(oldCluster *appv1.Cluster, newCluster *appv1.Cluster),
|
||||
handleDeleteEvent func(clusterServer string)) error
|
||||
// GetCluster get returns a cluster by given server url
|
||||
// GetCluster returns a cluster by given server url
|
||||
GetCluster(ctx context.Context, server string) (*appv1.Cluster, error)
|
||||
// GetClusterServersByName returns a cluster server urls by given cluster name
|
||||
GetClusterServersByName(ctx context.Context, name string) ([]string, error)
|
||||
// GetProjectClusters return project scoped clusters by given project name
|
||||
GetProjectClusters(ctx context.Context, project string) ([]*appv1.Cluster, error)
|
||||
// UpdateCluster updates a cluster
|
||||
|
||||
@@ -693,3 +693,59 @@ func TestHelmRepositorySecretsTrim(t *testing.T) {
|
||||
assert.Equal(t, tt.expectedSecret, tt.retrievedSecret)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetClusterServersByName(t *testing.T) {
|
||||
clientset := getClientset(nil, &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-cluster-secret",
|
||||
Namespace: testNamespace,
|
||||
Labels: map[string]string{
|
||||
common.LabelKeySecretType: common.LabelValueSecretTypeCluster,
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD,
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"name": []byte("my-cluster-name"),
|
||||
"server": []byte("https://my-cluster-server"),
|
||||
"config": []byte("{}"),
|
||||
},
|
||||
})
|
||||
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
|
||||
servers, err := db.GetClusterServersByName(context.Background(), "my-cluster-name")
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, []string{"https://my-cluster-server"}, servers)
|
||||
}
|
||||
|
||||
func TestGetClusterServersByName_InClusterNotConfigured(t *testing.T) {
|
||||
clientset := getClientset(nil)
|
||||
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
|
||||
servers, err := db.GetClusterServersByName(context.Background(), "in-cluster")
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, []string{v1alpha1.KubernetesInternalAPIServerAddr}, servers)
|
||||
}
|
||||
|
||||
func TestGetClusterServersByName_InClusterConfigured(t *testing.T) {
|
||||
clientset := getClientset(nil, &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-cluster-secret",
|
||||
Namespace: testNamespace,
|
||||
Labels: map[string]string{
|
||||
common.LabelKeySecretType: common.LabelValueSecretTypeCluster,
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
common.AnnotationKeyManagedBy: common.AnnotationValueManagedByArgoCD,
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"name": []byte("in-cluster-renamed"),
|
||||
"server": []byte(v1alpha1.KubernetesInternalAPIServerAddr),
|
||||
"config": []byte("{}"),
|
||||
},
|
||||
})
|
||||
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
|
||||
servers, err := db.GetClusterServersByName(context.Background(), "in-cluster-renamed")
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, []string{v1alpha1.KubernetesInternalAPIServerAddr}, servers)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by mockery v0.0.0-dev. DO NOT EDIT.
|
||||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -242,6 +242,29 @@ func (_m *ArgoDB) GetCluster(ctx context.Context, server string) (*v1alpha1.Clus
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetClusterServersByName provides a mock function with given fields: ctx, name
|
||||
func (_m *ArgoDB) GetClusterServersByName(ctx context.Context, name string) ([]string, error) {
|
||||
ret := _m.Called(ctx, name)
|
||||
|
||||
var r0 []string
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) []string); ok {
|
||||
r0 = rf(ctx, name)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]string)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, name)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetProjectClusters provides a mock function with given fields: ctx, project
|
||||
func (_m *ArgoDB) GetProjectClusters(ctx context.Context, project string) ([]*v1alpha1.Cluster, error) {
|
||||
ret := _m.Called(ctx, project)
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
executil "github.com/argoproj/argo-cd/v2/util/exec"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
pathutil "github.com/argoproj/argo-cd/v2/util/io/path"
|
||||
"github.com/argoproj/argo-cd/v2/util/proxy"
|
||||
)
|
||||
|
||||
@@ -286,8 +287,8 @@ type TemplateOpts struct {
|
||||
APIVersions []string
|
||||
Set map[string]string
|
||||
SetString map[string]string
|
||||
SetFile map[string]string
|
||||
Values []string
|
||||
SetFile map[string]pathutil.ResolvedFilePath
|
||||
Values []pathutil.ResolvedFilePath
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -322,10 +323,10 @@ func (c *Cmd) template(chartPath string, opts *TemplateOpts) (string, error) {
|
||||
args = append(args, "--set-string", key+"="+cleanSetParameters(val))
|
||||
}
|
||||
for key, val := range opts.SetFile {
|
||||
args = append(args, "--set-file", key+"="+cleanSetParameters(val))
|
||||
args = append(args, "--set-file", key+"="+cleanSetParameters(string(val)))
|
||||
}
|
||||
for _, val := range opts.Values {
|
||||
args = append(args, "--values", val)
|
||||
args = append(args, "--values", string(val))
|
||||
}
|
||||
for _, v := range opts.APIVersions {
|
||||
args = append(args, "--api-versions", v)
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/config"
|
||||
executil "github.com/argoproj/argo-cd/v2/util/exec"
|
||||
pathutil "github.com/argoproj/argo-cd/v2/util/io/path"
|
||||
)
|
||||
|
||||
type HelmRepository struct {
|
||||
@@ -27,7 +28,7 @@ type Helm interface {
|
||||
// Template returns a list of unstructured objects from a `helm template` command
|
||||
Template(opts *TemplateOpts) (string, error)
|
||||
// GetParameters returns a list of chart parameters taking into account values in provided YAML files.
|
||||
GetParameters(valuesFiles []string) (map[string]string, error)
|
||||
GetParameters(valuesFiles []pathutil.ResolvedFilePath) (map[string]string, error)
|
||||
// DependencyBuild runs `helm dependency build` to download a chart's dependencies
|
||||
DependencyBuild() error
|
||||
// Init runs `helm init --client-only`
|
||||
@@ -129,13 +130,14 @@ func Version(shortForm bool) (string, error) {
|
||||
return strings.TrimSpace(version), nil
|
||||
}
|
||||
|
||||
func (h *helm) GetParameters(valuesFiles []string) (map[string]string, error) {
|
||||
func (h *helm) GetParameters(valuesFiles []pathutil.ResolvedFilePath) (map[string]string, error) {
|
||||
out, err := h.cmd.inspectValues(".")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values := []string{out}
|
||||
for _, file := range valuesFiles {
|
||||
for i := range valuesFiles {
|
||||
file := string(valuesFiles[i])
|
||||
var fileValues []byte
|
||||
parsedURL, err := url.ParseRequestURI(file)
|
||||
if err == nil && (parsedURL.Scheme == "http" || parsedURL.Scheme == "https") {
|
||||
|
||||
@@ -5,8 +5,9 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/argo-cd/v2/util/io/path"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/stretchr/testify/assert"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
@@ -56,7 +57,7 @@ func TestHelmTemplateValues(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
opts := TemplateOpts{
|
||||
Name: "test",
|
||||
Values: []string{"values-production.yaml"},
|
||||
Values: []path.ResolvedFilePath{"values-production.yaml"},
|
||||
}
|
||||
objs, err := template(h, &opts)
|
||||
assert.Nil(t, err)
|
||||
@@ -75,7 +76,7 @@ func TestHelmTemplateValues(t *testing.T) {
|
||||
func TestHelmGetParams(t *testing.T) {
|
||||
h, err := NewHelmApp("./testdata/redis", nil, false, "", "", false)
|
||||
assert.NoError(t, err)
|
||||
params, err := h.GetParameters([]string{})
|
||||
params, err := h.GetParameters(nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
slaveCountParam := params["cluster.slaveCount"]
|
||||
@@ -85,7 +86,7 @@ func TestHelmGetParams(t *testing.T) {
|
||||
func TestHelmGetParamsValueFiles(t *testing.T) {
|
||||
h, err := NewHelmApp("./testdata/redis", nil, false, "", "", false)
|
||||
assert.NoError(t, err)
|
||||
params, err := h.GetParameters([]string{"values-production.yaml"})
|
||||
params, err := h.GetParameters([]path.ResolvedFilePath{"values-production.yaml"})
|
||||
assert.Nil(t, err)
|
||||
|
||||
slaveCountParam := params["cluster.slaveCount"]
|
||||
@@ -95,7 +96,7 @@ func TestHelmGetParamsValueFiles(t *testing.T) {
|
||||
func TestHelmGetParamsValueFilesThatExist(t *testing.T) {
|
||||
h, err := NewHelmApp("./testdata/redis", nil, false, "", "", false)
|
||||
assert.NoError(t, err)
|
||||
params, err := h.GetParameters([]string{"values-missing.yaml", "values-production.yaml"})
|
||||
params, err := h.GetParameters([]path.ResolvedFilePath{"values-missing.yaml", "values-production.yaml"})
|
||||
assert.Nil(t, err)
|
||||
|
||||
slaveCountParam := params["cluster.slaveCount"]
|
||||
|
||||
158
util/io/path/resolved.go
Normal file
158
util/io/path/resolved.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package path
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ResolvedFilePath represents a resolved file path and intended to prevent unintentional use of not verified file path.
|
||||
type ResolvedFilePath string
|
||||
|
||||
// resolveSymbolicLinkRecursive resolves the symlink path recursively to its
|
||||
// canonical path on the file system, with a maximum nesting level of maxDepth.
|
||||
// If path is not a symlink, returns the verbatim copy of path and err of nil.
|
||||
func resolveSymbolicLinkRecursive(path string, maxDepth int) (string, error) {
|
||||
resolved, err := os.Readlink(path)
|
||||
if err != nil {
|
||||
// path is not a symbolic link
|
||||
_, ok := err.(*os.PathError)
|
||||
if ok {
|
||||
return path, nil
|
||||
}
|
||||
// Other error has occured
|
||||
return "", err
|
||||
}
|
||||
|
||||
if maxDepth == 0 {
|
||||
return "", fmt.Errorf("maximum nesting level reached")
|
||||
}
|
||||
|
||||
// If we resolved to a relative symlink, make sure we use the absolute
|
||||
// path for further resolving
|
||||
if !strings.HasPrefix(resolved, "/") {
|
||||
basePath := filepath.Dir(path)
|
||||
resolved = filepath.Join(basePath, resolved)
|
||||
}
|
||||
|
||||
return resolveSymbolicLinkRecursive(resolved, maxDepth-1)
|
||||
}
|
||||
|
||||
// isURLSchemeAllowed returns true if the protocol scheme is in the list of
|
||||
// allowed URL schemes.
|
||||
func isURLSchemeAllowed(scheme string, allowed []string) bool {
|
||||
isAllowed := false
|
||||
if len(allowed) > 0 {
|
||||
for _, s := range allowed {
|
||||
if strings.EqualFold(scheme, s) {
|
||||
isAllowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Empty scheme means local file
|
||||
return isAllowed && scheme != ""
|
||||
}
|
||||
|
||||
// ResolveFilePath will inspect and resolve given file, and make sure that its final path is within the boundaries of
|
||||
// the path specified in repoRoot.
|
||||
//
|
||||
// appPath is the path we're operating in, e.g. where a Helm chart was unpacked
|
||||
// to. repoRoot is the path to the root of the repository.
|
||||
//
|
||||
// If either appPath or repoRoot is relative, it will be treated as relative
|
||||
// to the current working directory.
|
||||
//
|
||||
// valueFile is the path to a value file, relative to appPath. If valueFile is
|
||||
// specified as an absolute path (i.e. leading slash), it will be treated as
|
||||
// relative to the repoRoot. In case valueFile is a symlink in the extracted
|
||||
// chart, it will be resolved recursively and the decision of whether it is in
|
||||
// the boundary of repoRoot will be made using the final resolved path.
|
||||
// valueFile can also be a remote URL with a protocol scheme as prefix,
|
||||
// in which case the scheme must be included in the list of allowed schemes
|
||||
// specified by allowedURLSchemes.
|
||||
//
|
||||
// Will return an error if either valueFile is outside the boundaries of the
|
||||
// repoRoot, valueFile is an URL with a forbidden protocol scheme or if
|
||||
// valueFile is a recursive symlink nested too deep. May return errors for
|
||||
// other reasons as well.
|
||||
//
|
||||
// resolvedPath will hold the absolute, resolved path for valueFile on success
|
||||
// or set to the empty string on failure.
|
||||
//
|
||||
// isRemote will be set to true if valueFile is an URL using an allowed
|
||||
// protocol scheme, or to false if it resolved to a local file.
|
||||
func ResolveFilePath(appPath, repoRoot, valueFile string, allowedURLSchemes []string) (resolvedPath ResolvedFilePath, isRemote bool, err error) {
|
||||
// We do not provide the path in the error message, because it will be
|
||||
// returned to the user and could be used for information gathering.
|
||||
// Instead, we log the concrete error details.
|
||||
resolveFailure := func(path string, err error) error {
|
||||
log.Errorf("failed to resolve path '%s': %v", path, err)
|
||||
return fmt.Errorf("internal error: failed to resolve path. Check logs for more details")
|
||||
}
|
||||
|
||||
// A value file can be specified as an URL to a remote resource.
|
||||
// We only allow certain URL schemes for remote value files.
|
||||
url, err := url.Parse(valueFile)
|
||||
if err == nil {
|
||||
// If scheme is empty, it means we parsed a path only
|
||||
if url.Scheme != "" {
|
||||
if isURLSchemeAllowed(url.Scheme, allowedURLSchemes) {
|
||||
return ResolvedFilePath(valueFile), true, nil
|
||||
} else {
|
||||
return "", false, fmt.Errorf("the URL scheme '%s' is not allowed", url.Scheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that our repository root is absolute
|
||||
absRepoPath, err := filepath.Abs(repoRoot)
|
||||
if err != nil {
|
||||
return "", false, resolveFailure(repoRoot, err)
|
||||
}
|
||||
|
||||
// If the path to the file is relative, join it with the current working directory (appPath)
|
||||
// Otherwise, join it with the repository's root
|
||||
path := valueFile
|
||||
if !filepath.IsAbs(path) {
|
||||
absWorkDir, err := filepath.Abs(appPath)
|
||||
if err != nil {
|
||||
return "", false, resolveFailure(repoRoot, err)
|
||||
}
|
||||
path = filepath.Join(absWorkDir, path)
|
||||
} else {
|
||||
path = filepath.Join(absRepoPath, path)
|
||||
}
|
||||
|
||||
// Ensure any symbolic link is resolved before we
|
||||
delinkedPath, err := resolveSymbolicLinkRecursive(path, 10)
|
||||
if err != nil {
|
||||
return "", false, resolveFailure(path, err)
|
||||
}
|
||||
path = delinkedPath
|
||||
|
||||
// Resolve the joined path to an absolute path
|
||||
path, err = filepath.Abs(path)
|
||||
if err != nil {
|
||||
return "", false, resolveFailure(path, err)
|
||||
}
|
||||
|
||||
// Ensure our root path has a trailing slash, otherwise the following check
|
||||
// would return true if root is /foo and path would be /foo2
|
||||
requiredRootPath := absRepoPath
|
||||
if !strings.HasSuffix(requiredRootPath, "/") {
|
||||
requiredRootPath += "/"
|
||||
}
|
||||
|
||||
// Make sure that the resolved path to values file is within the repository's root path
|
||||
if !strings.HasPrefix(path, requiredRootPath) {
|
||||
return "", false, fmt.Errorf("value file '%s' resolved to outside repository root", valueFile)
|
||||
}
|
||||
|
||||
return ResolvedFilePath(path), false, nil
|
||||
}
|
||||
181
util/io/path/resolved_test.go
Normal file
181
util/io/path/resolved_test.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package path
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_resolveSymlinkRecursive(t *testing.T) {
|
||||
testsDir, err := filepath.Abs("./testdata")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
t.Run("Resolve non-symlink", func(t *testing.T) {
|
||||
r, err := resolveSymbolicLinkRecursive(testsDir+"/foo", 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testsDir+"/foo", r)
|
||||
})
|
||||
t.Run("Successfully resolve symlink", func(t *testing.T) {
|
||||
r, err := resolveSymbolicLinkRecursive(testsDir+"/bar", 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testsDir+"/foo", r)
|
||||
})
|
||||
t.Run("Do not allow symlink at all", func(t *testing.T) {
|
||||
r, err := resolveSymbolicLinkRecursive(testsDir+"/bar", 0)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "", r)
|
||||
})
|
||||
t.Run("Error because too nested symlink", func(t *testing.T) {
|
||||
r, err := resolveSymbolicLinkRecursive(testsDir+"/bam", 2)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "", r)
|
||||
})
|
||||
t.Run("No such file or directory", func(t *testing.T) {
|
||||
r, err := resolveSymbolicLinkRecursive(testsDir+"/foobar", 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testsDir+"/foobar", r)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_isURLSchemeAllowed(t *testing.T) {
|
||||
type testdata struct {
|
||||
name string
|
||||
scheme string
|
||||
allowed []string
|
||||
expected bool
|
||||
}
|
||||
var tts []testdata = []testdata{
|
||||
{
|
||||
name: "Allowed scheme matches",
|
||||
scheme: "http",
|
||||
allowed: []string{"http", "https"},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "Allowed scheme matches only partially",
|
||||
scheme: "http",
|
||||
allowed: []string{"https"},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Scheme is not allowed",
|
||||
scheme: "file",
|
||||
allowed: []string{"http", "https"},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Empty scheme with valid allowances is forbidden",
|
||||
scheme: "",
|
||||
allowed: []string{"http", "https"},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Empty scheme with empty allowances is forbidden",
|
||||
scheme: "",
|
||||
allowed: []string{},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "Some scheme with empty allowances is forbidden",
|
||||
scheme: "file",
|
||||
allowed: []string{},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tts {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := isURLSchemeAllowed(tt.scheme, tt.allowed)
|
||||
assert.Equal(t, tt.expected, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var allowedRemoteProtocols = []string{"http", "https"}
|
||||
|
||||
func Test_resolveFilePath(t *testing.T) {
|
||||
t.Run("Resolve normal relative path into absolute path", func(t *testing.T) {
|
||||
p, remote, err := ResolveFilePath("/foo/bar", "/foo", "baz/bim.yaml", allowedRemoteProtocols)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, remote)
|
||||
assert.Equal(t, "/foo/bar/baz/bim.yaml", string(p))
|
||||
})
|
||||
t.Run("Resolve normal relative path into absolute path", func(t *testing.T) {
|
||||
p, remote, err := ResolveFilePath("/foo/bar", "/foo", "baz/../../bim.yaml", allowedRemoteProtocols)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, remote)
|
||||
assert.Equal(t, "/foo/bim.yaml", string(p))
|
||||
})
|
||||
t.Run("Error on path resolving outside repository root", func(t *testing.T) {
|
||||
p, remote, err := ResolveFilePath("/foo/bar", "/foo", "baz/../../../bim.yaml", allowedRemoteProtocols)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "outside repository root")
|
||||
assert.False(t, remote)
|
||||
assert.Equal(t, "", string(p))
|
||||
})
|
||||
t.Run("Return verbatim URL", func(t *testing.T) {
|
||||
url := "https://some.where/foo,yaml"
|
||||
p, remote, err := ResolveFilePath("/foo/bar", "/foo", url, allowedRemoteProtocols)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, remote)
|
||||
assert.Equal(t, url, string(p))
|
||||
})
|
||||
t.Run("URL scheme not allowed", func(t *testing.T) {
|
||||
url := "file:///some.where/foo,yaml"
|
||||
p, remote, err := ResolveFilePath("/foo/bar", "/foo", url, allowedRemoteProtocols)
|
||||
assert.Error(t, err)
|
||||
assert.False(t, remote)
|
||||
assert.Equal(t, "", string(p))
|
||||
})
|
||||
t.Run("Implicit URL by absolute path", func(t *testing.T) {
|
||||
p, remote, err := ResolveFilePath("/foo/bar", "/foo", "/baz.yaml", allowedRemoteProtocols)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, remote)
|
||||
assert.Equal(t, "/foo/baz.yaml", string(p))
|
||||
})
|
||||
t.Run("Relative app path", func(t *testing.T) {
|
||||
p, remote, err := ResolveFilePath(".", "/foo", "/baz.yaml", allowedRemoteProtocols)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, remote)
|
||||
assert.Equal(t, "/foo/baz.yaml", string(p))
|
||||
})
|
||||
t.Run("Relative repo path", func(t *testing.T) {
|
||||
c, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
p, remote, err := ResolveFilePath(".", ".", "baz.yaml", allowedRemoteProtocols)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, remote)
|
||||
assert.Equal(t, c+"/baz.yaml", string(p))
|
||||
})
|
||||
t.Run("Overlapping root prefix without trailing slash", func(t *testing.T) {
|
||||
p, remote, err := ResolveFilePath(".", "/foo", "../foo2/baz.yaml", allowedRemoteProtocols)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "outside repository root")
|
||||
assert.False(t, remote)
|
||||
assert.Equal(t, "", string(p))
|
||||
})
|
||||
t.Run("Overlapping root prefix with trailing slash", func(t *testing.T) {
|
||||
p, remote, err := ResolveFilePath(".", "/foo/", "../foo2/baz.yaml", allowedRemoteProtocols)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "outside repository root")
|
||||
assert.False(t, remote)
|
||||
assert.Equal(t, "", string(p))
|
||||
})
|
||||
t.Run("Garbage input as values file", func(t *testing.T) {
|
||||
p, remote, err := ResolveFilePath(".", "/foo/", "kfdj\\ks&&&321209.,---e32908923%$§!\"", allowedRemoteProtocols)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "outside repository root")
|
||||
assert.False(t, remote)
|
||||
assert.Equal(t, "", string(p))
|
||||
})
|
||||
t.Run("NUL-byte path input as values file", func(t *testing.T) {
|
||||
p, remote, err := ResolveFilePath(".", "/foo/", "\000", allowedRemoteProtocols)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "outside repository root")
|
||||
assert.False(t, remote)
|
||||
assert.Equal(t, "", string(p))
|
||||
})
|
||||
}
|
||||
1
util/io/path/testdata/bam
vendored
Symbolic link
1
util/io/path/testdata/bam
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
baz
|
||||
1
util/io/path/testdata/bar
vendored
Symbolic link
1
util/io/path/testdata/bar
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
foo
|
||||
1
util/io/path/testdata/baz
vendored
Symbolic link
1
util/io/path/testdata/baz
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
bar
|
||||
1
util/io/path/testdata/foo
vendored
Normal file
1
util/io/path/testdata/foo
vendored
Normal file
@@ -0,0 +1 @@
|
||||
hello
|
||||
@@ -163,6 +163,23 @@ var (
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
ByClusterNameIndexer = "byClusterName"
|
||||
byClusterNameIndexerFunc = func(obj interface{}) ([]string, error) {
|
||||
s, ok := obj.(*apiv1.Secret)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
if s.Labels == nil || s.Labels[common.LabelKeySecretType] != common.LabelValueSecretTypeCluster {
|
||||
return nil, nil
|
||||
}
|
||||
if s.Data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if name, ok := s.Data["name"]; ok {
|
||||
return []string{string(name)}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
ByProjectClusterIndexer = "byProjectCluster"
|
||||
ByProjectRepoIndexer = "byProjectRepo"
|
||||
byProjectIndexerFunc = func(secretType string) func(obj interface{}) ([]string, error) {
|
||||
@@ -363,6 +380,8 @@ const (
|
||||
partOfArgoCDSelector = "app.kubernetes.io/part-of=argocd"
|
||||
// settingsPasswordPatternKey is the key to configure user password regular expression
|
||||
settingsPasswordPatternKey = "passwordPattern"
|
||||
// helmValuesFileSchemesKey is the key to configure the list of supported helm values file schemas
|
||||
helmValuesFileSchemesKey = "helm.valuesFileSchemes"
|
||||
)
|
||||
|
||||
// SettingsManager holds config info for a new manager with which to access Kubernetes ConfigMaps.
|
||||
@@ -786,6 +805,25 @@ func (mgr *SettingsManager) GetResourceCompareOptions() (ArgoCDDiffOptions, erro
|
||||
return diffOptions, nil
|
||||
}
|
||||
|
||||
// GetHelmSettings returns helm settings
|
||||
func (mgr *SettingsManager) GetHelmSettings() (*v1alpha1.HelmOptions, error) {
|
||||
argoCDCM, err := mgr.getConfigMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
helmOptions := &v1alpha1.HelmOptions{}
|
||||
if value, ok := argoCDCM.Data[helmValuesFileSchemesKey]; ok {
|
||||
for _, item := range strings.Split(value, ",") {
|
||||
if item := strings.TrimSpace(item); item != "" {
|
||||
helmOptions.ValuesFileSchemes = append(helmOptions.ValuesFileSchemes, item)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
helmOptions.ValuesFileSchemes = []string{"https", "http"}
|
||||
}
|
||||
return helmOptions, nil
|
||||
}
|
||||
|
||||
// GetKustomizeSettings loads the kustomize settings from argocd-cm ConfigMap
|
||||
func (mgr *SettingsManager) GetKustomizeSettings() (*KustomizeSettings, error) {
|
||||
argoCDCM, err := mgr.getConfigMap()
|
||||
@@ -1044,6 +1082,7 @@ func (mgr *SettingsManager) initialize(ctx context.Context) error {
|
||||
indexers := cache.Indexers{
|
||||
cache.NamespaceIndex: cache.MetaNamespaceIndexFunc,
|
||||
ByClusterURLIndexer: byClusterURLIndexerFunc,
|
||||
ByClusterNameIndexer: byClusterNameIndexerFunc,
|
||||
ByProjectClusterIndexer: byProjectIndexerFunc(common.LabelValueSecretTypeCluster),
|
||||
ByProjectRepoIndexer: byProjectIndexerFunc(common.LabelValueSecretTypeRepository),
|
||||
}
|
||||
|
||||
@@ -947,3 +947,72 @@ requestedIDTokenClaims: {"groups": {"essential": true}}`,
|
||||
oidcConfig := settings.OIDCConfig()
|
||||
assert.Equal(t, oidcConfig.ClientSecret, "deadbeef")
|
||||
}
|
||||
|
||||
func TestGetHelmSettings(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
data map[string]string
|
||||
expected []string
|
||||
}{{
|
||||
name: "Default",
|
||||
data: map[string]string{},
|
||||
expected: []string{"http", "https"},
|
||||
}, {
|
||||
name: "Configured Not Empty",
|
||||
data: map[string]string{
|
||||
"helm.valuesFileSchemes": "s3, git",
|
||||
},
|
||||
expected: []string{"s3", "git"},
|
||||
}, {
|
||||
name: "Configured Empty",
|
||||
data: map[string]string{
|
||||
"helm.valuesFileSchemes": "",
|
||||
},
|
||||
expected: nil,
|
||||
}}
|
||||
|
||||
for i := range testCases {
|
||||
tc := testCases[i]
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cm := &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDConfigMapName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: tc.data,
|
||||
}
|
||||
argocdSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDSecretName,
|
||||
Namespace: "default",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"admin.password": nil,
|
||||
"server.secretkey": nil,
|
||||
},
|
||||
}
|
||||
secret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "acme",
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"clientSecret": []byte("deadbeef"),
|
||||
},
|
||||
}
|
||||
kubeClient := fake.NewSimpleClientset(cm, secret, argocdSecret)
|
||||
settingsManager := NewSettingsManager(context.Background(), kubeClient, "default")
|
||||
|
||||
helmSettings, err := settingsManager.GetHelmSettings()
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.ElementsMatch(t, tc.expected, helmSettings.ValuesFileSchemes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user