mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-06 16:38:46 +01:00
Compare commits
41 Commits
commit-ser
...
v1.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fe1447b72 | ||
|
|
539516bd43 | ||
|
|
8a57d544ff | ||
|
|
cd77e2a048 | ||
|
|
a52f766815 | ||
|
|
646fd37e16 | ||
|
|
c74ca22023 | ||
|
|
2d170be242 | ||
|
|
079101522d | ||
|
|
1bea98e01b | ||
|
|
7c09221f7c | ||
|
|
6355e910d4 | ||
|
|
891e0320d7 | ||
|
|
486323ae58 | ||
|
|
4ef875aa0b | ||
|
|
e756b8db7a | ||
|
|
8023f8ac8d | ||
|
|
803408904a | ||
|
|
702f9095da | ||
|
|
0b9ee1ae6d | ||
|
|
2f003e08ff | ||
|
|
e090857d6b | ||
|
|
4e29fff5a3 | ||
|
|
e279377696 | ||
|
|
5e52839ce3 | ||
|
|
3ca632a552 | ||
|
|
cfe55357ac | ||
|
|
0f6d768eca | ||
|
|
75330da328 | ||
|
|
a1bcbab0e5 | ||
|
|
db9272032a | ||
|
|
d6d6c655ff | ||
|
|
58acc92790 | ||
|
|
c3074c0977 | ||
|
|
af254f3047 | ||
|
|
c140976eeb | ||
|
|
05c22d4ddc | ||
|
|
3e08938a20 | ||
|
|
5937bb574d | ||
|
|
ded55b26d1 | ||
|
|
d79ed65de0 |
22
CHANGELOG.md
22
CHANGELOG.md
@@ -22,17 +22,11 @@ Argo CD introduces Custom Resource Actions to allow users to provide their own L
|
|||||||
|
|
||||||
* Remove deprecated componentParameterOverrides field #1372
|
* Remove deprecated componentParameterOverrides field #1372
|
||||||
|
|
||||||
### Changes since v0.12.1
|
### Changes since v0.12.2
|
||||||
|
|
||||||
#### New Features
|
|
||||||
|
|
||||||
+ Issue #357 - Expose application nodes networking information (#1333)
|
|
||||||
+ Support for customizable resource actions as Lua scripts #86
|
|
||||||
+ Surface Service/Ingress external IPs, hostname to application #908
|
|
||||||
+ Update argocd-util import/export to support proper backup and restore (#1328)
|
|
||||||
|
|
||||||
#### Enhancements
|
#### Enhancements
|
||||||
|
|
||||||
|
* `argocd app wait` should have `--resource` flag like sync #1206
|
||||||
* Adds support for `kustomize edit set image`. Closes #1275 (#1324)
|
* Adds support for `kustomize edit set image`. Closes #1275 (#1324)
|
||||||
* Allow wait to return on health or suspended (#1392)
|
* Allow wait to return on health or suspended (#1392)
|
||||||
* Application warning when a manifest is defined twice #1070
|
* Application warning when a manifest is defined twice #1070
|
||||||
@@ -41,30 +35,32 @@ Argo CD introduces Custom Resource Actions to allow users to provide their own L
|
|||||||
* Display number of errors on resource tab #1477
|
* Display number of errors on resource tab #1477
|
||||||
* Displays resources that are being deleted as "Progressing". Closes #1410 (#1426)
|
* Displays resources that are being deleted as "Progressing". Closes #1410 (#1426)
|
||||||
* Generate random name for grpc proxy unix socket file instead of time stamp (#1455)
|
* Generate random name for grpc proxy unix socket file instead of time stamp (#1455)
|
||||||
|
* Issue #357 - Expose application nodes networking information (#1333)
|
||||||
* Issue #1404 - App controller unnecessary set namespace to cluster level resources (#1405)
|
* Issue #1404 - App controller unnecessary set namespace to cluster level resources (#1405)
|
||||||
* Nils health if the resource does not provide it. Closes #1383 (#1408)
|
* Nils health if the resource does not provide it. Closes #1383 (#1408)
|
||||||
* Perform health assessments on all resource nodes in the tree. Closes #1382 (#1422)
|
* Perform health assessments on all resource nodes in the tree. Closes #1382 (#1422)
|
||||||
* Remove deprecated componentParameterOverrides field #1372
|
* Remove deprecated componentParameterOverrides field #1372
|
||||||
* Shows the health of the application. Closes #1433 (#1434)
|
* Shows the health of the application. Closes #1433 (#1434)
|
||||||
|
* Surface Service/Ingress external IPs, hostname to application #908
|
||||||
* Surface pod status to tree view #1358
|
* Surface pod status to tree view #1358
|
||||||
|
* Support for customizable resource actions as Lua scripts #86
|
||||||
* UI / API Errors Truncated, Time Out #1386
|
* UI / API Errors Truncated, Time Out #1386
|
||||||
* UI Enhancement Proposals Quick Wins #1274
|
* UI Enhancement Proposals Quick Wins #1274
|
||||||
|
* Update argocd-util import/export to support proper backup and restore (#1328)
|
||||||
* Whitelisting repos/clusters in projects should consider repo/cluster permissions #1432
|
* Whitelisting repos/clusters in projects should consider repo/cluster permissions #1432
|
||||||
|
|
||||||
#### Bug Fixes
|
#### Bug Fixes
|
||||||
|
|
||||||
- "bind: address already in use" after switching to gRPC-Web #1451
|
|
||||||
- Annoying warning while using `--grpc-web` flag #1420
|
|
||||||
- Don't compare secrets in the CLI, since argo-cd doesn't have access to their data (#1459)
|
- Don't compare secrets in the CLI, since argo-cd doesn't have access to their data (#1459)
|
||||||
- Dropdown menu should not have sync item for unmanaged resources #1357
|
- Dropdown menu should not have sync item for unmanaged resources #1357
|
||||||
- Fixes goroutine leak. Closes #1381 (#1457)
|
- Fixes goroutine leak. Closes #1381 (#1457)
|
||||||
- Improve input style #1217
|
- Improve input style #1217
|
||||||
- Issue #1389 - Fix null pointer exception in secret normalization function (#1428)
|
|
||||||
- Issue #1425 - Argo CD should not delete CRDs (#1428)
|
|
||||||
- Issue #1446 - Delete helm temp directories (#1449)
|
|
||||||
- Issue #908 - Surface Service/Ingress external IPs, hostname to application (#1347)
|
- Issue #908 - Surface Service/Ingress external IPs, hostname to application (#1347)
|
||||||
|
- kustomization fields are all mandatory #1504
|
||||||
|
- Resource node details is crashing if live resource is missing $1505
|
||||||
- Rollback UI is not showing correct ksonnet parameters in preview #1326
|
- Rollback UI is not showing correct ksonnet parameters in preview #1326
|
||||||
- See details of applications fails with "r.nodes is undefined" #1371
|
- See details of applications fails with "r.nodes is undefined" #1371
|
||||||
|
- UI fails to load custom actions is resource is not deployed #1502
|
||||||
- Unable to create app from private repo: x509: certificate signed by unknown authority #1171
|
- Unable to create app from private repo: x509: certificate signed by unknown authority #1171
|
||||||
|
|
||||||
## v0.12.2 (2019-04-22)
|
## v0.12.2 (2019-04-22)
|
||||||
|
|||||||
5
Makefile
5
Makefile
@@ -11,6 +11,8 @@ GIT_TREE_STATE=$(shell if [ -z "`git status --porcelain`" ]; then echo "clean" ;
|
|||||||
PACKR_CMD=$(shell if [ "`which packr`" ]; then echo "packr"; else echo "go run vendor/github.com/gobuffalo/packr/packr/main.go"; fi)
|
PACKR_CMD=$(shell if [ "`which packr`" ]; then echo "packr"; else echo "go run vendor/github.com/gobuffalo/packr/packr/main.go"; fi)
|
||||||
TEST_CMD=$(shell [ "`which gotestsum`" != "" ] && echo gotestsum -- || echo go test)
|
TEST_CMD=$(shell [ "`which gotestsum`" != "" ] && echo gotestsum -- || echo go test)
|
||||||
|
|
||||||
|
PATH:=$(PATH):$(PWD)/hack
|
||||||
|
|
||||||
# docker image publishing options
|
# docker image publishing options
|
||||||
DOCKER_PUSH=false
|
DOCKER_PUSH=false
|
||||||
IMAGE_TAG=latest
|
IMAGE_TAG=latest
|
||||||
@@ -148,6 +150,7 @@ test-e2e: cli
|
|||||||
|
|
||||||
.PHONY: start-e2e
|
.PHONY: start-e2e
|
||||||
start-e2e: cli
|
start-e2e: cli
|
||||||
|
killall goreman || true
|
||||||
kubectl create ns argocd-e2e || true
|
kubectl create ns argocd-e2e || true
|
||||||
kubens argocd-e2e
|
kubens argocd-e2e
|
||||||
kustomize build test/manifests/base | kubectl apply -f -
|
kustomize build test/manifests/base | kubectl apply -f -
|
||||||
@@ -178,4 +181,4 @@ release-precheck: manifests
|
|||||||
@if [ "$(GIT_TAG)" != "v`cat VERSION`" ]; then echo 'VERSION does not match git tag'; exit 1; fi
|
@if [ "$(GIT_TAG)" != "v`cat VERSION`" ]; then echo 'VERSION does not match git tag'; exit 1; fi
|
||||||
|
|
||||||
.PHONY: release
|
.PHONY: release
|
||||||
release: release-precheck precheckin image release-cli
|
release: release-precheck pre-commit image release-cli
|
||||||
|
|||||||
2
Procfile
2
Procfile
@@ -2,4 +2,4 @@ controller: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true go run ./cmd/a
|
|||||||
api-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true go run ./cmd/argocd-server/main.go --loglevel debug --redis localhost:6379 --disable-auth --insecure --dex-server http://localhost:5556 --repo-server localhost:8081 --staticassets ../argo-cd-ui/dist/app"
|
api-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true go run ./cmd/argocd-server/main.go --loglevel debug --redis localhost:6379 --disable-auth --insecure --dex-server http://localhost:5556 --repo-server localhost:8081 --staticassets ../argo-cd-ui/dist/app"
|
||||||
repo-server: sh -c "FORCE_LOG_COLORS=1 go run ./cmd/argocd-repo-server/main.go --loglevel debug --redis localhost:6379"
|
repo-server: sh -c "FORCE_LOG_COLORS=1 go run ./cmd/argocd-repo-server/main.go --loglevel debug --redis localhost:6379"
|
||||||
dex: sh -c "go run ./cmd/argocd-util/main.go gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p 5556:5556 -v `pwd`/dist/dex.yaml:/dex.yaml quay.io/dexidp/dex:v2.14.0 serve /dex.yaml"
|
dex: sh -c "go run ./cmd/argocd-util/main.go gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p 5556:5556 -v `pwd`/dist/dex.yaml:/dex.yaml quay.io/dexidp/dex:v2.14.0 serve /dex.yaml"
|
||||||
redis: docker run --rm -i -p 6379:6379 redis:5.0.3-alpine --save "" --appendonly no
|
redis: docker run --rm --name argocd-redis -i -p 6379:6379 redis:5.0.3-alpine --save ""--appendonly no
|
||||||
|
|||||||
@@ -68,6 +68,11 @@
|
|||||||
},
|
},
|
||||||
"name": "project",
|
"name": "project",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "resourceVersion",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -212,6 +217,11 @@
|
|||||||
},
|
},
|
||||||
"name": "project",
|
"name": "project",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "resourceVersion",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -1387,6 +1397,11 @@
|
|||||||
},
|
},
|
||||||
"name": "project",
|
"name": "project",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "resourceVersion",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -1598,6 +1613,12 @@
|
|||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"scopes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2651,13 +2672,6 @@
|
|||||||
"$ref": "#/definitions/v1alpha1ApplicationCondition"
|
"$ref": "#/definitions/v1alpha1ApplicationCondition"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"externalURLs": {
|
|
||||||
"description": "ExternalURLs holds all external URLs of application child resources.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"health": {
|
"health": {
|
||||||
"$ref": "#/definitions/v1alpha1HealthStatus"
|
"$ref": "#/definitions/v1alpha1HealthStatus"
|
||||||
},
|
},
|
||||||
@@ -2685,11 +2699,33 @@
|
|||||||
"sourceType": {
|
"sourceType": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"summary": {
|
||||||
|
"$ref": "#/definitions/v1alpha1ApplicationSummary"
|
||||||
|
},
|
||||||
"sync": {
|
"sync": {
|
||||||
"$ref": "#/definitions/v1alpha1SyncStatus"
|
"$ref": "#/definitions/v1alpha1SyncStatus"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"v1alpha1ApplicationSummary": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"externalURLs": {
|
||||||
|
"description": "ExternalURLs holds all external URLs of application child resources.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"images": {
|
||||||
|
"description": "Images holds all images of application child resources.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"v1alpha1ApplicationTree": {
|
"v1alpha1ApplicationTree": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"title": "ApplicationTree holds nodes which belongs to the application",
|
"title": "ApplicationTree holds nodes which belongs to the application",
|
||||||
|
|||||||
@@ -37,14 +37,15 @@ const (
|
|||||||
|
|
||||||
func newCommand() *cobra.Command {
|
func newCommand() *cobra.Command {
|
||||||
var (
|
var (
|
||||||
clientConfig clientcmd.ClientConfig
|
clientConfig clientcmd.ClientConfig
|
||||||
appResyncPeriod int64
|
appResyncPeriod int64
|
||||||
repoServerAddress string
|
repoServerAddress string
|
||||||
statusProcessors int
|
repoServerTimeoutSeconds int
|
||||||
operationProcessors int
|
statusProcessors int
|
||||||
logLevel string
|
operationProcessors int
|
||||||
glogLevel int
|
logLevel string
|
||||||
cacheSrc func() (*cache.Cache, error)
|
glogLevel int
|
||||||
|
cacheSrc func() (*cache.Cache, error)
|
||||||
)
|
)
|
||||||
var command = cobra.Command{
|
var command = cobra.Command{
|
||||||
Use: cliName,
|
Use: cliName,
|
||||||
@@ -54,9 +55,9 @@ func newCommand() *cobra.Command {
|
|||||||
cli.SetGLogLevel(glogLevel)
|
cli.SetGLogLevel(glogLevel)
|
||||||
|
|
||||||
config, err := clientConfig.ClientConfig()
|
config, err := clientConfig.ClientConfig()
|
||||||
|
errors.CheckError(err)
|
||||||
config.QPS = common.K8sClientConfigQPS
|
config.QPS = common.K8sClientConfigQPS
|
||||||
config.Burst = common.K8sClientConfigBurst
|
config.Burst = common.K8sClientConfigBurst
|
||||||
errors.CheckError(err)
|
|
||||||
|
|
||||||
kubeClient := kubernetes.NewForConfigOrDie(config)
|
kubeClient := kubernetes.NewForConfigOrDie(config)
|
||||||
appClient := appclientset.NewForConfigOrDie(config)
|
appClient := appclientset.NewForConfigOrDie(config)
|
||||||
@@ -65,7 +66,7 @@ func newCommand() *cobra.Command {
|
|||||||
errors.CheckError(err)
|
errors.CheckError(err)
|
||||||
|
|
||||||
resyncDuration := time.Duration(appResyncPeriod) * time.Second
|
resyncDuration := time.Duration(appResyncPeriod) * time.Second
|
||||||
repoClientset := reposerver.NewRepoServerClientset(repoServerAddress)
|
repoClientset := reposerver.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@@ -98,6 +99,7 @@ func newCommand() *cobra.Command {
|
|||||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||||
command.Flags().Int64Var(&appResyncPeriod, "app-resync", defaultAppResyncPeriod, "Time period in seconds for application resync.")
|
command.Flags().Int64Var(&appResyncPeriod, "app-resync", defaultAppResyncPeriod, "Time period in seconds for application resync.")
|
||||||
command.Flags().StringVar(&repoServerAddress, "repo-server", common.DefaultRepoServerAddr, "Repo server address.")
|
command.Flags().StringVar(&repoServerAddress, "repo-server", common.DefaultRepoServerAddr, "Repo server address.")
|
||||||
|
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", 60, "Repo server RPC call timeout seconds.")
|
||||||
command.Flags().IntVar(&statusProcessors, "status-processors", 1, "Number of application status processors")
|
command.Flags().IntVar(&statusProcessors, "status-processors", 1, "Number of application status processors")
|
||||||
command.Flags().IntVar(&operationProcessors, "operation-processors", 1, "Number of application operation processors")
|
command.Flags().IntVar(&operationProcessors, "operation-processors", 1, "Number of application operation processors")
|
||||||
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ func NewCommand() *cobra.Command {
|
|||||||
|
|
||||||
kubeclientset := kubernetes.NewForConfigOrDie(config)
|
kubeclientset := kubernetes.NewForConfigOrDie(config)
|
||||||
appclientset := appclientset.NewForConfigOrDie(config)
|
appclientset := appclientset.NewForConfigOrDie(config)
|
||||||
repoclientset := reposerver.NewRepoServerClientset(repoServerAddress)
|
repoclientset := reposerver.NewRepoServerClientset(repoServerAddress, 0)
|
||||||
|
|
||||||
argoCDOpts := server.ArgoCDServerOpts{
|
argoCDOpts := server.ArgoCDServerOpts{
|
||||||
Insecure: insecure,
|
Insecure: insecure,
|
||||||
|
|||||||
@@ -957,6 +957,7 @@ func formatConditionsSummary(app argoappv1.Application) string {
|
|||||||
const (
|
const (
|
||||||
resourceFieldDelimiter = ":"
|
resourceFieldDelimiter = ":"
|
||||||
resourceFieldCount = 3
|
resourceFieldCount = 3
|
||||||
|
labelFieldDelimiter = "="
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseSelectedResources(resources []string) []argoappv1.SyncOperationResource {
|
func parseSelectedResources(resources []string) []argoappv1.SyncOperationResource {
|
||||||
@@ -979,6 +980,21 @@ func parseSelectedResources(resources []string) []argoappv1.SyncOperationResourc
|
|||||||
return selectedResources
|
return selectedResources
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseLabels(labels []string) (map[string]string, error) {
|
||||||
|
var selectedLabels map[string]string
|
||||||
|
if labels != nil {
|
||||||
|
selectedLabels = map[string]string{}
|
||||||
|
for _, r := range labels {
|
||||||
|
fields := strings.Split(r, labelFieldDelimiter)
|
||||||
|
if len(fields) != 2 {
|
||||||
|
return nil, fmt.Errorf("labels should have key%svalue, but instead got: %s", labelFieldDelimiter, r)
|
||||||
|
}
|
||||||
|
selectedLabels[fields[0]] = fields[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return selectedLabels, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewApplicationWaitCommand returns a new instance of an `argocd app wait` command
|
// NewApplicationWaitCommand returns a new instance of an `argocd app wait` command
|
||||||
func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||||
var (
|
var (
|
||||||
@@ -1073,6 +1089,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
|||||||
var (
|
var (
|
||||||
revision string
|
revision string
|
||||||
resources []string
|
resources []string
|
||||||
|
labels []string
|
||||||
prune bool
|
prune bool
|
||||||
dryRun bool
|
dryRun bool
|
||||||
timeout uint
|
timeout uint
|
||||||
@@ -1091,8 +1108,51 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
|||||||
conn, appIf := acdClient.NewApplicationClientOrDie()
|
conn, appIf := acdClient.NewApplicationClientOrDie()
|
||||||
defer util.Close(conn)
|
defer util.Close(conn)
|
||||||
|
|
||||||
selectedResources := parseSelectedResources(resources)
|
|
||||||
appName := args[0]
|
appName := args[0]
|
||||||
|
|
||||||
|
selectedLabels, parseErr := parseLabels(labels)
|
||||||
|
if parseErr != nil {
|
||||||
|
log.Fatal(parseErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(selectedLabels) > 0 {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
if revision == "" {
|
||||||
|
revision = "HEAD"
|
||||||
|
}
|
||||||
|
|
||||||
|
q := application.ApplicationManifestQuery{
|
||||||
|
Name: &appName,
|
||||||
|
Revision: revision,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := appIf.GetManifests(ctx, &q)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mfst := range res.Manifests {
|
||||||
|
obj, err := argoappv1.UnmarshalToUnstructured(mfst)
|
||||||
|
errors.CheckError(err)
|
||||||
|
for key, selectedValue := range selectedLabels {
|
||||||
|
if objectValue, ok := obj.GetLabels()[key]; ok && selectedValue == objectValue {
|
||||||
|
gvk := obj.GroupVersionKind()
|
||||||
|
resources = append(resources, fmt.Sprintf("%s:%s:%s", gvk.Group, gvk.Kind, obj.GetName()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If labels are provided and none are found return error only if specific resources were also not
|
||||||
|
// specified.
|
||||||
|
if len(resources) == 0 {
|
||||||
|
log.Fatalf("No matching resources found for labels: %v", labels)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedResources := parseSelectedResources(resources)
|
||||||
|
|
||||||
syncReq := application.ApplicationSyncRequest{
|
syncReq := application.ApplicationSyncRequest{
|
||||||
Name: &appName,
|
Name: &appName,
|
||||||
DryRun: dryRun,
|
DryRun: dryRun,
|
||||||
@@ -1117,18 +1177,21 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
|||||||
app, err := waitOnApplicationStatus(acdClient, appName, timeout, false, false, true, false, selectedResources)
|
app, err := waitOnApplicationStatus(acdClient, appName, timeout, false, false, true, false, selectedResources)
|
||||||
errors.CheckError(err)
|
errors.CheckError(err)
|
||||||
|
|
||||||
pruningRequired := 0
|
// Only get resources to be pruned if sync was application-wide
|
||||||
for _, resDetails := range app.Status.OperationState.SyncResult.Resources {
|
if len(selectedResources) == 0 {
|
||||||
if resDetails.Status == argoappv1.ResultCodePruneSkipped {
|
pruningRequired := 0
|
||||||
pruningRequired++
|
for _, resDetails := range app.Status.OperationState.SyncResult.Resources {
|
||||||
|
if resDetails.Status == argoappv1.ResultCodePruneSkipped {
|
||||||
|
pruningRequired++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pruningRequired > 0 {
|
||||||
|
log.Fatalf("%d resources require pruning", pruningRequired)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if pruningRequired > 0 {
|
|
||||||
log.Fatalf("%d resources require pruning", pruningRequired)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !app.Status.OperationState.Phase.Successful() && !dryRun {
|
if !app.Status.OperationState.Phase.Successful() && !dryRun {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -1136,6 +1199,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
|||||||
command.Flags().BoolVar(&prune, "prune", false, "Allow deleting unexpected resources")
|
command.Flags().BoolVar(&prune, "prune", false, "Allow deleting unexpected resources")
|
||||||
command.Flags().StringVar(&revision, "revision", "", "Sync to a specific revision. Preserves parameter overrides")
|
command.Flags().StringVar(&revision, "revision", "", "Sync to a specific revision. Preserves parameter overrides")
|
||||||
command.Flags().StringArrayVar(&resources, "resource", []string{}, fmt.Sprintf("Sync only specific resources as GROUP%sKIND%sNAME. Fields may be blank. This option may be specified repeatedly", resourceFieldDelimiter, resourceFieldDelimiter))
|
command.Flags().StringArrayVar(&resources, "resource", []string{}, fmt.Sprintf("Sync only specific resources as GROUP%sKIND%sNAME. Fields may be blank. This option may be specified repeatedly", resourceFieldDelimiter, resourceFieldDelimiter))
|
||||||
|
command.Flags().StringArrayVar(&labels, "label", []string{}, fmt.Sprintf("Sync only specific resources with a label. This option may be specified repeatedly."))
|
||||||
command.Flags().UintVar(&timeout, "timeout", defaultCheckTimeoutSeconds, "Time out after this many seconds")
|
command.Flags().UintVar(&timeout, "timeout", defaultCheckTimeoutSeconds, "Time out after this many seconds")
|
||||||
command.Flags().StringVar(&strategy, "strategy", "", "Sync strategy (one of: apply|hook)")
|
command.Flags().StringVar(&strategy, "strategy", "", "Sync strategy (one of: apply|hook)")
|
||||||
command.Flags().BoolVar(&force, "force", false, "Use a force apply")
|
command.Flags().BoolVar(&force, "force", false, "Use a force apply")
|
||||||
|
|||||||
25
cmd/argocd/commands/app_test.go
Normal file
25
cmd/argocd/commands/app_test.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseLabels(t *testing.T) {
|
||||||
|
validLabels := []string{"key=value", "foo=bar", "intuit=inc"}
|
||||||
|
|
||||||
|
result, err := parseLabels(validLabels)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, result, 3)
|
||||||
|
|
||||||
|
invalidLabels := []string{"key=value", "too=many=equals"}
|
||||||
|
_, err = parseLabels(invalidLabels)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
emptyLabels := []string{}
|
||||||
|
result, err = parseLabels(emptyLabels)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, result, 0)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -68,7 +68,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
|||||||
// See issue #315
|
// See issue #315
|
||||||
err := git.TestRepo(repo.Repo, "", "", repo.SSHPrivateKey, repo.InsecureIgnoreHostKey)
|
err := git.TestRepo(repo.Repo, "", "", repo.SSHPrivateKey, repo.InsecureIgnoreHostKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if git.IsSSHURL(repo.Repo) {
|
if yes, _ := git.IsSSHURL(repo.Repo); yes {
|
||||||
// If we failed using git SSH credentials, then the repo is automatically bad
|
// If we failed using git SSH credentials, then the repo is automatically bad
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/pkg/apis/application"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||||
@@ -110,21 +112,43 @@ func NewApplicationController(
|
|||||||
}
|
}
|
||||||
appInformer, appLister := ctrl.newApplicationInformerAndLister()
|
appInformer, appLister := ctrl.newApplicationInformerAndLister()
|
||||||
projInformer := v1alpha1.NewAppProjectInformer(applicationClientset, namespace, appResyncPeriod, cache.Indexers{})
|
projInformer := v1alpha1.NewAppProjectInformer(applicationClientset, namespace, appResyncPeriod, cache.Indexers{})
|
||||||
stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settings, kubectlCmd, func(appName string, fullRefresh bool) {
|
metricsAddr := fmt.Sprintf("0.0.0.0:%d", common.PortArgoCDMetrics)
|
||||||
ctrl.requestAppRefresh(appName, fullRefresh)
|
ctrl.metricsServer = metrics.NewMetricsServer(metricsAddr, appLister)
|
||||||
ctrl.appRefreshQueue.Add(fmt.Sprintf("%s/%s", ctrl.namespace, appName))
|
stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settings, kubectlCmd, ctrl.metricsServer, ctrl.handleAppUpdated)
|
||||||
})
|
appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectlCmd, ctrl.settings, stateCache, projInformer, ctrl.metricsServer)
|
||||||
appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectlCmd, ctrl.settings, stateCache, projInformer)
|
|
||||||
ctrl.appInformer = appInformer
|
ctrl.appInformer = appInformer
|
||||||
ctrl.appLister = appLister
|
ctrl.appLister = appLister
|
||||||
ctrl.projInformer = projInformer
|
ctrl.projInformer = projInformer
|
||||||
ctrl.appStateManager = appStateManager
|
ctrl.appStateManager = appStateManager
|
||||||
ctrl.stateCache = stateCache
|
ctrl.stateCache = stateCache
|
||||||
metricsAddr := fmt.Sprintf("0.0.0.0:%d", common.PortArgoCDMetrics)
|
|
||||||
ctrl.metricsServer = metrics.NewMetricsServer(metricsAddr, ctrl.appLister)
|
|
||||||
return &ctrl, nil
|
return &ctrl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isSelfReferencedApp(app *appv1.Application, ref v1.ObjectReference) bool {
|
||||||
|
gvk := ref.GroupVersionKind()
|
||||||
|
return ref.UID == app.UID &&
|
||||||
|
ref.Name == app.Name &&
|
||||||
|
ref.Namespace == app.Namespace &&
|
||||||
|
gvk.Group == application.Group &&
|
||||||
|
gvk.Kind == application.ApplicationKind
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl *ApplicationController) handleAppUpdated(appName string, fullRefresh bool, ref v1.ObjectReference) {
|
||||||
|
skipForceRefresh := false
|
||||||
|
|
||||||
|
obj, exists, err := ctrl.appInformer.GetIndexer().GetByKey(ctrl.namespace + "/" + appName)
|
||||||
|
if app, ok := obj.(*appv1.Application); exists && err == nil && ok && isSelfReferencedApp(app, ref) {
|
||||||
|
// Don't force refresh app if related resource is application itself. This prevents infinite reconciliation loop.
|
||||||
|
skipForceRefresh = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !skipForceRefresh {
|
||||||
|
ctrl.requestAppRefresh(appName, fullRefresh)
|
||||||
|
}
|
||||||
|
ctrl.appRefreshQueue.Add(fmt.Sprintf("%s/%s", ctrl.namespace, appName))
|
||||||
|
}
|
||||||
|
|
||||||
func (ctrl *ApplicationController) setAppManagedResources(a *appv1.Application, comparisonResult *comparisonResult) (*appv1.ApplicationTree, error) {
|
func (ctrl *ApplicationController) setAppManagedResources(a *appv1.Application, comparisonResult *comparisonResult) (*appv1.ApplicationTree, error) {
|
||||||
managedResources, err := ctrl.managedResources(a, comparisonResult)
|
managedResources, err := ctrl.managedResources(a, comparisonResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -327,6 +351,10 @@ func (ctrl *ApplicationController) processAppOperationQueueItem() (processNext b
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldBeDeleted(app *appv1.Application, obj *unstructured.Unstructured) bool {
|
||||||
|
return !kube.IsCRD(obj) && !isSelfReferencedApp(app, kube.GetObjectRef(obj))
|
||||||
|
}
|
||||||
|
|
||||||
func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Application) error {
|
func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Application) error {
|
||||||
logCtx := log.WithField("application", app.Name)
|
logCtx := log.WithField("application", app.Name)
|
||||||
logCtx.Infof("Deleting resources")
|
logCtx.Infof("Deleting resources")
|
||||||
@@ -345,13 +373,20 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic
|
|||||||
}
|
}
|
||||||
objs := make([]*unstructured.Unstructured, 0)
|
objs := make([]*unstructured.Unstructured, 0)
|
||||||
for k := range objsMap {
|
for k := range objsMap {
|
||||||
if objsMap[k].GetDeletionTimestamp() == nil && !kube.IsCRD(objsMap[k]) {
|
if objsMap[k].GetDeletionTimestamp() == nil && shouldBeDeleted(app, objsMap[k]) {
|
||||||
objs = append(objs, objsMap[k])
|
objs = append(objs, objsMap[k])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cluster, err := ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config := metrics.AddMetricsTransportWrapper(ctrl.metricsServer, app, cluster.RESTConfig())
|
||||||
|
|
||||||
err = util.RunAllAsync(len(objs), func(i int) error {
|
err = util.RunAllAsync(len(objs), func(i int) error {
|
||||||
obj := objs[i]
|
obj := objs[i]
|
||||||
return ctrl.stateCache.Delete(app.Spec.Destination.Server, obj)
|
return ctrl.kubectl.DeleteResource(config, obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), false)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -361,6 +396,11 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
for k, obj := range objsMap {
|
||||||
|
if !shouldBeDeleted(app, obj) {
|
||||||
|
delete(objsMap, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(objsMap) > 0 {
|
if len(objsMap) > 0 {
|
||||||
logCtx.Infof("%d objects remaining for deletion", len(objsMap))
|
logCtx.Infof("%d objects remaining for deletion", len(objsMap))
|
||||||
return nil
|
return nil
|
||||||
@@ -589,7 +629,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
|
|||||||
if tree, err := ctrl.getResourceTree(app, managedResources); err != nil {
|
if tree, err := ctrl.getResourceTree(app, managedResources); err != nil {
|
||||||
app.Status.Conditions = []appv1.ApplicationCondition{{Type: appv1.ApplicationConditionComparisonError, Message: err.Error()}}
|
app.Status.Conditions = []appv1.ApplicationCondition{{Type: appv1.ApplicationConditionComparisonError, Message: err.Error()}}
|
||||||
} else {
|
} else {
|
||||||
app.Status.ExternalURLs = tree.GetBrowableURLs()
|
app.Status.Summary = tree.GetSummary()
|
||||||
if err = ctrl.cache.SetAppResourcesTree(app.Name, tree); err != nil {
|
if err = ctrl.cache.SetAppResourcesTree(app.Name, tree); err != nil {
|
||||||
logCtx.Errorf("Failed to cache resources tree: %v", err)
|
logCtx.Errorf("Failed to cache resources tree: %v", err)
|
||||||
return
|
return
|
||||||
@@ -621,7 +661,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logCtx.Errorf("Failed to cache app resources: %v", err)
|
logCtx.Errorf("Failed to cache app resources: %v", err)
|
||||||
} else {
|
} else {
|
||||||
app.Status.ExternalURLs = tree.GetBrowableURLs()
|
app.Status.Summary = tree.GetSummary()
|
||||||
}
|
}
|
||||||
|
|
||||||
syncErrCond := ctrl.autoSync(app, compareResult.syncStatus)
|
syncErrCond := ctrl.autoSync(app, compareResult.syncStatus)
|
||||||
@@ -688,7 +728,7 @@ func (ctrl *ApplicationController) refreshAppConditions(app *appv1.Application)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
specConditions, _, err := argo.GetSpecErrors(context.Background(), &app.Spec, proj, ctrl.repoClientset, ctrl.db)
|
specConditions, err := argo.ValidatePermissions(context.Background(), &app.Spec, proj, ctrl.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conditions = append(conditions, appv1.ApplicationCondition{
|
conditions = append(conditions, appv1.ApplicationCondition{
|
||||||
Type: appv1.ApplicationConditionUnknownError,
|
Type: appv1.ApplicationConditionUnknownError,
|
||||||
@@ -927,6 +967,7 @@ func (ctrl *ApplicationController) watchSettings(ctx context.Context) {
|
|||||||
ctrl.settingsMgr.Subscribe(updateCh)
|
ctrl.settingsMgr.Subscribe(updateCh)
|
||||||
prevAppLabelKey := ctrl.settings.GetAppInstanceLabelKey()
|
prevAppLabelKey := ctrl.settings.GetAppInstanceLabelKey()
|
||||||
prevResourceExclusions := ctrl.settings.ResourceExclusions
|
prevResourceExclusions := ctrl.settings.ResourceExclusions
|
||||||
|
prevResourceInclusions := ctrl.settings.ResourceInclusions
|
||||||
done := false
|
done := false
|
||||||
for !done {
|
for !done {
|
||||||
select {
|
select {
|
||||||
@@ -939,10 +980,15 @@ func (ctrl *ApplicationController) watchSettings(ctx context.Context) {
|
|||||||
prevAppLabelKey = newAppLabelKey
|
prevAppLabelKey = newAppLabelKey
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(prevResourceExclusions, newSettings.ResourceExclusions) {
|
if !reflect.DeepEqual(prevResourceExclusions, newSettings.ResourceExclusions) {
|
||||||
log.Infof("resource exclusions modified")
|
log.WithFields(log.Fields{"prevResourceExclusions": prevResourceExclusions, "newResourceExclusions": newSettings.ResourceExclusions}).Info("resource exclusions modified")
|
||||||
ctrl.stateCache.Invalidate()
|
ctrl.stateCache.Invalidate()
|
||||||
prevResourceExclusions = newSettings.ResourceExclusions
|
prevResourceExclusions = newSettings.ResourceExclusions
|
||||||
}
|
}
|
||||||
|
if !reflect.DeepEqual(prevResourceInclusions, newSettings.ResourceInclusions) {
|
||||||
|
log.WithFields(log.Fields{"prevResourceInclusions": prevResourceInclusions, "newResourceInclusions": newSettings.ResourceInclusions}).Info("resource inclusions modified")
|
||||||
|
ctrl.stateCache.Invalidate()
|
||||||
|
prevResourceInclusions = newSettings.ResourceInclusions
|
||||||
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
done = true
|
done = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/common"
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
@@ -119,6 +121,7 @@ var fakeApp = `
|
|||||||
apiVersion: argoproj.io/v1alpha1
|
apiVersion: argoproj.io/v1alpha1
|
||||||
kind: Application
|
kind: Application
|
||||||
metadata:
|
metadata:
|
||||||
|
uid: "123"
|
||||||
name: my-app
|
name: my-app
|
||||||
namespace: ` + test.FakeArgoCDNamespace + `
|
namespace: ` + test.FakeArgoCDNamespace + `
|
||||||
spec:
|
spec:
|
||||||
@@ -353,20 +356,26 @@ func TestAutoSyncParameterOverrides(t *testing.T) {
|
|||||||
// TestFinalizeAppDeletion verifies application deletion
|
// TestFinalizeAppDeletion verifies application deletion
|
||||||
func TestFinalizeAppDeletion(t *testing.T) {
|
func TestFinalizeAppDeletion(t *testing.T) {
|
||||||
app := newFakeApp()
|
app := newFakeApp()
|
||||||
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}})
|
app.Spec.Destination.Namespace = test.FakeArgoCDNamespace
|
||||||
|
appObj := kube.MustToUnstructured(&app)
|
||||||
|
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}, managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
|
||||||
|
kube.GetResourceKey(appObj): appObj,
|
||||||
|
}})
|
||||||
|
|
||||||
fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset)
|
|
||||||
patched := false
|
patched := false
|
||||||
|
fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset)
|
||||||
|
defaultReactor := fakeAppCs.ReactionChain[0]
|
||||||
fakeAppCs.ReactionChain = nil
|
fakeAppCs.ReactionChain = nil
|
||||||
|
fakeAppCs.AddReactor("get", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||||
|
return defaultReactor.React(action)
|
||||||
|
})
|
||||||
fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
fakeAppCs.AddReactor("patch", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||||
patched = true
|
patched = true
|
||||||
return true, nil, nil
|
return true, nil, nil
|
||||||
})
|
})
|
||||||
err := ctrl.finalizeApplicationDeletion(app)
|
err := ctrl.finalizeApplicationDeletion(app)
|
||||||
// TODO: use an interface to fake out the calls to GetResourcesWithLabel and DeleteResourceWithLabel
|
assert.NoError(t, err)
|
||||||
// For now just ensure we have an expected error condition
|
assert.True(t, patched)
|
||||||
assert.Error(t, err) // Change this to assert.Nil when we stub out GetResourcesWithLabel/DeleteResourceWithLabel
|
|
||||||
assert.False(t, patched) // Change this to assert.True when we stub out GetResourcesWithLabel/DeleteResourceWithLabel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestNormalizeApplication verifies we normalize an application during reconciliation
|
// TestNormalizeApplication verifies we normalize an application during reconciliation
|
||||||
@@ -442,3 +451,18 @@ func TestNormalizeApplication(t *testing.T) {
|
|||||||
assert.False(t, normalized)
|
assert.False(t, normalized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHandleAppUpdated(t *testing.T) {
|
||||||
|
app := newFakeApp()
|
||||||
|
app.Spec.Destination.Namespace = test.FakeArgoCDNamespace
|
||||||
|
app.Spec.Destination.Server = common.KubernetesInternalAPIServerAddr
|
||||||
|
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}})
|
||||||
|
|
||||||
|
ctrl.handleAppUpdated(app.Name, true, kube.GetObjectRef(kube.MustToUnstructured(app)))
|
||||||
|
isRequested, _ := ctrl.isRefreshRequested(app.Name)
|
||||||
|
assert.False(t, isRequested)
|
||||||
|
|
||||||
|
ctrl.handleAppUpdated(app.Name, true, corev1.ObjectReference{UID: "test", Kind: kube.DeploymentKind, Name: "test", Namespace: "default"})
|
||||||
|
isRequested, _ = ctrl.isRefreshRequested(app.Name)
|
||||||
|
assert.True(t, isRequested)
|
||||||
|
}
|
||||||
|
|||||||
55
controller/cache/cache.go
vendored
55
controller/cache/cache.go
vendored
@@ -5,11 +5,13 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/controller/metrics"
|
||||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||||
"github.com/argoproj/argo-cd/util"
|
"github.com/argoproj/argo-cd/util"
|
||||||
"github.com/argoproj/argo-cd/util/db"
|
"github.com/argoproj/argo-cd/util/db"
|
||||||
@@ -25,12 +27,12 @@ type LiveStateCache interface {
|
|||||||
GetManagedLiveObjs(a *appv1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error)
|
GetManagedLiveObjs(a *appv1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error)
|
||||||
// Starts watching resources of each controlled cluster.
|
// Starts watching resources of each controlled cluster.
|
||||||
Run(ctx context.Context)
|
Run(ctx context.Context)
|
||||||
// Deletes specified resource from cluster.
|
|
||||||
Delete(server string, obj *unstructured.Unstructured) error
|
|
||||||
// Invalidate invalidates the entire cluster state cache
|
// Invalidate invalidates the entire cluster state cache
|
||||||
Invalidate()
|
Invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AppUpdatedHandler = func(appName string, fullRefresh bool, ref v1.ObjectReference)
|
||||||
|
|
||||||
func GetTargetObjKey(a *appv1.Application, un *unstructured.Unstructured, isNamespaced bool) kube.ResourceKey {
|
func GetTargetObjKey(a *appv1.Application, un *unstructured.Unstructured, isNamespaced bool) kube.ResourceKey {
|
||||||
key := kube.GetResourceKey(un)
|
key := kube.GetResourceKey(un)
|
||||||
if !isNamespaced {
|
if !isNamespaced {
|
||||||
@@ -42,26 +44,35 @@ func GetTargetObjKey(a *appv1.Application, un *unstructured.Unstructured, isName
|
|||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLiveStateCache(db db.ArgoDB, appInformer cache.SharedIndexInformer, settings *settings.ArgoCDSettings, kubectl kube.Kubectl, onAppUpdated func(appName string, fullRefresh bool)) LiveStateCache {
|
func NewLiveStateCache(
|
||||||
|
db db.ArgoDB,
|
||||||
|
appInformer cache.SharedIndexInformer,
|
||||||
|
settings *settings.ArgoCDSettings,
|
||||||
|
kubectl kube.Kubectl,
|
||||||
|
metricsServer *metrics.MetricsServer,
|
||||||
|
onAppUpdated AppUpdatedHandler) LiveStateCache {
|
||||||
|
|
||||||
return &liveStateCache{
|
return &liveStateCache{
|
||||||
appInformer: appInformer,
|
appInformer: appInformer,
|
||||||
db: db,
|
db: db,
|
||||||
clusters: make(map[string]*clusterInfo),
|
clusters: make(map[string]*clusterInfo),
|
||||||
lock: &sync.Mutex{},
|
lock: &sync.Mutex{},
|
||||||
onAppUpdated: onAppUpdated,
|
onAppUpdated: onAppUpdated,
|
||||||
kubectl: kubectl,
|
kubectl: kubectl,
|
||||||
settings: settings,
|
settings: settings,
|
||||||
|
metricsServer: metricsServer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type liveStateCache struct {
|
type liveStateCache struct {
|
||||||
db db.ArgoDB
|
db db.ArgoDB
|
||||||
clusters map[string]*clusterInfo
|
clusters map[string]*clusterInfo
|
||||||
lock *sync.Mutex
|
lock *sync.Mutex
|
||||||
appInformer cache.SharedIndexInformer
|
appInformer cache.SharedIndexInformer
|
||||||
onAppUpdated func(appName string, fullRefresh bool)
|
onAppUpdated AppUpdatedHandler
|
||||||
kubectl kube.Kubectl
|
kubectl kube.Kubectl
|
||||||
settings *settings.ArgoCDSettings
|
settings *settings.ArgoCDSettings
|
||||||
|
metricsServer *metrics.MetricsServer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *liveStateCache) getCluster(server string) (*clusterInfo, error) {
|
func (c *liveStateCache) getCluster(server string) (*clusterInfo, error) {
|
||||||
@@ -116,14 +127,6 @@ func (c *liveStateCache) Invalidate() {
|
|||||||
log.Info("live state cache invalidated")
|
log.Info("live state cache invalidated")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *liveStateCache) Delete(server string, obj *unstructured.Unstructured) error {
|
|
||||||
clusterInfo, err := c.getSyncedCluster(server)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return clusterInfo.delete(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *liveStateCache) IsNamespaced(server string, obj *unstructured.Unstructured) (bool, error) {
|
func (c *liveStateCache) IsNamespaced(server string, obj *unstructured.Unstructured) (bool, error) {
|
||||||
clusterInfo, err := c.getSyncedCluster(server)
|
clusterInfo, err := c.getSyncedCluster(server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -146,7 +149,7 @@ func (c *liveStateCache) GetManagedLiveObjs(a *appv1.Application, targetObjs []*
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return clusterInfo.getManagedLiveObjs(a, targetObjs)
|
return clusterInfo.getManagedLiveObjs(a, targetObjs, c.metricsServer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isClusterHasApps(apps []interface{}, cluster *appv1.Cluster) bool {
|
func isClusterHasApps(apps []interface{}, cluster *appv1.Cluster) bool {
|
||||||
|
|||||||
41
controller/cache/cluster.go
vendored
41
controller/cache/cluster.go
vendored
@@ -7,8 +7,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/controller/metrics"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
@@ -44,7 +45,7 @@ type clusterInfo struct {
|
|||||||
nodes map[kube.ResourceKey]*node
|
nodes map[kube.ResourceKey]*node
|
||||||
nsIndex map[string]map[kube.ResourceKey]*node
|
nsIndex map[string]map[kube.ResourceKey]*node
|
||||||
|
|
||||||
onAppUpdated func(appName string, fullRefresh bool)
|
onAppUpdated AppUpdatedHandler
|
||||||
kubectl kube.Kubectl
|
kubectl kube.Kubectl
|
||||||
cluster *appv1.Cluster
|
cluster *appv1.Cluster
|
||||||
log *log.Entry
|
log *log.Entry
|
||||||
@@ -93,13 +94,8 @@ func (c *clusterInfo) createObjInfo(un *unstructured.Unstructured, appInstanceLa
|
|||||||
}
|
}
|
||||||
nodeInfo := &node{
|
nodeInfo := &node{
|
||||||
resourceVersion: un.GetResourceVersion(),
|
resourceVersion: un.GetResourceVersion(),
|
||||||
ref: v1.ObjectReference{
|
ref: kube.GetObjectRef(un),
|
||||||
APIVersion: un.GetAPIVersion(),
|
ownerRefs: ownerRefs,
|
||||||
Kind: un.GetKind(),
|
|
||||||
Name: un.GetName(),
|
|
||||||
Namespace: un.GetNamespace(),
|
|
||||||
},
|
|
||||||
ownerRefs: ownerRefs,
|
|
||||||
}
|
}
|
||||||
populateNodeInfo(un, nodeInfo)
|
populateNodeInfo(un, nodeInfo)
|
||||||
appName := kube.GetAppInstanceLabel(un, appInstanceLabel)
|
appName := kube.GetAppInstanceLabel(un, appInstanceLabel)
|
||||||
@@ -351,7 +347,7 @@ func (c *clusterInfo) isNamespaced(obj *unstructured.Unstructured) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clusterInfo) getManagedLiveObjs(a *appv1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error) {
|
func (c *clusterInfo) getManagedLiveObjs(a *appv1.Application, targetObjs []*unstructured.Unstructured, metricsServer *metrics.MetricsServer) (map[kube.ResourceKey]*unstructured.Unstructured, error) {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
@@ -362,6 +358,7 @@ func (c *clusterInfo) getManagedLiveObjs(a *appv1.Application, targetObjs []*uns
|
|||||||
managedObjs[key] = o.resource
|
managedObjs[key] = o.resource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
config := metrics.AddMetricsTransportWrapper(metricsServer, a, c.cluster.RESTConfig())
|
||||||
// iterate target objects and identify ones that already exist in the cluster,\
|
// iterate target objects and identify ones that already exist in the cluster,\
|
||||||
// but are simply missing our label
|
// but are simply missing our label
|
||||||
lock := &sync.Mutex{}
|
lock := &sync.Mutex{}
|
||||||
@@ -378,7 +375,7 @@ func (c *clusterInfo) getManagedLiveObjs(a *appv1.Application, targetObjs []*uns
|
|||||||
managedObj = existingObj.resource
|
managedObj = existingObj.resource
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
managedObj, err = c.kubectl.GetResource(c.cluster.RESTConfig(), targetObj.GroupVersionKind(), existingObj.ref.Name, existingObj.ref.Namespace)
|
managedObj, err = c.kubectl.GetResource(config, targetObj.GroupVersionKind(), existingObj.ref.Name, existingObj.ref.Namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.IsNotFound(err) {
|
if errors.IsNotFound(err) {
|
||||||
return nil
|
return nil
|
||||||
@@ -390,9 +387,19 @@ func (c *clusterInfo) getManagedLiveObjs(a *appv1.Application, targetObjs []*uns
|
|||||||
}
|
}
|
||||||
|
|
||||||
if managedObj != nil {
|
if managedObj != nil {
|
||||||
managedObj, err := c.kubectl.ConvertToVersion(managedObj, targetObj.GroupVersionKind().Group, targetObj.GroupVersionKind().Version)
|
converted, err := c.kubectl.ConvertToVersion(managedObj, targetObj.GroupVersionKind().Group, targetObj.GroupVersionKind().Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
// fallback to loading resource from kubernetes if conversion fails
|
||||||
|
log.Warnf("Failed to convert resource: %v", err)
|
||||||
|
managedObj, err = c.kubectl.GetResource(config, targetObj.GroupVersionKind(), managedObj.GetName(), managedObj.GetNamespace())
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
managedObj = converted
|
||||||
}
|
}
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
managedObjs[key] = managedObj
|
managedObjs[key] = managedObj
|
||||||
@@ -407,10 +414,6 @@ func (c *clusterInfo) getManagedLiveObjs(a *appv1.Application, targetObjs []*uns
|
|||||||
return managedObjs, nil
|
return managedObjs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clusterInfo) delete(obj *unstructured.Unstructured) error {
|
|
||||||
return c.kubectl.DeleteResource(c.cluster.RESTConfig(), obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *clusterInfo) processEvent(event watch.EventType, un *unstructured.Unstructured) error {
|
func (c *clusterInfo) processEvent(event watch.EventType, un *unstructured.Unstructured) error {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
@@ -447,7 +450,7 @@ func (c *clusterInfo) onNodeUpdated(exists bool, existingNode *node, un *unstruc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for name, full := range toNotify {
|
for name, full := range toNotify {
|
||||||
c.onAppUpdated(name, full)
|
c.onAppUpdated(name, full, newObj.ref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,7 +462,7 @@ func (c *clusterInfo) onNodeRemoved(key kube.ResourceKey, n *node) {
|
|||||||
|
|
||||||
c.removeNode(key)
|
c.removeNode(key)
|
||||||
if appName != "" {
|
if appName != "" {
|
||||||
c.onAppUpdated(appName, n.isRootAppNode())
|
c.onAppUpdated(appName, n.isRootAppNode(), n.ref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
controller/cache/cluster_test.go
vendored
10
controller/cache/cluster_test.go
vendored
@@ -107,6 +107,10 @@ var (
|
|||||||
serviceName: helm-guestbook
|
serviceName: helm-guestbook
|
||||||
servicePort: 443
|
servicePort: 443
|
||||||
path: /
|
path: /
|
||||||
|
- backend:
|
||||||
|
serviceName: helm-guestbook
|
||||||
|
servicePort: https
|
||||||
|
path: /
|
||||||
status:
|
status:
|
||||||
loadBalancer:
|
loadBalancer:
|
||||||
ingress:
|
ingress:
|
||||||
@@ -142,7 +146,7 @@ func newClusterExt(kubectl kube.Kubectl) *clusterInfo {
|
|||||||
return &clusterInfo{
|
return &clusterInfo{
|
||||||
lock: &sync.Mutex{},
|
lock: &sync.Mutex{},
|
||||||
nodes: make(map[kube.ResourceKey]*node),
|
nodes: make(map[kube.ResourceKey]*node),
|
||||||
onAppUpdated: func(appName string, fullRefresh bool) {},
|
onAppUpdated: func(appName string, fullRefresh bool, reference corev1.ObjectReference) {},
|
||||||
kubectl: kubectl,
|
kubectl: kubectl,
|
||||||
nsIndex: make(map[string]map[kube.ResourceKey]*node),
|
nsIndex: make(map[string]map[kube.ResourceKey]*node),
|
||||||
cluster: &appv1.Cluster{},
|
cluster: &appv1.Cluster{},
|
||||||
@@ -225,7 +229,7 @@ metadata:
|
|||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, []*unstructured.Unstructured{targetDeploy})
|
}, []*unstructured.Unstructured{targetDeploy}, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, managedObjs, map[kube.ResourceKey]*unstructured.Unstructured{
|
assert.Equal(t, managedObjs, map[kube.ResourceKey]*unstructured.Unstructured{
|
||||||
kube.NewResourceKey("apps", "Deployment", "default", "helm-guestbook"): testDeploy,
|
kube.NewResourceKey("apps", "Deployment", "default", "helm-guestbook"): testDeploy,
|
||||||
@@ -351,7 +355,7 @@ func TestUpdateResourceTags(t *testing.T) {
|
|||||||
func TestUpdateAppResource(t *testing.T) {
|
func TestUpdateAppResource(t *testing.T) {
|
||||||
updatesReceived := make([]string, 0)
|
updatesReceived := make([]string, 0)
|
||||||
cluster := newCluster(testPod, testRS, testDeploy)
|
cluster := newCluster(testPod, testRS, testDeploy)
|
||||||
cluster.onAppUpdated = func(appName string, fullRefresh bool) {
|
cluster.onAppUpdated = func(appName string, fullRefresh bool, _ corev1.ObjectReference) {
|
||||||
updatesReceived = append(updatesReceived, fmt.Sprintf("%s: %v", appName, fullRefresh))
|
updatesReceived = append(updatesReceived, fmt.Sprintf("%s: %v", appName, fullRefresh))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
63
controller/cache/info.go
vendored
63
controller/cache/info.go
vendored
@@ -9,6 +9,7 @@ import (
|
|||||||
k8snode "k8s.io/kubernetes/pkg/util/node"
|
k8snode "k8s.io/kubernetes/pkg/util/node"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||||
|
"github.com/argoproj/argo-cd/util"
|
||||||
"github.com/argoproj/argo-cd/util/kube"
|
"github.com/argoproj/argo-cd/util/kube"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -63,14 +64,15 @@ func populateServiceInfo(un *unstructured.Unstructured, node *node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func populateIngressInfo(un *unstructured.Unstructured, node *node) {
|
func populateIngressInfo(un *unstructured.Unstructured, node *node) {
|
||||||
targets := make([]v1alpha1.ResourceRef, 0)
|
ingress := getIngress(un)
|
||||||
|
targetsMap := make(map[v1alpha1.ResourceRef]bool)
|
||||||
if backend, ok, err := unstructured.NestedMap(un.Object, "spec", "backend"); ok && err == nil {
|
if backend, ok, err := unstructured.NestedMap(un.Object, "spec", "backend"); ok && err == nil {
|
||||||
targets = append(targets, v1alpha1.ResourceRef{
|
targetsMap[v1alpha1.ResourceRef{
|
||||||
Group: "",
|
Group: "",
|
||||||
Kind: kube.ServiceKind,
|
Kind: kube.ServiceKind,
|
||||||
Namespace: un.GetNamespace(),
|
Namespace: un.GetNamespace(),
|
||||||
Name: fmt.Sprintf("%s", backend["serviceName"]),
|
Name: fmt.Sprintf("%s", backend["serviceName"]),
|
||||||
})
|
}] = true
|
||||||
}
|
}
|
||||||
urlsSet := make(map[string]bool)
|
urlsSet := make(map[string]bool)
|
||||||
if rules, ok, err := unstructured.NestedSlice(un.Object, "spec", "rules"); ok && err == nil {
|
if rules, ok, err := unstructured.NestedSlice(un.Object, "spec", "rules"); ok && err == nil {
|
||||||
@@ -80,6 +82,14 @@ func populateIngressInfo(un *unstructured.Unstructured, node *node) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
host := rule["host"]
|
host := rule["host"]
|
||||||
|
if host == nil || host == "" {
|
||||||
|
for i := range ingress {
|
||||||
|
host = util.FirstNonEmpty(ingress[i].Hostname, ingress[i].IP)
|
||||||
|
if host != "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
paths, ok, err := unstructured.NestedSlice(rule, "http", "paths")
|
paths, ok, err := unstructured.NestedSlice(rule, "http", "paths")
|
||||||
if !ok || err != nil {
|
if !ok || err != nil {
|
||||||
continue
|
continue
|
||||||
@@ -91,32 +101,48 @@ func populateIngressInfo(un *unstructured.Unstructured, node *node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if serviceName, ok, err := unstructured.NestedString(path, "backend", "serviceName"); ok && err == nil {
|
if serviceName, ok, err := unstructured.NestedString(path, "backend", "serviceName"); ok && err == nil {
|
||||||
targets = append(targets, v1alpha1.ResourceRef{
|
targetsMap[v1alpha1.ResourceRef{
|
||||||
Group: "",
|
Group: "",
|
||||||
Kind: kube.ServiceKind,
|
Kind: kube.ServiceKind,
|
||||||
Namespace: un.GetNamespace(),
|
Namespace: un.GetNamespace(),
|
||||||
Name: serviceName,
|
Name: serviceName,
|
||||||
})
|
}] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if port, ok, err := unstructured.NestedFieldNoCopy(path, "backend", "servicePort"); ok && err == nil && host != "" {
|
if port, ok, err := unstructured.NestedFieldNoCopy(path, "backend", "servicePort"); ok && err == nil && host != "" && host != nil {
|
||||||
switch fmt.Sprintf("%v", port) {
|
stringPort := ""
|
||||||
case "80":
|
switch typedPod := port.(type) {
|
||||||
|
case int64:
|
||||||
|
stringPort = fmt.Sprintf("%d", typedPod)
|
||||||
|
case float64:
|
||||||
|
stringPort = fmt.Sprintf("%d", int64(typedPod))
|
||||||
|
case string:
|
||||||
|
stringPort = typedPod
|
||||||
|
default:
|
||||||
|
stringPort = fmt.Sprintf("%v", port)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch stringPort {
|
||||||
|
case "80", "http":
|
||||||
urlsSet[fmt.Sprintf("http://%s", host)] = true
|
urlsSet[fmt.Sprintf("http://%s", host)] = true
|
||||||
case "443":
|
case "443", "https":
|
||||||
urlsSet[fmt.Sprintf("https://%s", host)] = true
|
urlsSet[fmt.Sprintf("https://%s", host)] = true
|
||||||
default:
|
default:
|
||||||
urlsSet[fmt.Sprintf("http://%s:%s", host, port)] = true
|
urlsSet[fmt.Sprintf("http://%s:%s", host, stringPort)] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
targets := make([]v1alpha1.ResourceRef, 0)
|
||||||
|
for target := range targetsMap {
|
||||||
|
targets = append(targets, target)
|
||||||
|
}
|
||||||
urls := make([]string, 0)
|
urls := make([]string, 0)
|
||||||
for url := range urlsSet {
|
for url := range urlsSet {
|
||||||
urls = append(urls, url)
|
urls = append(urls, url)
|
||||||
}
|
}
|
||||||
node.networkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetRefs: targets, Ingress: getIngress(un), ExternalURLs: urls}
|
node.networkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetRefs: targets, Ingress: ingress, ExternalURLs: urls}
|
||||||
}
|
}
|
||||||
|
|
||||||
func populatePodInfo(un *unstructured.Unstructured, node *node) {
|
func populatePodInfo(un *unstructured.Unstructured, node *node) {
|
||||||
@@ -135,13 +161,20 @@ func populatePodInfo(un *unstructured.Unstructured, node *node) {
|
|||||||
reason = pod.Status.Reason
|
reason = pod.Status.Reason
|
||||||
}
|
}
|
||||||
|
|
||||||
initializing := false
|
imagesSet := make(map[string]bool)
|
||||||
|
for _, container := range pod.Spec.InitContainers {
|
||||||
// note that I ignore initContainers
|
imagesSet[container.Image] = true
|
||||||
|
}
|
||||||
for _, container := range pod.Spec.Containers {
|
for _, container := range pod.Spec.Containers {
|
||||||
node.images = append(node.images, container.Image)
|
imagesSet[container.Image] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node.images = nil
|
||||||
|
for image := range imagesSet {
|
||||||
|
node.images = append(node.images, image)
|
||||||
|
}
|
||||||
|
|
||||||
|
initializing := false
|
||||||
for i := range pod.Status.InitContainerStatuses {
|
for i := range pod.Status.InitContainerStatuses {
|
||||||
container := pod.Status.InitContainerStatuses[i]
|
container := pod.Status.InitContainerStatuses[i]
|
||||||
restarts += int(container.RestartCount)
|
restarts += int(container.RestartCount)
|
||||||
|
|||||||
40
controller/cache/info_test.go
vendored
40
controller/cache/info_test.go
vendored
@@ -1,6 +1,8 @@
|
|||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
@@ -50,6 +52,9 @@ func TestGetIngressInfo(t *testing.T) {
|
|||||||
node := &node{}
|
node := &node{}
|
||||||
populateNodeInfo(testIngress, node)
|
populateNodeInfo(testIngress, node)
|
||||||
assert.Equal(t, 0, len(node.info))
|
assert.Equal(t, 0, len(node.info))
|
||||||
|
sort.Slice(node.networkingInfo.TargetRefs, func(i, j int) bool {
|
||||||
|
return strings.Compare(node.networkingInfo.TargetRefs[j].Name, node.networkingInfo.TargetRefs[i].Name) < 0
|
||||||
|
})
|
||||||
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
|
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
|
||||||
Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}},
|
Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}},
|
||||||
TargetRefs: []v1alpha1.ResourceRef{{
|
TargetRefs: []v1alpha1.ResourceRef{{
|
||||||
@@ -66,3 +71,38 @@ func TestGetIngressInfo(t *testing.T) {
|
|||||||
ExternalURLs: []string{"https://helm-guestbook.com"},
|
ExternalURLs: []string{"https://helm-guestbook.com"},
|
||||||
}, node.networkingInfo)
|
}, node.networkingInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetIngressInfoNoHost(t *testing.T) {
|
||||||
|
ingress := strToUnstructured(`
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: helm-guestbook
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
serviceName: helm-guestbook
|
||||||
|
servicePort: 443
|
||||||
|
path: /
|
||||||
|
status:
|
||||||
|
loadBalancer:
|
||||||
|
ingress:
|
||||||
|
- ip: 107.178.210.11`)
|
||||||
|
|
||||||
|
node := &node{}
|
||||||
|
populateNodeInfo(ingress, node)
|
||||||
|
|
||||||
|
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
|
||||||
|
Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}},
|
||||||
|
TargetRefs: []v1alpha1.ResourceRef{{
|
||||||
|
Namespace: "default",
|
||||||
|
Group: "",
|
||||||
|
Kind: kube.ServiceKind,
|
||||||
|
Name: "helm-guestbook",
|
||||||
|
}},
|
||||||
|
ExternalURLs: []string{"https://107.178.210.11"},
|
||||||
|
}, node.networkingInfo)
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package metrics
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
@@ -17,6 +18,7 @@ import (
|
|||||||
type MetricsServer struct {
|
type MetricsServer struct {
|
||||||
*http.Server
|
*http.Server
|
||||||
syncCounter *prometheus.CounterVec
|
syncCounter *prometheus.CounterVec
|
||||||
|
k8sRequestCounter *prometheus.CounterVec
|
||||||
reconcileHistogram *prometheus.HistogramVec
|
reconcileHistogram *prometheus.HistogramVec
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +74,14 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister) *Metri
|
|||||||
append(descAppDefaultLabels, "phase"),
|
append(descAppDefaultLabels, "phase"),
|
||||||
)
|
)
|
||||||
appRegistry.MustRegister(syncCounter)
|
appRegistry.MustRegister(syncCounter)
|
||||||
|
k8sRequestCounter := prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "argocd_app_k8s_request_total",
|
||||||
|
Help: "Number of kubernetes requests executed during application reconciliation.",
|
||||||
|
},
|
||||||
|
append(descAppDefaultLabels, "response_code"),
|
||||||
|
)
|
||||||
|
appRegistry.MustRegister(k8sRequestCounter)
|
||||||
|
|
||||||
reconcileHistogram := prometheus.NewHistogramVec(
|
reconcileHistogram := prometheus.NewHistogramVec(
|
||||||
prometheus.HistogramOpts{
|
prometheus.HistogramOpts{
|
||||||
@@ -91,6 +101,7 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister) *Metri
|
|||||||
Handler: mux,
|
Handler: mux,
|
||||||
},
|
},
|
||||||
syncCounter: syncCounter,
|
syncCounter: syncCounter,
|
||||||
|
k8sRequestCounter: k8sRequestCounter,
|
||||||
reconcileHistogram: reconcileHistogram,
|
reconcileHistogram: reconcileHistogram,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,6 +114,11 @@ func (m *MetricsServer) IncSync(app *argoappv1.Application, state *argoappv1.Ope
|
|||||||
m.syncCounter.WithLabelValues(app.Namespace, app.Name, app.Spec.GetProject(), string(state.Phase)).Inc()
|
m.syncCounter.WithLabelValues(app.Namespace, app.Name, app.Spec.GetProject(), string(state.Phase)).Inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IncKubernetesRequest increments the kubernetes requests counter for an application
|
||||||
|
func (m *MetricsServer) IncKubernetesRequest(app *argoappv1.Application, statusCode int) {
|
||||||
|
m.k8sRequestCounter.WithLabelValues(app.Namespace, app.Name, app.Spec.GetProject(), strconv.Itoa(statusCode)).Inc()
|
||||||
|
}
|
||||||
|
|
||||||
// IncReconcile increments the reconcile counter for an application
|
// IncReconcile increments the reconcile counter for an application
|
||||||
func (m *MetricsServer) IncReconcile(app *argoappv1.Application, duration time.Duration) {
|
func (m *MetricsServer) IncReconcile(app *argoappv1.Application, duration time.Duration) {
|
||||||
m.reconcileHistogram.WithLabelValues(app.Namespace, app.Name, app.Spec.GetProject()).Observe(duration.Seconds())
|
m.reconcileHistogram.WithLabelValues(app.Namespace, app.Name, app.Spec.GetProject()).Observe(duration.Seconds())
|
||||||
|
|||||||
37
controller/metrics/transportwrapper.go
Normal file
37
controller/metrics/transportwrapper.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metricsRoundTripper struct {
|
||||||
|
roundTripper http.RoundTripper
|
||||||
|
app *v1alpha1.Application
|
||||||
|
metricsServer *MetricsServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mrt *metricsRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||||
|
resp, err := mrt.roundTripper.RoundTrip(r)
|
||||||
|
statusCode := 0
|
||||||
|
if resp != nil {
|
||||||
|
statusCode = resp.StatusCode
|
||||||
|
}
|
||||||
|
mrt.metricsServer.IncKubernetesRequest(mrt.app, statusCode)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMetricsTransportWrapper adds a transport wrapper which increments 'argocd_app_k8s_request_total' counter on each kubernetes request
|
||||||
|
func AddMetricsTransportWrapper(server *MetricsServer, app *v1alpha1.Application, config *rest.Config) *rest.Config {
|
||||||
|
wrap := config.WrapTransport
|
||||||
|
config.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
|
||||||
|
if wrap != nil {
|
||||||
|
rt = wrap(rt)
|
||||||
|
}
|
||||||
|
return &metricsRoundTripper{roundTripper: rt, metricsServer: server, app: app}
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/argoproj/argo-cd/common"
|
"github.com/argoproj/argo-cd/common"
|
||||||
statecache "github.com/argoproj/argo-cd/controller/cache"
|
statecache "github.com/argoproj/argo-cd/controller/cache"
|
||||||
|
"github.com/argoproj/argo-cd/controller/metrics"
|
||||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||||
@@ -73,6 +74,7 @@ type comparisonResult struct {
|
|||||||
|
|
||||||
// appStateManager allows to compare applications to git
|
// appStateManager allows to compare applications to git
|
||||||
type appStateManager struct {
|
type appStateManager struct {
|
||||||
|
metricsServer *metrics.MetricsServer
|
||||||
db db.ArgoDB
|
db db.ArgoDB
|
||||||
settings *settings.ArgoCDSettings
|
settings *settings.ArgoCDSettings
|
||||||
appclientset appclientset.Interface
|
appclientset appclientset.Interface
|
||||||
@@ -88,7 +90,10 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
repo := m.getRepo(source.RepoURL)
|
repo, err := m.db.GetRepository(context.Background(), source.RepoURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
conn, repoClient, err := m.repoClientset.NewRepoServerClient()
|
conn, repoClient, err := m.repoClientset.NewRepoServerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
@@ -311,7 +316,10 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st
|
|||||||
syncStatus.Revision = manifestInfo.Revision
|
syncStatus.Revision = manifestInfo.Revision
|
||||||
}
|
}
|
||||||
|
|
||||||
healthStatus, err := health.SetApplicationHealth(resourceSummaries, GetLiveObjs(managedResources), m.settings.ResourceOverrides)
|
healthStatus, err := health.SetApplicationHealth(resourceSummaries, GetLiveObjs(managedResources), m.settings.ResourceOverrides, func(obj *unstructured.Unstructured) bool {
|
||||||
|
return !isSelfReferencedApp(app, kubeutil.GetObjectRef(obj))
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conditions = append(conditions, appv1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error()})
|
conditions = append(conditions, appv1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error()})
|
||||||
}
|
}
|
||||||
@@ -332,15 +340,6 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st
|
|||||||
return &compRes, nil
|
return &compRes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *appStateManager) getRepo(repoURL string) *v1alpha1.Repository {
|
|
||||||
repo, err := m.db.GetRepository(context.Background(), repoURL)
|
|
||||||
if err != nil {
|
|
||||||
// If we couldn't retrieve from the repo service, assume public repositories
|
|
||||||
repo = &v1alpha1.Repository{Repo: repoURL}
|
|
||||||
}
|
|
||||||
return repo
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource) error {
|
func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource) error {
|
||||||
var nextID int64
|
var nextID int64
|
||||||
if len(app.Status.History) > 0 {
|
if len(app.Status.History) > 0 {
|
||||||
@@ -379,6 +378,7 @@ func NewAppStateManager(
|
|||||||
settings *settings.ArgoCDSettings,
|
settings *settings.ArgoCDSettings,
|
||||||
liveStateCache statecache.LiveStateCache,
|
liveStateCache statecache.LiveStateCache,
|
||||||
projInformer cache.SharedIndexInformer,
|
projInformer cache.SharedIndexInformer,
|
||||||
|
metricsServer *metrics.MetricsServer,
|
||||||
) AppStateManager {
|
) AppStateManager {
|
||||||
return &appStateManager{
|
return &appStateManager{
|
||||||
liveStateCache: liveStateCache,
|
liveStateCache: liveStateCache,
|
||||||
@@ -389,5 +389,6 @@ func NewAppStateManager(
|
|||||||
namespace: namespace,
|
namespace: namespace,
|
||||||
settings: settings,
|
settings: settings,
|
||||||
projInformer: projInformer,
|
projInformer: projInformer,
|
||||||
|
metricsServer: metricsServer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
v1 "k8s.io/api/apps/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@@ -178,3 +181,83 @@ func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) {
|
|||||||
})
|
})
|
||||||
assert.Equal(t, 2, len(compRes.resources))
|
assert.Equal(t, 2, len(compRes.resources))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var defaultProj = argoappv1.AppProject{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "default",
|
||||||
|
Namespace: test.FakeArgoCDNamespace,
|
||||||
|
},
|
||||||
|
Spec: argoappv1.AppProjectSpec{
|
||||||
|
SourceRepos: []string{"*"},
|
||||||
|
Destinations: []argoappv1.ApplicationDestination{
|
||||||
|
{
|
||||||
|
Server: "*",
|
||||||
|
Namespace: "*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetHealth(t *testing.T) {
|
||||||
|
app := newFakeApp()
|
||||||
|
deployment := kube.MustToUnstructured(&v1.Deployment{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: "apps/v1beta1",
|
||||||
|
Kind: "Deployment",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "demo",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
ctrl := newFakeController(&fakeData{
|
||||||
|
apps: []runtime.Object{app, &defaultProj},
|
||||||
|
manifestResponse: &repository.ManifestResponse{
|
||||||
|
Manifests: []string{},
|
||||||
|
Namespace: test.FakeDestNamespace,
|
||||||
|
Server: test.FakeClusterURL,
|
||||||
|
Revision: "abc123",
|
||||||
|
},
|
||||||
|
managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
|
||||||
|
kube.GetResourceKey(deployment): deployment,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
compRes, err := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, compRes.healthStatus.Status, argoappv1.HealthStatusHealthy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetHealthSelfReferencedApp(t *testing.T) {
|
||||||
|
app := newFakeApp()
|
||||||
|
unstructuredApp := kube.MustToUnstructured(app)
|
||||||
|
deployment := kube.MustToUnstructured(&v1.Deployment{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: "apps/v1beta1",
|
||||||
|
Kind: "Deployment",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "demo",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
ctrl := newFakeController(&fakeData{
|
||||||
|
apps: []runtime.Object{app, &defaultProj},
|
||||||
|
manifestResponse: &repository.ManifestResponse{
|
||||||
|
Manifests: []string{},
|
||||||
|
Namespace: test.FakeDestNamespace,
|
||||||
|
Server: test.FakeClusterURL,
|
||||||
|
Revision: "abc123",
|
||||||
|
},
|
||||||
|
managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
|
||||||
|
kube.GetResourceKey(deployment): deployment,
|
||||||
|
kube.GetResourceKey(unstructuredApp): unstructuredApp,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
compRes, err := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, compRes.healthStatus.Status, argoappv1.HealthStatusHealthy)
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/controller/metrics"
|
||||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||||
"github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
|
"github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
|
||||||
"github.com/argoproj/argo-cd/util/argo"
|
"github.com/argoproj/argo-cd/util/argo"
|
||||||
@@ -116,7 +117,7 @@ func (m *appStateManager) SyncAppState(app *appv1.Application, state *appv1.Oper
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
restConfig := clst.RESTConfig()
|
restConfig := metrics.AddMetricsTransportWrapper(m.metricsServer, app, clst.RESTConfig())
|
||||||
dynamicIf, err := dynamic.NewForConfig(restConfig)
|
dynamicIf, err := dynamic.NewForConfig(restConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.Phase = appv1.OperationError
|
state.Phase = appv1.OperationError
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ data:
|
|||||||
issuer: https://dev-123456.oktapreview.com
|
issuer: https://dev-123456.oktapreview.com
|
||||||
clientID: aaaabbbbccccddddeee
|
clientID: aaaabbbbccccddddeee
|
||||||
clientSecret: $oidc.okta.clientSecret
|
clientSecret: $oidc.okta.clientSecret
|
||||||
|
# Optional set of OIDC scopes to request. If omitted, defaults to: ["openid", "profile", "email", "groups"]
|
||||||
|
requestedScopes: ["openid", "profile", "email"]
|
||||||
|
|
||||||
# Git repositories configure Argo CD with (optional).
|
# Git repositories configure Argo CD with (optional).
|
||||||
# This list is updated when configuring/removing repos from the UI/CLI
|
# This list is updated when configuring/removing repos from the UI/CLI
|
||||||
|
|||||||
@@ -19,3 +19,8 @@ data:
|
|||||||
# authorizing API requests (optional). If omitted or empty, users may be still be able to login,
|
# authorizing API requests (optional). If omitted or empty, users may be still be able to login,
|
||||||
# but will see no apps, projects, etc...
|
# but will see no apps, projects, etc...
|
||||||
policy.default: role:readonly
|
policy.default: role:readonly
|
||||||
|
|
||||||
|
# scopes controls which OIDC scopes to examine during rbac enforcement (in addition to `sub` scope).
|
||||||
|
# If omitted, defaults to: `[groups]`. The scope value can be a string, or a list of strings.
|
||||||
|
scopes: [cognito:groups, email]
|
||||||
|
|
||||||
|
|||||||
@@ -150,6 +150,88 @@ data:
|
|||||||
key: sshPrivateKey
|
key: sshPrivateKey
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
The Kubernetes documentation has [instructions for creating a secret containing a private key](https://kubernetes.io/docs/concepts/configuration/secret/#use-case-pod-with-ssh-keys).
|
||||||
|
|
||||||
|
|
||||||
|
### Repository Credentials (v1.1+)
|
||||||
|
|
||||||
|
If you want to use the same credentials for multiple repositories, you can use `repository.credentials`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: argocd-cm
|
||||||
|
data:
|
||||||
|
repositories: |
|
||||||
|
- url: https://github.com/argoproj/private-repo
|
||||||
|
- url: https://github.com/argoproj/other-private-repo
|
||||||
|
repository.credentials: |
|
||||||
|
- url: https://github.com/argoproj
|
||||||
|
passwordSecret:
|
||||||
|
name: my-secret
|
||||||
|
key: password
|
||||||
|
usernameSecret:
|
||||||
|
name: my-secret
|
||||||
|
key: username
|
||||||
|
```
|
||||||
|
|
||||||
|
Argo CD will only use the credentials if you omit `usernameSecret`, `passwordSecret`, and `sshPrivateKeySecret` fields (`insecureIgnoreHostKey` is ignored).
|
||||||
|
|
||||||
|
A credential may be match if it's URL is the prefix of the repository's URL. The means that credentials may match, e.g in the above example both [https://github.com/argoproj](https://github.com/argoproj) and [https://github.com](https://github.com) would match. Argo CD selects the first one that matches.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
Order your credentials with the most specific at the top and the least specific at the bottom.
|
||||||
|
|
||||||
|
A complete example.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: argocd-cm
|
||||||
|
data:
|
||||||
|
repositories: |
|
||||||
|
# this has it's own credentials
|
||||||
|
- url: https://github.com/argoproj/private-repo
|
||||||
|
passwordSecret:
|
||||||
|
name: private-repo-secret
|
||||||
|
key: password
|
||||||
|
usernameSecret:
|
||||||
|
name: private-repo-secret
|
||||||
|
key: username
|
||||||
|
sshPrivateKeySecret:
|
||||||
|
name: private-repo-secret
|
||||||
|
key: sshPrivateKey
|
||||||
|
- url: https://github.com/argoproj/other-private-repo
|
||||||
|
- url: https://github.com/otherproj/another-private-repo
|
||||||
|
repository.credentials: |
|
||||||
|
# this will be used for the second repo
|
||||||
|
- url: https://github.com/argoproj
|
||||||
|
passwordSecret:
|
||||||
|
name: other-private-repo-secret
|
||||||
|
key: password
|
||||||
|
usernameSecret:
|
||||||
|
name: other-private-repo-secret
|
||||||
|
key: username
|
||||||
|
sshPrivateKeySecret:
|
||||||
|
name: other-private-repo-secret
|
||||||
|
key: sshPrivateKey
|
||||||
|
# this will be used for the third repo
|
||||||
|
- url: https://github.com
|
||||||
|
passwordSecret:
|
||||||
|
name: another-private-repo-secret
|
||||||
|
key: password
|
||||||
|
usernameSecret:
|
||||||
|
name: another-private-repo-secret
|
||||||
|
key: username
|
||||||
|
sshPrivateKeySecret:
|
||||||
|
name: another-private-repo-secret
|
||||||
|
key: sshPrivateKey
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Clusters
|
## Clusters
|
||||||
|
|
||||||
Cluster credentials are stored in secrets same as repository credentials but does not require entry in `argocd-cm` config map. Each secret must have label
|
Cluster credentials are stored in secrets same as repository credentials but does not require entry in `argocd-cm` config map. Each secret must have label
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ bases:
|
|||||||
images:
|
images:
|
||||||
- name: argoproj/argocd
|
- name: argoproj/argocd
|
||||||
newName: argoproj/argocd
|
newName: argoproj/argocd
|
||||||
newTag: latest
|
newTag: v1.0.1
|
||||||
- name: argoproj/argocd-ui
|
- name: argoproj/argocd-ui
|
||||||
newName: argoproj/argocd-ui
|
newName: argoproj/argocd-ui
|
||||||
newTag: latest
|
newTag: v1.0.1
|
||||||
|
|||||||
@@ -32,3 +32,8 @@ spec:
|
|||||||
port: 8081
|
port: 8081
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
|
livenessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: 8081
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ spec:
|
|||||||
port: 8080
|
port: 8080
|
||||||
initialDelaySeconds: 3
|
initialDelaySeconds: 3
|
||||||
periodSeconds: 30
|
periodSeconds: 30
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 3
|
||||||
|
periodSeconds: 30
|
||||||
volumes:
|
volumes:
|
||||||
- emptyDir: {}
|
- emptyDir: {}
|
||||||
name: static-files
|
name: static-files
|
||||||
|
|||||||
@@ -479,12 +479,6 @@ spec:
|
|||||||
- message
|
- message
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
externalURLs:
|
|
||||||
description: ExternalURLs holds all external URLs of application child
|
|
||||||
resources.
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
health:
|
health:
|
||||||
properties:
|
properties:
|
||||||
message:
|
message:
|
||||||
@@ -1144,6 +1138,20 @@ spec:
|
|||||||
type: array
|
type: array
|
||||||
sourceType:
|
sourceType:
|
||||||
type: string
|
type: string
|
||||||
|
summary:
|
||||||
|
properties:
|
||||||
|
externalURLs:
|
||||||
|
description: ExternalURLs holds all external URLs of application
|
||||||
|
child resources.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
images:
|
||||||
|
description: Images holds all images of application child resources.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
sync:
|
sync:
|
||||||
description: SyncStatus is a comparison result of application spec and
|
description: SyncStatus is a comparison result of application spec and
|
||||||
deployed application.
|
deployed application.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ patchesStrategicMerge:
|
|||||||
images:
|
images:
|
||||||
- name: argoproj/argocd
|
- name: argoproj/argocd
|
||||||
newName: argoproj/argocd
|
newName: argoproj/argocd
|
||||||
newTag: latest
|
newTag: v1.0.1
|
||||||
- name: argoproj/argocd-ui
|
- name: argoproj/argocd-ui
|
||||||
newName: argoproj/argocd-ui
|
newName: argoproj/argocd-ui
|
||||||
newTag: latest
|
newTag: v1.0.1
|
||||||
|
|||||||
@@ -185,10 +185,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -419,10 +415,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources for
|
description: NamePrefix is a prefix appended to resources for
|
||||||
kustomize apps
|
kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository containing
|
description: Path is a directory path within the repository containing
|
||||||
@@ -633,10 +625,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -670,9 +658,6 @@ spec:
|
|||||||
- source
|
- source
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
ingress:
|
|
||||||
items: {}
|
|
||||||
type: array
|
|
||||||
observedAt: {}
|
observedAt: {}
|
||||||
operationState:
|
operationState:
|
||||||
description: OperationState contains information about state of currently
|
description: OperationState contains information about state of currently
|
||||||
@@ -853,10 +838,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to
|
description: NamePrefix is a prefix appended to
|
||||||
resources for kustomize apps
|
resources for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -1092,10 +1073,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -1162,6 +1139,20 @@ spec:
|
|||||||
type: array
|
type: array
|
||||||
sourceType:
|
sourceType:
|
||||||
type: string
|
type: string
|
||||||
|
summary:
|
||||||
|
properties:
|
||||||
|
externalURLs:
|
||||||
|
description: ExternalURLs holds all external URLs of application
|
||||||
|
child resources.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
images:
|
||||||
|
description: Images holds all images of application child resources.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
sync:
|
sync:
|
||||||
description: SyncStatus is a comparison result of application spec and
|
description: SyncStatus is a comparison result of application spec and
|
||||||
deployed application.
|
deployed application.
|
||||||
@@ -1312,10 +1303,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -2138,7 +2125,7 @@ spec:
|
|||||||
- argocd-redis-ha-announce-2:26379
|
- argocd-redis-ha-announce-2:26379
|
||||||
- --sentinelmaster
|
- --sentinelmaster
|
||||||
- argocd
|
- argocd
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: argocd-application-controller
|
name: argocd-application-controller
|
||||||
ports:
|
ports:
|
||||||
@@ -2185,7 +2172,7 @@ spec:
|
|||||||
- cp
|
- cp
|
||||||
- /usr/local/bin/argocd-util
|
- /usr/local/bin/argocd-util
|
||||||
- /shared
|
- /shared
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: copyutil
|
name: copyutil
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
@@ -2240,8 +2227,13 @@ spec:
|
|||||||
- argocd-redis-ha-announce-2:26379
|
- argocd-redis-ha-announce-2:26379
|
||||||
- --sentinelmaster
|
- --sentinelmaster
|
||||||
- argocd
|
- argocd
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
livenessProbe:
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
tcpSocket:
|
||||||
|
port: 8081
|
||||||
name: argocd-repo-server
|
name: argocd-repo-server
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8081
|
- containerPort: 8081
|
||||||
@@ -2297,8 +2289,14 @@ spec:
|
|||||||
- argocd-redis-ha-announce-2:26379
|
- argocd-redis-ha-announce-2:26379
|
||||||
- --sentinelmaster
|
- --sentinelmaster
|
||||||
- argocd
|
- argocd
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 3
|
||||||
|
periodSeconds: 30
|
||||||
name: argocd-server
|
name: argocd-server
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
@@ -2318,7 +2316,7 @@ spec:
|
|||||||
- -r
|
- -r
|
||||||
- /app
|
- /app
|
||||||
- /shared
|
- /shared
|
||||||
image: argoproj/argocd-ui:latest
|
image: argoproj/argocd-ui:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: ui
|
name: ui
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
|||||||
@@ -185,10 +185,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -419,10 +415,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources for
|
description: NamePrefix is a prefix appended to resources for
|
||||||
kustomize apps
|
kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository containing
|
description: Path is a directory path within the repository containing
|
||||||
@@ -633,10 +625,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -670,9 +658,6 @@ spec:
|
|||||||
- source
|
- source
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
ingress:
|
|
||||||
items: {}
|
|
||||||
type: array
|
|
||||||
observedAt: {}
|
observedAt: {}
|
||||||
operationState:
|
operationState:
|
||||||
description: OperationState contains information about state of currently
|
description: OperationState contains information about state of currently
|
||||||
@@ -853,10 +838,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to
|
description: NamePrefix is a prefix appended to
|
||||||
resources for kustomize apps
|
resources for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -1092,10 +1073,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -1162,6 +1139,20 @@ spec:
|
|||||||
type: array
|
type: array
|
||||||
sourceType:
|
sourceType:
|
||||||
type: string
|
type: string
|
||||||
|
summary:
|
||||||
|
properties:
|
||||||
|
externalURLs:
|
||||||
|
description: ExternalURLs holds all external URLs of application
|
||||||
|
child resources.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
images:
|
||||||
|
description: Images holds all images of application child resources.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
sync:
|
sync:
|
||||||
description: SyncStatus is a comparison result of application spec and
|
description: SyncStatus is a comparison result of application spec and
|
||||||
deployed application.
|
deployed application.
|
||||||
@@ -1312,10 +1303,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -2053,7 +2040,7 @@ spec:
|
|||||||
- argocd-redis-ha-announce-2:26379
|
- argocd-redis-ha-announce-2:26379
|
||||||
- --sentinelmaster
|
- --sentinelmaster
|
||||||
- argocd
|
- argocd
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: argocd-application-controller
|
name: argocd-application-controller
|
||||||
ports:
|
ports:
|
||||||
@@ -2100,7 +2087,7 @@ spec:
|
|||||||
- cp
|
- cp
|
||||||
- /usr/local/bin/argocd-util
|
- /usr/local/bin/argocd-util
|
||||||
- /shared
|
- /shared
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: copyutil
|
name: copyutil
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
@@ -2155,8 +2142,13 @@ spec:
|
|||||||
- argocd-redis-ha-announce-2:26379
|
- argocd-redis-ha-announce-2:26379
|
||||||
- --sentinelmaster
|
- --sentinelmaster
|
||||||
- argocd
|
- argocd
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
livenessProbe:
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
tcpSocket:
|
||||||
|
port: 8081
|
||||||
name: argocd-repo-server
|
name: argocd-repo-server
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8081
|
- containerPort: 8081
|
||||||
@@ -2212,8 +2204,14 @@ spec:
|
|||||||
- argocd-redis-ha-announce-2:26379
|
- argocd-redis-ha-announce-2:26379
|
||||||
- --sentinelmaster
|
- --sentinelmaster
|
||||||
- argocd
|
- argocd
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 3
|
||||||
|
periodSeconds: 30
|
||||||
name: argocd-server
|
name: argocd-server
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
@@ -2233,7 +2231,7 @@ spec:
|
|||||||
- -r
|
- -r
|
||||||
- /app
|
- /app
|
||||||
- /shared
|
- /shared
|
||||||
image: argoproj/argocd-ui:latest
|
image: argoproj/argocd-ui:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: ui
|
name: ui
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
|||||||
@@ -185,10 +185,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -419,10 +415,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources for
|
description: NamePrefix is a prefix appended to resources for
|
||||||
kustomize apps
|
kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository containing
|
description: Path is a directory path within the repository containing
|
||||||
@@ -633,10 +625,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -670,9 +658,6 @@ spec:
|
|||||||
- source
|
- source
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
ingress:
|
|
||||||
items: {}
|
|
||||||
type: array
|
|
||||||
observedAt: {}
|
observedAt: {}
|
||||||
operationState:
|
operationState:
|
||||||
description: OperationState contains information about state of currently
|
description: OperationState contains information about state of currently
|
||||||
@@ -853,10 +838,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to
|
description: NamePrefix is a prefix appended to
|
||||||
resources for kustomize apps
|
resources for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -1092,10 +1073,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -1162,6 +1139,20 @@ spec:
|
|||||||
type: array
|
type: array
|
||||||
sourceType:
|
sourceType:
|
||||||
type: string
|
type: string
|
||||||
|
summary:
|
||||||
|
properties:
|
||||||
|
externalURLs:
|
||||||
|
description: ExternalURLs holds all external URLs of application
|
||||||
|
child resources.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
images:
|
||||||
|
description: Images holds all images of application child resources.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
sync:
|
sync:
|
||||||
description: SyncStatus is a comparison result of application spec and
|
description: SyncStatus is a comparison result of application spec and
|
||||||
deployed application.
|
deployed application.
|
||||||
@@ -1312,10 +1303,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -1902,7 +1889,7 @@ spec:
|
|||||||
- "20"
|
- "20"
|
||||||
- --operation-processors
|
- --operation-processors
|
||||||
- "10"
|
- "10"
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: argocd-application-controller
|
name: argocd-application-controller
|
||||||
ports:
|
ports:
|
||||||
@@ -1949,7 +1936,7 @@ spec:
|
|||||||
- cp
|
- cp
|
||||||
- /usr/local/bin/argocd-util
|
- /usr/local/bin/argocd-util
|
||||||
- /shared
|
- /shared
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: copyutil
|
name: copyutil
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
@@ -2012,8 +1999,13 @@ spec:
|
|||||||
- argocd-repo-server
|
- argocd-repo-server
|
||||||
- --redis
|
- --redis
|
||||||
- argocd-redis:6379
|
- argocd-redis:6379
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
livenessProbe:
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
tcpSocket:
|
||||||
|
port: 8081
|
||||||
name: argocd-repo-server
|
name: argocd-repo-server
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8081
|
- containerPort: 8081
|
||||||
@@ -2046,8 +2038,14 @@ spec:
|
|||||||
- argocd-server
|
- argocd-server
|
||||||
- --staticassets
|
- --staticassets
|
||||||
- /shared/app
|
- /shared/app
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 3
|
||||||
|
periodSeconds: 30
|
||||||
name: argocd-server
|
name: argocd-server
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
@@ -2067,7 +2065,7 @@ spec:
|
|||||||
- -r
|
- -r
|
||||||
- /app
|
- /app
|
||||||
- /shared
|
- /shared
|
||||||
image: argoproj/argocd-ui:latest
|
image: argoproj/argocd-ui:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: ui
|
name: ui
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
|||||||
@@ -185,10 +185,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -419,10 +415,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources for
|
description: NamePrefix is a prefix appended to resources for
|
||||||
kustomize apps
|
kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository containing
|
description: Path is a directory path within the repository containing
|
||||||
@@ -633,10 +625,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -670,9 +658,6 @@ spec:
|
|||||||
- source
|
- source
|
||||||
type: object
|
type: object
|
||||||
type: array
|
type: array
|
||||||
ingress:
|
|
||||||
items: {}
|
|
||||||
type: array
|
|
||||||
observedAt: {}
|
observedAt: {}
|
||||||
operationState:
|
operationState:
|
||||||
description: OperationState contains information about state of currently
|
description: OperationState contains information about state of currently
|
||||||
@@ -853,10 +838,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to
|
description: NamePrefix is a prefix appended to
|
||||||
resources for kustomize apps
|
resources for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -1092,10 +1073,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -1162,6 +1139,20 @@ spec:
|
|||||||
type: array
|
type: array
|
||||||
sourceType:
|
sourceType:
|
||||||
type: string
|
type: string
|
||||||
|
summary:
|
||||||
|
properties:
|
||||||
|
externalURLs:
|
||||||
|
description: ExternalURLs holds all external URLs of application
|
||||||
|
child resources.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
images:
|
||||||
|
description: Images holds all images of application child resources.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
sync:
|
sync:
|
||||||
description: SyncStatus is a comparison result of application spec and
|
description: SyncStatus is a comparison result of application spec and
|
||||||
deployed application.
|
deployed application.
|
||||||
@@ -1312,10 +1303,6 @@ spec:
|
|||||||
description: NamePrefix is a prefix appended to resources
|
description: NamePrefix is a prefix appended to resources
|
||||||
for kustomize apps
|
for kustomize apps
|
||||||
type: string
|
type: string
|
||||||
required:
|
|
||||||
- namePrefix
|
|
||||||
- imageTags
|
|
||||||
- images
|
|
||||||
type: object
|
type: object
|
||||||
path:
|
path:
|
||||||
description: Path is a directory path within the repository
|
description: Path is a directory path within the repository
|
||||||
@@ -1817,7 +1804,7 @@ spec:
|
|||||||
- "20"
|
- "20"
|
||||||
- --operation-processors
|
- --operation-processors
|
||||||
- "10"
|
- "10"
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: argocd-application-controller
|
name: argocd-application-controller
|
||||||
ports:
|
ports:
|
||||||
@@ -1864,7 +1851,7 @@ spec:
|
|||||||
- cp
|
- cp
|
||||||
- /usr/local/bin/argocd-util
|
- /usr/local/bin/argocd-util
|
||||||
- /shared
|
- /shared
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: copyutil
|
name: copyutil
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
@@ -1927,8 +1914,13 @@ spec:
|
|||||||
- argocd-repo-server
|
- argocd-repo-server
|
||||||
- --redis
|
- --redis
|
||||||
- argocd-redis:6379
|
- argocd-redis:6379
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
livenessProbe:
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
tcpSocket:
|
||||||
|
port: 8081
|
||||||
name: argocd-repo-server
|
name: argocd-repo-server
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8081
|
- containerPort: 8081
|
||||||
@@ -1961,8 +1953,14 @@ spec:
|
|||||||
- argocd-server
|
- argocd-server
|
||||||
- --staticassets
|
- --staticassets
|
||||||
- /shared/app
|
- /shared/app
|
||||||
image: argoproj/argocd:latest
|
image: argoproj/argocd:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
initialDelaySeconds: 3
|
||||||
|
periodSeconds: 30
|
||||||
name: argocd-server
|
name: argocd-server
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
@@ -1982,7 +1980,7 @@ spec:
|
|||||||
- -r
|
- -r
|
||||||
- /app
|
- /app
|
||||||
- /shared
|
- /shared
|
||||||
image: argoproj/argocd-ui:latest
|
image: argoproj/argocd-ui:v1.0.1
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: ui
|
name: ui
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
oidc "github.com/coreos/go-oidc"
|
"github.com/coreos/go-oidc"
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
@@ -38,6 +38,7 @@ import (
|
|||||||
"github.com/argoproj/argo-cd/server/version"
|
"github.com/argoproj/argo-cd/server/version"
|
||||||
grpc_util "github.com/argoproj/argo-cd/util/grpc"
|
grpc_util "github.com/argoproj/argo-cd/util/grpc"
|
||||||
"github.com/argoproj/argo-cd/util/localconfig"
|
"github.com/argoproj/argo-cd/util/localconfig"
|
||||||
|
oidcutil "github.com/argoproj/argo-cd/util/oidc"
|
||||||
tls_util "github.com/argoproj/argo-cd/util/tls"
|
tls_util "github.com/argoproj/argo-cd/util/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -51,10 +52,6 @@ const (
|
|||||||
MaxGRPCMessageSize = 100 * 1024 * 1024
|
MaxGRPCMessageSize = 100 * 1024 * 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
clientScopes = []string{"openid", "profile", "email", "groups", "offline_access"}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Client defines an interface for interaction with an Argo CD server.
|
// Client defines an interface for interaction with an Argo CD server.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
ClientOptions() ClientOptions
|
ClientOptions() ClientOptions
|
||||||
@@ -197,16 +194,18 @@ func NewClient(opts *ClientOptions) (Client, error) {
|
|||||||
func (c *client) OIDCConfig(ctx context.Context, set *settings.Settings) (*oauth2.Config, *oidc.Provider, error) {
|
func (c *client) OIDCConfig(ctx context.Context, set *settings.Settings) (*oauth2.Config, *oidc.Provider, error) {
|
||||||
var clientID string
|
var clientID string
|
||||||
var issuerURL string
|
var issuerURL string
|
||||||
if set.DexConfig != nil && len(set.DexConfig.Connectors) > 0 {
|
var scopes []string
|
||||||
clientID = common.ArgoCDCLIClientAppID
|
if set.OIDCConfig != nil && set.OIDCConfig.Issuer != "" {
|
||||||
issuerURL = fmt.Sprintf("%s%s", set.URL, common.DexAPIEndpoint)
|
|
||||||
} else if set.OIDCConfig != nil && set.OIDCConfig.Issuer != "" {
|
|
||||||
if set.OIDCConfig.CLIClientID != "" {
|
if set.OIDCConfig.CLIClientID != "" {
|
||||||
clientID = set.OIDCConfig.CLIClientID
|
clientID = set.OIDCConfig.CLIClientID
|
||||||
} else {
|
} else {
|
||||||
clientID = set.OIDCConfig.ClientID
|
clientID = set.OIDCConfig.ClientID
|
||||||
}
|
}
|
||||||
issuerURL = set.OIDCConfig.Issuer
|
issuerURL = set.OIDCConfig.Issuer
|
||||||
|
scopes = set.OIDCConfig.Scopes
|
||||||
|
} else if set.DexConfig != nil && len(set.DexConfig.Connectors) > 0 {
|
||||||
|
clientID = common.ArgoCDCLIClientAppID
|
||||||
|
issuerURL = fmt.Sprintf("%s%s", set.URL, common.DexAPIEndpoint)
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, fmt.Errorf("%s is not configured with SSO", c.ServerAddr)
|
return nil, nil, fmt.Errorf("%s is not configured with SSO", c.ServerAddr)
|
||||||
}
|
}
|
||||||
@@ -214,9 +213,17 @@ func (c *client) OIDCConfig(ctx context.Context, set *settings.Settings) (*oauth
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("Failed to query provider %q: %v", issuerURL, err)
|
return nil, nil, fmt.Errorf("Failed to query provider %q: %v", issuerURL, err)
|
||||||
}
|
}
|
||||||
|
oidcConf, err := oidcutil.ParseConfig(provider)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("Failed to parse provider config: %v", err)
|
||||||
|
}
|
||||||
|
scopes = oidcutil.GetScopesOrDefault(scopes)
|
||||||
|
if oidcutil.OfflineAccess(oidcConf.ScopesSupported) {
|
||||||
|
scopes = append(scopes, oidc.ScopeOfflineAccess)
|
||||||
|
}
|
||||||
oauth2conf := oauth2.Config{
|
oauth2conf := oauth2.Config{
|
||||||
ClientID: clientID,
|
ClientID: clientID,
|
||||||
Scopes: clientScopes,
|
Scopes: scopes,
|
||||||
Endpoint: provider.Endpoint(),
|
Endpoint: provider.Endpoint(),
|
||||||
}
|
}
|
||||||
return &oauth2conf, provider, nil
|
return &oauth2conf, provider, nil
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -223,8 +223,15 @@ message ApplicationStatus {
|
|||||||
|
|
||||||
optional string sourceType = 9;
|
optional string sourceType = 9;
|
||||||
|
|
||||||
|
optional ApplicationSummary summary = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ApplicationSummary {
|
||||||
// ExternalURLs holds all external URLs of application child resources.
|
// ExternalURLs holds all external URLs of application child resources.
|
||||||
repeated string externalURLs = 10;
|
repeated string externalURLs = 1;
|
||||||
|
|
||||||
|
// Images holds all images of application child resources.
|
||||||
|
repeated string images = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplicationTree holds nodes which belongs to the application
|
// ApplicationTree holds nodes which belongs to the application
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
|||||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationSourcePlugin": schema_pkg_apis_application_v1alpha1_ApplicationSourcePlugin(ref),
|
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationSourcePlugin": schema_pkg_apis_application_v1alpha1_ApplicationSourcePlugin(ref),
|
||||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationSpec": schema_pkg_apis_application_v1alpha1_ApplicationSpec(ref),
|
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationSpec": schema_pkg_apis_application_v1alpha1_ApplicationSpec(ref),
|
||||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationStatus": schema_pkg_apis_application_v1alpha1_ApplicationStatus(ref),
|
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationStatus": schema_pkg_apis_application_v1alpha1_ApplicationStatus(ref),
|
||||||
|
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationSummary": schema_pkg_apis_application_v1alpha1_ApplicationSummary(ref),
|
||||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationTree": schema_pkg_apis_application_v1alpha1_ApplicationTree(ref),
|
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationTree": schema_pkg_apis_application_v1alpha1_ApplicationTree(ref),
|
||||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationWatchEvent": schema_pkg_apis_application_v1alpha1_ApplicationWatchEvent(ref),
|
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationWatchEvent": schema_pkg_apis_application_v1alpha1_ApplicationWatchEvent(ref),
|
||||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.Cluster": schema_pkg_apis_application_v1alpha1_Cluster(ref),
|
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.Cluster": schema_pkg_apis_application_v1alpha1_Cluster(ref),
|
||||||
@@ -840,6 +841,25 @@ func schema_pkg_apis_application_v1alpha1_ApplicationStatus(ref common.Reference
|
|||||||
Format: "",
|
Format: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"summary": {
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Ref: ref("github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationSummary"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Dependencies: []string{
|
||||||
|
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationCondition", "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationSummary", "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.HealthStatus", "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.OperationState", "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ResourceStatus", "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.RevisionHistory", "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.SyncStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func schema_pkg_apis_application_v1alpha1_ApplicationSummary(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||||
|
return common.OpenAPIDefinition{
|
||||||
|
Schema: spec.Schema{
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Type: []string{"object"},
|
||||||
|
Properties: map[string]spec.Schema{
|
||||||
"externalURLs": {
|
"externalURLs": {
|
||||||
SchemaProps: spec.SchemaProps{
|
SchemaProps: spec.SchemaProps{
|
||||||
Description: "ExternalURLs holds all external URLs of application child resources.",
|
Description: "ExternalURLs holds all external URLs of application child resources.",
|
||||||
@@ -854,11 +874,23 @@ func schema_pkg_apis_application_v1alpha1_ApplicationStatus(ref common.Reference
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"images": {
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Description: "Images holds all images of application child resources.",
|
||||||
|
Type: []string{"array"},
|
||||||
|
Items: &spec.SchemaOrArray{
|
||||||
|
Schema: &spec.Schema{
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Type: []string{"string"},
|
||||||
|
Format: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Dependencies: []string{
|
|
||||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ApplicationCondition", "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.HealthStatus", "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.OperationState", "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ResourceStatus", "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.RevisionHistory", "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.SyncStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -225,8 +225,7 @@ type ApplicationStatus struct {
|
|||||||
OperationState *OperationState `json:"operationState,omitempty" protobuf:"bytes,7,opt,name=operationState"`
|
OperationState *OperationState `json:"operationState,omitempty" protobuf:"bytes,7,opt,name=operationState"`
|
||||||
ObservedAt metav1.Time `json:"observedAt,omitempty" protobuf:"bytes,8,opt,name=observedAt"`
|
ObservedAt metav1.Time `json:"observedAt,omitempty" protobuf:"bytes,8,opt,name=observedAt"`
|
||||||
SourceType ApplicationSourceType `json:"sourceType,omitempty" protobuf:"bytes,9,opt,name=sourceType"`
|
SourceType ApplicationSourceType `json:"sourceType,omitempty" protobuf:"bytes,9,opt,name=sourceType"`
|
||||||
// ExternalURLs holds all external URLs of application child resources.
|
Summary ApplicationSummary `json:"summary,omitempty" protobuf:"bytes,10,opt,name=summary"`
|
||||||
ExternalURLs []string `json:"externalURLs,omitempty" protobuf:"bytes,10,opt,name=externalURLs"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operation contains requested operation parameters.
|
// Operation contains requested operation parameters.
|
||||||
@@ -538,6 +537,13 @@ type ApplicationTree struct {
|
|||||||
Nodes []ResourceNode `json:"nodes,omitempty" protobuf:"bytes,1,rep,name=nodes"`
|
Nodes []ResourceNode `json:"nodes,omitempty" protobuf:"bytes,1,rep,name=nodes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ApplicationSummary struct {
|
||||||
|
// ExternalURLs holds all external URLs of application child resources.
|
||||||
|
ExternalURLs []string `json:"externalURLs,omitempty" protobuf:"bytes,1,opt,name=externalURLs"`
|
||||||
|
// Images holds all images of application child resources.
|
||||||
|
Images []string `json:"images,omitempty" protobuf:"bytes,2,opt,name=images"`
|
||||||
|
}
|
||||||
|
|
||||||
func (t *ApplicationTree) FindNode(group string, kind string, namespace string, name string) *ResourceNode {
|
func (t *ApplicationTree) FindNode(group string, kind string, namespace string, name string) *ResourceNode {
|
||||||
for _, n := range t.Nodes {
|
for _, n := range t.Nodes {
|
||||||
if n.Group == group && n.Kind == kind && n.Namespace == namespace && n.Name == name {
|
if n.Group == group && n.Kind == kind && n.Namespace == namespace && n.Name == name {
|
||||||
@@ -547,20 +553,28 @@ func (t *ApplicationTree) FindNode(group string, kind string, namespace string,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *ApplicationTree) GetBrowableURLs() []string {
|
func (t *ApplicationTree) GetSummary() ApplicationSummary {
|
||||||
urlsSet := make(map[string]bool)
|
urlsSet := make(map[string]bool)
|
||||||
|
imagesSet := make(map[string]bool)
|
||||||
for _, node := range t.Nodes {
|
for _, node := range t.Nodes {
|
||||||
if node.NetworkingInfo != nil {
|
if node.NetworkingInfo != nil {
|
||||||
for _, url := range node.NetworkingInfo.ExternalURLs {
|
for _, url := range node.NetworkingInfo.ExternalURLs {
|
||||||
urlsSet[url] = true
|
urlsSet[url] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, image := range node.Images {
|
||||||
|
imagesSet[image] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
urls := make([]string, 0)
|
urls := make([]string, 0)
|
||||||
for url := range urlsSet {
|
for url := range urlsSet {
|
||||||
urls = append(urls, url)
|
urls = append(urls, url)
|
||||||
}
|
}
|
||||||
return urls
|
images := make([]string, 0)
|
||||||
|
for image := range imagesSet {
|
||||||
|
images = append(images, image)
|
||||||
|
}
|
||||||
|
return ApplicationSummary{ExternalURLs: urls, Images: images}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourceRef includes fields which unique identify resource
|
// ResourceRef includes fields which unique identify resource
|
||||||
@@ -756,6 +770,17 @@ type Repository struct {
|
|||||||
InsecureIgnoreHostKey bool `json:"insecureIgnoreHostKey,omitempty" protobuf:"bytes,6,opt,name=insecureIgnoreHostKey"`
|
InsecureIgnoreHostKey bool `json:"insecureIgnoreHostKey,omitempty" protobuf:"bytes,6,opt,name=insecureIgnoreHostKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Repository) HasCredentials() bool {
|
||||||
|
return m.Username != "" || m.Password != "" || m.SSHPrivateKey != "" || m.InsecureIgnoreHostKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Repository) CopyCredentialsFrom(source Repository) {
|
||||||
|
m.Username = source.Username
|
||||||
|
m.Password = source.Password
|
||||||
|
m.SSHPrivateKey = source.SSHPrivateKey
|
||||||
|
m.InsecureIgnoreHostKey = source.InsecureIgnoreHostKey
|
||||||
|
}
|
||||||
|
|
||||||
// RepositoryList is a collection of Repositories.
|
// RepositoryList is a collection of Repositories.
|
||||||
type RepositoryList struct {
|
type RepositoryList struct {
|
||||||
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||||
|
|||||||
@@ -199,3 +199,87 @@ func TestAppProjectSpec_DestinationClusters(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRepository_HasCredentials(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
repo Repository
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "TestHasRepo",
|
||||||
|
repo: Repository{Repo: "foo"},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestHasUsername",
|
||||||
|
repo: Repository{Username: "foo"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestHasPassword",
|
||||||
|
repo: Repository{Password: "foo"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestHasSSHPrivateKey",
|
||||||
|
repo: Repository{SSHPrivateKey: "foo"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestHasInsecureHostKey",
|
||||||
|
repo: Repository{InsecureIgnoreHostKey: true},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := tt.repo.HasCredentials(); got != tt.want {
|
||||||
|
t.Errorf("Repository.HasCredentials() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepository_CopyCredentialsFrom(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
source Repository
|
||||||
|
want Repository
|
||||||
|
}{
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "TestHasRepo",
|
||||||
|
source: Repository{Repo: "foo"},
|
||||||
|
want: Repository{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestHasUsername",
|
||||||
|
source: Repository{Username: "foo"},
|
||||||
|
want: Repository{Username: "foo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestHasPassword",
|
||||||
|
source: Repository{Password: "foo"},
|
||||||
|
want: Repository{Password: "foo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestHasSSHPrivateKey",
|
||||||
|
source: Repository{SSHPrivateKey: "foo"},
|
||||||
|
want: Repository{SSHPrivateKey: "foo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestHasInsecureHostKey",
|
||||||
|
source: Repository{InsecureIgnoreHostKey: true},
|
||||||
|
want: Repository{InsecureIgnoreHostKey: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
repo := Repository{}
|
||||||
|
repo.CopyCredentialsFrom(tt.source)
|
||||||
|
assert.Equal(t, tt.want, repo)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -461,11 +461,7 @@ func (in *ApplicationStatus) DeepCopyInto(out *ApplicationStatus) {
|
|||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
in.ObservedAt.DeepCopyInto(&out.ObservedAt)
|
in.ObservedAt.DeepCopyInto(&out.ObservedAt)
|
||||||
if in.ExternalURLs != nil {
|
in.Summary.DeepCopyInto(&out.Summary)
|
||||||
in, out := &in.ExternalURLs, &out.ExternalURLs
|
|
||||||
*out = make([]string, len(*in))
|
|
||||||
copy(*out, *in)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,6 +475,32 @@ func (in *ApplicationStatus) DeepCopy() *ApplicationStatus {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ApplicationSummary) DeepCopyInto(out *ApplicationSummary) {
|
||||||
|
*out = *in
|
||||||
|
if in.ExternalURLs != nil {
|
||||||
|
in, out := &in.ExternalURLs, &out.ExternalURLs
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.Images != nil {
|
||||||
|
in, out := &in.Images, &out.Images
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationSummary.
|
||||||
|
func (in *ApplicationSummary) DeepCopy() *ApplicationSummary {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ApplicationSummary)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ApplicationTree) DeepCopyInto(out *ApplicationTree) {
|
func (in *ApplicationTree) DeepCopyInto(out *ApplicationTree) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/reposerver/repository"
|
"github.com/argoproj/argo-cd/reposerver/repository"
|
||||||
"github.com/argoproj/argo-cd/util"
|
"github.com/argoproj/argo-cd/util"
|
||||||
|
argogrpc "github.com/argoproj/argo-cd/util/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Clientset represets repository server api clients
|
// Clientset represets repository server api clients
|
||||||
@@ -20,7 +20,8 @@ type Clientset interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type clientSet struct {
|
type clientSet struct {
|
||||||
address string
|
address string
|
||||||
|
timeoutSeconds int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientSet) NewRepoServerClient() (util.Closer, repository.RepoServerServiceClient, error) {
|
func (c *clientSet) NewRepoServerClient() (util.Closer, repository.RepoServerServiceClient, error) {
|
||||||
@@ -28,10 +29,14 @@ func (c *clientSet) NewRepoServerClient() (util.Closer, repository.RepoServerSer
|
|||||||
grpc_retry.WithMax(3),
|
grpc_retry.WithMax(3),
|
||||||
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(1000 * time.Millisecond)),
|
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(1000 * time.Millisecond)),
|
||||||
}
|
}
|
||||||
conn, err := grpc.Dial(c.address,
|
opts := []grpc.DialOption{
|
||||||
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})),
|
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})),
|
||||||
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)),
|
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)),
|
||||||
grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(retryOpts...)))
|
grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(retryOpts...))}
|
||||||
|
if c.timeoutSeconds > 0 {
|
||||||
|
opts = append(opts, grpc.WithUnaryInterceptor(argogrpc.WithTimeout(time.Duration(c.timeoutSeconds)*time.Second)))
|
||||||
|
}
|
||||||
|
conn, err := grpc.Dial(c.address, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to connect to repository service with address %s", c.address)
|
log.Errorf("Unable to connect to repository service with address %s", c.address)
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -40,6 +45,6 @@ func (c *clientSet) NewRepoServerClient() (util.Closer, repository.RepoServerSer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewRepoServerClientset creates new instance of repo server Clientset
|
// NewRepoServerClientset creates new instance of repo server Clientset
|
||||||
func NewRepoServerClientset(address string) Clientset {
|
func NewRepoServerClientset(address string, timeoutSeconds int) Clientset {
|
||||||
return &clientSet{address: address}
|
return &clientSet{address: address, timeoutSeconds: timeoutSeconds}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -490,7 +490,7 @@ func pathExists(ss ...string) bool {
|
|||||||
func (s *Service) newClientResolveRevision(repo *v1alpha1.Repository, revision string) (git.Client, string, error) {
|
func (s *Service) newClientResolveRevision(repo *v1alpha1.Repository, revision string) (git.Client, string, error) {
|
||||||
repoURL := git.NormalizeGitURL(repo.Repo)
|
repoURL := git.NormalizeGitURL(repo.Repo)
|
||||||
appRepoPath := tempRepoPath(repoURL)
|
appRepoPath := tempRepoPath(repoURL)
|
||||||
gitClient, err := s.gitFactory.NewClient(repoURL, appRepoPath, repo.Username, repo.Password, repo.SSHPrivateKey, repo.InsecureIgnoreHostKey)
|
gitClient, err := s.gitFactory.NewClient(repo.Repo, appRepoPath, repo.Username, repo.Password, repo.SSHPrivateKey, repo.InsecureIgnoreHostKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ discoveryTests:
|
|||||||
- inputPath: testdata/paused_rollout.yaml
|
- inputPath: testdata/paused_rollout.yaml
|
||||||
result:
|
result:
|
||||||
- name: resume
|
- name: resume
|
||||||
|
- inputPath: testdata/v0.2_paused_rollout.yaml
|
||||||
|
result:
|
||||||
|
- name: resume
|
||||||
- inputPath: testdata/not_paused_rollout.yaml
|
- inputPath: testdata/not_paused_rollout.yaml
|
||||||
result: []
|
result: []
|
||||||
- inputPath: testdata/nil_paused_rollout.yaml
|
- inputPath: testdata/nil_paused_rollout.yaml
|
||||||
@@ -10,4 +13,6 @@ actionTests:
|
|||||||
- action: resume
|
- action: resume
|
||||||
inputPath: testdata/paused_rollout.yaml
|
inputPath: testdata/paused_rollout.yaml
|
||||||
expectedOutputPath: testdata/not_paused_rollout.yaml
|
expectedOutputPath: testdata/not_paused_rollout.yaml
|
||||||
|
- action: resume
|
||||||
|
inputPath: testdata/v0.2_paused_rollout.yaml
|
||||||
|
expectedOutputPath: testdata/v0.2_not_paused_rollout.yaml
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
actions = {}
|
actions = {}
|
||||||
|
|
||||||
if obj.spec.paused ~= nil and obj.spec.paused then
|
local paused = false
|
||||||
|
if obj.status.verifyingPreview ~= nil then
|
||||||
|
paused = obj.status.verifyingPreview
|
||||||
|
elseif obj.spec.paused ~= nil then
|
||||||
|
paused = obj.spec.paused
|
||||||
|
end
|
||||||
|
|
||||||
|
if paused then
|
||||||
actions["resume"] = {}
|
actions["resume"] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,9 @@
|
|||||||
obj.spec.paused = false
|
if obj.status.verifyingPreview ~= nil and obj.status.verifyingPreview then
|
||||||
|
obj.status.verifyingPreview = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if obj.spec.paused ~= nil and obj.spec.paused then
|
||||||
|
obj.spec.paused = false
|
||||||
|
end
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
55
resource_customizations/argoproj.io/Rollout/actions/testdata/v0.2_not_paused_rollout.yaml
vendored
Normal file
55
resource_customizations/argoproj.io/Rollout/actions/testdata/v0.2_not_paused_rollout.yaml
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Rollout
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rollout.argoproj.io/revision: "7"
|
||||||
|
clusterName: ""
|
||||||
|
creationTimestamp: 2019-01-22T16:52:54Z
|
||||||
|
generation: 1
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/instance: guestbook-default
|
||||||
|
name: ks-guestbook-ui
|
||||||
|
namespace: default
|
||||||
|
resourceVersion: "164113"
|
||||||
|
selfLink: /apis/argoproj.io/v1alpha1/namespaces/default/rollouts/ks-guestbook-ui
|
||||||
|
uid: 29802403-1e66-11e9-a6a4-025000000001
|
||||||
|
spec:
|
||||||
|
minReadySeconds: 30
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: ks-guestbook-ui
|
||||||
|
strategy:
|
||||||
|
blueGreen:
|
||||||
|
activeService: ks-guestbook-ui-active
|
||||||
|
previewService: ks-guestbook-ui-preview
|
||||||
|
type: BlueGreenUpdate
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: ks-guestbook-ui
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
|
||||||
|
name: ks-guestbook-ui
|
||||||
|
ports:
|
||||||
|
- containerPort: 83
|
||||||
|
resources: {}
|
||||||
|
status:
|
||||||
|
blueGreen:
|
||||||
|
activeSelector: 85f9884f5d
|
||||||
|
previewSelector: 697fb9575c
|
||||||
|
availableReplicas: 6
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: 2019-01-25T07:44:26Z
|
||||||
|
lastUpdateTime: 2019-01-25T07:44:26Z
|
||||||
|
message: Rollout is serving traffic from the active service.
|
||||||
|
reason: Available
|
||||||
|
status: "True"
|
||||||
|
type: Available
|
||||||
|
currentPodHash: 697fb9575c
|
||||||
|
observedGeneration: 767f98959f
|
||||||
|
readyReplicas: 6
|
||||||
|
replicas: 6
|
||||||
|
updatedReplicas: 3
|
||||||
|
verifyingPreview: false
|
||||||
55
resource_customizations/argoproj.io/Rollout/actions/testdata/v0.2_paused_rollout.yaml
vendored
Normal file
55
resource_customizations/argoproj.io/Rollout/actions/testdata/v0.2_paused_rollout.yaml
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Rollout
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rollout.argoproj.io/revision: "7"
|
||||||
|
clusterName: ""
|
||||||
|
creationTimestamp: 2019-01-22T16:52:54Z
|
||||||
|
generation: 1
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/instance: guestbook-default
|
||||||
|
name: ks-guestbook-ui
|
||||||
|
namespace: default
|
||||||
|
resourceVersion: "164113"
|
||||||
|
selfLink: /apis/argoproj.io/v1alpha1/namespaces/default/rollouts/ks-guestbook-ui
|
||||||
|
uid: 29802403-1e66-11e9-a6a4-025000000001
|
||||||
|
spec:
|
||||||
|
minReadySeconds: 30
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: ks-guestbook-ui
|
||||||
|
strategy:
|
||||||
|
blueGreen:
|
||||||
|
activeService: ks-guestbook-ui-active
|
||||||
|
previewService: ks-guestbook-ui-preview
|
||||||
|
type: BlueGreenUpdate
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: ks-guestbook-ui
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
|
||||||
|
name: ks-guestbook-ui
|
||||||
|
ports:
|
||||||
|
- containerPort: 83
|
||||||
|
resources: {}
|
||||||
|
status:
|
||||||
|
blueGreen:
|
||||||
|
activeSelector: 85f9884f5d
|
||||||
|
previewSelector: 697fb9575c
|
||||||
|
availableReplicas: 6
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: 2019-01-25T07:44:26Z
|
||||||
|
lastUpdateTime: 2019-01-25T07:44:26Z
|
||||||
|
message: Rollout is serving traffic from the active service.
|
||||||
|
reason: Available
|
||||||
|
status: "True"
|
||||||
|
type: Available
|
||||||
|
currentPodHash: 697fb9575c
|
||||||
|
observedGeneration: 767f98959f
|
||||||
|
readyReplicas: 6
|
||||||
|
replicas: 6
|
||||||
|
updatedReplicas: 3
|
||||||
|
verifyingPreview: true
|
||||||
@@ -1,3 +1,55 @@
|
|||||||
|
function checkReplicasStatus(obj)
|
||||||
|
hs = {}
|
||||||
|
if obj.spec.replicas ~= nil and obj.status.updatedReplicas < obj.spec.replicas then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Waiting for roll out to finish: More replicas need to be updated"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if obj.status.replicas > obj.status.updatedReplicas then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Waiting for roll out to finish: old replicas are pending termination"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if obj.status.availableReplicas < obj.status.updatedReplicas then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Waiting for roll out to finish: updated replicas are still becoming available"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if obj.spec.replicas ~= nil and obj.status.updatedReplicas < obj.spec.replicas then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Waiting for roll out to finish: More replicas need to be updated"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if obj.status.replicas > obj.status.updatedReplicas then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Waiting for roll out to finish: old replicas are pending termination"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if obj.status.availableReplicas < obj.status.updatedReplicas then
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Waiting for roll out to finish: updated replicas are still becoming available"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function checkPaused(obj)
|
||||||
|
hs = {}
|
||||||
|
local paused = false
|
||||||
|
if obj.status.verifyingPreview ~= nil then
|
||||||
|
paused = obj.status.verifyingPreview
|
||||||
|
elseif obj.spec.paused ~= nil then
|
||||||
|
paused = obj.spec.paused
|
||||||
|
end
|
||||||
|
|
||||||
|
if paused then
|
||||||
|
hs.status = "Suspended"
|
||||||
|
hs.message = "Rollout is paused"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
hs = {}
|
hs = {}
|
||||||
if obj.status ~= nil then
|
if obj.status ~= nil then
|
||||||
if obj.status.conditions ~= nil then
|
if obj.status.conditions ~= nil then
|
||||||
@@ -7,44 +59,76 @@ if obj.status ~= nil then
|
|||||||
hs.message = condition.message
|
hs.message = condition.message
|
||||||
return hs
|
return hs
|
||||||
end
|
end
|
||||||
|
if condition.type == "Progressing" and condition.reason == "ProgressDeadlineExceeded" then
|
||||||
|
hs.status = "Degraded"
|
||||||
|
hs.message = condition.message
|
||||||
|
return hs
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if obj.status.currentPodHash ~= nil then
|
if obj.status.currentPodHash ~= nil then
|
||||||
if obj.spec.replicas ~= nil and obj.status.updatedReplicas < obj.spec.replicas then
|
if obj.spec.strategy.blueGreen ~= nil then
|
||||||
hs.status = "Progressing"
|
isPaused = checkPaused(obj)
|
||||||
hs.message = "Waiting for roll out to finish: More replicas need to be updated"
|
if isPaused ~= nil then
|
||||||
return hs
|
return isPaused
|
||||||
end
|
end
|
||||||
|
replicasHS = checkReplicasStatus(obj)
|
||||||
if obj.spec.paused then
|
if replicasHS ~= nil then
|
||||||
hs.status = "Suspended"
|
return replicasHS
|
||||||
if obj.status.blueGreen.previewSelector ~= nil and obj.status.blueGreen.previewSelector == obj.status.currentPodHash then
|
end
|
||||||
hs.message = "The preview Service is serving traffic to the current pod spec"
|
if obj.status.blueGreen.activeSelector ~= nil and obj.status.blueGreen.activeSelector == obj.status.currentPodHash then
|
||||||
elseif obj.status.blueGreen.activeSelector ~= nil and obj.status.blueGreen.activeSelector == obj.status.currentPodHash then
|
hs.status = "Healthy"
|
||||||
hs.message = "The active Service is serving traffic to the current pod spec"
|
hs.message = "The active Service is serving traffic to the current pod spec"
|
||||||
|
return hs
|
||||||
end
|
end
|
||||||
return hs
|
|
||||||
end
|
|
||||||
|
|
||||||
if obj.status.replicas > obj.status.updatedReplicas then
|
|
||||||
hs.status = "Progressing"
|
hs.status = "Progressing"
|
||||||
hs.message = "Waiting for roll out to finish: old replicas are pending termination"
|
hs.message = "The current pod spec is not receiving traffic from the active service"
|
||||||
return hs
|
|
||||||
end
|
|
||||||
if obj.status.availableReplicas < obj.status.updatedReplicas then
|
|
||||||
hs.status = "Progressing"
|
|
||||||
hs.message = "Waiting for roll out to finish: updated replicas are still becoming available"
|
|
||||||
return hs
|
return hs
|
||||||
end
|
end
|
||||||
|
if obj.spec.strategy.canary ~= nil then
|
||||||
|
currentRSIsStable = obj.status.canary.stableRS == obj.status.currentPodHash
|
||||||
|
if obj.spec.strategy.canary.steps ~= nil then
|
||||||
|
stepCount = table.getn(obj.spec.strategy.canary.steps)
|
||||||
|
if obj.status.currentStepIndex ~= nil then
|
||||||
|
currentStepIndex = obj.status.currentStepIndex
|
||||||
|
isPaused = checkPaused(obj)
|
||||||
|
if isPaused ~= nil then
|
||||||
|
return isPaused
|
||||||
|
end
|
||||||
|
|
||||||
|
if paused then
|
||||||
|
hs.status = "Suspended"
|
||||||
|
hs.message = "Rollout is paused"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
if currentRSIsStable and stepCount == currentStepIndex then
|
||||||
|
replicasHS = checkReplicasStatus(obj)
|
||||||
|
if replicasHS ~= nil then
|
||||||
|
return replicasHS
|
||||||
|
end
|
||||||
|
hs.status = "Healthy"
|
||||||
|
hs.message = "The rollout has completed all steps"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Waiting for rollout to finish steps"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
|
||||||
if obj.status.blueGreen.activeSelector ~= nil and obj.status.blueGreen.activeSelector == obj.status.currentPodHash then
|
-- The detecting the health of the Canary deployment when there are no steps
|
||||||
hs.status = "Healthy"
|
replicasHS = checkReplicasStatus(obj)
|
||||||
hs.message = "The active Service is serving traffic to the current pod spec"
|
if replicasHS ~= nil then
|
||||||
return hs
|
return replicasHS
|
||||||
|
end
|
||||||
|
if currentRSIsStable then
|
||||||
|
hs.status = "Healthy"
|
||||||
|
hs.message = "The rollout has completed canary deployment"
|
||||||
|
return hs
|
||||||
|
end
|
||||||
|
hs.status = "Progressing"
|
||||||
|
hs.message = "Waiting for rollout to finish canary deployment"
|
||||||
end
|
end
|
||||||
hs.status = "Progressing"
|
|
||||||
hs.message = "The current pod spec is not receiving traffic from the active service"
|
|
||||||
return hs
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
hs.status = "Progressing"
|
hs.status = "Progressing"
|
||||||
|
|||||||
@@ -1,25 +1,55 @@
|
|||||||
tests:
|
tests:
|
||||||
- healthStatus:
|
|
||||||
status: Healthy
|
|
||||||
message: The active Service is serving traffic to the current pod spec
|
|
||||||
inputPath: testdata/healthy_servingActiveService.yaml
|
|
||||||
- healthStatus:
|
|
||||||
status: Suspended
|
|
||||||
message: The preview Service is serving traffic to the current pod spec
|
|
||||||
inputPath: testdata/suspended_servingPreviewService.yaml
|
|
||||||
- healthStatus:
|
|
||||||
status: Progressing
|
|
||||||
message: "Waiting for roll out to finish: More replicas need to be updated"
|
|
||||||
inputPath: testdata/progressing_addingMoreReplicas.yaml
|
|
||||||
- healthStatus:
|
|
||||||
status: Progressing
|
|
||||||
message: "Waiting for roll out to finish: old replicas are pending termination"
|
|
||||||
inputPath: testdata/progressing_killingOldReplicas.yaml
|
|
||||||
- healthStatus:
|
|
||||||
status: Progressing
|
|
||||||
message: "Waiting for roll out to finish: updated replicas are still becoming available"
|
|
||||||
inputPath: testdata/progressing_waitingUntilAvailable.yaml
|
|
||||||
- healthStatus:
|
- healthStatus:
|
||||||
status: Degraded
|
status: Degraded
|
||||||
message: Rollout has missing field '.Spec.Strategy.Type'
|
message: Rollout has missing field '.Spec.Strategy.Type'
|
||||||
inputPath: testdata/degraded_invalidSpec.yaml
|
inputPath: testdata/degraded_invalidSpec.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Degraded
|
||||||
|
message: ReplicaSet "guestbook-bluegreen-helm-guestbook-6b8cf6f7db" has timed out progressing.
|
||||||
|
inputPath: testdata/degraded_rolloutTimeout.yaml
|
||||||
|
#BlueGreen
|
||||||
|
- healthStatus:
|
||||||
|
status: Healthy
|
||||||
|
message: The active Service is serving traffic to the current pod spec
|
||||||
|
inputPath: testdata/bluegreen/healthy_servingActiveService.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Suspended
|
||||||
|
message: Rollout is paused
|
||||||
|
inputPath: testdata/bluegreen/suspended_servingPreviewService.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Suspended
|
||||||
|
message: Rollout is paused
|
||||||
|
inputPath: testdata/v0.2_suspended_servingPreviewService.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: "Waiting for roll out to finish: More replicas need to be updated"
|
||||||
|
inputPath: testdata/bluegreen/progressing_addingMoreReplicas.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: "Waiting for roll out to finish: old replicas are pending termination"
|
||||||
|
inputPath: testdata/bluegreen/progressing_killingOldReplicas.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: "Waiting for roll out to finish: updated replicas are still becoming available"
|
||||||
|
inputPath: testdata/bluegreen/progressing_waitingUntilAvailable.yaml
|
||||||
|
#Canary
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: Waiting for rollout to finish steps
|
||||||
|
inputPath: testdata/canary/progressing_setWeightStep.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Suspended
|
||||||
|
message: Rollout is paused
|
||||||
|
inputPath: testdata/canary/suspended_pausedStep.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Healthy
|
||||||
|
message: The rollout has completed all steps
|
||||||
|
inputPath: testdata/canary/healthy_executedAllSteps.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Progressing
|
||||||
|
message: 'Waiting for roll out to finish: old replicas are pending termination'
|
||||||
|
inputPath: testdata/canary/progressing_noSteps.yaml
|
||||||
|
- healthStatus:
|
||||||
|
status: Healthy
|
||||||
|
message: The rollout has completed canary deployment
|
||||||
|
inputPath: testdata/canary/healthy_noSteps.yaml
|
||||||
73
resource_customizations/argoproj.io/Rollout/testdata/canary/healthy_executedAllSteps.yaml
vendored
Normal file
73
resource_customizations/argoproj.io/Rollout/testdata/canary/healthy_executedAllSteps.yaml
vendored
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Rollout
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kubectl.kubernetes.io/last-applied-configuration: >
|
||||||
|
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-canary","ksonnet.io/component":"guestbook-ui"},"name":"guestbook-canary","namespace":"default"},"spec":{"minReadySeconds":10,"replicas":5,"selector":{"matchLabels":{"app":"guestbook-canary"}},"strategy":{"canary":{"maxSurge":1,"maxUnavailable":0,"steps":[{"setWeight":20},{"pause":{"duration":30}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook-canary"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook-canary","ports":[{"containerPort":80}]}]}}}}
|
||||||
|
rollout.argoproj.io/revision: '1'
|
||||||
|
clusterName: ''
|
||||||
|
creationTimestamp: '2019-05-01T21:55:30Z'
|
||||||
|
generation: 1
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/instance: guestbook-canary
|
||||||
|
ksonnet.io/component: guestbook-ui
|
||||||
|
name: guestbook-canary
|
||||||
|
namespace: default
|
||||||
|
resourceVersion: '955764'
|
||||||
|
selfLink: /apis/argoproj.io/v1alpha1/namespaces/default/rollouts/guestbook-canary
|
||||||
|
uid: d6105ccd-6c5b-11e9-b8d7-025000000001
|
||||||
|
spec:
|
||||||
|
minReadySeconds: 10
|
||||||
|
replicas: 5
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: guestbook-canary
|
||||||
|
strategy:
|
||||||
|
canary:
|
||||||
|
maxSurge: 1
|
||||||
|
maxUnavailable: 0
|
||||||
|
steps:
|
||||||
|
- setWeight: 20
|
||||||
|
- pause:
|
||||||
|
duration: 30
|
||||||
|
- setWeight: 40
|
||||||
|
- pause: {}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
app: guestbook-canary
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: 'gcr.io/heptio-images/ks-guestbook-demo:0.1'
|
||||||
|
name: guestbook-canary
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
resources: {}
|
||||||
|
status:
|
||||||
|
HPAReplicas: 5
|
||||||
|
availableReplicas: 5
|
||||||
|
blueGreen: {}
|
||||||
|
canary:
|
||||||
|
stableRS: 84ccfddd66
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: '2019-05-01T21:55:30Z'
|
||||||
|
lastUpdateTime: '2019-05-01T21:55:58Z'
|
||||||
|
message: ReplicaSet "guestbook-canary-84ccfddd66" has successfully progressed.
|
||||||
|
reason: NewReplicaSetAvailable
|
||||||
|
status: 'True'
|
||||||
|
type: Progressing
|
||||||
|
- lastTransitionTime: '2019-05-01T21:55:58Z'
|
||||||
|
lastUpdateTime: '2019-05-01T21:55:58Z'
|
||||||
|
message: Rollout has minimum availability
|
||||||
|
reason: AvailableReason
|
||||||
|
status: 'True'
|
||||||
|
type: Available
|
||||||
|
currentPodHash: 84ccfddd66
|
||||||
|
currentStepHash: 5f8fbdf7bb
|
||||||
|
currentStepIndex: 4
|
||||||
|
observedGeneration: c45557fd9
|
||||||
|
readyReplicas: 5
|
||||||
|
replicas: 5
|
||||||
|
selector: app=guestbook-canary
|
||||||
|
updatedReplicas: 5
|
||||||
66
resource_customizations/argoproj.io/Rollout/testdata/canary/healthy_noSteps.yaml
vendored
Normal file
66
resource_customizations/argoproj.io/Rollout/testdata/canary/healthy_noSteps.yaml
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Rollout
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kubectl.kubernetes.io/last-applied-configuration: >
|
||||||
|
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-canary","ksonnet.io/component":"guestbook-ui"},"name":"guestbook-canary","namespace":"default"},"spec":{"minReadySeconds":10,"replicas":5,"selector":{"matchLabels":{"app":"guestbook-canary"}},"strategy":{"canary":{"maxSurge":1,"maxUnavailable":0,"steps":[{"setWeight":20},{"pause":{"duration":30}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook-canary"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook-canary","ports":[{"containerPort":80}]}]}}}}
|
||||||
|
rollout.argoproj.io/revision: '2'
|
||||||
|
clusterName: ''
|
||||||
|
creationTimestamp: '2019-05-01T21:55:30Z'
|
||||||
|
generation: 1
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/instance: guestbook-canary
|
||||||
|
ksonnet.io/component: guestbook-ui
|
||||||
|
name: guestbook-canary
|
||||||
|
namespace: default
|
||||||
|
resourceVersion: '956205'
|
||||||
|
selfLink: /apis/argoproj.io/v1alpha1/namespaces/default/rollouts/guestbook-canary
|
||||||
|
uid: d6105ccd-6c5b-11e9-b8d7-025000000001
|
||||||
|
spec:
|
||||||
|
minReadySeconds: 10
|
||||||
|
replicas: 5
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: guestbook-canary
|
||||||
|
strategy:
|
||||||
|
canary:
|
||||||
|
maxSurge: 1
|
||||||
|
maxUnavailable: 0
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
app: guestbook-canary
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: 'gcr.io/heptio-images/ks-guestbook-demo:0.2'
|
||||||
|
name: guestbook-canary
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
resources: {}
|
||||||
|
status:
|
||||||
|
HPAReplicas: 5
|
||||||
|
availableReplicas: 5
|
||||||
|
blueGreen: {}
|
||||||
|
canary:
|
||||||
|
stableRS: 567dd56d89
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: '2019-05-01T22:00:16Z'
|
||||||
|
lastUpdateTime: '2019-05-01T22:00:16Z'
|
||||||
|
message: Rollout has minimum availability
|
||||||
|
reason: AvailableReason
|
||||||
|
status: 'True'
|
||||||
|
type: Available
|
||||||
|
- lastTransitionTime: '2019-05-01T21:55:30Z'
|
||||||
|
lastUpdateTime: '2019-05-01T22:00:16Z'
|
||||||
|
message: ReplicaSet "guestbook-canary-567dd56d89" has successfully progressed.
|
||||||
|
reason: NewReplicaSetAvailable
|
||||||
|
status: 'True'
|
||||||
|
type: Progressing
|
||||||
|
currentPodHash: 567dd56d89
|
||||||
|
currentStepHash: 6c9545789c
|
||||||
|
observedGeneration: 6886f85bff
|
||||||
|
readyReplicas: 5
|
||||||
|
replicas: 5
|
||||||
|
selector: app=guestbook-canary
|
||||||
|
updatedReplicas: 5
|
||||||
66
resource_customizations/argoproj.io/Rollout/testdata/canary/progressing_noSteps.yaml
vendored
Normal file
66
resource_customizations/argoproj.io/Rollout/testdata/canary/progressing_noSteps.yaml
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Rollout
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kubectl.kubernetes.io/last-applied-configuration: >
|
||||||
|
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-canary","ksonnet.io/component":"guestbook-ui"},"name":"guestbook-canary","namespace":"default"},"spec":{"minReadySeconds":10,"replicas":5,"selector":{"matchLabels":{"app":"guestbook-canary"}},"strategy":{"canary":{"maxSurge":1,"maxUnavailable":0,"steps":[{"setWeight":20},{"pause":{"duration":30}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook-canary"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook-canary","ports":[{"containerPort":80}]}]}}}}
|
||||||
|
rollout.argoproj.io/revision: '2'
|
||||||
|
clusterName: ''
|
||||||
|
creationTimestamp: '2019-05-01T21:55:30Z'
|
||||||
|
generation: 1
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/instance: guestbook-canary
|
||||||
|
ksonnet.io/component: guestbook-ui
|
||||||
|
name: guestbook-canary
|
||||||
|
namespace: default
|
||||||
|
resourceVersion: '956159'
|
||||||
|
selfLink: /apis/argoproj.io/v1alpha1/namespaces/default/rollouts/guestbook-canary
|
||||||
|
uid: d6105ccd-6c5b-11e9-b8d7-025000000001
|
||||||
|
spec:
|
||||||
|
minReadySeconds: 10
|
||||||
|
replicas: 5
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: guestbook-canary
|
||||||
|
strategy:
|
||||||
|
canary:
|
||||||
|
maxSurge: 1
|
||||||
|
maxUnavailable: 0
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
app: guestbook-canary
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: 'gcr.io/heptio-images/ks-guestbook-demo:0.2'
|
||||||
|
name: guestbook-canary
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
resources: {}
|
||||||
|
status:
|
||||||
|
HPAReplicas: 6
|
||||||
|
availableReplicas: 2
|
||||||
|
blueGreen: {}
|
||||||
|
canary:
|
||||||
|
stableRS: 567dd56d89
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: '2019-05-01T21:59:58Z'
|
||||||
|
lastUpdateTime: '2019-05-01T21:59:58Z'
|
||||||
|
message: Rollout does not have minimum availability
|
||||||
|
reason: AvailableReason
|
||||||
|
status: 'False'
|
||||||
|
type: Available
|
||||||
|
- lastTransitionTime: '2019-05-01T21:55:30Z'
|
||||||
|
lastUpdateTime: '2019-05-01T22:00:05Z'
|
||||||
|
message: ReplicaSet "guestbook-canary-567dd56d89" is progressing.
|
||||||
|
reason: ReplicaSetUpdated
|
||||||
|
status: 'True'
|
||||||
|
type: Progressing
|
||||||
|
currentPodHash: 567dd56d89
|
||||||
|
currentStepHash: 6c9545789c
|
||||||
|
observedGeneration: 6886f85bff
|
||||||
|
readyReplicas: 4
|
||||||
|
replicas: 6
|
||||||
|
selector: app=guestbook-canary
|
||||||
|
updatedReplicas: 5
|
||||||
69
resource_customizations/argoproj.io/Rollout/testdata/canary/progressing_setWeightStep.yaml
vendored
Normal file
69
resource_customizations/argoproj.io/Rollout/testdata/canary/progressing_setWeightStep.yaml
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Rollout
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kubectl.kubernetes.io/last-applied-configuration: |
|
||||||
|
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"name":"example-rollout-canary","namespace":"default"},"spec":{"minReadySeconds":30,"replicas":5,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook"}},"strategy":{"canary":{"steps":[{"setWeight":20},{"pause":{"duration":20}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook","ports":[{"containerPort":80}]}]}}}}
|
||||||
|
rollout.argoproj.io/revision: "2"
|
||||||
|
clusterName: ""
|
||||||
|
creationTimestamp: 2019-04-26T20:17:43Z
|
||||||
|
generation: 1
|
||||||
|
name: example-rollout-canary
|
||||||
|
namespace: default
|
||||||
|
resourceVersion: "696688"
|
||||||
|
selfLink: /apis/argoproj.io/v1alpha1/namespaces/default/rollouts/example-rollout-canary
|
||||||
|
uid: 58f6f1bb-6860-11e9-b8d7-025000000001
|
||||||
|
spec:
|
||||||
|
minReadySeconds: 30
|
||||||
|
replicas: 5
|
||||||
|
revisionHistoryLimit: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: guestbook
|
||||||
|
strategy:
|
||||||
|
canary:
|
||||||
|
steps:
|
||||||
|
- setWeight: 20
|
||||||
|
- pause:
|
||||||
|
duration: 20
|
||||||
|
- setWeight: 40
|
||||||
|
- pause: {}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
app: guestbook
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
||||||
|
name: guestbook
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
resources: {}
|
||||||
|
status:
|
||||||
|
HPAReplicas: 5
|
||||||
|
availableReplicas: 5
|
||||||
|
blueGreen: {}
|
||||||
|
canary:
|
||||||
|
stableRS: df986d68
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: 2019-04-26T20:18:05Z
|
||||||
|
lastUpdateTime: 2019-04-26T20:18:05Z
|
||||||
|
message: Rollout is not serving traffic from the active service.
|
||||||
|
reason: Available
|
||||||
|
status: "False"
|
||||||
|
type: Available
|
||||||
|
- lastTransitionTime: 2019-04-26T20:18:58Z
|
||||||
|
lastUpdateTime: 2019-04-26T20:19:29Z
|
||||||
|
message: ReplicaSet "example-rollout-canary-6b566f47b7" is progressing.
|
||||||
|
reason: ReplicaSetUpdated
|
||||||
|
status: "True"
|
||||||
|
type: Progressing
|
||||||
|
currentPodHash: 6b566f47b7
|
||||||
|
currentStepHash: 6567fc959c
|
||||||
|
currentStepIndex: 3
|
||||||
|
observedGeneration: 6df79499bc
|
||||||
|
readyReplicas: 5
|
||||||
|
replicas: 5
|
||||||
|
selector: app=guestbook
|
||||||
|
updatedReplicas: 2
|
||||||
71
resource_customizations/argoproj.io/Rollout/testdata/canary/suspended_pausedStep.yaml
vendored
Normal file
71
resource_customizations/argoproj.io/Rollout/testdata/canary/suspended_pausedStep.yaml
vendored
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Rollout
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
kubectl.kubernetes.io/last-applied-configuration: |
|
||||||
|
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"name":"example-rollout-canary","namespace":"default"},"spec":{"minReadySeconds":30,"replicas":5,"revisionHistoryLimit":3,"selector":{"matchLabels":{"app":"guestbook"}},"strategy":{"canary":{"steps":[{"setWeight":20},{"pause":{"duration":20}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook","ports":[{"containerPort":80}]}]}}}}
|
||||||
|
rollout.argoproj.io/revision: "2"
|
||||||
|
clusterName: ""
|
||||||
|
creationTimestamp: 2019-04-26T20:17:43Z
|
||||||
|
generation: 1
|
||||||
|
name: example-rollout-canary
|
||||||
|
namespace: default
|
||||||
|
resourceVersion: "696597"
|
||||||
|
selfLink: /apis/argoproj.io/v1alpha1/namespaces/default/rollouts/example-rollout-canary
|
||||||
|
uid: 58f6f1bb-6860-11e9-b8d7-025000000001
|
||||||
|
spec:
|
||||||
|
minReadySeconds: 30
|
||||||
|
paused: true
|
||||||
|
replicas: 5
|
||||||
|
revisionHistoryLimit: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: guestbook
|
||||||
|
strategy:
|
||||||
|
canary:
|
||||||
|
steps:
|
||||||
|
- setWeight: 20
|
||||||
|
- pause:
|
||||||
|
duration: 20
|
||||||
|
- setWeight: 40
|
||||||
|
- pause: {}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
app: guestbook
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
||||||
|
name: guestbook
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
resources: {}
|
||||||
|
status:
|
||||||
|
HPAReplicas: 5
|
||||||
|
availableReplicas: 5
|
||||||
|
blueGreen: {}
|
||||||
|
canary:
|
||||||
|
stableRS: df986d68
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: 2019-04-26T20:18:05Z
|
||||||
|
lastUpdateTime: 2019-04-26T20:18:05Z
|
||||||
|
message: Rollout is not serving traffic from the active service.
|
||||||
|
reason: Available
|
||||||
|
status: "False"
|
||||||
|
type: Available
|
||||||
|
- lastTransitionTime: 2019-04-26T20:18:38Z
|
||||||
|
lastUpdateTime: 2019-04-26T20:18:38Z
|
||||||
|
message: Rollout is paused
|
||||||
|
reason: RolloutPaused
|
||||||
|
status: Unknown
|
||||||
|
type: Progressing
|
||||||
|
currentPodHash: 6b566f47b7
|
||||||
|
currentStepHash: 6567fc959c
|
||||||
|
currentStepIndex: 1
|
||||||
|
observedGeneration: 5c788f4484
|
||||||
|
pauseStartTime: 2019-04-26T20:18:38Z
|
||||||
|
readyReplicas: 5
|
||||||
|
replicas: 5
|
||||||
|
selector: app=guestbook
|
||||||
|
updatedReplicas: 1
|
||||||
84
resource_customizations/argoproj.io/Rollout/testdata/degraded_rolloutTimeout.yaml
vendored
Normal file
84
resource_customizations/argoproj.io/Rollout/testdata/degraded_rolloutTimeout.yaml
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Rollout
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rollout.argoproj.io/revision: '4'
|
||||||
|
clusterName: ''
|
||||||
|
creationTimestamp: '2019-04-29T21:37:38Z'
|
||||||
|
generation: 1
|
||||||
|
labels:
|
||||||
|
app: helm-guestbook
|
||||||
|
app.kubernetes.io/instance: guestbook-bluegreen
|
||||||
|
chart: helm-guestbook-0.1.0
|
||||||
|
heritage: Tiller
|
||||||
|
release: guestbook-bluegreen
|
||||||
|
name: guestbook-bluegreen-helm-guestbook
|
||||||
|
namespace: default
|
||||||
|
selfLink: >-
|
||||||
|
/apis/argoproj.io/v1alpha1/namespaces/default/rollouts/guestbook-bluegreen-helm-guestbook
|
||||||
|
spec:
|
||||||
|
minReadySeconds: 0
|
||||||
|
progressDeadlineSeconds: 32
|
||||||
|
replicas: 3
|
||||||
|
revisionHistoryLimit: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: helm-guestbook
|
||||||
|
release: guestbook-bluegreen
|
||||||
|
strategy:
|
||||||
|
blueGreen:
|
||||||
|
activeService: guestbook-bluegreen-helm-guestbook
|
||||||
|
previewReplicaCount: 1
|
||||||
|
previewService: guestbook-bluegreen-helm-guestbook-preview
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
app: helm-guestbook
|
||||||
|
release: guestbook-bluegreen
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: 'gcr.io/heptio-images/ks-guestbook-demo:0.3'
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: http
|
||||||
|
name: helm-guestbook
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
name: http
|
||||||
|
protocol: TCP
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: http
|
||||||
|
resources: {}
|
||||||
|
status:
|
||||||
|
HPAReplicas: 3
|
||||||
|
availableReplicas: 3
|
||||||
|
blueGreen:
|
||||||
|
activeSelector: 8464d8564d
|
||||||
|
canary: {}
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: '2019-05-01T17:52:59Z'
|
||||||
|
lastUpdateTime: '2019-05-01T17:52:59Z'
|
||||||
|
message: Rollout has minimum availability
|
||||||
|
reason: AvailableReason
|
||||||
|
status: 'True'
|
||||||
|
type: Available
|
||||||
|
- lastTransitionTime: '2019-05-01T21:36:03Z'
|
||||||
|
lastUpdateTime: '2019-05-01T21:36:03Z'
|
||||||
|
message: >-
|
||||||
|
ReplicaSet "guestbook-bluegreen-helm-guestbook-6b8cf6f7db" has timed out
|
||||||
|
progressing.
|
||||||
|
reason: ProgressDeadlineExceeded
|
||||||
|
status: 'False'
|
||||||
|
type: Progressing
|
||||||
|
currentPodHash: 6b8cf6f7db
|
||||||
|
observedGeneration: 7bcdbf7bd9
|
||||||
|
readyReplicas: 3
|
||||||
|
replicas: 4
|
||||||
|
selector: >-
|
||||||
|
app=helm-guestbook,release=guestbook-bluegreen,rollouts-pod-template-hash=8464d8564d
|
||||||
|
updatedReplicas: 1
|
||||||
56
resource_customizations/argoproj.io/Rollout/testdata/v0.2_suspended_servingPreviewService.yaml
vendored
Normal file
56
resource_customizations/argoproj.io/Rollout/testdata/v0.2_suspended_servingPreviewService.yaml
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Rollout
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rollout.argoproj.io/revision: "7"
|
||||||
|
clusterName: ""
|
||||||
|
creationTimestamp: 2019-01-22T16:52:54Z
|
||||||
|
generation: 1
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/instance: guestbook-default
|
||||||
|
name: ks-guestbook-ui
|
||||||
|
namespace: default
|
||||||
|
resourceVersion: "164113"
|
||||||
|
selfLink: /apis/argoproj.io/v1alpha1/namespaces/default/rollouts/ks-guestbook-ui
|
||||||
|
uid: 29802403-1e66-11e9-a6a4-025000000001
|
||||||
|
spec:
|
||||||
|
minReadySeconds: 30
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: ks-guestbook-ui
|
||||||
|
strategy:
|
||||||
|
blueGreen:
|
||||||
|
activeService: ks-guestbook-ui-active
|
||||||
|
previewService: ks-guestbook-ui-preview
|
||||||
|
type: BlueGreenUpdate
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
app: ks-guestbook-ui
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: gcr.io/heptio-images/ks-guestbook-demo:0.1
|
||||||
|
name: ks-guestbook-ui
|
||||||
|
ports:
|
||||||
|
- containerPort: 83
|
||||||
|
resources: {}
|
||||||
|
status:
|
||||||
|
blueGreen:
|
||||||
|
activeSelector: 85f9884f5d
|
||||||
|
previewSelector: 697fb9575c
|
||||||
|
availableReplicas: 6
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: 2019-01-25T07:44:26Z
|
||||||
|
lastUpdateTime: 2019-01-25T07:44:26Z
|
||||||
|
message: Rollout is serving traffic from the active service.
|
||||||
|
reason: Available
|
||||||
|
status: "True"
|
||||||
|
type: Available
|
||||||
|
currentPodHash: 697fb9575c
|
||||||
|
observedGeneration: 767f98959f
|
||||||
|
readyReplicas: 6
|
||||||
|
replicas: 6
|
||||||
|
updatedReplicas: 3
|
||||||
|
verifyingPreview: true
|
||||||
@@ -19,11 +19,12 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||||
v1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
|
||||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||||
"github.com/argoproj/argo-cd/reposerver"
|
"github.com/argoproj/argo-cd/reposerver"
|
||||||
"github.com/argoproj/argo-cd/reposerver/repository"
|
"github.com/argoproj/argo-cd/reposerver/repository"
|
||||||
@@ -163,8 +164,10 @@ func (s *Server) GetManifests(ctx context.Context, q *ApplicationManifestQuery)
|
|||||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, appRBACName(*a)); err != nil {
|
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, appRBACName(*a)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
repo := s.getRepo(ctx, a.Spec.Source.RepoURL)
|
repo, err := s.db.GetRepository(ctx, a.Spec.Source.RepoURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
conn, repoClient, err := s.repoClientset.NewRepoServerClient()
|
conn, repoClient, err := s.repoClientset.NewRepoServerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -406,9 +409,9 @@ func (s *Server) Delete(ctx context.Context, q *ApplicationDeleteRequest) (*Appl
|
|||||||
}
|
}
|
||||||
|
|
||||||
if patchFinalizer {
|
if patchFinalizer {
|
||||||
// Prior to v0.6, the cascaded deletion finalizer was set during app creation.
|
// Although the cascaded deletion finalizer is not set when apps are created via API,
|
||||||
// For backward compatibility, we always calculate the patch to see if we need to
|
// they will often be set by the user as part of declarative config. As part of a delete
|
||||||
// set/unset the finalizer (in case we are dealing with an app created prior to v0.6)
|
// request, we always calculate the patch to see if we need to set/unset the finalizer.
|
||||||
patch, err := json.Marshal(map[string]interface{}{
|
patch, err := json.Marshal(map[string]interface{}{
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
"finalizers": a.Finalizers,
|
"finalizers": a.Finalizers,
|
||||||
@@ -433,33 +436,62 @@ func (s *Server) Delete(ctx context.Context, q *ApplicationDeleteRequest) (*Appl
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Watch(q *ApplicationQuery, ws ApplicationService_WatchServer) error {
|
func (s *Server) Watch(q *ApplicationQuery, ws ApplicationService_WatchServer) error {
|
||||||
w, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Watch(metav1.ListOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer w.Stop()
|
|
||||||
logCtx := log.NewEntry(log.New())
|
logCtx := log.NewEntry(log.New())
|
||||||
if q.Name != nil {
|
if q.Name != nil {
|
||||||
logCtx = logCtx.WithField("application", *q.Name)
|
logCtx = logCtx.WithField("application", *q.Name)
|
||||||
}
|
}
|
||||||
claims := ws.Context().Value("claims")
|
claims := ws.Context().Value("claims")
|
||||||
|
// sendIfPermitted is a helper to send the application to the client's streaming channel if the
|
||||||
|
// caller has RBAC privileges permissions to view it
|
||||||
|
sendIfPermitted := func(a appv1.Application, eventType watch.EventType) error {
|
||||||
|
if !s.enf.Enforce(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, appRBACName(a)) {
|
||||||
|
// do not emit apps user does not have accessing
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := ws.Send(&appv1.ApplicationWatchEvent{
|
||||||
|
Type: eventType,
|
||||||
|
Application: a,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logCtx.Warnf("Unable to send stream message: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var listOpts metav1.ListOptions
|
||||||
|
if q.Name != nil && *q.Name != "" {
|
||||||
|
listOpts.FieldSelector = fmt.Sprintf("metadata.name=%s", *q.Name)
|
||||||
|
}
|
||||||
|
listOpts.ResourceVersion = q.ResourceVersion
|
||||||
|
if listOpts.ResourceVersion == "" {
|
||||||
|
// If resourceVersion is not supplied, we need to get latest version of the apps by first
|
||||||
|
// making a list request, which we then supply to the watch request. We always need to
|
||||||
|
// supply a resourceVersion to watch requests since without it, the return values may return
|
||||||
|
// stale data. See: https://github.com/argoproj/argo-cd/issues/1605
|
||||||
|
appsList, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).List(listOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, a := range appsList.Items {
|
||||||
|
err = sendIfPermitted(a, watch.Modified)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listOpts.ResourceVersion = appsList.ResourceVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := s.appclientset.ArgoprojV1alpha1().Applications(s.ns).Watch(listOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer w.Stop()
|
||||||
done := make(chan bool)
|
done := make(chan bool)
|
||||||
go func() {
|
go func() {
|
||||||
for next := range w.ResultChan() {
|
for next := range w.ResultChan() {
|
||||||
a := *next.Object.(*appv1.Application)
|
a := *next.Object.(*appv1.Application)
|
||||||
if q.Name == nil || *q.Name == "" || *q.Name == a.Name {
|
_ = sendIfPermitted(a, next.Type)
|
||||||
if !s.enf.Enforce(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, appRBACName(a)) {
|
|
||||||
// do not emit apps user does not have accessing
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = ws.Send(&appv1.ApplicationWatchEvent{
|
|
||||||
Type: next.Type,
|
|
||||||
Application: a,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
logCtx.Warnf("Unable to send stream message: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
logCtx.Info("k8s application watch event channel closed")
|
logCtx.Info("k8s application watch event channel closed")
|
||||||
close(done)
|
close(done)
|
||||||
@@ -501,7 +533,14 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conditions, appSourceType, err := argo.GetSpecErrors(ctx, &app.Spec, proj, s.repoClientset, s.db)
|
conditions, err := argo.ValidatePermissions(ctx, &app.Spec, proj, s.db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(conditions) > 0 {
|
||||||
|
return status.Errorf(codes.InvalidArgument, "application spec is invalid: %s", argo.FormatAppConditions(conditions))
|
||||||
|
}
|
||||||
|
conditions, appSourceType, err := argo.ValidateRepo(ctx, &app.Spec, s.repoClientset, s.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -772,15 +811,6 @@ func (s *Server) getApplicationDestination(ctx context.Context, name string) (st
|
|||||||
return server, namespace, nil
|
return server, namespace, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getRepo(ctx context.Context, repoURL string) *appv1.Repository {
|
|
||||||
repo, err := s.db.GetRepository(ctx, repoURL)
|
|
||||||
if err != nil {
|
|
||||||
// If we couldn't retrieve from the repo service, assume public repositories
|
|
||||||
repo = &appv1.Repository{Repo: repoURL}
|
|
||||||
}
|
|
||||||
return repo
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync syncs an application to its target state
|
// Sync syncs an application to its target state
|
||||||
func (s *Server) Sync(ctx context.Context, syncReq *ApplicationSyncRequest) (*appv1.Application, error) {
|
func (s *Server) Sync(ctx context.Context, syncReq *ApplicationSyncRequest) (*appv1.Application, error) {
|
||||||
appIf := s.appclientset.ArgoprojV1alpha1().Applications(s.ns)
|
appIf := s.appclientset.ArgoprojV1alpha1().Applications(s.ns)
|
||||||
@@ -795,7 +825,7 @@ func (s *Server) Sync(ctx context.Context, syncReq *ApplicationSyncRequest) (*ap
|
|||||||
return nil, status.Errorf(codes.FailedPrecondition, "application is deleting")
|
return nil, status.Errorf(codes.FailedPrecondition, "application is deleting")
|
||||||
}
|
}
|
||||||
if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.Automated != nil {
|
if a.Spec.SyncPolicy != nil && a.Spec.SyncPolicy.Automated != nil {
|
||||||
if syncReq.Revision != "" && syncReq.Revision != a.Spec.Source.TargetRevision {
|
if syncReq.Revision != "" && syncReq.Revision != util.FirstNonEmpty(a.Spec.Source.TargetRevision, "HEAD") {
|
||||||
return nil, status.Errorf(codes.FailedPrecondition, "Cannot sync to %s: auto-sync currently set to %s", syncReq.Revision, a.Spec.Source.TargetRevision)
|
return nil, status.Errorf(codes.FailedPrecondition, "Cannot sync to %s: auto-sync currently set to %s", syncReq.Revision, a.Spec.Source.TargetRevision)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -887,8 +917,7 @@ func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, sy
|
|||||||
}
|
}
|
||||||
repo, err := s.db.GetRepository(ctx, app.Spec.Source.RepoURL)
|
repo, err := s.db.GetRepository(ctx, app.Spec.Source.RepoURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If we couldn't retrieve from the repo service, assume public repositories
|
return "", "", err
|
||||||
repo = &appv1.Repository{Repo: app.Spec.Source.RepoURL}
|
|
||||||
}
|
}
|
||||||
gitClient, err := s.gitFactory.NewClient(repo.Repo, "", repo.Username, repo.Password, repo.SSHPrivateKey, repo.InsecureIgnoreHostKey)
|
gitClient, err := s.gitFactory.NewClient(repo.Repo, "", repo.Username, repo.Password, repo.SSHPrivateKey, repo.InsecureIgnoreHostKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ type ApplicationQuery struct {
|
|||||||
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||||
Refresh *string `protobuf:"bytes,2,opt,name=refresh" json:"refresh,omitempty"`
|
Refresh *string `protobuf:"bytes,2,opt,name=refresh" json:"refresh,omitempty"`
|
||||||
Projects []string `protobuf:"bytes,3,rep,name=project" json:"project,omitempty"`
|
Projects []string `protobuf:"bytes,3,rep,name=project" json:"project,omitempty"`
|
||||||
|
ResourceVersion string `protobuf:"bytes,4,opt,name=resourceVersion" json:"resourceVersion"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
@@ -51,7 +52,7 @@ func (m *ApplicationQuery) Reset() { *m = ApplicationQuery{} }
|
|||||||
func (m *ApplicationQuery) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationQuery) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationQuery) ProtoMessage() {}
|
func (*ApplicationQuery) ProtoMessage() {}
|
||||||
func (*ApplicationQuery) Descriptor() ([]byte, []int) {
|
func (*ApplicationQuery) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{0}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{0}
|
||||||
}
|
}
|
||||||
func (m *ApplicationQuery) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationQuery) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -101,6 +102,13 @@ func (m *ApplicationQuery) GetProjects() []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *ApplicationQuery) GetResourceVersion() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.ResourceVersion
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// ApplicationEventsQuery is a query for application resource events
|
// ApplicationEventsQuery is a query for application resource events
|
||||||
type ApplicationResourceEventsQuery struct {
|
type ApplicationResourceEventsQuery struct {
|
||||||
Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
|
Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
|
||||||
@@ -116,7 +124,7 @@ func (m *ApplicationResourceEventsQuery) Reset() { *m = ApplicationResou
|
|||||||
func (m *ApplicationResourceEventsQuery) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationResourceEventsQuery) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationResourceEventsQuery) ProtoMessage() {}
|
func (*ApplicationResourceEventsQuery) ProtoMessage() {}
|
||||||
func (*ApplicationResourceEventsQuery) Descriptor() ([]byte, []int) {
|
func (*ApplicationResourceEventsQuery) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{1}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{1}
|
||||||
}
|
}
|
||||||
func (m *ApplicationResourceEventsQuery) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationResourceEventsQuery) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -186,7 +194,7 @@ func (m *ApplicationManifestQuery) Reset() { *m = ApplicationManifestQue
|
|||||||
func (m *ApplicationManifestQuery) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationManifestQuery) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationManifestQuery) ProtoMessage() {}
|
func (*ApplicationManifestQuery) ProtoMessage() {}
|
||||||
func (*ApplicationManifestQuery) Descriptor() ([]byte, []int) {
|
func (*ApplicationManifestQuery) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{2}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{2}
|
||||||
}
|
}
|
||||||
func (m *ApplicationManifestQuery) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationManifestQuery) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -239,7 +247,7 @@ func (m *ApplicationResponse) Reset() { *m = ApplicationResponse{} }
|
|||||||
func (m *ApplicationResponse) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationResponse) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationResponse) ProtoMessage() {}
|
func (*ApplicationResponse) ProtoMessage() {}
|
||||||
func (*ApplicationResponse) Descriptor() ([]byte, []int) {
|
func (*ApplicationResponse) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{3}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{3}
|
||||||
}
|
}
|
||||||
func (m *ApplicationResponse) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationResponse) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -280,7 +288,7 @@ func (m *ApplicationCreateRequest) Reset() { *m = ApplicationCreateReque
|
|||||||
func (m *ApplicationCreateRequest) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationCreateRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationCreateRequest) ProtoMessage() {}
|
func (*ApplicationCreateRequest) ProtoMessage() {}
|
||||||
func (*ApplicationCreateRequest) Descriptor() ([]byte, []int) {
|
func (*ApplicationCreateRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{4}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{4}
|
||||||
}
|
}
|
||||||
func (m *ApplicationCreateRequest) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationCreateRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -334,7 +342,7 @@ func (m *ApplicationUpdateRequest) Reset() { *m = ApplicationUpdateReque
|
|||||||
func (m *ApplicationUpdateRequest) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationUpdateRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationUpdateRequest) ProtoMessage() {}
|
func (*ApplicationUpdateRequest) ProtoMessage() {}
|
||||||
func (*ApplicationUpdateRequest) Descriptor() ([]byte, []int) {
|
func (*ApplicationUpdateRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{5}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{5}
|
||||||
}
|
}
|
||||||
func (m *ApplicationUpdateRequest) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationUpdateRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -382,7 +390,7 @@ func (m *ApplicationDeleteRequest) Reset() { *m = ApplicationDeleteReque
|
|||||||
func (m *ApplicationDeleteRequest) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationDeleteRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationDeleteRequest) ProtoMessage() {}
|
func (*ApplicationDeleteRequest) ProtoMessage() {}
|
||||||
func (*ApplicationDeleteRequest) Descriptor() ([]byte, []int) {
|
func (*ApplicationDeleteRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{6}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{6}
|
||||||
}
|
}
|
||||||
func (m *ApplicationDeleteRequest) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationDeleteRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -442,7 +450,7 @@ func (m *ApplicationSyncRequest) Reset() { *m = ApplicationSyncRequest{}
|
|||||||
func (m *ApplicationSyncRequest) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationSyncRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationSyncRequest) ProtoMessage() {}
|
func (*ApplicationSyncRequest) ProtoMessage() {}
|
||||||
func (*ApplicationSyncRequest) Descriptor() ([]byte, []int) {
|
func (*ApplicationSyncRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{7}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{7}
|
||||||
}
|
}
|
||||||
func (m *ApplicationSyncRequest) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationSyncRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -526,7 +534,7 @@ func (m *ApplicationUpdateSpecRequest) Reset() { *m = ApplicationUpdateS
|
|||||||
func (m *ApplicationUpdateSpecRequest) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationUpdateSpecRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationUpdateSpecRequest) ProtoMessage() {}
|
func (*ApplicationUpdateSpecRequest) ProtoMessage() {}
|
||||||
func (*ApplicationUpdateSpecRequest) Descriptor() ([]byte, []int) {
|
func (*ApplicationUpdateSpecRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{8}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{8}
|
||||||
}
|
}
|
||||||
func (m *ApplicationUpdateSpecRequest) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationUpdateSpecRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -582,7 +590,7 @@ func (m *ApplicationPatchRequest) Reset() { *m = ApplicationPatchRequest
|
|||||||
func (m *ApplicationPatchRequest) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationPatchRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationPatchRequest) ProtoMessage() {}
|
func (*ApplicationPatchRequest) ProtoMessage() {}
|
||||||
func (*ApplicationPatchRequest) Descriptor() ([]byte, []int) {
|
func (*ApplicationPatchRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{9}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{9}
|
||||||
}
|
}
|
||||||
func (m *ApplicationPatchRequest) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationPatchRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -639,7 +647,7 @@ func (m *ApplicationRollbackRequest) Reset() { *m = ApplicationRollbackR
|
|||||||
func (m *ApplicationRollbackRequest) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationRollbackRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationRollbackRequest) ProtoMessage() {}
|
func (*ApplicationRollbackRequest) ProtoMessage() {}
|
||||||
func (*ApplicationRollbackRequest) Descriptor() ([]byte, []int) {
|
func (*ApplicationRollbackRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{10}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{10}
|
||||||
}
|
}
|
||||||
func (m *ApplicationRollbackRequest) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationRollbackRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -712,7 +720,7 @@ func (m *ApplicationResourceRequest) Reset() { *m = ApplicationResourceR
|
|||||||
func (m *ApplicationResourceRequest) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationResourceRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationResourceRequest) ProtoMessage() {}
|
func (*ApplicationResourceRequest) ProtoMessage() {}
|
||||||
func (*ApplicationResourceRequest) Descriptor() ([]byte, []int) {
|
func (*ApplicationResourceRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{11}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{11}
|
||||||
}
|
}
|
||||||
func (m *ApplicationResourceRequest) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationResourceRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -801,7 +809,7 @@ func (m *ApplicationResourcePatchRequest) Reset() { *m = ApplicationReso
|
|||||||
func (m *ApplicationResourcePatchRequest) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationResourcePatchRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationResourcePatchRequest) ProtoMessage() {}
|
func (*ApplicationResourcePatchRequest) ProtoMessage() {}
|
||||||
func (*ApplicationResourcePatchRequest) Descriptor() ([]byte, []int) {
|
func (*ApplicationResourcePatchRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{12}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{12}
|
||||||
}
|
}
|
||||||
func (m *ApplicationResourcePatchRequest) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationResourcePatchRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -903,7 +911,7 @@ func (m *ApplicationResourceDeleteRequest) Reset() { *m = ApplicationRes
|
|||||||
func (m *ApplicationResourceDeleteRequest) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationResourceDeleteRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationResourceDeleteRequest) ProtoMessage() {}
|
func (*ApplicationResourceDeleteRequest) ProtoMessage() {}
|
||||||
func (*ApplicationResourceDeleteRequest) Descriptor() ([]byte, []int) {
|
func (*ApplicationResourceDeleteRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{13}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{13}
|
||||||
}
|
}
|
||||||
func (m *ApplicationResourceDeleteRequest) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationResourceDeleteRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -998,7 +1006,7 @@ func (m *ResourceActionRunRequest) Reset() { *m = ResourceActionRunReque
|
|||||||
func (m *ResourceActionRunRequest) String() string { return proto.CompactTextString(m) }
|
func (m *ResourceActionRunRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ResourceActionRunRequest) ProtoMessage() {}
|
func (*ResourceActionRunRequest) ProtoMessage() {}
|
||||||
func (*ResourceActionRunRequest) Descriptor() ([]byte, []int) {
|
func (*ResourceActionRunRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{14}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{14}
|
||||||
}
|
}
|
||||||
func (m *ResourceActionRunRequest) XXX_Unmarshal(b []byte) error {
|
func (m *ResourceActionRunRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -1087,7 +1095,7 @@ func (m *ResourceActionsListResponse) Reset() { *m = ResourceActionsList
|
|||||||
func (m *ResourceActionsListResponse) String() string { return proto.CompactTextString(m) }
|
func (m *ResourceActionsListResponse) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ResourceActionsListResponse) ProtoMessage() {}
|
func (*ResourceActionsListResponse) ProtoMessage() {}
|
||||||
func (*ResourceActionsListResponse) Descriptor() ([]byte, []int) {
|
func (*ResourceActionsListResponse) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{15}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{15}
|
||||||
}
|
}
|
||||||
func (m *ResourceActionsListResponse) XXX_Unmarshal(b []byte) error {
|
func (m *ResourceActionsListResponse) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -1134,7 +1142,7 @@ func (m *ApplicationResourceResponse) Reset() { *m = ApplicationResource
|
|||||||
func (m *ApplicationResourceResponse) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationResourceResponse) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationResourceResponse) ProtoMessage() {}
|
func (*ApplicationResourceResponse) ProtoMessage() {}
|
||||||
func (*ApplicationResourceResponse) Descriptor() ([]byte, []int) {
|
func (*ApplicationResourceResponse) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{16}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{16}
|
||||||
}
|
}
|
||||||
func (m *ApplicationResourceResponse) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationResourceResponse) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -1188,7 +1196,7 @@ func (m *ApplicationPodLogsQuery) Reset() { *m = ApplicationPodLogsQuery
|
|||||||
func (m *ApplicationPodLogsQuery) String() string { return proto.CompactTextString(m) }
|
func (m *ApplicationPodLogsQuery) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ApplicationPodLogsQuery) ProtoMessage() {}
|
func (*ApplicationPodLogsQuery) ProtoMessage() {}
|
||||||
func (*ApplicationPodLogsQuery) Descriptor() ([]byte, []int) {
|
func (*ApplicationPodLogsQuery) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{17}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{17}
|
||||||
}
|
}
|
||||||
func (m *ApplicationPodLogsQuery) XXX_Unmarshal(b []byte) error {
|
func (m *ApplicationPodLogsQuery) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -1285,7 +1293,7 @@ func (m *LogEntry) Reset() { *m = LogEntry{} }
|
|||||||
func (m *LogEntry) String() string { return proto.CompactTextString(m) }
|
func (m *LogEntry) String() string { return proto.CompactTextString(m) }
|
||||||
func (*LogEntry) ProtoMessage() {}
|
func (*LogEntry) ProtoMessage() {}
|
||||||
func (*LogEntry) Descriptor() ([]byte, []int) {
|
func (*LogEntry) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{18}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{18}
|
||||||
}
|
}
|
||||||
func (m *LogEntry) XXX_Unmarshal(b []byte) error {
|
func (m *LogEntry) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -1339,7 +1347,7 @@ func (m *OperationTerminateRequest) Reset() { *m = OperationTerminateReq
|
|||||||
func (m *OperationTerminateRequest) String() string { return proto.CompactTextString(m) }
|
func (m *OperationTerminateRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*OperationTerminateRequest) ProtoMessage() {}
|
func (*OperationTerminateRequest) ProtoMessage() {}
|
||||||
func (*OperationTerminateRequest) Descriptor() ([]byte, []int) {
|
func (*OperationTerminateRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{19}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{19}
|
||||||
}
|
}
|
||||||
func (m *OperationTerminateRequest) XXX_Unmarshal(b []byte) error {
|
func (m *OperationTerminateRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -1385,7 +1393,7 @@ func (m *OperationTerminateResponse) Reset() { *m = OperationTerminateRe
|
|||||||
func (m *OperationTerminateResponse) String() string { return proto.CompactTextString(m) }
|
func (m *OperationTerminateResponse) String() string { return proto.CompactTextString(m) }
|
||||||
func (*OperationTerminateResponse) ProtoMessage() {}
|
func (*OperationTerminateResponse) ProtoMessage() {}
|
||||||
func (*OperationTerminateResponse) Descriptor() ([]byte, []int) {
|
func (*OperationTerminateResponse) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{20}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{20}
|
||||||
}
|
}
|
||||||
func (m *OperationTerminateResponse) XXX_Unmarshal(b []byte) error {
|
func (m *OperationTerminateResponse) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -1425,7 +1433,7 @@ func (m *ResourcesQuery) Reset() { *m = ResourcesQuery{} }
|
|||||||
func (m *ResourcesQuery) String() string { return proto.CompactTextString(m) }
|
func (m *ResourcesQuery) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ResourcesQuery) ProtoMessage() {}
|
func (*ResourcesQuery) ProtoMessage() {}
|
||||||
func (*ResourcesQuery) Descriptor() ([]byte, []int) {
|
func (*ResourcesQuery) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{21}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{21}
|
||||||
}
|
}
|
||||||
func (m *ResourcesQuery) XXX_Unmarshal(b []byte) error {
|
func (m *ResourcesQuery) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -1472,7 +1480,7 @@ func (m *ManagedResourcesResponse) Reset() { *m = ManagedResourcesRespon
|
|||||||
func (m *ManagedResourcesResponse) String() string { return proto.CompactTextString(m) }
|
func (m *ManagedResourcesResponse) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ManagedResourcesResponse) ProtoMessage() {}
|
func (*ManagedResourcesResponse) ProtoMessage() {}
|
||||||
func (*ManagedResourcesResponse) Descriptor() ([]byte, []int) {
|
func (*ManagedResourcesResponse) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_application_c1cee2c3bb672fdc, []int{22}
|
return fileDescriptor_application_a312b6d468f6c22a, []int{22}
|
||||||
}
|
}
|
||||||
func (m *ManagedResourcesResponse) XXX_Unmarshal(b []byte) error {
|
func (m *ManagedResourcesResponse) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -2397,6 +2405,10 @@ func (m *ApplicationQuery) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
i += copy(dAtA[i:], s)
|
i += copy(dAtA[i:], s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dAtA[i] = 0x22
|
||||||
|
i++
|
||||||
|
i = encodeVarintApplication(dAtA, i, uint64(len(m.ResourceVersion)))
|
||||||
|
i += copy(dAtA[i:], m.ResourceVersion)
|
||||||
if m.XXX_unrecognized != nil {
|
if m.XXX_unrecognized != nil {
|
||||||
i += copy(dAtA[i:], m.XXX_unrecognized)
|
i += copy(dAtA[i:], m.XXX_unrecognized)
|
||||||
}
|
}
|
||||||
@@ -3314,6 +3326,8 @@ func (m *ApplicationQuery) Size() (n int) {
|
|||||||
n += 1 + l + sovApplication(uint64(l))
|
n += 1 + l + sovApplication(uint64(l))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
l = len(m.ResourceVersion)
|
||||||
|
n += 1 + l + sovApplication(uint64(l))
|
||||||
if m.XXX_unrecognized != nil {
|
if m.XXX_unrecognized != nil {
|
||||||
n += len(m.XXX_unrecognized)
|
n += len(m.XXX_unrecognized)
|
||||||
}
|
}
|
||||||
@@ -3828,6 +3842,35 @@ func (m *ApplicationQuery) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
m.Projects = append(m.Projects, string(dAtA[iNdEx:postIndex]))
|
m.Projects = append(m.Projects, string(dAtA[iNdEx:postIndex]))
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
|
case 4:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field ResourceVersion", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowApplication
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthApplication
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.ResourceVersion = string(dAtA[iNdEx:postIndex])
|
||||||
|
iNdEx = postIndex
|
||||||
default:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skipApplication(dAtA[iNdEx:])
|
skippy, err := skipApplication(dAtA[iNdEx:])
|
||||||
@@ -7221,121 +7264,121 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterFile("server/application/application.proto", fileDescriptor_application_c1cee2c3bb672fdc)
|
proto.RegisterFile("server/application/application.proto", fileDescriptor_application_a312b6d468f6c22a)
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileDescriptor_application_c1cee2c3bb672fdc = []byte{
|
var fileDescriptor_application_a312b6d468f6c22a = []byte{
|
||||||
// 1779 bytes of a gzipped FileDescriptorProto
|
// 1792 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0xcd, 0x6f, 0xdc, 0xc6,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0xcd, 0x6f, 0xdc, 0x5a,
|
||||||
0x15, 0xef, 0xec, 0xae, 0x76, 0x57, 0x4f, 0xaa, 0x6b, 0x8f, 0x3f, 0x4a, 0x53, 0xb2, 0xb4, 0x18,
|
0x15, 0xe7, 0xce, 0x4c, 0x66, 0x26, 0x27, 0xe1, 0x7d, 0xdc, 0xf7, 0x5e, 0xf1, 0x73, 0xd2, 0x64,
|
||||||
0xcb, 0xb2, 0x2c, 0x5b, 0xa4, 0xa5, 0x1a, 0xad, 0x21, 0x18, 0xb0, 0xad, 0xca, 0x95, 0x55, 0xc8,
|
0x74, 0x9b, 0xa6, 0x69, 0x5e, 0x63, 0x37, 0xa1, 0x82, 0xa7, 0xe8, 0x49, 0xaf, 0x0d, 0x29, 0x69,
|
||||||
0xaa, 0xba, 0x92, 0x51, 0xa0, 0x40, 0x51, 0xd0, 0xe4, 0x68, 0xc5, 0x6a, 0x97, 0x64, 0x49, 0xee,
|
0x50, 0x1a, 0xc2, 0x24, 0x05, 0x09, 0x09, 0x21, 0xd7, 0xbe, 0x99, 0x98, 0xcc, 0xd8, 0xc6, 0xf6,
|
||||||
0x16, 0x5b, 0xc3, 0x87, 0x1a, 0x45, 0xd1, 0x43, 0x90, 0x20, 0x48, 0x0e, 0x4e, 0x90, 0x2f, 0xf8,
|
0x0c, 0x1a, 0xaa, 0x2e, 0xa8, 0x10, 0x62, 0x81, 0x40, 0x08, 0x16, 0x05, 0xf1, 0xa5, 0xae, 0xd9,
|
||||||
0x9c, 0x5b, 0x90, 0x4b, 0x0e, 0xb9, 0x05, 0xf0, 0x31, 0x40, 0x72, 0x36, 0x02, 0xc5, 0x7f, 0x40,
|
0x21, 0x36, 0x2c, 0xd8, 0x21, 0x75, 0x89, 0x04, 0xeb, 0x0a, 0x85, 0xfe, 0x01, 0xac, 0x58, 0xa3,
|
||||||
0x4e, 0x39, 0x07, 0x33, 0x1c, 0x72, 0x87, 0xd2, 0x2e, 0x57, 0xb2, 0x37, 0x07, 0xdf, 0xb8, 0x6f,
|
0x7b, 0x7d, 0xaf, 0x7d, 0x9d, 0xcc, 0x78, 0x92, 0x76, 0x58, 0x74, 0xe7, 0x39, 0xf7, 0xfa, 0x9c,
|
||||||
0x86, 0xef, 0xfd, 0xde, 0xc7, 0xbc, 0x79, 0x3f, 0x2e, 0x4c, 0x05, 0xd4, 0x6f, 0x51, 0x5f, 0x37,
|
0xdf, 0xf9, 0xb8, 0xe7, 0x9e, 0x9f, 0x07, 0x16, 0x22, 0x1a, 0xf6, 0x68, 0x68, 0x5a, 0x41, 0xd0,
|
||||||
0x3c, 0xaf, 0x6e, 0x9b, 0x46, 0x68, 0xbb, 0x8e, 0xfc, 0xac, 0x79, 0xbe, 0x1b, 0xba, 0x78, 0x44,
|
0x76, 0x6d, 0x2b, 0x76, 0x7d, 0x4f, 0x7d, 0x36, 0x82, 0xd0, 0x8f, 0x7d, 0x3c, 0xa5, 0x88, 0xf4,
|
||||||
0x12, 0xa9, 0xa7, 0x6a, 0x6e, 0xcd, 0xe5, 0x72, 0x9d, 0x3d, 0x45, 0x5b, 0xd4, 0xf1, 0x9a, 0xeb,
|
0xf7, 0x5b, 0x7e, 0xcb, 0xe7, 0x72, 0x93, 0x3d, 0x25, 0x5b, 0xf4, 0xd9, 0x96, 0xef, 0xb7, 0xda,
|
||||||
0xd6, 0xea, 0x54, 0x37, 0x3c, 0x5b, 0x37, 0x1c, 0xc7, 0x0d, 0xf9, 0xe6, 0x40, 0xac, 0x92, 0xdd,
|
0xd4, 0xb4, 0x02, 0xd7, 0xb4, 0x3c, 0xcf, 0x8f, 0xf9, 0xe6, 0x48, 0xac, 0x92, 0xe3, 0x8f, 0x23,
|
||||||
0xeb, 0x81, 0x66, 0xbb, 0x7c, 0xd5, 0x74, 0x7d, 0xaa, 0xb7, 0xe6, 0xf5, 0x1a, 0x75, 0xa8, 0x6f,
|
0xc3, 0xf5, 0xf9, 0xaa, 0xed, 0x87, 0xd4, 0xec, 0xad, 0x9a, 0x2d, 0xea, 0xd1, 0xd0, 0x8a, 0xa9,
|
||||||
0x84, 0xd4, 0x12, 0x7b, 0xae, 0x75, 0xf6, 0x34, 0x0c, 0x73, 0xc7, 0x76, 0xa8, 0xdf, 0xd6, 0xbd,
|
0x23, 0xf6, 0xdc, 0xca, 0xf6, 0x74, 0x2c, 0xfb, 0xc8, 0xf5, 0x68, 0xd8, 0x37, 0x83, 0xe3, 0x16,
|
||||||
0xdd, 0x1a, 0x13, 0x04, 0x7a, 0x83, 0x86, 0x46, 0xb7, 0xb7, 0x56, 0x6b, 0x76, 0xb8, 0xd3, 0x7c,
|
0x13, 0x44, 0x66, 0x87, 0xc6, 0xd6, 0xa0, 0xb7, 0xb6, 0x5b, 0x6e, 0x7c, 0xd4, 0x7d, 0x68, 0xd8,
|
||||||
0xa0, 0x99, 0x6e, 0x43, 0x37, 0x7c, 0x0e, 0xec, 0x1f, 0xfc, 0x61, 0xce, 0xb4, 0x3a, 0x6f, 0xcb,
|
0x7e, 0xc7, 0xb4, 0x42, 0x0e, 0xec, 0x3b, 0xfc, 0x61, 0xc5, 0x76, 0xb2, 0xb7, 0x55, 0xf7, 0x7a,
|
||||||
0xee, 0xb5, 0xe6, 0x8d, 0xba, 0xb7, 0x63, 0x1c, 0x54, 0xb5, 0x94, 0xa5, 0xca, 0xa7, 0x9e, 0x2b,
|
0xab, 0x56, 0x3b, 0x38, 0xb2, 0xce, 0xaa, 0xda, 0x28, 0x52, 0x15, 0xd2, 0xc0, 0x17, 0xb1, 0xe2,
|
||||||
0x62, 0xc5, 0x1f, 0xed, 0xd0, 0xf5, 0xdb, 0xd2, 0x63, 0xa4, 0x83, 0xec, 0xc0, 0xf1, 0xdb, 0x1d,
|
0x8f, 0x6e, 0xec, 0x87, 0x7d, 0xe5, 0x31, 0xd1, 0x41, 0x9e, 0x22, 0x78, 0xe7, 0x4e, 0x66, 0xec,
|
||||||
0x5b, 0x7f, 0x6e, 0x52, 0xbf, 0x8d, 0x31, 0x14, 0x1c, 0xa3, 0x41, 0x15, 0x54, 0x41, 0x33, 0xc3,
|
0x6b, 0x5d, 0x1a, 0xf6, 0x31, 0x86, 0x8a, 0x67, 0x75, 0xa8, 0x86, 0x1a, 0x68, 0x69, 0xb2, 0xc9,
|
||||||
0x55, 0xfe, 0x8c, 0x15, 0x28, 0xf9, 0x74, 0xdb, 0xa7, 0xc1, 0x8e, 0x92, 0xe3, 0xe2, 0xf8, 0x27,
|
0x9f, 0xb1, 0x06, 0xb5, 0x90, 0x1e, 0x86, 0x34, 0x3a, 0xd2, 0x4a, 0x5c, 0x2c, 0x7f, 0xe2, 0x45,
|
||||||
0x9e, 0x86, 0x12, 0x33, 0x4c, 0xcd, 0x50, 0xc9, 0x57, 0xf2, 0x33, 0xc3, 0x4b, 0xa3, 0x7b, 0xcf,
|
0xa8, 0x31, 0xcb, 0xd4, 0x8e, 0xb5, 0x72, 0xa3, 0xbc, 0x34, 0xb9, 0x31, 0x7d, 0xf2, 0x62, 0xbe,
|
||||||
0x27, 0xcb, 0x1b, 0x91, 0x28, 0xa8, 0xc6, 0x8b, 0xe4, 0x0b, 0x04, 0x13, 0x92, 0xa9, 0x2a, 0x0d,
|
0xbe, 0x97, 0x88, 0xa2, 0xa6, 0x5c, 0xc4, 0x06, 0xbc, 0x1d, 0xd2, 0xc8, 0xef, 0x86, 0x36, 0xfd,
|
||||||
0xdc, 0xa6, 0x6f, 0xd2, 0x3b, 0x2d, 0xea, 0x84, 0xc1, 0x7e, 0xc3, 0xb9, 0xc4, 0xf0, 0x02, 0x9c,
|
0x3a, 0x0d, 0x23, 0xd7, 0xf7, 0xb4, 0x0a, 0xd3, 0xb4, 0x51, 0x79, 0xfe, 0x62, 0xfe, 0x33, 0xcd,
|
||||||
0xf0, 0xc5, 0xd6, 0x75, 0xa3, 0x41, 0x03, 0xcf, 0x30, 0xa9, 0x92, 0x63, 0x1b, 0x96, 0x0a, 0xcf,
|
0xd3, 0x8b, 0xe4, 0x2f, 0x08, 0xe6, 0x14, 0x68, 0x4d, 0xb1, 0x7c, 0xb7, 0x47, 0xbd, 0x38, 0x3a,
|
||||||
0x9e, 0x4f, 0xfe, 0xa2, 0x7a, 0x70, 0x19, 0xcf, 0xc0, 0xa8, 0x2c, 0x54, 0xf2, 0xd2, 0xf6, 0xd4,
|
0x0d, 0xb4, 0x94, 0x02, 0x5d, 0x83, 0x77, 0xa5, 0xa6, 0x5d, 0xab, 0x43, 0xa3, 0xc0, 0xb2, 0xa9,
|
||||||
0x0a, 0x9e, 0x86, 0x91, 0xf8, 0xf7, 0xfd, 0xd5, 0x65, 0xa5, 0x20, 0x6d, 0x94, 0x17, 0xc8, 0x06,
|
0x56, 0x62, 0x1b, 0x84, 0xa1, 0xb3, 0xcb, 0x78, 0x09, 0xa6, 0x55, 0xa1, 0x56, 0x56, 0xb6, 0xe7,
|
||||||
0x28, 0x12, 0xf6, 0x7b, 0x86, 0x63, 0x6f, 0xd3, 0x20, 0xec, 0x8d, 0xba, 0x02, 0x65, 0x9f, 0xb6,
|
0x56, 0xf0, 0x22, 0x4c, 0xc9, 0xdf, 0x0f, 0xb6, 0x37, 0xb5, 0x8a, 0xb2, 0x51, 0x5d, 0x20, 0x7b,
|
||||||
0xec, 0xc0, 0x76, 0x9d, 0x28, 0x5e, 0x42, 0x69, 0x22, 0x25, 0xa7, 0xe1, 0x64, 0x3a, 0x1a, 0x9e,
|
0xa0, 0x29, 0xd8, 0xef, 0x5b, 0x9e, 0x7b, 0x48, 0xa3, 0x78, 0x38, 0xea, 0x06, 0xd4, 0x43, 0xda,
|
||||||
0xeb, 0x04, 0x94, 0x3c, 0x45, 0x29, 0x4b, 0xbf, 0xf7, 0xa9, 0x11, 0xd2, 0x2a, 0xfd, 0x67, 0x93,
|
0x73, 0x79, 0x54, 0x4a, 0x4a, 0x54, 0x52, 0x29, 0xf9, 0x00, 0xde, 0xcb, 0x47, 0x23, 0xf0, 0xbd,
|
||||||
0x06, 0x21, 0x76, 0x40, 0x2e, 0x6c, 0x6e, 0x70, 0x64, 0xe1, 0x0f, 0x5a, 0xa7, 0x0c, 0xb4, 0xb8,
|
0x88, 0x92, 0x67, 0x28, 0x67, 0xe9, 0x4b, 0x21, 0xb5, 0x62, 0xda, 0xa4, 0xdf, 0xed, 0xd2, 0x28,
|
||||||
0x0c, 0xf8, 0xc3, 0xdf, 0x4d, 0x4b, 0xf3, 0x76, 0x6b, 0x1a, 0xab, 0x28, 0x4d, 0x3e, 0x24, 0x71,
|
0xc6, 0x1e, 0xa8, 0x27, 0x81, 0x1b, 0x9c, 0x5a, 0xfb, 0xb2, 0x91, 0xd5, 0x8d, 0x21, 0xeb, 0x86,
|
||||||
0x45, 0x69, 0x92, 0xa5, 0xd8, 0x6b, 0x69, 0x1f, 0x3e, 0x03, 0xc5, 0xa6, 0x17, 0x50, 0x3f, 0xe4,
|
0x3f, 0x7c, 0xdb, 0x76, 0x8c, 0xe0, 0xb8, 0x65, 0xb0, 0x12, 0x34, 0xd4, 0x53, 0x25, 0x4b, 0xd0,
|
||||||
0x3e, 0x94, 0xab, 0xe2, 0x17, 0xf9, 0x6f, 0x1a, 0xe4, 0x7d, 0xcf, 0x92, 0x40, 0xee, 0xfc, 0x8c,
|
0x50, 0x2c, 0x49, 0xaf, 0x95, 0x7d, 0xf8, 0x12, 0x54, 0xbb, 0x41, 0x44, 0xc3, 0x98, 0xfb, 0x50,
|
||||||
0x20, 0x53, 0xf0, 0xc8, 0xdd, 0x14, 0x8a, 0x65, 0x5a, 0xa7, 0x1d, 0x14, 0xdd, 0x92, 0xa2, 0x40,
|
0x6f, 0x8a, 0x5f, 0xe4, 0x87, 0x79, 0x90, 0x0f, 0x02, 0x47, 0x01, 0x79, 0xf4, 0x7f, 0x04, 0x99,
|
||||||
0xc9, 0x34, 0x02, 0xd3, 0xb0, 0xa8, 0xf0, 0x27, 0xfe, 0x49, 0xbe, 0xcf, 0xc1, 0x19, 0x49, 0xd5,
|
0x83, 0x47, 0xee, 0xe5, 0x50, 0x6c, 0xd2, 0x36, 0xcd, 0x50, 0x0c, 0x4a, 0x8a, 0x06, 0x35, 0xdb,
|
||||||
0x66, 0xdb, 0x31, 0xb3, 0x14, 0xf5, 0xcd, 0x2e, 0x1e, 0x87, 0xa2, 0xe5, 0xb7, 0xab, 0x4d, 0x47,
|
0x8a, 0x6c, 0xcb, 0xa1, 0xc2, 0x1f, 0xf9, 0x93, 0xfc, 0xbb, 0x04, 0x97, 0x14, 0x55, 0xfb, 0x7d,
|
||||||
0xc9, 0x33, 0x4b, 0x62, 0x5d, 0xc8, 0xb0, 0x0a, 0x43, 0x9e, 0xdf, 0x74, 0xa8, 0x52, 0x90, 0x16,
|
0xcf, 0x2e, 0x52, 0x34, 0x32, 0xbb, 0x78, 0x16, 0xaa, 0x4e, 0xd8, 0x6f, 0x76, 0x3d, 0xad, 0xcc,
|
||||||
0x23, 0x11, 0x36, 0xa1, 0x1c, 0x84, 0xec, 0x94, 0xd7, 0xda, 0xca, 0x50, 0x05, 0xcd, 0x8c, 0x2c,
|
0x2c, 0x89, 0x75, 0x21, 0xc3, 0x3a, 0x4c, 0x04, 0x61, 0xd7, 0xa3, 0xfc, 0xc0, 0xc8, 0xc5, 0x44,
|
||||||
0xac, 0xbc, 0x42, 0xec, 0x98, 0x27, 0x9b, 0x42, 0x5d, 0x35, 0x51, 0x8c, 0x43, 0x18, 0x8e, 0xab,
|
0x84, 0x6d, 0xa8, 0x47, 0x31, 0x6b, 0x0b, 0xad, 0xbe, 0x36, 0xd1, 0x40, 0x4b, 0x53, 0x6b, 0x5b,
|
||||||
0x3b, 0x50, 0x4a, 0x95, 0xfc, 0xcc, 0xc8, 0xc2, 0xc6, 0x2b, 0x5a, 0xf9, 0x93, 0xc7, 0x7a, 0x93,
|
0xaf, 0x11, 0x3b, 0xe6, 0xc9, 0xbe, 0x50, 0xd7, 0x4c, 0x15, 0xe3, 0x18, 0x26, 0x65, 0x75, 0x47,
|
||||||
0x74, 0xb0, 0x85, 0x5b, 0x1d, 0x43, 0xe4, 0x09, 0x82, 0xf1, 0x03, 0x65, 0xb3, 0xe9, 0xd1, 0xcc,
|
0x5a, 0xad, 0x51, 0x5e, 0x9a, 0x5a, 0xdb, 0x7b, 0x4d, 0x2b, 0x5f, 0x0d, 0x58, 0x33, 0x53, 0x0e,
|
||||||
0x58, 0x5b, 0x50, 0x08, 0x3c, 0x6a, 0xf2, 0x23, 0x3f, 0xb2, 0xf0, 0xc7, 0xc1, 0xd4, 0x11, 0x33,
|
0xb6, 0x70, 0x2b, 0x33, 0xc4, 0x9a, 0xd3, 0xec, 0x99, 0xb2, 0xd9, 0x0f, 0x68, 0x61, 0xac, 0x1d,
|
||||||
0x2a, 0xf0, 0x71, 0xed, 0x64, 0x15, 0x7e, 0x2d, 0x2d, 0x6f, 0x18, 0xa1, 0xb9, 0x93, 0x05, 0x8a,
|
0xa8, 0x44, 0x01, 0xb5, 0xf9, 0x91, 0x9f, 0x5a, 0xfb, 0xca, 0x78, 0xea, 0x88, 0x19, 0x15, 0xf8,
|
||||||
0x25, 0x90, 0xed, 0x49, 0x35, 0xa2, 0x48, 0x44, 0xfe, 0x87, 0x40, 0x95, 0x4b, 0xd6, 0xad, 0xd7,
|
0xb8, 0x76, 0xb2, 0x0d, 0x9f, 0x53, 0x96, 0xf7, 0xac, 0xd8, 0x3e, 0x2a, 0x02, 0xc5, 0x12, 0xc8,
|
||||||
0x1f, 0x18, 0xe6, 0x6e, 0xb6, 0xba, 0x9c, 0x6d, 0x71, 0x5d, 0xf9, 0x25, 0x60, 0xba, 0xf6, 0x9e,
|
0xf6, 0xe4, 0x1a, 0x51, 0x22, 0x22, 0x3f, 0x42, 0xa0, 0xab, 0x25, 0xeb, 0xb7, 0xdb, 0x0f, 0x2d,
|
||||||
0x4f, 0xe6, 0x56, 0x97, 0xab, 0x39, 0xdb, 0x7a, 0xf9, 0x4a, 0x22, 0xdf, 0xee, 0x03, 0x22, 0xf2,
|
0xfb, 0xb8, 0x58, 0x5d, 0xc9, 0x75, 0xb8, 0xae, 0xf2, 0x06, 0x30, 0x5d, 0x27, 0x2f, 0xe6, 0x4b,
|
||||||
0x90, 0x05, 0x84, 0xc0, 0xb0, 0xd3, 0xb5, 0xc9, 0x76, 0xc4, 0x47, 0x68, 0xae, 0x13, 0x50, 0x6a,
|
0xdb, 0x9b, 0xcd, 0x92, 0xeb, 0xbc, 0x7a, 0x25, 0x91, 0x7f, 0x9e, 0x02, 0x22, 0xf2, 0x50, 0x04,
|
||||||
0x51, 0x9f, 0x9f, 0x12, 0xb9, 0xb1, 0xc6, 0x42, 0x06, 0xbe, 0xe6, 0xbb, 0x4d, 0x4f, 0x19, 0x92,
|
0x84, 0xc0, 0xa4, 0x37, 0xb0, 0xc9, 0x66, 0xe2, 0x0b, 0x34, 0xd7, 0x39, 0xa8, 0xf5, 0xd2, 0x9b,
|
||||||
0xa3, 0xc8, 0x45, 0x58, 0x81, 0xc2, 0xae, 0xed, 0x58, 0x4a, 0x51, 0x5a, 0xe2, 0x12, 0xf2, 0x5e,
|
0x21, 0xdb, 0x24, 0x85, 0x0c, 0x7c, 0x2b, 0xf4, 0xbb, 0x81, 0x36, 0xa1, 0x46, 0x91, 0x8b, 0xb0,
|
||||||
0x0e, 0x26, 0xbb, 0xb8, 0xd5, 0x37, 0x67, 0xaf, 0x81, 0x6f, 0x9d, 0xba, 0x2a, 0x1d, 0xa8, 0x2b,
|
0x06, 0x95, 0x63, 0xd7, 0x73, 0xb4, 0xaa, 0xb2, 0xc4, 0x25, 0xe4, 0x57, 0x25, 0x98, 0x1f, 0xe0,
|
||||||
0x86, 0x9f, 0x3f, 0x6c, 0xb5, 0x3d, 0xaa, 0x94, 0x65, 0xfc, 0x89, 0x98, 0xfc, 0x88, 0xa0, 0xd2,
|
0xd6, 0xc8, 0x9c, 0xbd, 0x01, 0xbe, 0x65, 0x75, 0x55, 0x3b, 0x53, 0x57, 0x0c, 0x3f, 0x7f, 0x38,
|
||||||
0x25, 0x36, 0xfd, 0x5b, 0xe3, 0x6b, 0x12, 0x9c, 0x6d, 0xd7, 0x37, 0xa9, 0x52, 0x4a, 0x6a, 0x1d,
|
0xe8, 0x07, 0x54, 0xab, 0xab, 0xf8, 0x53, 0x31, 0xf9, 0x2f, 0x82, 0xc6, 0x80, 0xd8, 0x8c, 0x6e,
|
||||||
0x55, 0x23, 0x11, 0xf9, 0x01, 0x81, 0x12, 0x7b, 0x7b, 0xdb, 0xe4, 0xbe, 0x37, 0x9d, 0xd7, 0xdd,
|
0x8d, 0x6f, 0x48, 0x70, 0x0e, 0xfd, 0xd0, 0xa6, 0x5a, 0x2d, 0xad, 0x75, 0xd4, 0x4c, 0x44, 0xe4,
|
||||||
0xe1, 0x71, 0x28, 0x1a, 0xdc, 0x97, 0x54, 0x39, 0x08, 0x19, 0xf9, 0x3f, 0x82, 0xb1, 0xb4, 0xcb,
|
0x3f, 0x08, 0x34, 0xe9, 0xed, 0x1d, 0x9b, 0xfb, 0xde, 0xf5, 0xde, 0x74, 0x87, 0x67, 0xa1, 0x6a,
|
||||||
0xc1, 0x9a, 0x1d, 0x84, 0xf1, 0x24, 0x81, 0x6d, 0x28, 0x45, 0x3b, 0x03, 0x05, 0xf1, 0x0e, 0xbf,
|
0x71, 0x5f, 0x72, 0xe5, 0x20, 0x64, 0xe4, 0xc7, 0x08, 0x66, 0xf2, 0x2e, 0x47, 0x3b, 0x6e, 0x14,
|
||||||
0xfa, 0x0a, 0xbd, 0x33, 0x6d, 0x28, 0x76, 0x4f, 0xe8, 0x27, 0x37, 0x61, 0xac, 0x6b, 0xa3, 0x11,
|
0xcb, 0x49, 0x02, 0xbb, 0x50, 0x4b, 0x76, 0x46, 0x1a, 0xe2, 0x1d, 0x7e, 0xfb, 0x35, 0x7a, 0x67,
|
||||||
0x48, 0x2a, 0x50, 0x6e, 0x88, 0x89, 0x29, 0xca, 0x41, 0x7c, 0x5d, 0xc6, 0x52, 0xf2, 0x55, 0x2e,
|
0xde, 0x90, 0x74, 0x4f, 0xe8, 0x27, 0x9f, 0xc2, 0xcc, 0xc0, 0x46, 0x23, 0x90, 0x34, 0xa0, 0xde,
|
||||||
0xdd, 0x7f, 0x5d, 0x6b, 0xcd, 0xad, 0x65, 0x0c, 0x85, 0x87, 0xc9, 0x9e, 0x02, 0x25, 0xcf, 0xb5,
|
0x11, 0x13, 0x53, 0x92, 0x03, 0x79, 0x5d, 0x4a, 0x29, 0xf9, 0x5b, 0x29, 0xdf, 0x7f, 0x7d, 0x67,
|
||||||
0x3a, 0x89, 0xab, 0xc6, 0x3f, 0xd9, 0xdb, 0xa6, 0xeb, 0x84, 0x06, 0x9b, 0xd8, 0x53, 0xf9, 0xea,
|
0xc7, 0x6f, 0x15, 0x0c, 0x85, 0xe7, 0xc9, 0x9e, 0x06, 0xb5, 0xc0, 0x77, 0xb2, 0xc4, 0x35, 0xe5,
|
||||||
0x88, 0x59, 0xee, 0x03, 0xdb, 0x31, 0xe9, 0x26, 0x35, 0x5d, 0xc7, 0x0a, 0x78, 0xe2, 0xf2, 0x71,
|
0x4f, 0xf6, 0xb6, 0xed, 0x7b, 0xb1, 0xc5, 0x46, 0xfc, 0x5c, 0xbe, 0x32, 0x31, 0xcb, 0x7d, 0xe4,
|
||||||
0xee, 0xe5, 0x15, 0x7c, 0x17, 0x86, 0xf9, 0xef, 0x2d, 0xbb, 0x41, 0x95, 0x22, 0xbf, 0xb1, 0x67,
|
0x7a, 0x36, 0xdd, 0xa7, 0xb6, 0xef, 0x39, 0x11, 0x4f, 0x5c, 0x59, 0xe6, 0x5e, 0x5d, 0xc1, 0xf7,
|
||||||
0xb5, 0x88, 0x1a, 0x68, 0x32, 0x35, 0xe8, 0x44, 0x98, 0x51, 0x03, 0xad, 0x35, 0xaf, 0xb1, 0x37,
|
0x60, 0x92, 0xff, 0x3e, 0x70, 0x3b, 0x54, 0xab, 0xf2, 0x1b, 0x7b, 0xd9, 0x48, 0xb8, 0x84, 0xa1,
|
||||||
0xaa, 0x9d, 0x97, 0x19, 0xae, 0xd0, 0xb0, 0xeb, 0x6b, 0xb6, 0xc3, 0x6f, 0xe5, 0x8e, 0xc1, 0x8e,
|
0x72, 0x89, 0x2c, 0xc2, 0x8c, 0x4b, 0x18, 0xbd, 0x55, 0x83, 0xbd, 0xd1, 0xcc, 0x5e, 0x66, 0xb8,
|
||||||
0x98, 0xd5, 0xc4, 0xb6, 0x5b, 0xaf, 0xbb, 0xff, 0xe2, 0x2d, 0x20, 0xb9, 0x0e, 0x22, 0x19, 0xf9,
|
0x62, 0xcb, 0x6d, 0xef, 0xb8, 0x1e, 0xbf, 0x95, 0x33, 0x83, 0x99, 0x98, 0xd5, 0xc4, 0xa1, 0xdf,
|
||||||
0x37, 0x94, 0xd7, 0xdc, 0xda, 0x1d, 0x27, 0xf4, 0xdb, 0xac, 0x26, 0x99, 0x3b, 0xd4, 0x49, 0x07,
|
0x6e, 0xfb, 0xdf, 0xe3, 0x2d, 0x20, 0xbd, 0x0e, 0x12, 0x19, 0xf9, 0x3e, 0xd4, 0x77, 0xfc, 0xd6,
|
||||||
0x3d, 0x16, 0xe2, 0x75, 0x18, 0x0e, 0xed, 0x06, 0xdd, 0x0c, 0x8d, 0x86, 0x27, 0x6e, 0xd7, 0x23,
|
0x5d, 0x2f, 0x0e, 0xfb, 0xac, 0x26, 0x99, 0x3b, 0xd4, 0xcb, 0x07, 0x5d, 0x0a, 0xf1, 0x2e, 0x4c,
|
||||||
0xe0, 0x4e, 0x90, 0xc5, 0x2a, 0x88, 0x0e, 0x67, 0x93, 0x19, 0x60, 0x8b, 0xfa, 0x0d, 0xdb, 0x31,
|
0xc6, 0x6e, 0x87, 0xee, 0xc7, 0x56, 0x27, 0x10, 0xb7, 0xeb, 0x05, 0x70, 0xa7, 0xc8, 0xa4, 0x0a,
|
||||||
0x32, 0x7b, 0x0e, 0x19, 0x07, 0xb5, 0xdb, 0x0b, 0x62, 0x10, 0xbe, 0x05, 0xc7, 0xe2, 0x42, 0x12,
|
0x62, 0xc2, 0x87, 0xe9, 0x0c, 0x70, 0x40, 0xc3, 0x8e, 0xeb, 0x59, 0x85, 0x3d, 0x87, 0xcc, 0x82,
|
||||||
0x85, 0xa0, 0xc1, 0xaf, 0xa4, 0xda, 0x5c, 0x4f, 0xd4, 0x89, 0x4e, 0xb0, 0x7f, 0x91, 0xb4, 0x41,
|
0x3e, 0xe8, 0x05, 0x31, 0x08, 0xdf, 0x86, 0xb7, 0x64, 0x21, 0x89, 0x42, 0x30, 0xe0, 0x6d, 0xa5,
|
||||||
0xb9, 0x67, 0x38, 0x46, 0x8d, 0x5a, 0x89, 0xa2, 0xa4, 0x24, 0xff, 0x06, 0x43, 0x76, 0x48, 0x1b,
|
0x36, 0x77, 0x53, 0x75, 0xa2, 0x13, 0x9c, 0x5e, 0x24, 0x7d, 0xd0, 0xee, 0x5b, 0x9e, 0xd5, 0xa2,
|
||||||
0xf1, 0xd1, 0x58, 0x19, 0xc0, 0xd1, 0x58, 0xb6, 0xb7, 0xb7, 0xab, 0x91, 0xd6, 0x85, 0x17, 0x67,
|
0x4e, 0xaa, 0x28, 0x2d, 0xc9, 0x6f, 0xc1, 0x84, 0x1b, 0xd3, 0x8e, 0x3c, 0x1a, 0x5b, 0x63, 0x38,
|
||||||
0x01, 0xcb, 0xe3, 0x06, 0xf5, 0x5b, 0xb6, 0x49, 0xf1, 0x5b, 0x08, 0x0a, 0xec, 0x8c, 0xe2, 0x73,
|
0x1a, 0x9b, 0xee, 0xe1, 0x61, 0x33, 0xd1, 0xba, 0xf6, 0xf2, 0x43, 0xc0, 0xea, 0xb8, 0x41, 0xc3,
|
||||||
0x29, 0x55, 0xfb, 0x09, 0x98, 0x3a, 0xa0, 0x29, 0x87, 0x99, 0x22, 0xe3, 0x8f, 0xbf, 0x79, 0xf1,
|
0x9e, 0x6b, 0x53, 0xfc, 0x33, 0x04, 0x15, 0x76, 0x46, 0xf1, 0xe5, 0x9c, 0xaa, 0xd3, 0x84, 0x4d,
|
||||||
0x4e, 0xee, 0x0c, 0x3e, 0xc5, 0xb9, 0x6c, 0x6b, 0x5e, 0xa6, 0x96, 0x01, 0x7e, 0x03, 0x01, 0x16,
|
0x1f, 0xd3, 0x94, 0xc3, 0x4c, 0x91, 0xd9, 0x27, 0xff, 0x78, 0xf9, 0x8b, 0xd2, 0x25, 0xfc, 0x3e,
|
||||||
0x5d, 0x43, 0x62, 0x63, 0xf8, 0x72, 0x2f, 0x7c, 0x5d, 0x58, 0x9b, 0x7a, 0x4e, 0xaa, 0x1a, 0x8d,
|
0x27, 0xbf, 0xbd, 0x55, 0x95, 0x8b, 0x46, 0xf8, 0x27, 0x08, 0xb0, 0xe8, 0x1a, 0x0a, 0x1b, 0xc3,
|
||||||
0x91, 0x65, 0x56, 0x23, 0x7c, 0x03, 0x07, 0x30, 0xcb, 0x01, 0x4c, 0x61, 0xd2, 0x0d, 0x80, 0xfe,
|
0x1f, 0x0d, 0xc3, 0x37, 0x80, 0xb5, 0xe9, 0x97, 0x95, 0xaa, 0x31, 0x18, 0xbb, 0x66, 0x35, 0xc2,
|
||||||
0x90, 0x95, 0xc2, 0x23, 0x9d, 0x46, 0x76, 0x3f, 0x46, 0x30, 0xf4, 0x17, 0x7e, 0xdb, 0xf5, 0x89,
|
0x37, 0x70, 0x00, 0xcb, 0x1c, 0xc0, 0x02, 0x26, 0x83, 0x00, 0x98, 0x8f, 0x58, 0x29, 0x3c, 0x36,
|
||||||
0xd0, 0xc6, 0x60, 0x22, 0xc4, 0x6d, 0x71, 0xa8, 0xe4, 0x3c, 0x87, 0x79, 0x0e, 0x8f, 0xc5, 0x30,
|
0x69, 0x62, 0xf7, 0xf7, 0x08, 0x26, 0xbe, 0xc1, 0x6f, 0xbb, 0x11, 0x11, 0xda, 0x1b, 0x4f, 0x84,
|
||||||
0x83, 0xd0, 0xa7, 0x46, 0x23, 0x85, 0xf6, 0x2a, 0xc2, 0x4f, 0x11, 0x14, 0x23, 0x52, 0x86, 0x2f,
|
0xb8, 0x2d, 0x0e, 0x95, 0x5c, 0xe1, 0x30, 0x2f, 0xe3, 0x19, 0x09, 0x33, 0x8a, 0x43, 0x6a, 0x75,
|
||||||
0xf4, 0x82, 0x98, 0x22, 0x6d, 0xea, 0x80, 0xa8, 0x0f, 0xb9, 0xc4, 0x01, 0x9e, 0x27, 0x5d, 0x13,
|
0x72, 0x68, 0x6f, 0x22, 0xfc, 0x0c, 0x41, 0x35, 0x21, 0x65, 0xf8, 0xea, 0x30, 0x88, 0x39, 0xd2,
|
||||||
0xb9, 0x98, 0xe2, 0x6d, 0x6f, 0x23, 0xc8, 0xaf, 0xd0, 0xbe, 0x65, 0x36, 0x28, 0x64, 0x07, 0x42,
|
0xa6, 0x8f, 0x89, 0xfa, 0x90, 0xeb, 0x1c, 0xe0, 0x15, 0x32, 0x30, 0x91, 0xeb, 0x39, 0xde, 0xf6,
|
||||||
0xd7, 0x25, 0xc3, 0xf8, 0x31, 0x82, 0xd1, 0x15, 0x1a, 0xc6, 0xd4, 0x39, 0xe8, 0x1d, 0xbe, 0x14,
|
0x73, 0x04, 0xe5, 0x2d, 0x3a, 0xb2, 0xcc, 0xc6, 0x85, 0xec, 0x4c, 0xe8, 0x06, 0x64, 0x18, 0x3f,
|
||||||
0xbb, 0x56, 0xc7, 0x35, 0xe9, 0x9b, 0x45, 0xbc, 0x94, 0x74, 0x89, 0x39, 0x6e, 0xfa, 0x22, 0xbe,
|
0x41, 0x30, 0xbd, 0x45, 0x63, 0x49, 0x9d, 0xa3, 0xe1, 0xe1, 0xcb, 0xb1, 0x6b, 0x7d, 0xd6, 0x50,
|
||||||
0x90, 0x55, 0x5c, 0x8d, 0xc4, 0xe6, 0x97, 0x08, 0x8a, 0x11, 0xed, 0xe8, 0x6d, 0x3e, 0xc5, 0x66,
|
0x3e, 0x72, 0xc8, 0xa5, 0xb4, 0x4b, 0xac, 0x70, 0xd3, 0xd7, 0xf0, 0xd5, 0xa2, 0xe2, 0xea, 0xa4,
|
||||||
0x07, 0x16, 0xa3, 0x3b, 0x1c, 0xe8, 0x4d, 0xf5, 0x6a, 0x77, 0xa0, 0xf2, 0xfb, 0xac, 0xcd, 0x5a,
|
0x36, 0xff, 0x8a, 0xa0, 0x9a, 0xd0, 0x8e, 0xe1, 0xe6, 0x73, 0x6c, 0x76, 0x6c, 0x31, 0xba, 0xcb,
|
||||||
0x46, 0x68, 0x68, 0x1c, 0x7d, 0x3a, 0xb3, 0x9f, 0x21, 0x80, 0x0e, 0x6f, 0xc2, 0x97, 0xb2, 0x9d,
|
0x81, 0x7e, 0xaa, 0xdf, 0x1c, 0x0c, 0x54, 0x7d, 0x9f, 0xb5, 0x59, 0xc7, 0x8a, 0x2d, 0x83, 0xa3,
|
||||||
0x90, 0xb8, 0x95, 0x3a, 0x40, 0xe6, 0x44, 0x34, 0xee, 0xcc, 0x8c, 0x5a, 0xc9, 0x8a, 0x3a, 0xe3,
|
0xcf, 0x67, 0xf6, 0x4f, 0x08, 0x20, 0xe3, 0x4d, 0xf8, 0x7a, 0xb1, 0x13, 0x0a, 0xb7, 0xd2, 0xc7,
|
||||||
0x55, 0x8b, 0x9c, 0x5d, 0xe1, 0x0f, 0x11, 0x0c, 0xf1, 0xf9, 0x1c, 0x4f, 0xf5, 0x02, 0x2c, 0x8f,
|
0xc8, 0x9c, 0x88, 0xc1, 0x9d, 0x59, 0xd2, 0x1b, 0x45, 0x51, 0x67, 0xbc, 0x6a, 0x9d, 0xb3, 0x2b,
|
||||||
0xef, 0x03, 0x0b, 0xfa, 0x34, 0xc7, 0x59, 0x59, 0xc8, 0x2a, 0xcc, 0x45, 0x34, 0x8b, 0x5b, 0x50,
|
0xfc, 0x5b, 0x04, 0x13, 0x7c, 0x3e, 0xc7, 0x0b, 0xc3, 0x00, 0xab, 0xe3, 0xfb, 0xd8, 0x82, 0xbe,
|
||||||
0x8c, 0x46, 0xe4, 0xde, 0x55, 0x91, 0x1a, 0xa1, 0xd5, 0x4a, 0x46, 0x7f, 0x8c, 0x0a, 0x53, 0x9c,
|
0xc8, 0x71, 0x36, 0xd6, 0x8a, 0x0a, 0x73, 0x1d, 0x2d, 0xe3, 0x1e, 0x54, 0x93, 0x11, 0x79, 0x78,
|
||||||
0x89, 0xd9, 0xcc, 0x33, 0xf1, 0x09, 0x82, 0x02, 0xe3, 0xce, 0xf8, 0x7c, 0x2f, 0x7d, 0xd2, 0x97,
|
0x55, 0xe4, 0x46, 0x68, 0xbd, 0x51, 0xd0, 0x1f, 0x93, 0xc2, 0x14, 0x67, 0x62, 0xb9, 0xf0, 0x4c,
|
||||||
0x88, 0x81, 0x45, 0xe5, 0x32, 0x87, 0x76, 0x81, 0x64, 0x67, 0xaf, 0xed, 0x98, 0x2c, 0x34, 0x4f,
|
0xfc, 0x01, 0x41, 0x85, 0x71, 0x67, 0x7c, 0x65, 0x98, 0x3e, 0xe5, 0x4b, 0xc4, 0xd8, 0xa2, 0xf2,
|
||||||
0x10, 0x1c, 0xdf, 0x7f, 0x8b, 0xe2, 0xb1, 0x94, 0x91, 0xf4, 0x35, 0xad, 0xa6, 0x43, 0xd8, 0xeb,
|
0x11, 0x87, 0x76, 0x95, 0x14, 0x67, 0xaf, 0xef, 0xd9, 0x2c, 0x34, 0x4f, 0x11, 0xbc, 0x73, 0xfa,
|
||||||
0x06, 0x26, 0xb7, 0x38, 0x8a, 0x45, 0x7c, 0xbd, 0xef, 0x81, 0x58, 0x8f, 0x0f, 0x31, 0x53, 0x34,
|
0x16, 0xc5, 0x33, 0x39, 0x23, 0xf9, 0x6b, 0x5a, 0xcf, 0x87, 0x70, 0xd8, 0x0d, 0x4c, 0x6e, 0x73,
|
||||||
0x97, 0x7c, 0x4e, 0xc0, 0x9f, 0x23, 0x18, 0x8d, 0xf5, 0x6e, 0xf9, 0x94, 0x66, 0xc3, 0x1a, 0x50,
|
0x14, 0xeb, 0xf8, 0xe3, 0x91, 0x07, 0x62, 0x57, 0x1e, 0x62, 0xa6, 0x68, 0x25, 0xfd, 0x9c, 0x80,
|
||||||
0xfd, 0x33, 0x43, 0xe4, 0x06, 0xc7, 0xfe, 0x5b, 0x7c, 0xed, 0x90, 0xd8, 0x63, 0xcc, 0x73, 0x21,
|
0xff, 0x8c, 0x60, 0x5a, 0xea, 0x3d, 0x08, 0x29, 0x2d, 0x86, 0x35, 0xa6, 0xfa, 0x67, 0x86, 0xc8,
|
||||||
0x83, 0xf9, 0x29, 0x82, 0x72, 0xfc, 0x55, 0x00, 0x5f, 0xec, 0x59, 0x49, 0xe9, 0xef, 0x06, 0x03,
|
0x27, 0x1c, 0xfb, 0x17, 0xf0, 0xad, 0x73, 0x62, 0x97, 0x98, 0x57, 0x62, 0x06, 0xf3, 0x8f, 0x08,
|
||||||
0xcb, 0xbe, 0xce, 0xb1, 0x5f, 0x22, 0x53, 0x59, 0xd9, 0xf7, 0x85, 0x71, 0x56, 0x01, 0xef, 0x22,
|
0xea, 0xf2, 0xab, 0x00, 0xbe, 0x36, 0xb4, 0x92, 0xf2, 0xdf, 0x0d, 0xc6, 0x96, 0x7d, 0x93, 0x63,
|
||||||
0xc0, 0xc9, 0x78, 0x96, 0x0c, 0x6c, 0x78, 0x3a, 0x65, 0xaa, 0xe7, 0xe4, 0xa7, 0x5e, 0xec, 0xbb,
|
0xbf, 0x4e, 0x16, 0x8a, 0xb2, 0x1f, 0x0a, 0xe3, 0xac, 0x02, 0x7e, 0x89, 0x00, 0xa7, 0xe3, 0x59,
|
||||||
0x2f, 0xdd, 0xca, 0x67, 0x33, 0x5b, 0xb9, 0x9b, 0xd8, 0x7f, 0x13, 0xc1, 0xc8, 0x0a, 0x4d, 0x06,
|
0x3a, 0xb0, 0xe1, 0xc5, 0x9c, 0xa9, 0xa1, 0x93, 0x9f, 0x7e, 0x6d, 0xe4, 0xbe, 0x7c, 0x2b, 0x5f,
|
||||||
0x97, 0x8c, 0x40, 0xa6, 0xbf, 0x7b, 0xa8, 0x33, 0xfd, 0x37, 0x0a, 0x44, 0x57, 0x38, 0xa2, 0x69,
|
0x2e, 0x6c, 0xe5, 0x7e, 0x6a, 0xff, 0xa7, 0x08, 0xa6, 0xb6, 0x68, 0x3a, 0xb8, 0x14, 0x04, 0x32,
|
||||||
0x9c, 0x1d, 0xaa, 0x18, 0xc0, 0x07, 0x08, 0x7e, 0x29, 0xba, 0x98, 0x90, 0x5c, 0xe9, 0x67, 0x29,
|
0xff, 0xdd, 0x43, 0x5f, 0x1a, 0xbd, 0x51, 0x20, 0xba, 0xc1, 0x11, 0x2d, 0xe2, 0xe2, 0x50, 0x49,
|
||||||
0xd5, 0xf4, 0x0e, 0x8f, 0xeb, 0x37, 0x1c, 0xd7, 0x1c, 0x39, 0x14, 0xae, 0x45, 0xf1, 0xf9, 0xe0,
|
0x00, 0xbf, 0x41, 0xf0, 0x59, 0xd1, 0xc5, 0x84, 0xe4, 0xc6, 0x28, 0x4b, 0xb9, 0xa6, 0x77, 0x7e,
|
||||||
0x23, 0x04, 0x27, 0xe5, 0x49, 0x4f, 0x50, 0xc6, 0x97, 0x8d, 0x5b, 0x06, 0xf3, 0x24, 0xd7, 0x38,
|
0x5c, 0x9f, 0xe7, 0xb8, 0x56, 0xc8, 0xb9, 0x70, 0xad, 0x8b, 0xcf, 0x07, 0xbf, 0x43, 0xf0, 0x9e,
|
||||||
0x3e, 0x0d, 0x5f, 0x39, 0x0c, 0x3e, 0x5d, 0x90, 0x48, 0xfc, 0x3e, 0x82, 0x13, 0x9c, 0xb4, 0xcb,
|
0x3a, 0xe9, 0x09, 0xca, 0xf8, 0xaa, 0x71, 0x2b, 0x60, 0x9e, 0xe4, 0x16, 0xc7, 0x67, 0xe0, 0x1b,
|
||||||
0x8a, 0xf7, 0x35, 0xe4, 0x5e, 0x14, 0xff, 0x10, 0x0d, 0x59, 0x9c, 0x59, 0x72, 0x24, 0x50, 0x8b,
|
0xe7, 0xc1, 0x67, 0x0a, 0x12, 0x89, 0x7f, 0x8d, 0xe0, 0x5d, 0x4e, 0xda, 0x55, 0xc5, 0xa7, 0x1a,
|
||||||
0x82, 0x6c, 0xb3, 0xc9, 0xfd, 0x58, 0x7c, 0x05, 0x88, 0xec, 0xce, 0xf5, 0x0b, 0xdc, 0x51, 0xaf,
|
0xf2, 0x30, 0x8a, 0x7f, 0x8e, 0x86, 0x2c, 0xce, 0x2c, 0xb9, 0x10, 0xa8, 0x75, 0x41, 0xb6, 0xd9,
|
||||||
0x0c, 0x51, 0x6e, 0xb3, 0x87, 0x2b, 0xb7, 0xff, 0x20, 0x28, 0x09, 0x9e, 0x9c, 0x71, 0xab, 0x4a,
|
0xe4, 0xfe, 0x96, 0xbc, 0x02, 0x44, 0x76, 0x57, 0x46, 0x05, 0xee, 0xa2, 0x57, 0x86, 0x28, 0xb7,
|
||||||
0x44, 0x5a, 0x3d, 0x9d, 0xda, 0x15, 0xf3, 0x44, 0xf2, 0x3b, 0x6e, 0x76, 0x1e, 0xeb, 0x59, 0x66,
|
0xe5, 0xf3, 0x95, 0xdb, 0x0f, 0x10, 0xd4, 0x04, 0x4f, 0x2e, 0xb8, 0x55, 0x15, 0x22, 0xad, 0x7f,
|
||||||
0x3d, 0xd7, 0x0a, 0xf4, 0x87, 0x82, 0x40, 0x3f, 0xd2, 0xeb, 0x6e, 0x2d, 0xb8, 0x8a, 0x96, 0x6e,
|
0x90, 0xdb, 0x25, 0x79, 0x22, 0xf9, 0x22, 0x37, 0xbb, 0x8a, 0xcd, 0x22, 0xb3, 0x81, 0xef, 0x44,
|
||||||
0x3c, 0xdb, 0x9b, 0x40, 0x5f, 0xef, 0x4d, 0xa0, 0xef, 0xf6, 0x26, 0xd0, 0x5f, 0xb5, 0xac, 0xbf,
|
0xe6, 0x23, 0x41, 0xa0, 0x1f, 0x9b, 0x6d, 0xbf, 0x15, 0xdd, 0x44, 0x1b, 0x9f, 0x3c, 0x3f, 0x99,
|
||||||
0xa3, 0x0e, 0xfe, 0x6d, 0xf7, 0x53, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5c, 0x47, 0xa4, 0xb4, 0xcb,
|
0x43, 0x7f, 0x3f, 0x99, 0x43, 0xff, 0x3a, 0x99, 0x43, 0xdf, 0x34, 0x8a, 0xfe, 0xbf, 0x3a, 0xfb,
|
||||||
0x1b, 0x00, 0x00,
|
0x3f, 0xdf, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0xba, 0x1f, 0x43, 0xcd, 0xfc, 0x1b, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ message ApplicationQuery {
|
|||||||
optional string name = 1;
|
optional string name = 1;
|
||||||
optional string refresh = 2;
|
optional string refresh = 2;
|
||||||
repeated string project = 3 [(gogoproto.customname) = "Projects"];
|
repeated string project = 3 [(gogoproto.customname) = "Projects"];
|
||||||
|
optional string resourceVersion = 4 [(gogoproto.nullable) = false];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplicationEventsQuery is a query for application resource events
|
// ApplicationEventsQuery is a query for application resource events
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
@@ -166,7 +166,7 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
source:
|
source:
|
||||||
path: some/path
|
path: some/path
|
||||||
repoURL: https://git.com/repo.git
|
repoURL: https://github.com/argoproj/argocd-example-apps.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ksonnet:
|
ksonnet:
|
||||||
environment: default
|
environment: default
|
||||||
|
|||||||
@@ -26,12 +26,17 @@ const (
|
|||||||
ActionSync = "sync"
|
ActionSync = "sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultScopes = []string{"groups"}
|
||||||
|
)
|
||||||
|
|
||||||
// RBACPolicyEnforcer provides an RBAC Claims Enforcer which additionally consults AppProject
|
// RBACPolicyEnforcer provides an RBAC Claims Enforcer which additionally consults AppProject
|
||||||
// roles, jwt tokens, and groups. It is backed by a AppProject informer/lister cache and does not
|
// roles, jwt tokens, and groups. It is backed by a AppProject informer/lister cache and does not
|
||||||
// make any API calls during enforcement.
|
// make any API calls during enforcement.
|
||||||
type RBACPolicyEnforcer struct {
|
type RBACPolicyEnforcer struct {
|
||||||
enf *rbac.Enforcer
|
enf *rbac.Enforcer
|
||||||
projLister applister.AppProjectNamespaceLister
|
projLister applister.AppProjectNamespaceLister
|
||||||
|
scopes []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRBACPolicyEnforcer returns a new RBAC Enforcer for the Argo CD API Server
|
// NewRBACPolicyEnforcer returns a new RBAC Enforcer for the Argo CD API Server
|
||||||
@@ -39,9 +44,14 @@ func NewRBACPolicyEnforcer(enf *rbac.Enforcer, projLister applister.AppProjectNa
|
|||||||
return &RBACPolicyEnforcer{
|
return &RBACPolicyEnforcer{
|
||||||
enf: enf,
|
enf: enf,
|
||||||
projLister: projLister,
|
projLister: projLister,
|
||||||
|
scopes: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *RBACPolicyEnforcer) SetScopes(scopes []string) {
|
||||||
|
p.scopes = scopes
|
||||||
|
}
|
||||||
|
|
||||||
// EnforceClaims is an RBAC claims enforcer specific to the Argo CD API server
|
// EnforceClaims is an RBAC claims enforcer specific to the Argo CD API server
|
||||||
func (p *RBACPolicyEnforcer) EnforceClaims(claims jwt.Claims, rvals ...interface{}) bool {
|
func (p *RBACPolicyEnforcer) EnforceClaims(claims jwt.Claims, rvals ...interface{}) bool {
|
||||||
mapClaims, err := jwtutil.MapClaims(claims)
|
mapClaims, err := jwtutil.MapClaims(claims)
|
||||||
@@ -68,8 +78,12 @@ func (p *RBACPolicyEnforcer) EnforceClaims(claims jwt.Claims, rvals ...interface
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scopes := p.scopes
|
||||||
|
if scopes == nil {
|
||||||
|
scopes = defaultScopes
|
||||||
|
}
|
||||||
// Finally check if any of the user's groups grant them permissions
|
// Finally check if any of the user's groups grant them permissions
|
||||||
groups := jwtutil.GetGroups(mapClaims)
|
groups := jwtutil.GetScopeValues(mapClaims, scopes)
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
vals := append([]interface{}{group}, rvals[1:]...)
|
vals := append([]interface{}{group}, rvals[1:]...)
|
||||||
if p.enf.EnforceRuntimePolicy(runtimePolicy, vals...) {
|
if p.enf.EnforceRuntimePolicy(runtimePolicy, vals...) {
|
||||||
|
|||||||
@@ -68,4 +68,9 @@ func TestEnforceAllPolicies(t *testing.T) {
|
|||||||
assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
|
assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
|
||||||
claims = jwt.MapClaims{"groups": []string{"my-org:other-group"}}
|
claims = jwt.MapClaims{"groups": []string{"my-org:other-group"}}
|
||||||
assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
|
assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
|
||||||
|
|
||||||
|
// AWS cognito returns its groups in cognito:groups
|
||||||
|
rbacEnf.SetScopes([]string{"cognito:groups"})
|
||||||
|
claims = jwt.MapClaims{"cognito:groups": []string{"my-org:my-team"}}
|
||||||
|
assert.True(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,13 +164,7 @@ func (s *Server) ListApps(ctx context.Context, q *RepoAppsQuery) (*RepoAppsRespo
|
|||||||
}
|
}
|
||||||
repo, err := s.db.GetRepository(ctx, q.Repo)
|
repo, err := s.db.GetRepository(ctx, q.Repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errStatus, ok := status.FromError(err); ok && errStatus.Code() == codes.NotFound {
|
return nil, err
|
||||||
repo = &appsv1.Repository{
|
|
||||||
Repo: q.Repo,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the repo
|
// Test the repo
|
||||||
@@ -202,13 +196,7 @@ func (s *Server) GetAppDetails(ctx context.Context, q *RepoAppDetailsQuery) (*re
|
|||||||
}
|
}
|
||||||
repo, err := s.db.GetRepository(ctx, q.Repo)
|
repo, err := s.db.GetRepository(ctx, q.Repo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errStatus, ok := status.FromError(err); ok && errStatus.Code() == codes.NotFound {
|
return nil, err
|
||||||
repo = &appsv1.Repository{
|
|
||||||
Repo: q.Repo,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
conn, repoClient, err := s.repoClientset.NewRepoServerClient()
|
conn, repoClient, err := s.repoClientset.NewRepoServerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
golang_proto "github.com/golang/protobuf/proto"
|
golang_proto "github.com/golang/protobuf/proto"
|
||||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||||
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
||||||
@@ -103,13 +106,14 @@ var (
|
|||||||
type ArgoCDServer struct {
|
type ArgoCDServer struct {
|
||||||
ArgoCDServerOpts
|
ArgoCDServerOpts
|
||||||
|
|
||||||
ssoClientApp *oidc.ClientApp
|
ssoClientApp *oidc.ClientApp
|
||||||
settings *settings_util.ArgoCDSettings
|
settings *settings_util.ArgoCDSettings
|
||||||
log *log.Entry
|
log *log.Entry
|
||||||
sessionMgr *util_session.SessionManager
|
sessionMgr *util_session.SessionManager
|
||||||
settingsMgr *settings_util.SettingsManager
|
settingsMgr *settings_util.SettingsManager
|
||||||
enf *rbac.Enforcer
|
enf *rbac.Enforcer
|
||||||
projInformer cache.SharedIndexInformer
|
projInformer cache.SharedIndexInformer
|
||||||
|
policyEnforcer *rbacpolicy.RBACPolicyEnforcer
|
||||||
|
|
||||||
// stopCh is the channel which when closed, will shutdown the Argo CD server
|
// stopCh is the channel which when closed, will shutdown the Argo CD server
|
||||||
stopCh chan struct{}
|
stopCh chan struct{}
|
||||||
@@ -177,6 +181,7 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer {
|
|||||||
settingsMgr: settingsMgr,
|
settingsMgr: settingsMgr,
|
||||||
enf: enf,
|
enf: enf,
|
||||||
projInformer: projInformer,
|
projInformer: projInformer,
|
||||||
|
policyEnforcer: policyEnf,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +355,19 @@ func (a *ArgoCDServer) watchSettings(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *ArgoCDServer) rbacPolicyLoader(ctx context.Context) {
|
func (a *ArgoCDServer) rbacPolicyLoader(ctx context.Context) {
|
||||||
err := a.enf.RunPolicyLoader(ctx)
|
err := a.enf.RunPolicyLoader(ctx, func(cm *v1.ConfigMap) error {
|
||||||
|
var scopes []string
|
||||||
|
if scopesStr, ok := cm.Data[rbac.ConfigMapScopesKey]; len(scopesStr) > 0 && ok {
|
||||||
|
scopes = make([]string, 0)
|
||||||
|
err := yaml.Unmarshal([]byte(scopesStr), &scopes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.policyEnforcer.SetScopes(scopes)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
errors.CheckError(err)
|
errors.CheckError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ func (s *Server) Get(ctx context.Context, q *SettingsQuery) (*Settings, error) {
|
|||||||
Issuer: oidcConfig.Issuer,
|
Issuer: oidcConfig.Issuer,
|
||||||
ClientID: oidcConfig.ClientID,
|
ClientID: oidcConfig.ClientID,
|
||||||
CLIClientID: oidcConfig.CLIClientID,
|
CLIClientID: oidcConfig.CLIClientID,
|
||||||
|
Scopes: oidcConfig.RequestedScopes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &set, nil
|
return &set, nil
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func (m *SettingsQuery) Reset() { *m = SettingsQuery{} }
|
|||||||
func (m *SettingsQuery) String() string { return proto.CompactTextString(m) }
|
func (m *SettingsQuery) String() string { return proto.CompactTextString(m) }
|
||||||
func (*SettingsQuery) ProtoMessage() {}
|
func (*SettingsQuery) ProtoMessage() {}
|
||||||
func (*SettingsQuery) Descriptor() ([]byte, []int) {
|
func (*SettingsQuery) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_settings_f96c7d59ef70e4fa, []int{0}
|
return fileDescriptor_settings_4373c06dcc35da1b, []int{0}
|
||||||
}
|
}
|
||||||
func (m *SettingsQuery) XXX_Unmarshal(b []byte) error {
|
func (m *SettingsQuery) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -87,7 +87,7 @@ func (m *Settings) Reset() { *m = Settings{} }
|
|||||||
func (m *Settings) String() string { return proto.CompactTextString(m) }
|
func (m *Settings) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Settings) ProtoMessage() {}
|
func (*Settings) ProtoMessage() {}
|
||||||
func (*Settings) Descriptor() ([]byte, []int) {
|
func (*Settings) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_settings_f96c7d59ef70e4fa, []int{1}
|
return fileDescriptor_settings_4373c06dcc35da1b, []int{1}
|
||||||
}
|
}
|
||||||
func (m *Settings) XXX_Unmarshal(b []byte) error {
|
func (m *Settings) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -162,7 +162,7 @@ func (m *DexConfig) Reset() { *m = DexConfig{} }
|
|||||||
func (m *DexConfig) String() string { return proto.CompactTextString(m) }
|
func (m *DexConfig) String() string { return proto.CompactTextString(m) }
|
||||||
func (*DexConfig) ProtoMessage() {}
|
func (*DexConfig) ProtoMessage() {}
|
||||||
func (*DexConfig) Descriptor() ([]byte, []int) {
|
func (*DexConfig) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_settings_f96c7d59ef70e4fa, []int{2}
|
return fileDescriptor_settings_4373c06dcc35da1b, []int{2}
|
||||||
}
|
}
|
||||||
func (m *DexConfig) XXX_Unmarshal(b []byte) error {
|
func (m *DexConfig) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -210,7 +210,7 @@ func (m *Connector) Reset() { *m = Connector{} }
|
|||||||
func (m *Connector) String() string { return proto.CompactTextString(m) }
|
func (m *Connector) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Connector) ProtoMessage() {}
|
func (*Connector) ProtoMessage() {}
|
||||||
func (*Connector) Descriptor() ([]byte, []int) {
|
func (*Connector) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_settings_f96c7d59ef70e4fa, []int{3}
|
return fileDescriptor_settings_4373c06dcc35da1b, []int{3}
|
||||||
}
|
}
|
||||||
func (m *Connector) XXX_Unmarshal(b []byte) error {
|
func (m *Connector) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -258,6 +258,7 @@ type OIDCConfig struct {
|
|||||||
Issuer string `protobuf:"bytes,2,opt,name=issuer,proto3" json:"issuer,omitempty"`
|
Issuer string `protobuf:"bytes,2,opt,name=issuer,proto3" json:"issuer,omitempty"`
|
||||||
ClientID string `protobuf:"bytes,3,opt,name=clientID,proto3" json:"clientID,omitempty"`
|
ClientID string `protobuf:"bytes,3,opt,name=clientID,proto3" json:"clientID,omitempty"`
|
||||||
CLIClientID string `protobuf:"bytes,4,opt,name=cliClientID,proto3" json:"cliClientID,omitempty"`
|
CLIClientID string `protobuf:"bytes,4,opt,name=cliClientID,proto3" json:"cliClientID,omitempty"`
|
||||||
|
Scopes []string `protobuf:"bytes,5,rep,name=scopes" json:"scopes,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
@@ -267,7 +268,7 @@ func (m *OIDCConfig) Reset() { *m = OIDCConfig{} }
|
|||||||
func (m *OIDCConfig) String() string { return proto.CompactTextString(m) }
|
func (m *OIDCConfig) String() string { return proto.CompactTextString(m) }
|
||||||
func (*OIDCConfig) ProtoMessage() {}
|
func (*OIDCConfig) ProtoMessage() {}
|
||||||
func (*OIDCConfig) Descriptor() ([]byte, []int) {
|
func (*OIDCConfig) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_settings_f96c7d59ef70e4fa, []int{4}
|
return fileDescriptor_settings_4373c06dcc35da1b, []int{4}
|
||||||
}
|
}
|
||||||
func (m *OIDCConfig) XXX_Unmarshal(b []byte) error {
|
func (m *OIDCConfig) XXX_Unmarshal(b []byte) error {
|
||||||
return m.Unmarshal(b)
|
return m.Unmarshal(b)
|
||||||
@@ -324,6 +325,13 @@ func (m *OIDCConfig) GetCLIClientID() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *OIDCConfig) GetScopes() []string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Scopes
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterType((*SettingsQuery)(nil), "cluster.SettingsQuery")
|
proto.RegisterType((*SettingsQuery)(nil), "cluster.SettingsQuery")
|
||||||
proto.RegisterType((*Settings)(nil), "cluster.Settings")
|
proto.RegisterType((*Settings)(nil), "cluster.Settings")
|
||||||
@@ -614,6 +622,21 @@ func (m *OIDCConfig) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
i = encodeVarintSettings(dAtA, i, uint64(len(m.CLIClientID)))
|
i = encodeVarintSettings(dAtA, i, uint64(len(m.CLIClientID)))
|
||||||
i += copy(dAtA[i:], m.CLIClientID)
|
i += copy(dAtA[i:], m.CLIClientID)
|
||||||
}
|
}
|
||||||
|
if len(m.Scopes) > 0 {
|
||||||
|
for _, s := range m.Scopes {
|
||||||
|
dAtA[i] = 0x2a
|
||||||
|
i++
|
||||||
|
l = len(s)
|
||||||
|
for l >= 1<<7 {
|
||||||
|
dAtA[i] = uint8(uint64(l)&0x7f | 0x80)
|
||||||
|
l >>= 7
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
dAtA[i] = uint8(l)
|
||||||
|
i++
|
||||||
|
i += copy(dAtA[i:], s)
|
||||||
|
}
|
||||||
|
}
|
||||||
if m.XXX_unrecognized != nil {
|
if m.XXX_unrecognized != nil {
|
||||||
i += copy(dAtA[i:], m.XXX_unrecognized)
|
i += copy(dAtA[i:], m.XXX_unrecognized)
|
||||||
}
|
}
|
||||||
@@ -727,6 +750,12 @@ func (m *OIDCConfig) Size() (n int) {
|
|||||||
if l > 0 {
|
if l > 0 {
|
||||||
n += 1 + l + sovSettings(uint64(l))
|
n += 1 + l + sovSettings(uint64(l))
|
||||||
}
|
}
|
||||||
|
if len(m.Scopes) > 0 {
|
||||||
|
for _, s := range m.Scopes {
|
||||||
|
l = len(s)
|
||||||
|
n += 1 + l + sovSettings(uint64(l))
|
||||||
|
}
|
||||||
|
}
|
||||||
if m.XXX_unrecognized != nil {
|
if m.XXX_unrecognized != nil {
|
||||||
n += len(m.XXX_unrecognized)
|
n += len(m.XXX_unrecognized)
|
||||||
}
|
}
|
||||||
@@ -1431,6 +1460,35 @@ func (m *OIDCConfig) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
m.CLIClientID = string(dAtA[iNdEx:postIndex])
|
m.CLIClientID = string(dAtA[iNdEx:postIndex])
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
|
case 5:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Scopes", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowSettings
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthSettings
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Scopes = append(m.Scopes, string(dAtA[iNdEx:postIndex]))
|
||||||
|
iNdEx = postIndex
|
||||||
default:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skipSettings(dAtA[iNdEx:])
|
skippy, err := skipSettings(dAtA[iNdEx:])
|
||||||
@@ -1559,45 +1617,46 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterFile("server/settings/settings.proto", fileDescriptor_settings_f96c7d59ef70e4fa)
|
proto.RegisterFile("server/settings/settings.proto", fileDescriptor_settings_4373c06dcc35da1b)
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileDescriptor_settings_f96c7d59ef70e4fa = []byte{
|
var fileDescriptor_settings_4373c06dcc35da1b = []byte{
|
||||||
// 566 bytes of a gzipped FileDescriptorProto
|
// 577 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0x5d, 0x6b, 0x13, 0x41,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x54, 0x5d, 0x6b, 0x13, 0x41,
|
||||||
0x14, 0x65, 0xbb, 0xfd, 0xca, 0x8d, 0xda, 0x76, 0x94, 0x12, 0x83, 0x24, 0x21, 0x4f, 0x01, 0x71,
|
0x14, 0x65, 0xbb, 0xfd, 0xda, 0x1b, 0xb5, 0xed, 0x28, 0x25, 0x06, 0x49, 0x42, 0x9e, 0x02, 0xe2,
|
||||||
0xd7, 0xa4, 0x2f, 0xea, 0x8b, 0x90, 0x54, 0x24, 0xb6, 0x50, 0x9c, 0xa2, 0x0f, 0x82, 0xc8, 0x74,
|
0xae, 0x49, 0x5f, 0xd4, 0x17, 0x21, 0xa9, 0x48, 0x6c, 0xa1, 0x38, 0x45, 0x1f, 0x04, 0x91, 0xe9,
|
||||||
0x72, 0xdd, 0x8e, 0xd9, 0xee, 0x2c, 0xb3, 0xb3, 0x8b, 0x79, 0xf5, 0x1f, 0x88, 0xf8, 0x27, 0xfc,
|
0xe4, 0xba, 0x1d, 0xb3, 0xdd, 0x59, 0x66, 0x67, 0x83, 0x79, 0xf5, 0x1f, 0x88, 0xff, 0x42, 0xf0,
|
||||||
0x25, 0x3e, 0x0a, 0xbe, 0x07, 0x59, 0xfc, 0x21, 0xb2, 0xb3, 0x1f, 0x59, 0x9a, 0xe2, 0xdb, 0xd9,
|
0x7f, 0xf8, 0x28, 0xf8, 0x1e, 0x64, 0xf1, 0x87, 0xc8, 0xce, 0x7e, 0x64, 0x69, 0x8a, 0x6f, 0x67,
|
||||||
0x73, 0xee, 0x99, 0x9d, 0x7b, 0xcf, 0x5c, 0xe8, 0x44, 0xa8, 0x12, 0x54, 0x6e, 0x84, 0x5a, 0x8b,
|
0xce, 0xb9, 0x67, 0x76, 0xee, 0x9c, 0xb9, 0x0b, 0xed, 0x18, 0xd5, 0x1c, 0x95, 0x17, 0xa3, 0xd6,
|
||||||
0xc0, 0x8b, 0x2a, 0xe0, 0x84, 0x4a, 0x6a, 0x49, 0x76, 0xb8, 0x1f, 0x47, 0x1a, 0x55, 0xfb, 0x9e,
|
0x22, 0xf4, 0xe3, 0x0a, 0xb8, 0x91, 0x92, 0x5a, 0x92, 0x1d, 0x1e, 0x24, 0xb1, 0x46, 0xd5, 0xba,
|
||||||
0x27, 0x3d, 0x69, 0x38, 0x37, 0x43, 0xb9, 0xdc, 0x7e, 0xe0, 0x49, 0xe9, 0xf9, 0xe8, 0xb2, 0x50,
|
0xe7, 0x4b, 0x5f, 0x1a, 0xce, 0xcb, 0x50, 0x2e, 0xb7, 0x1e, 0xf8, 0x52, 0xfa, 0x01, 0x7a, 0x2c,
|
||||||
0xb8, 0x2c, 0x08, 0xa4, 0x66, 0x5a, 0xc8, 0xa0, 0x30, 0xb7, 0xa7, 0x9e, 0xd0, 0x97, 0xf1, 0x85,
|
0x12, 0x1e, 0x0b, 0x43, 0xa9, 0x99, 0x16, 0x32, 0x2c, 0xcc, 0xad, 0x89, 0x2f, 0xf4, 0x65, 0x72,
|
||||||
0xc3, 0xe5, 0x95, 0xcb, 0x94, 0xb1, 0x7f, 0x32, 0xe0, 0x11, 0x9f, 0xb9, 0xe1, 0xdc, 0xcb, 0x6c,
|
0xe1, 0x72, 0x79, 0xe5, 0x31, 0x65, 0xec, 0x9f, 0x0c, 0x78, 0xc4, 0xa7, 0x5e, 0x34, 0xf3, 0x33,
|
||||||
0x91, 0xcb, 0xc2, 0xd0, 0x17, 0xdc, 0x18, 0xdd, 0x64, 0xc8, 0xfc, 0xf0, 0x92, 0x0d, 0x5d, 0x0f,
|
0x5b, 0xec, 0xb1, 0x28, 0x0a, 0x04, 0x37, 0x46, 0x6f, 0x3e, 0x60, 0x41, 0x74, 0xc9, 0x06, 0x9e,
|
||||||
0x03, 0x54, 0x4c, 0xe3, 0x2c, 0x3f, 0xaa, 0xbf, 0x07, 0xb7, 0xcf, 0x8b, 0x9b, 0xbd, 0x8e, 0x51,
|
0x8f, 0x21, 0x2a, 0xa6, 0x71, 0x9a, 0x6f, 0xd5, 0xdb, 0x83, 0xdb, 0xe7, 0xc5, 0xc9, 0x5e, 0x27,
|
||||||
0x2d, 0xfa, 0x3f, 0x6c, 0xd8, 0x2d, 0x19, 0x72, 0x1f, 0xec, 0x58, 0xf9, 0x2d, 0xab, 0x67, 0x0d,
|
0xa8, 0x16, 0xbd, 0xef, 0x36, 0xec, 0x96, 0x0c, 0xb9, 0x0f, 0x76, 0xa2, 0x82, 0xa6, 0xd5, 0xb5,
|
||||||
0x1a, 0xe3, 0x9d, 0x74, 0xd9, 0xb5, 0xdf, 0xd0, 0x53, 0x9a, 0x71, 0xe4, 0x31, 0x34, 0x66, 0xf8,
|
0xfa, 0xce, 0x68, 0x27, 0x5d, 0x76, 0xec, 0x37, 0xf4, 0x94, 0x66, 0x1c, 0x79, 0x0c, 0xce, 0x14,
|
||||||
0x79, 0x22, 0x83, 0x8f, 0xc2, 0x6b, 0x6d, 0xf4, 0xac, 0x41, 0x73, 0x44, 0x9c, 0xa2, 0x29, 0xe7,
|
0x3f, 0x8f, 0x65, 0xf8, 0x51, 0xf8, 0xcd, 0x8d, 0xae, 0xd5, 0x6f, 0x0c, 0x89, 0x5b, 0x34, 0xe5,
|
||||||
0xb8, 0x54, 0xe8, 0xaa, 0x88, 0x4c, 0x00, 0xa4, 0x98, 0xf1, 0xc2, 0x62, 0x1b, 0xcb, 0xdd, 0xca,
|
0x1e, 0x97, 0x0a, 0x5d, 0x15, 0x91, 0x31, 0x80, 0x14, 0x53, 0x5e, 0x58, 0x6c, 0x63, 0xb9, 0x5b,
|
||||||
0x72, 0x36, 0x3d, 0x9e, 0xe4, 0xd2, 0xf8, 0x4e, 0xba, 0xec, 0xc2, 0xea, 0x9b, 0xd6, 0x6c, 0xa4,
|
0x59, 0xce, 0x26, 0xc7, 0xe3, 0x5c, 0x1a, 0xdd, 0x49, 0x97, 0x1d, 0x58, 0xad, 0x69, 0xcd, 0x46,
|
||||||
0x07, 0x4d, 0x16, 0x86, 0xa7, 0xec, 0x02, 0xfd, 0x13, 0x5c, 0xb4, 0x36, 0xb3, 0x9b, 0xd1, 0x3a,
|
0xba, 0xd0, 0x60, 0x51, 0x74, 0xca, 0x2e, 0x30, 0x38, 0xc1, 0x45, 0x73, 0x33, 0x3b, 0x19, 0xad,
|
||||||
0x45, 0xde, 0xc2, 0x81, 0xc2, 0x48, 0xc6, 0x8a, 0xe3, 0x59, 0x82, 0x4a, 0x89, 0x19, 0x46, 0xad,
|
0x53, 0xe4, 0x2d, 0x1c, 0x28, 0x8c, 0x65, 0xa2, 0x38, 0x9e, 0xcd, 0x51, 0x29, 0x31, 0xc5, 0xb8,
|
||||||
0xad, 0x9e, 0x3d, 0x68, 0x8e, 0x06, 0xd5, 0xdf, 0xca, 0x0e, 0x1d, 0x7a, 0xbd, 0xf4, 0x45, 0xa0,
|
0xb9, 0xd5, 0xb5, 0xfb, 0x8d, 0x61, 0xbf, 0xfa, 0x5a, 0xd9, 0xa1, 0x4b, 0xaf, 0x97, 0xbe, 0x08,
|
||||||
0xd5, 0x82, 0xae, 0x1f, 0xd1, 0xfe, 0x6a, 0xc1, 0xe1, 0xcd, 0xd5, 0x64, 0x1f, 0xec, 0x39, 0x2e,
|
0xb5, 0x5a, 0xd0, 0xf5, 0x2d, 0x5a, 0x5f, 0x2d, 0x38, 0xbc, 0xb9, 0x9a, 0xec, 0x83, 0x3d, 0xc3,
|
||||||
0xf2, 0x31, 0xd1, 0x0c, 0x12, 0x06, 0x5b, 0x09, 0xf3, 0x63, 0x2c, 0x26, 0x73, 0xe2, 0xac, 0x12,
|
0x45, 0x7e, 0x4d, 0x34, 0x83, 0x84, 0xc1, 0xd6, 0x9c, 0x05, 0x09, 0x16, 0x37, 0x73, 0xe2, 0xae,
|
||||||
0x73, 0xca, 0xc4, 0x0c, 0xf8, 0xc0, 0x67, 0x4e, 0x38, 0xf7, 0x9c, 0x2c, 0x31, 0xa7, 0x96, 0x98,
|
0x12, 0x73, 0xcb, 0xc4, 0x0c, 0xf8, 0xc0, 0xa7, 0x6e, 0x34, 0xf3, 0xdd, 0x2c, 0x31, 0xb7, 0x96,
|
||||||
0x53, 0x26, 0xb6, 0x76, 0x43, 0x9a, 0x9f, 0xfc, 0x6c, 0xe3, 0x89, 0xd5, 0x7f, 0x0e, 0x8d, 0x6a,
|
0x98, 0x5b, 0x26, 0xb6, 0x76, 0x42, 0x9a, 0xef, 0xfc, 0x6c, 0xe3, 0x89, 0xd5, 0x7b, 0x0e, 0x4e,
|
||||||
0xd4, 0x64, 0x04, 0xc0, 0x65, 0x10, 0x20, 0xd7, 0x52, 0x45, 0x2d, 0xcb, 0x74, 0xbc, 0x8a, 0x64,
|
0x75, 0xd5, 0x64, 0x08, 0xc0, 0x65, 0x18, 0x22, 0xd7, 0x52, 0xc5, 0x4d, 0xcb, 0x74, 0xbc, 0x8a,
|
||||||
0x52, 0x4a, 0xb4, 0x56, 0xd5, 0x3f, 0x82, 0x46, 0x25, 0x10, 0x02, 0x9b, 0x01, 0xbb, 0xc2, 0xa2,
|
0x64, 0x5c, 0x4a, 0xb4, 0x56, 0xd5, 0x3b, 0x02, 0xa7, 0x12, 0x08, 0x81, 0xcd, 0x90, 0x5d, 0x61,
|
||||||
0x0f, 0x83, 0x33, 0x4e, 0x2f, 0xc2, 0xbc, 0x8f, 0x06, 0x35, 0xb8, 0xff, 0xdd, 0x82, 0x5a, 0x3c,
|
0xd1, 0x87, 0xc1, 0x19, 0xa7, 0x17, 0x51, 0xde, 0x87, 0x43, 0x0d, 0xee, 0xfd, 0xb0, 0xa0, 0x16,
|
||||||
0x37, 0xda, 0x0e, 0x61, 0x5b, 0x44, 0x51, 0x8c, 0xaa, 0x30, 0x16, 0x5f, 0x64, 0x00, 0xbb, 0xdc,
|
0xcf, 0x8d, 0xb6, 0x43, 0xd8, 0x16, 0x71, 0x9c, 0xa0, 0x2a, 0x8c, 0xc5, 0x8a, 0xf4, 0x61, 0x97,
|
||||||
0x17, 0x18, 0xe8, 0xe9, 0xb1, 0x79, 0x01, 0x8d, 0xf1, 0xad, 0x74, 0xd9, 0xdd, 0x9d, 0x14, 0x1c,
|
0x07, 0x02, 0x43, 0x3d, 0x39, 0x36, 0x2f, 0xc0, 0x19, 0xdd, 0x4a, 0x97, 0x9d, 0xdd, 0x71, 0xc1,
|
||||||
0xad, 0x54, 0x32, 0x84, 0x26, 0xf7, 0x45, 0x29, 0xe4, 0x41, 0x8f, 0xf7, 0xd2, 0x65, 0xb7, 0x39,
|
0xd1, 0x4a, 0x25, 0x03, 0x68, 0xf0, 0x40, 0x94, 0x42, 0x1e, 0xf4, 0x68, 0x2f, 0x5d, 0x76, 0x1a,
|
||||||
0x39, 0x9d, 0x56, 0xf5, 0xf5, 0x9a, 0xd1, 0x7b, 0xd8, 0x2b, 0x73, 0x3d, 0x47, 0x95, 0x08, 0x8e,
|
0xe3, 0xd3, 0x49, 0x55, 0x5f, 0xaf, 0xc9, 0x3e, 0x1a, 0x73, 0x19, 0x15, 0x71, 0x3b, 0xb4, 0x58,
|
||||||
0xe4, 0x15, 0xd8, 0x2f, 0x51, 0x93, 0xc3, 0xb5, 0xe0, 0xcd, 0x63, 0x6f, 0x1f, 0xac, 0xf1, 0xfd,
|
0x0d, 0xdf, 0xc3, 0x5e, 0x99, 0xf7, 0x39, 0xaa, 0xb9, 0xe0, 0x48, 0x5e, 0x81, 0xfd, 0x12, 0x35,
|
||||||
0xd6, 0x97, 0xdf, 0x7f, 0xbf, 0x6d, 0x10, 0xb2, 0x6f, 0x76, 0x2f, 0x19, 0x56, 0x8b, 0x3b, 0x7e,
|
0x39, 0x5c, 0x7b, 0x10, 0x66, 0x08, 0x5a, 0x07, 0x6b, 0x7c, 0xaf, 0xf9, 0xe5, 0xf7, 0xdf, 0x6f,
|
||||||
0xfa, 0x33, 0xed, 0x58, 0xbf, 0xd2, 0x8e, 0xf5, 0x27, 0xed, 0x58, 0xef, 0x1e, 0xfe, 0x6f, 0x07,
|
0x1b, 0x84, 0xec, 0x9b, 0x99, 0x9c, 0x0f, 0xaa, 0x81, 0x1e, 0x3d, 0xfd, 0x99, 0xb6, 0xad, 0x5f,
|
||||||
0xaf, 0x2d, 0xff, 0xc5, 0xb6, 0x59, 0xb6, 0xa3, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x62, 0x4c,
|
0x69, 0xdb, 0xfa, 0x93, 0xb6, 0xad, 0x77, 0x0f, 0xff, 0x37, 0x9b, 0xd7, 0x7e, 0x0a, 0x17, 0xdb,
|
||||||
0x68, 0x20, 0x16, 0x04, 0x00, 0x00,
|
0x66, 0x08, 0x8f, 0xfe, 0x05, 0x00, 0x00, 0xff, 0xff, 0x0d, 0x66, 0xd0, 0x3d, 0x2e, 0x04, 0x00,
|
||||||
|
0x00,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ message OIDCConfig {
|
|||||||
string issuer = 2;
|
string issuer = 2;
|
||||||
string clientID = 3 [(gogoproto.customname) = "ClientID"];
|
string clientID = 3 [(gogoproto.customname) = "ClientID"];
|
||||||
string cliClientID = 4 [(gogoproto.customname) = "CLIClientID"];
|
string cliClientID = 4 [(gogoproto.customname) = "CLIClientID"];
|
||||||
|
repeated string scopes = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SettingsService
|
// SettingsService
|
||||||
|
|||||||
@@ -406,6 +406,7 @@ func TestEdgeCasesApplicationResources(t *testing.T) {
|
|||||||
"DeprecatedExtensions": "deprecated-extensions",
|
"DeprecatedExtensions": "deprecated-extensions",
|
||||||
"CRDs": "crd-creation",
|
"CRDs": "crd-creation",
|
||||||
"DuplicatedResources": "duplicated-resources",
|
"DuplicatedResources": "duplicated-resources",
|
||||||
|
"FailedConversion": "failed-conversion",
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, appPath := range apps {
|
for name, appPath := range apps {
|
||||||
@@ -516,3 +517,75 @@ func TestResourceAction(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(t, "test", deployment.Labels["sample"])
|
assert.Equal(t, "test", deployment.Labels["sample"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSyncResourceByLabel(t *testing.T) {
|
||||||
|
fixture.EnsureCleanState()
|
||||||
|
|
||||||
|
app := createAndSyncDefault(t)
|
||||||
|
|
||||||
|
res, _ := fixture.RunCli("app", "sync", app.Name, "--label",
|
||||||
|
fmt.Sprintf("app.kubernetes.io/instance=test-%s", strings.Split(app.Name, "-")[1]))
|
||||||
|
assert.Contains(t, res, "guestbook-ui Synced Healthy")
|
||||||
|
|
||||||
|
res, _ = fixture.RunCli("app", "sync", app.Name, "--label", "this-label=does-not-exist")
|
||||||
|
assert.Contains(t, res, "level=fatal")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPermissions(t *testing.T) {
|
||||||
|
fixture.EnsureCleanState()
|
||||||
|
appName := "test-app"
|
||||||
|
_, err := fixture.RunCli("proj", "create", "test")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// make sure app cannot be created without permissions in project
|
||||||
|
output, err := fixture.RunCli("app", "create", appName, "--repo", fixture.RepoURL(),
|
||||||
|
"--path", guestbookPath, "--project", "test", "--dest-server", common.KubernetesInternalAPIServerAddr, "--dest-namespace", fixture.DeploymentNamespace)
|
||||||
|
assert.Error(t, err)
|
||||||
|
sourceError := fmt.Sprintf("application repo %s is not permitted in project 'test'", fixture.RepoURL())
|
||||||
|
destinationError := fmt.Sprintf("application destination {%s %s} is not permitted in project 'test'", common.KubernetesInternalAPIServerAddr, fixture.DeploymentNamespace)
|
||||||
|
|
||||||
|
assert.Contains(t, output, sourceError)
|
||||||
|
assert.Contains(t, output, destinationError)
|
||||||
|
|
||||||
|
proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get("test", metav1.GetOptions{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
proj.Spec.Destinations = []v1alpha1.ApplicationDestination{{Server: "*", Namespace: "*"}}
|
||||||
|
proj.Spec.SourceRepos = []string{"*"}
|
||||||
|
proj, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Update(proj)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// make sure controller report permissions issues in conditions
|
||||||
|
_, err = fixture.RunCli("app", "create", "test-app", "--repo", fixture.RepoURL(),
|
||||||
|
"--path", guestbookPath, "--project", "test", "--dest-server", common.KubernetesInternalAPIServerAddr, "--dest-namespace", fixture.DeploymentNamespace)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer func() {
|
||||||
|
err = fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.ArgoCDNamespace).Delete(appName, &metav1.DeleteOptions{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
proj.Spec.Destinations = []v1alpha1.ApplicationDestination{}
|
||||||
|
proj.Spec.SourceRepos = []string{}
|
||||||
|
_, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Update(proj)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
closer, client, err := fixture.ArgoCDClientset.NewApplicationClient()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer util.Close(closer)
|
||||||
|
|
||||||
|
refresh := string(v1alpha1.RefreshTypeNormal)
|
||||||
|
app, err := client.Get(context.Background(), &application.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.True(t, destinationErrorExist)
|
||||||
|
assert.True(t, sourceErrorExist)
|
||||||
|
}
|
||||||
|
|||||||
16
test/e2e/testdata/failed-conversion/apiservice.yaml
vendored
Normal file
16
test/e2e/testdata/failed-conversion/apiservice.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
apiVersion: apiregistration.k8s.io/v1beta1
|
||||||
|
kind: APIService
|
||||||
|
metadata:
|
||||||
|
name: v1beta1.metrics.k8s.io
|
||||||
|
labels:
|
||||||
|
app: metrics-server
|
||||||
|
chart: metrics-server
|
||||||
|
spec:
|
||||||
|
service:
|
||||||
|
name: metrics-server
|
||||||
|
namespace: kube-system
|
||||||
|
group: metrics.k8s.io
|
||||||
|
version: v1beta1
|
||||||
|
insecureSkipTLSVerify: true
|
||||||
|
groupPriorityMinimum: 100
|
||||||
|
versionPriority: 100
|
||||||
@@ -127,28 +127,13 @@ func WaitForRefresh(ctx context.Context, appIf v1alpha1.ApplicationInterface, na
|
|||||||
return nil, fmt.Errorf("application refresh deadline exceeded")
|
return nil, fmt.Errorf("application refresh deadline exceeded")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSpecErrors returns list of conditions which indicates that app spec is invalid. Following is checked:
|
// ValidateRepo validates the repository specified in application spec. Following is checked:
|
||||||
// * the git repository is accessible
|
// * the git repository is accessible
|
||||||
// * the git path contains valid manifests
|
// * the git path contains valid manifests
|
||||||
// * the referenced cluster has been added to Argo CD
|
|
||||||
// * the app source repo and destination namespace/cluster are permitted in app project
|
|
||||||
// * there are parameters of only one app source type
|
// * there are parameters of only one app source type
|
||||||
// * ksonnet: the specified environment exists
|
// * ksonnet: the specified environment exists
|
||||||
func GetSpecErrors(
|
func ValidateRepo(ctx context.Context, spec *argoappv1.ApplicationSpec, repoClientset reposerver.Clientset, db db.ArgoDB) ([]argoappv1.ApplicationCondition, argoappv1.ApplicationSourceType, error) {
|
||||||
ctx context.Context,
|
|
||||||
spec *argoappv1.ApplicationSpec,
|
|
||||||
proj *argoappv1.AppProject,
|
|
||||||
repoClientset reposerver.Clientset,
|
|
||||||
db db.ArgoDB,
|
|
||||||
) ([]argoappv1.ApplicationCondition, argoappv1.ApplicationSourceType, error) {
|
|
||||||
conditions := make([]argoappv1.ApplicationCondition, 0)
|
conditions := make([]argoappv1.ApplicationCondition, 0)
|
||||||
if spec.Source.RepoURL == "" || spec.Source.Path == "" {
|
|
||||||
conditions = append(conditions, argoappv1.ApplicationCondition{
|
|
||||||
Type: argoappv1.ApplicationConditionInvalidSpecError,
|
|
||||||
Message: "spec.source.repoURL and spec.source.path are required",
|
|
||||||
})
|
|
||||||
return conditions, "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test the repo
|
// Test the repo
|
||||||
conn, repoClient, err := repoClientset.NewRepoServerClient()
|
conn, repoClient, err := repoClientset.NewRepoServerClient()
|
||||||
@@ -158,24 +143,16 @@ func GetSpecErrors(
|
|||||||
defer util.Close(conn)
|
defer util.Close(conn)
|
||||||
repoAccessable := false
|
repoAccessable := false
|
||||||
repoRes, err := db.GetRepository(ctx, spec.Source.RepoURL)
|
repoRes, err := db.GetRepository(ctx, spec.Source.RepoURL)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errStatus, ok := status.FromError(err); ok && errStatus.Code() == codes.NotFound {
|
return nil, "", err
|
||||||
// The repo has not been added to Argo CD so we do not have credentials to access it.
|
}
|
||||||
// We support the mode where apps can be created from public repositories. Test the
|
|
||||||
// repo to make sure it is publicly accessible
|
err = git.TestRepo(repoRes.Repo, repoRes.Username, repoRes.Password, repoRes.SSHPrivateKey, repoRes.InsecureIgnoreHostKey)
|
||||||
err = git.TestRepo(spec.Source.RepoURL, "", "", "", false)
|
if err != nil {
|
||||||
if err != nil {
|
conditions = append(conditions, argoappv1.ApplicationCondition{
|
||||||
conditions = append(conditions, argoappv1.ApplicationCondition{
|
Type: argoappv1.ApplicationConditionInvalidSpecError,
|
||||||
Type: argoappv1.ApplicationConditionInvalidSpecError,
|
Message: fmt.Sprintf("repository not accessible: %v", err),
|
||||||
Message: fmt.Sprintf("No credentials available for source repository and repository is not publicly accessible: %v", err),
|
})
|
||||||
})
|
|
||||||
} else {
|
|
||||||
repoAccessable = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
repoAccessable = true
|
repoAccessable = true
|
||||||
}
|
}
|
||||||
@@ -218,18 +195,31 @@ func GetSpecErrors(
|
|||||||
conditions = append(conditions, helmConditions...)
|
conditions = append(conditions, helmConditions...)
|
||||||
}
|
}
|
||||||
case argoappv1.ApplicationSourceTypeDirectory, argoappv1.ApplicationSourceTypeKustomize:
|
case argoappv1.ApplicationSourceTypeDirectory, argoappv1.ApplicationSourceTypeKustomize:
|
||||||
maniDirConditions := verifyGenerateManifests(ctx, repoRes, []*argoappv1.HelmRepository{}, spec, repoClient)
|
mainDirConditions := verifyGenerateManifests(ctx, repoRes, []*argoappv1.HelmRepository{}, spec, repoClient)
|
||||||
if len(maniDirConditions) > 0 {
|
if len(mainDirConditions) > 0 {
|
||||||
conditions = append(conditions, maniDirConditions...)
|
conditions = append(conditions, mainDirConditions...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return conditions, appSourceType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatePermissions ensures that the referenced cluster has been added to Argo CD and the app source repo and destination namespace/cluster are permitted in app project
|
||||||
|
func ValidatePermissions(ctx context.Context, spec *argoappv1.ApplicationSpec, proj *argoappv1.AppProject, db db.ArgoDB) ([]argoappv1.ApplicationCondition, error) {
|
||||||
|
conditions := make([]argoappv1.ApplicationCondition, 0)
|
||||||
|
if spec.Source.RepoURL == "" || spec.Source.Path == "" {
|
||||||
|
conditions = append(conditions, argoappv1.ApplicationCondition{
|
||||||
|
Type: argoappv1.ApplicationConditionInvalidSpecError,
|
||||||
|
Message: "spec.source.repoURL and spec.source.path are required",
|
||||||
|
})
|
||||||
|
return conditions, nil
|
||||||
|
}
|
||||||
|
|
||||||
if !proj.IsSourcePermitted(spec.Source) {
|
if !proj.IsSourcePermitted(spec.Source) {
|
||||||
conditions = append(conditions, argoappv1.ApplicationCondition{
|
conditions = append(conditions, argoappv1.ApplicationCondition{
|
||||||
Type: argoappv1.ApplicationConditionInvalidSpecError,
|
Type: argoappv1.ApplicationConditionInvalidSpecError,
|
||||||
Message: fmt.Sprintf("application source %v is not permitted in project '%s'", spec.Source, spec.Project),
|
Message: fmt.Sprintf("application repo %s is not permitted in project '%s'", spec.Source.RepoURL, spec.Project),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +231,7 @@ func GetSpecErrors(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Ensure the k8s cluster the app is referencing, is configured in Argo CD
|
// Ensure the k8s cluster the app is referencing, is configured in Argo CD
|
||||||
_, err = db.GetCluster(ctx, spec.Destination.Server)
|
_, err := db.GetCluster(ctx, spec.Destination.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errStatus, ok := status.FromError(err); ok && errStatus.Code() == codes.NotFound {
|
if errStatus, ok := status.FromError(err); ok && errStatus.Code() == codes.NotFound {
|
||||||
conditions = append(conditions, argoappv1.ApplicationCondition{
|
conditions = append(conditions, argoappv1.ApplicationCondition{
|
||||||
@@ -249,11 +239,11 @@ func GetSpecErrors(
|
|||||||
Message: fmt.Sprintf("cluster '%s' has not been configured", spec.Destination.Server),
|
Message: fmt.Sprintf("cluster '%s' has not been configured", spec.Destination.Server),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return nil, "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return conditions, appSourceType, nil
|
return conditions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAppProject returns a project from an application
|
// GetAppProject returns a project from an application
|
||||||
@@ -340,6 +330,7 @@ func verifyAppYAML(ctx context.Context, repoRes *argoappv1.Repository, spec *arg
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verifyHelmChart verifies a helm chart is functional
|
||||||
// verifyHelmChart verifies a helm chart is functional
|
// verifyHelmChart verifies a helm chart is functional
|
||||||
func verifyHelmChart(ctx context.Context, repoRes *argoappv1.Repository, spec *argoappv1.ApplicationSpec, repoClient repository.RepoServerServiceClient) []argoappv1.ApplicationCondition {
|
func verifyHelmChart(ctx context.Context, repoRes *argoappv1.Repository, spec *argoappv1.ApplicationSpec, repoClient repository.RepoServerServiceClient) []argoappv1.ApplicationCondition {
|
||||||
var conditions []argoappv1.ApplicationCondition
|
var conditions []argoappv1.ApplicationCondition
|
||||||
|
|||||||
@@ -4,20 +4,16 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v2"
|
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||||
"github.com/argoproj/argo-cd/util/diff"
|
"github.com/argoproj/argo-cd/util/diff"
|
||||||
|
|
||||||
jsonpatch "github.com/evanphx/json-patch"
|
jsonpatch "github.com/evanphx/json-patch"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
noMatchingPathError = "Unable to remove nonexistent key: not-matching-path"
|
|
||||||
)
|
|
||||||
|
|
||||||
type normalizerPatch struct {
|
type normalizerPatch struct {
|
||||||
groupKind schema.GroupKind
|
groupKind schema.GroupKind
|
||||||
namespace string
|
namespace string
|
||||||
@@ -103,10 +99,8 @@ func (n *normalizer) Normalize(un *unstructured.Unstructured) error {
|
|||||||
for _, patch := range matched {
|
for _, patch := range matched {
|
||||||
patchedData, err := patch.patch.Apply(docData)
|
patchedData, err := patch.patch.Apply(docData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == noMatchingPathError {
|
log.Debugf("Failed to apply normalization: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
docData = patchedData
|
docData = patchedData
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package argo
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ghodss/yaml"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
|
||||||
@@ -73,3 +74,45 @@ func TestNormalizeMatchedResourceOverrides(t *testing.T) {
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.False(t, has)
|
assert.False(t, has)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const testCRDYAML = `
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
name: certificates.certmanager.k8s.io
|
||||||
|
spec:
|
||||||
|
group: certmanager.k8s.io
|
||||||
|
names:
|
||||||
|
kind: Certificate
|
||||||
|
listKind: CertificateList
|
||||||
|
plural: certificates
|
||||||
|
shortNames:
|
||||||
|
- cert
|
||||||
|
- certs
|
||||||
|
singular: certificate
|
||||||
|
scope: Namespaced
|
||||||
|
version: v1alpha1`
|
||||||
|
|
||||||
|
func TestNormalizeMissingJsonPointer(t *testing.T) {
|
||||||
|
normalizer, err := NewDiffNormalizer([]v1alpha1.ResourceIgnoreDifferences{}, map[string]v1alpha1.ResourceOverride{
|
||||||
|
"apps/Deployment": {
|
||||||
|
IgnoreDifferences: `jsonPointers: ["/garbage"]`,
|
||||||
|
},
|
||||||
|
"apiextensions.k8s.io/CustomResourceDefinition": {
|
||||||
|
IgnoreDifferences: `jsonPointers: ["/spec/additionalPrinterColumns/0/priority"]`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
deployment := kube.MustToUnstructured(test.DemoDeployment())
|
||||||
|
|
||||||
|
err = normalizer.Normalize(deployment)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
crd := unstructured.Unstructured{}
|
||||||
|
err = yaml.Unmarshal([]byte(testCRDYAML), &crd)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = normalizer.Normalize(&crd)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,10 +80,14 @@ func TestCreateExistingRepository(t *testing.T) {
|
|||||||
assert.Equal(t, codes.AlreadyExists, status.Convert(err).Code())
|
assert.Equal(t, codes.AlreadyExists, status.Convert(err).Code())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteRepositoryManagedSecrets(t *testing.T) {
|
func TestGetRepository(t *testing.T) {
|
||||||
config := map[string]string{
|
config := map[string]string{
|
||||||
"repositories": `
|
"repositories": `
|
||||||
- url: https://github.com/argoproj/argocd-example-apps
|
- url: https://known/repo
|
||||||
|
- url: https://secured/repo
|
||||||
|
`,
|
||||||
|
"repository.credentials": `
|
||||||
|
- url: https://secured
|
||||||
usernameSecret:
|
usernameSecret:
|
||||||
name: managed-secret
|
name: managed-secret
|
||||||
key: username
|
key: username
|
||||||
@@ -91,7 +95,41 @@ func TestDeleteRepositoryManagedSecrets(t *testing.T) {
|
|||||||
name: managed-secret
|
name: managed-secret
|
||||||
key: password
|
key: password
|
||||||
`}
|
`}
|
||||||
clientset := getClientset(config, &v1.Secret{
|
clientset := getClientset(config, newManagedSecret())
|
||||||
|
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
repoURL string
|
||||||
|
want *v1alpha1.Repository
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "TestUnknownRepo",
|
||||||
|
repoURL: "https://unknown/repo",
|
||||||
|
want: &v1alpha1.Repository{Repo: "https://unknown/repo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestKnownRepo",
|
||||||
|
repoURL: "https://known/repo",
|
||||||
|
want: &v1alpha1.Repository{Repo: "https://known/repo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TestSecuredRepo",
|
||||||
|
repoURL: "https://secured/repo",
|
||||||
|
want: &v1alpha1.Repository{Repo: "https://secured/repo", Username: "test-username", Password: "test-password"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := db.GetRepository(context.TODO(), tt.repoURL)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newManagedSecret() *v1.Secret {
|
||||||
|
return &v1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "managed-secret",
|
Name: "managed-secret",
|
||||||
Namespace: testNamespace,
|
Namespace: testNamespace,
|
||||||
@@ -103,7 +141,21 @@ func TestDeleteRepositoryManagedSecrets(t *testing.T) {
|
|||||||
username: []byte("test-username"),
|
username: []byte("test-username"),
|
||||||
password: []byte("test-password"),
|
password: []byte("test-password"),
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteRepositoryManagedSecrets(t *testing.T) {
|
||||||
|
config := map[string]string{
|
||||||
|
"repositories": `
|
||||||
|
- url: https://github.com/argoproj/argocd-example-apps
|
||||||
|
usernameSecret:
|
||||||
|
name: managed-secret
|
||||||
|
key: username
|
||||||
|
passwordSecret:
|
||||||
|
name: managed-secret
|
||||||
|
key: password
|
||||||
|
`}
|
||||||
|
clientset := getClientset(config, newManagedSecret())
|
||||||
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
|
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
|
||||||
|
|
||||||
err := db.DeleteRepository(context.Background(), "https://github.com/argoproj/argocd-example-apps")
|
err := db.DeleteRepository(context.Background(), "https://github.com/argoproj/argocd-example-apps")
|
||||||
|
|||||||
262
util/db/mocks/ArgoDB.go
Normal file
262
util/db/mocks/ArgoDB.go
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import context "context"
|
||||||
|
import db "github.com/argoproj/argo-cd/util/db"
|
||||||
|
import mock "github.com/stretchr/testify/mock"
|
||||||
|
import v1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||||
|
|
||||||
|
// ArgoDB is an autogenerated mock type for the ArgoDB type
|
||||||
|
type ArgoDB struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCluster provides a mock function with given fields: ctx, c
|
||||||
|
func (_m *ArgoDB) CreateCluster(ctx context.Context, c *v1alpha1.Cluster) (*v1alpha1.Cluster, error) {
|
||||||
|
ret := _m.Called(ctx, c)
|
||||||
|
|
||||||
|
var r0 *v1alpha1.Cluster
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.Cluster) *v1alpha1.Cluster); ok {
|
||||||
|
r0 = rf(ctx, c)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*v1alpha1.Cluster)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.Cluster) error); ok {
|
||||||
|
r1 = rf(ctx, c)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRepository provides a mock function with given fields: ctx, r
|
||||||
|
func (_m *ArgoDB) CreateRepository(ctx context.Context, r *v1alpha1.Repository) (*v1alpha1.Repository, error) {
|
||||||
|
ret := _m.Called(ctx, r)
|
||||||
|
|
||||||
|
var r0 *v1alpha1.Repository
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.Repository) *v1alpha1.Repository); ok {
|
||||||
|
r0 = rf(ctx, r)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*v1alpha1.Repository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.Repository) error); ok {
|
||||||
|
r1 = rf(ctx, r)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCluster provides a mock function with given fields: ctx, name
|
||||||
|
func (_m *ArgoDB) DeleteCluster(ctx context.Context, name string) error {
|
||||||
|
ret := _m.Called(ctx, name)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
|
||||||
|
r0 = rf(ctx, name)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRepository provides a mock function with given fields: ctx, name
|
||||||
|
func (_m *ArgoDB) DeleteRepository(ctx context.Context, name string) error {
|
||||||
|
ret := _m.Called(ctx, name)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
|
||||||
|
r0 = rf(ctx, name)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCluster provides a mock function with given fields: ctx, name
|
||||||
|
func (_m *ArgoDB) GetCluster(ctx context.Context, name string) (*v1alpha1.Cluster, error) {
|
||||||
|
ret := _m.Called(ctx, name)
|
||||||
|
|
||||||
|
var r0 *v1alpha1.Cluster
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string) *v1alpha1.Cluster); ok {
|
||||||
|
r0 = rf(ctx, name)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*v1alpha1.Cluster)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRepository provides a mock function with given fields: ctx, repoURL
|
||||||
|
func (_m *ArgoDB) GetRepository(ctx context.Context, repoURL string) (*v1alpha1.Repository, error) {
|
||||||
|
ret := _m.Called(ctx, repoURL)
|
||||||
|
|
||||||
|
var r0 *v1alpha1.Repository
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string) *v1alpha1.Repository); ok {
|
||||||
|
r0 = rf(ctx, repoURL)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*v1alpha1.Repository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||||
|
r1 = rf(ctx, repoURL)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListClusters provides a mock function with given fields: ctx
|
||||||
|
func (_m *ArgoDB) ListClusters(ctx context.Context) (*v1alpha1.ClusterList, error) {
|
||||||
|
ret := _m.Called(ctx)
|
||||||
|
|
||||||
|
var r0 *v1alpha1.ClusterList
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context) *v1alpha1.ClusterList); ok {
|
||||||
|
r0 = rf(ctx)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*v1alpha1.ClusterList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
|
||||||
|
r1 = rf(ctx)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListHelmRepos provides a mock function with given fields: ctx
|
||||||
|
func (_m *ArgoDB) ListHelmRepos(ctx context.Context) ([]*v1alpha1.HelmRepository, error) {
|
||||||
|
ret := _m.Called(ctx)
|
||||||
|
|
||||||
|
var r0 []*v1alpha1.HelmRepository
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context) []*v1alpha1.HelmRepository); ok {
|
||||||
|
r0 = rf(ctx)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*v1alpha1.HelmRepository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
|
||||||
|
r1 = rf(ctx)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoURLs provides a mock function with given fields: ctx
|
||||||
|
func (_m *ArgoDB) ListRepoURLs(ctx context.Context) ([]string, error) {
|
||||||
|
ret := _m.Called(ctx)
|
||||||
|
|
||||||
|
var r0 []string
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context) []string); ok {
|
||||||
|
r0 = rf(ctx)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
|
||||||
|
r1 = rf(ctx)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateCluster provides a mock function with given fields: ctx, c
|
||||||
|
func (_m *ArgoDB) UpdateCluster(ctx context.Context, c *v1alpha1.Cluster) (*v1alpha1.Cluster, error) {
|
||||||
|
ret := _m.Called(ctx, c)
|
||||||
|
|
||||||
|
var r0 *v1alpha1.Cluster
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.Cluster) *v1alpha1.Cluster); ok {
|
||||||
|
r0 = rf(ctx, c)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*v1alpha1.Cluster)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.Cluster) error); ok {
|
||||||
|
r1 = rf(ctx, c)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRepository provides a mock function with given fields: ctx, r
|
||||||
|
func (_m *ArgoDB) UpdateRepository(ctx context.Context, r *v1alpha1.Repository) (*v1alpha1.Repository, error) {
|
||||||
|
ret := _m.Called(ctx, r)
|
||||||
|
|
||||||
|
var r0 *v1alpha1.Repository
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.Repository) *v1alpha1.Repository); ok {
|
||||||
|
r0 = rf(ctx, r)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*v1alpha1.Repository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.Repository) error); ok {
|
||||||
|
r1 = rf(ctx, r)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchClusters provides a mock function with given fields: ctx, callback
|
||||||
|
func (_m *ArgoDB) WatchClusters(ctx context.Context, callback func(*db.ClusterEvent)) error {
|
||||||
|
ret := _m.Called(ctx, callback)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, func(*db.ClusterEvent)) error); ok {
|
||||||
|
r0 = rf(ctx, callback)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
@@ -5,17 +5,18 @@ import (
|
|||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/common"
|
log "github.com/sirupsen/logrus"
|
||||||
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
|
||||||
"github.com/argoproj/argo-cd/util/git"
|
|
||||||
"github.com/argoproj/argo-cd/util/settings"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
apiv1 "k8s.io/api/core/v1"
|
apiv1 "k8s.io/api/core/v1"
|
||||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/common"
|
||||||
|
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||||
|
"github.com/argoproj/argo-cd/util/git"
|
||||||
|
"github.com/argoproj/argo-cd/util/settings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -45,7 +46,7 @@ func (db *db) CreateRepository(ctx context.Context, r *appsv1.Repository) (*apps
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
index := getRepoCredIndex(s, r.Repo)
|
index := getRepositoryIndex(s, r.Repo)
|
||||||
if index > -1 {
|
if index > -1 {
|
||||||
return nil, status.Errorf(codes.AlreadyExists, "repository '%s' already exists", r.Repo)
|
return nil, status.Errorf(codes.AlreadyExists, "repository '%s' already exists", r.Repo)
|
||||||
}
|
}
|
||||||
@@ -85,25 +86,45 @@ func (db *db) GetRepository(ctx context.Context, repoURL string) (*appsv1.Reposi
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
index := getRepoCredIndex(s, repoURL)
|
repo := &appsv1.Repository{Repo: repoURL}
|
||||||
if index < 0 {
|
index := getRepositoryIndex(s, repoURL)
|
||||||
return nil, status.Errorf(codes.NotFound, "repo '%s' not found", repoURL)
|
if index >= 0 {
|
||||||
|
repo, err = db.credentialsToRepository(s.Repositories[index])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
repoInfo := s.Repositories[index]
|
|
||||||
|
if !repo.HasCredentials() {
|
||||||
|
index := getRepositoryCredentialIndex(s, repoURL)
|
||||||
|
if index >= 0 {
|
||||||
|
|
||||||
|
credential, err := db.credentialsToRepository(s.RepositoryCredentials[index])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
log.WithFields(log.Fields{"repoURL": repo.Repo, "credUrl": credential.Repo}).Info("copying credentials")
|
||||||
|
repo.CopyCredentialsFrom(*credential)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *db) credentialsToRepository(repoInfo settings.RepoCredentials) (*appsv1.Repository, error) {
|
||||||
repo := &appsv1.Repository{
|
repo := &appsv1.Repository{
|
||||||
Repo: repoInfo.URL,
|
Repo: repoInfo.URL,
|
||||||
InsecureIgnoreHostKey: repoInfo.InsecureIgnoreHostKey,
|
InsecureIgnoreHostKey: repoInfo.InsecureIgnoreHostKey,
|
||||||
}
|
}
|
||||||
|
err := db.unmarshalFromSecretsStr(map[*string]*apiv1.SecretKeySelector{
|
||||||
err = db.unmarshalFromSecretsStr(map[*string]*apiv1.SecretKeySelector{
|
|
||||||
&repo.Username: repoInfo.UsernameSecret,
|
&repo.Username: repoInfo.UsernameSecret,
|
||||||
&repo.Password: repoInfo.PasswordSecret,
|
&repo.Password: repoInfo.PasswordSecret,
|
||||||
&repo.SSHPrivateKey: repoInfo.SSHPrivateKeySecret,
|
&repo.SSHPrivateKey: repoInfo.SSHPrivateKeySecret,
|
||||||
}, make(map[string]*apiv1.Secret))
|
}, make(map[string]*apiv1.Secret))
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return repo, err
|
||||||
}
|
|
||||||
return repo, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRepository updates a repository
|
// UpdateRepository updates a repository
|
||||||
@@ -113,7 +134,7 @@ func (db *db) UpdateRepository(ctx context.Context, r *appsv1.Repository) (*apps
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
index := getRepoCredIndex(s, r.Repo)
|
index := getRepositoryIndex(s, r.Repo)
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
return nil, status.Errorf(codes.NotFound, "repo '%s' not found", r.Repo)
|
return nil, status.Errorf(codes.NotFound, "repo '%s' not found", r.Repo)
|
||||||
}
|
}
|
||||||
@@ -138,7 +159,7 @@ func (db *db) DeleteRepository(ctx context.Context, repoURL string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
index := getRepoCredIndex(s, repoURL)
|
index := getRepositoryIndex(s, repoURL)
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
return status.Errorf(codes.NotFound, "repo '%s' not found", repoURL)
|
return status.Errorf(codes.NotFound, "repo '%s' not found", repoURL)
|
||||||
}
|
}
|
||||||
@@ -243,9 +264,20 @@ func (db *db) upsertSecret(name string, data map[string][]byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRepoCredIndex(s *settings.ArgoCDSettings, repoURL string) int {
|
func getRepositoryIndex(s *settings.ArgoCDSettings, repoURL string) int {
|
||||||
for i, cred := range s.Repositories {
|
for i, repo := range s.Repositories {
|
||||||
if git.SameURL(cred.URL, repoURL) {
|
if git.SameURL(repo.URL, repoURL) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRepositoryCredentialIndex(s *settings.ArgoCDSettings, repoURL string) int {
|
||||||
|
repoURL = git.NormalizeGitURL(repoURL)
|
||||||
|
for i, cred := range s.RepositoryCredentials {
|
||||||
|
credUrl := git.NormalizeGitURL(cred.URL)
|
||||||
|
if strings.HasPrefix(repoURL, credUrl) {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package db
|
package db
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/util/settings"
|
||||||
|
)
|
||||||
|
|
||||||
func TestRepoURLToSecretName(t *testing.T) {
|
func TestRepoURLToSecretName(t *testing.T) {
|
||||||
tables := map[string]string{
|
tables := map[string]string{
|
||||||
@@ -18,3 +22,24 @@ func TestRepoURLToSecretName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_getRepositoryCredentialIndex(t *testing.T) {
|
||||||
|
argoCDSettings := settings.ArgoCDSettings{
|
||||||
|
RepositoryCredentials: []settings.RepoCredentials{{URL: "http://known"}},
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
repoURL string
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{"TestNotFound", "", -1},
|
||||||
|
{"TestFoundFound", "http://known/repo", 0},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := getRepositoryCredentialIndex(&argoCDSettings, tt.repoURL); got != tt.want {
|
||||||
|
t.Errorf("getRepositoryCredentialIndex() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
git "gopkg.in/src-d/go-git.v4"
|
"gopkg.in/src-d/go-git.v4"
|
||||||
"gopkg.in/src-d/go-git.v4/config"
|
"gopkg.in/src-d/go-git.v4/config"
|
||||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||||
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
||||||
@@ -47,9 +47,14 @@ func NewFactory() ClientFactory {
|
|||||||
return &factory{}
|
return &factory{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *factory) NewClient(repoURL, path, username, password, sshPrivateKey string, insecureIgnoreHostKey bool) (Client, error) {
|
func (f *factory) NewClient(rawRepoURL, path, username, password, sshPrivateKey string, insecureIgnoreHostKey bool) (Client, error) {
|
||||||
|
var sshUser string
|
||||||
|
if isSSH, user := IsSSHURL(rawRepoURL); isSSH {
|
||||||
|
sshUser = user
|
||||||
|
}
|
||||||
|
|
||||||
clnt := nativeGitClient{
|
clnt := nativeGitClient{
|
||||||
repoURL: repoURL,
|
repoURL: rawRepoURL,
|
||||||
root: path,
|
root: path,
|
||||||
}
|
}
|
||||||
if sshPrivateKey != "" {
|
if sshPrivateKey != "" {
|
||||||
@@ -57,7 +62,7 @@ func (f *factory) NewClient(repoURL, path, username, password, sshPrivateKey str
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
auth := &ssh2.PublicKeys{User: "git", Signer: signer}
|
auth := &ssh2.PublicKeys{User: sshUser, Signer: signer}
|
||||||
if insecureIgnoreHostKey {
|
if insecureIgnoreHostKey {
|
||||||
auth.HostKeyCallback = ssh.InsecureIgnoreHostKey()
|
auth.HostKeyCallback = ssh.InsecureIgnoreHostKey()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,10 @@ func removeSuffix(s, suffix string) string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
var commitSHARegex = regexp.MustCompile("^[0-9A-Fa-f]{40}$")
|
var (
|
||||||
|
commitSHARegex = regexp.MustCompile("^[0-9A-Fa-f]{40}$")
|
||||||
|
sshURLRegex = regexp.MustCompile("^(ssh://)?([^/@:]*?)@.*")
|
||||||
|
)
|
||||||
|
|
||||||
// IsCommitSHA returns whether or not a string is a 40 character SHA-1
|
// IsCommitSHA returns whether or not a string is a 40 character SHA-1
|
||||||
func IsCommitSHA(sha string) bool {
|
func IsCommitSHA(sha string) bool {
|
||||||
@@ -47,7 +50,7 @@ func SameURL(leftRepo, rightRepo string) bool {
|
|||||||
// and should not be considered stable from release to release
|
// and should not be considered stable from release to release
|
||||||
func NormalizeGitURL(repo string) string {
|
func NormalizeGitURL(repo string) string {
|
||||||
repo = strings.ToLower(strings.TrimSpace(repo))
|
repo = strings.ToLower(strings.TrimSpace(repo))
|
||||||
if IsSSHURL(repo) {
|
if yes, _ := IsSSHURL(repo); yes {
|
||||||
repo = ensurePrefix(repo, "ssh://")
|
repo = ensurePrefix(repo, "ssh://")
|
||||||
}
|
}
|
||||||
repo = removeSuffix(repo, ".git")
|
repo = removeSuffix(repo, ".git")
|
||||||
@@ -60,8 +63,12 @@ func NormalizeGitURL(repo string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsSSHURL returns true if supplied URL is SSH URL
|
// IsSSHURL returns true if supplied URL is SSH URL
|
||||||
func IsSSHURL(url string) bool {
|
func IsSSHURL(url string) (bool, string) {
|
||||||
return strings.HasPrefix(url, "git@") || strings.HasPrefix(url, "ssh://")
|
matches := sshURLRegex.FindStringSubmatch(url)
|
||||||
|
if len(matches) > 2 {
|
||||||
|
return true, matches[2]
|
||||||
|
}
|
||||||
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestRepo tests if a repo exists and is accessible with the given credentials
|
// TestRepo tests if a repo exists and is accessible with the given credentials
|
||||||
|
|||||||
@@ -73,10 +73,21 @@ func TestIsSSHURL(t *testing.T) {
|
|||||||
"ssh://git@github.com:test.git": true,
|
"ssh://git@github.com:test.git": true,
|
||||||
}
|
}
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
assert.Equal(t, v, IsSSHURL(k))
|
isSSH, _ := IsSSHURL(k)
|
||||||
|
assert.Equal(t, v, isSSH)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsSSHURLUserName(t *testing.T) {
|
||||||
|
isSSH, user := IsSSHURL("ssh://john@john-server.org:29418/project")
|
||||||
|
assert.True(t, isSSH)
|
||||||
|
assert.Equal(t, "john", user)
|
||||||
|
|
||||||
|
isSSH, user = IsSSHURL("john@john-server.org:29418/project")
|
||||||
|
assert.True(t, isSSH)
|
||||||
|
assert.Equal(t, "john", user)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSameURL(t *testing.T) {
|
func TestSameURL(t *testing.T) {
|
||||||
data := map[string]string{
|
data := map[string]string{
|
||||||
"git@GITHUB.com:argoproj/test": "git@github.com:argoproj/test.git",
|
"git@GITHUB.com:argoproj/test": "git@github.com:argoproj/test.git",
|
||||||
|
|||||||
@@ -149,3 +149,12 @@ func TestTLS(address string) (*TLSTestResult, error) {
|
|||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithTimeout(duration time.Duration) grpc.UnaryClientInterceptor {
|
||||||
|
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||||
|
clientDeadline := time.Now().Add(duration)
|
||||||
|
ctx, cancel := context.WithDeadline(ctx, clientDeadline)
|
||||||
|
defer cancel()
|
||||||
|
return invoker(ctx, method, req, reply, cc, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// SetApplicationHealth updates the health statuses of all resources performed in the comparison
|
// SetApplicationHealth updates the health statuses of all resources performed in the comparison
|
||||||
func SetApplicationHealth(resStatuses []appv1.ResourceStatus, liveObjs []*unstructured.Unstructured, resourceOverrides map[string]appv1.ResourceOverride) (*appv1.HealthStatus, error) {
|
func SetApplicationHealth(resStatuses []appv1.ResourceStatus, liveObjs []*unstructured.Unstructured, resourceOverrides map[string]appv1.ResourceOverride, filter func(obj *unstructured.Unstructured) bool) (*appv1.HealthStatus, error) {
|
||||||
var savedErr error
|
var savedErr error
|
||||||
appHealth := appv1.HealthStatus{Status: appv1.HealthStatusHealthy}
|
appHealth := appv1.HealthStatus{Status: appv1.HealthStatusHealthy}
|
||||||
for i, liveObj := range liveObjs {
|
for i, liveObj := range liveObjs {
|
||||||
@@ -30,9 +30,11 @@ func SetApplicationHealth(resStatuses []appv1.ResourceStatus, liveObjs []*unstru
|
|||||||
if liveObj == nil {
|
if liveObj == nil {
|
||||||
resHealth = &appv1.HealthStatus{Status: appv1.HealthStatusMissing}
|
resHealth = &appv1.HealthStatus{Status: appv1.HealthStatusMissing}
|
||||||
} else {
|
} else {
|
||||||
resHealth, err = GetResourceHealth(liveObj, resourceOverrides)
|
if filter(liveObj) {
|
||||||
if err != nil && savedErr == nil {
|
resHealth, err = GetResourceHealth(liveObj, resourceOverrides)
|
||||||
savedErr = err
|
if err != nil && savedErr == nil {
|
||||||
|
savedErr = err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if resHealth != nil {
|
if resHealth != nil {
|
||||||
|
|||||||
@@ -118,13 +118,17 @@ func TestSetApplicationHealth(t *testing.T) {
|
|||||||
&runningPod,
|
&runningPod,
|
||||||
&failedJob,
|
&failedJob,
|
||||||
}
|
}
|
||||||
healthStatus, err := SetApplicationHealth(resources, liveObjs, nil)
|
healthStatus, err := SetApplicationHealth(resources, liveObjs, nil, func(obj *unstructured.Unstructured) bool {
|
||||||
|
return true
|
||||||
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, appv1.HealthStatusDegraded, healthStatus.Status)
|
assert.Equal(t, appv1.HealthStatusDegraded, healthStatus.Status)
|
||||||
|
|
||||||
// now mark the job as a hook and retry. it should ignore the hook and consider the app healthy
|
// now mark the job as a hook and retry. it should ignore the hook and consider the app healthy
|
||||||
failedJob.SetAnnotations(map[string]string{common.AnnotationKeyHook: "PreSync"})
|
failedJob.SetAnnotations(map[string]string{common.AnnotationKeyHook: "PreSync"})
|
||||||
healthStatus, err = SetApplicationHealth(resources, liveObjs, nil)
|
healthStatus, err = SetApplicationHealth(resources, liveObjs, nil, func(obj *unstructured.Unstructured) bool {
|
||||||
|
return true
|
||||||
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, appv1.HealthStatusHealthy, healthStatus.Status)
|
assert.Equal(t, appv1.HealthStatusHealthy, healthStatus.Status)
|
||||||
|
|
||||||
|
|||||||
@@ -31,23 +31,30 @@ func GetField(claims jwtgo.MapClaims, fieldName string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGroups extracts the groups from a claims
|
// GetScopeValues extracts the values of specified scopes from the claims
|
||||||
func GetGroups(claims jwtgo.MapClaims) []string {
|
func GetScopeValues(claims jwtgo.MapClaims, scopes []string) []string {
|
||||||
groups := make([]string, 0)
|
groups := make([]string, 0)
|
||||||
groupsIf, ok := claims["groups"]
|
for i := range scopes {
|
||||||
if !ok {
|
scopeIf, ok := claims[scopes[i]]
|
||||||
return groups
|
if !ok {
|
||||||
}
|
continue
|
||||||
groupIfList, ok := groupsIf.([]interface{})
|
}
|
||||||
if !ok {
|
|
||||||
return groups
|
switch val := scopeIf.(type) {
|
||||||
}
|
case []interface{}:
|
||||||
for _, groupIf := range groupIfList {
|
for _, groupIf := range val {
|
||||||
group, ok := groupIf.(string)
|
group, ok := groupIf.(string)
|
||||||
if ok {
|
if ok {
|
||||||
groups = append(groups, group)
|
groups = append(groups, group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case []string:
|
||||||
|
groups = append(groups, val...)
|
||||||
|
case string:
|
||||||
|
groups = append(groups, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return groups
|
return groups
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
21
util/jwt/jwt_test.go
Normal file
21
util/jwt/jwt_test.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
jwt "github.com/dgrijalva/jwt-go"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetSingleStringScope(t *testing.T) {
|
||||||
|
claims := jwt.MapClaims{"groups": "my-org:my-team"}
|
||||||
|
groups := GetScopeValues(claims, []string{"groups"})
|
||||||
|
assert.Contains(t, groups, "my-org:my-team")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMultipleListScopes(t *testing.T) {
|
||||||
|
claims := jwt.MapClaims{"groups1": []string{"my-org:my-team1"}, "groups2": []string{"my-org:my-team2"}}
|
||||||
|
groups := GetScopeValues(claims, []string{"groups1", "groups2"})
|
||||||
|
assert.Contains(t, groups, "my-org:my-team1")
|
||||||
|
assert.Contains(t, groups, "my-org:my-team2")
|
||||||
|
}
|
||||||
@@ -80,9 +80,11 @@ func filterAPIResources(config *rest.Config, resourceFilter ResourceFilter, filt
|
|||||||
gv = schema.GroupVersion{}
|
gv = schema.GroupVersion{}
|
||||||
}
|
}
|
||||||
for _, apiResource := range apiResourcesList.APIResources {
|
for _, apiResource := range apiResourcesList.APIResources {
|
||||||
|
|
||||||
if resourceFilter.IsExcludedResource(gv.Group, apiResource.Kind, config.Host) {
|
if resourceFilter.IsExcludedResource(gv.Group, apiResource.Kind, config.Host) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := isObsoleteExtensionsGroupKind(gv.Group, apiResource.Kind); ok &&
|
if _, ok := isObsoleteExtensionsGroupKind(gv.Group, apiResource.Kind); ok &&
|
||||||
// Edge case for deprecated Ingress kind.
|
// Edge case for deprecated Ingress kind.
|
||||||
!(gv.Group == "extensions" && apiResource.Kind == IngressKind && version.LessThan(ingressDeprecationVersion)) {
|
!(gv.Group == "extensions" && apiResource.Kind == IngressKind && version.LessThan(ingressDeprecationVersion)) {
|
||||||
|
|||||||
@@ -30,4 +30,4 @@ func TestConvertToVersion(t *testing.T) {
|
|||||||
gvk = newObj.GroupVersionKind()
|
gvk = newObj.GroupVersionKind()
|
||||||
assert.Equal(t, "apps", gvk.Group)
|
assert.Equal(t, "apps", gvk.Group)
|
||||||
assert.Equal(t, "v1", gvk.Version)
|
assert.Equal(t, "v1", gvk.Version)
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
@@ -101,6 +103,16 @@ func GetResourceKey(obj *unstructured.Unstructured) ResourceKey {
|
|||||||
return NewResourceKey(gvk.Group, gvk.Kind, obj.GetNamespace(), obj.GetName())
|
return NewResourceKey(gvk.Group, gvk.Kind, obj.GetNamespace(), obj.GetName())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetObjectRef(obj *unstructured.Unstructured) v1.ObjectReference {
|
||||||
|
return v1.ObjectReference{
|
||||||
|
UID: obj.GetUID(),
|
||||||
|
APIVersion: obj.GetAPIVersion(),
|
||||||
|
Kind: obj.GetKind(),
|
||||||
|
Name: obj.GetName(),
|
||||||
|
Namespace: obj.GetNamespace(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestConfig tests to make sure the REST config is usable
|
// TestConfig tests to make sure the REST config is usable
|
||||||
func TestConfig(config *rest.Config) error {
|
func TestConfig(config *rest.Config) error {
|
||||||
kubeclientset, err := kubernetes.NewForConfig(config)
|
kubeclientset, err := kubernetes.NewForConfig(config)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ type ImageTag struct {
|
|||||||
|
|
||||||
func newImageTag(image Image) ImageTag {
|
func newImageTag(image Image) ImageTag {
|
||||||
parts := strings.Split(image, ":")
|
parts := strings.Split(image, ":")
|
||||||
if len(parts) > 0 {
|
if len(parts) > 1 {
|
||||||
return ImageTag{Name: parts[0], Value: parts[1]}
|
return ImageTag{Name: parts[0], Value: parts[1]}
|
||||||
} else {
|
} else {
|
||||||
return ImageTag{Name: parts[0], Value: "latest"}
|
return ImageTag{Name: parts[0], Value: "latest"}
|
||||||
|
|||||||
@@ -134,3 +134,13 @@ func TestPrivateRemoteBase(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, objs, 2)
|
assert.Len(t, objs, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewImageTag(t *testing.T) {
|
||||||
|
tag := newImageTag(Image("busybox"))
|
||||||
|
assert.Equal(t, tag.Name, "busybox")
|
||||||
|
assert.Equal(t, tag.Value, "latest")
|
||||||
|
|
||||||
|
tag = newImageTag(Image("k8s.gcr.io/nginx-slim:0.8"))
|
||||||
|
assert.Equal(t, tag.Name, "k8s.gcr.io/nginx-slim")
|
||||||
|
assert.Equal(t, tag.Value, "0.8")
|
||||||
|
}
|
||||||
|
|||||||
@@ -62,6 +62,13 @@ type ClientApp struct {
|
|||||||
cache *cache.Cache
|
cache *cache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetScopesOrDefault(scopes []string) []string {
|
||||||
|
if len(scopes) == 0 {
|
||||||
|
return []string{"openid", "profile", "email", "groups"}
|
||||||
|
}
|
||||||
|
return scopes
|
||||||
|
}
|
||||||
|
|
||||||
// NewClientApp will register the Argo CD client app (either via Dex or external OIDC) and return an
|
// NewClientApp will register the Argo CD client app (either via Dex or external OIDC) and return an
|
||||||
// object which has HTTP handlers for handling the HTTP responses for login and callback
|
// object which has HTTP handlers for handling the HTTP responses for login and callback
|
||||||
func NewClientApp(settings *settings.ArgoCDSettings, cache *cache.Cache, dexServerAddr string) (*ClientApp, error) {
|
func NewClientApp(settings *settings.ArgoCDSettings, cache *cache.Cache, dexServerAddr string) (*ClientApp, error) {
|
||||||
@@ -93,7 +100,7 @@ func NewClientApp(settings *settings.ArgoCDSettings, cache *cache.Cache, dexServ
|
|||||||
ExpectContinueTimeout: 1 * time.Second,
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if settings.DexConfig != "" {
|
if settings.DexConfig != "" && settings.OIDCConfigRAW == "" {
|
||||||
a.client.Transport = dex.NewDexRewriteURLRoundTripper(dexServerAddr, a.client.Transport)
|
a.client.Transport = dex.NewDexRewriteURLRoundTripper(dexServerAddr, a.client.Transport)
|
||||||
}
|
}
|
||||||
if os.Getenv(common.EnvVarSSODebug) == "1" {
|
if os.Getenv(common.EnvVarSSODebug) == "1" {
|
||||||
@@ -157,8 +164,11 @@ func (a *ClientApp) HandleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
scopes := []string{"openid", "profile", "email", "groups"}
|
scopes := make([]string, 0)
|
||||||
oauth2Config, err := a.oauth2Config(scopes)
|
if config := a.settings.OIDCConfig(); config != nil {
|
||||||
|
scopes = config.RequestedScopes
|
||||||
|
}
|
||||||
|
oauth2Config, err := a.oauth2Config(GetScopesOrDefault(scopes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
ConfigMapPolicyCSVKey = "policy.csv"
|
ConfigMapPolicyCSVKey = "policy.csv"
|
||||||
ConfigMapPolicyDefaultKey = "policy.default"
|
ConfigMapPolicyDefaultKey = "policy.default"
|
||||||
|
ConfigMapScopesKey = "scopes"
|
||||||
|
|
||||||
defaultRBACSyncPeriod = 10 * time.Minute
|
defaultRBACSyncPeriod = 10 * time.Minute
|
||||||
)
|
)
|
||||||
@@ -171,29 +172,29 @@ func (e *Enforcer) newInformer() cache.SharedIndexInformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RunPolicyLoader runs the policy loader which watches policy updates from the configmap and reloads them
|
// RunPolicyLoader runs the policy loader which watches policy updates from the configmap and reloads them
|
||||||
func (e *Enforcer) RunPolicyLoader(ctx context.Context) error {
|
func (e *Enforcer) RunPolicyLoader(ctx context.Context, onUpdated func(cm *apiv1.ConfigMap) error) error {
|
||||||
cm, err := e.clientset.CoreV1().ConfigMaps(e.namespace).Get(e.configmap, metav1.GetOptions{})
|
cm, err := e.clientset.CoreV1().ConfigMaps(e.namespace).Get(e.configmap, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !apierr.IsNotFound(err) {
|
if !apierr.IsNotFound(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = e.syncUpdate(cm)
|
err = e.syncUpdate(cm, onUpdated)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e.runInformer(ctx)
|
e.runInformer(ctx, onUpdated)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Enforcer) runInformer(ctx context.Context) {
|
func (e *Enforcer) runInformer(ctx context.Context, onUpdated func(cm *apiv1.ConfigMap) error) {
|
||||||
cmInformer := e.newInformer()
|
cmInformer := e.newInformer()
|
||||||
cmInformer.AddEventHandler(
|
cmInformer.AddEventHandler(
|
||||||
cache.ResourceEventHandlerFuncs{
|
cache.ResourceEventHandlerFuncs{
|
||||||
AddFunc: func(obj interface{}) {
|
AddFunc: func(obj interface{}) {
|
||||||
if cm, ok := obj.(*apiv1.ConfigMap); ok {
|
if cm, ok := obj.(*apiv1.ConfigMap); ok {
|
||||||
err := e.syncUpdate(cm)
|
err := e.syncUpdate(cm, onUpdated)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
} else {
|
} else {
|
||||||
@@ -207,7 +208,7 @@ func (e *Enforcer) runInformer(ctx context.Context) {
|
|||||||
if oldCM.ResourceVersion == newCM.ResourceVersion {
|
if oldCM.ResourceVersion == newCM.ResourceVersion {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := e.syncUpdate(newCM)
|
err := e.syncUpdate(newCM, onUpdated)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
} else {
|
} else {
|
||||||
@@ -222,12 +223,15 @@ func (e *Enforcer) runInformer(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// syncUpdate updates the enforcer
|
// syncUpdate updates the enforcer
|
||||||
func (e *Enforcer) syncUpdate(cm *apiv1.ConfigMap) error {
|
func (e *Enforcer) syncUpdate(cm *apiv1.ConfigMap, onUpdated func(cm *apiv1.ConfigMap) error) error {
|
||||||
e.SetDefaultRole(cm.Data[ConfigMapPolicyDefaultKey])
|
e.SetDefaultRole(cm.Data[ConfigMapPolicyDefaultKey])
|
||||||
policyCSV, ok := cm.Data[ConfigMapPolicyCSVKey]
|
policyCSV, ok := cm.Data[ConfigMapPolicyCSVKey]
|
||||||
if !ok {
|
if !ok {
|
||||||
policyCSV = ""
|
policyCSV = ""
|
||||||
}
|
}
|
||||||
|
if err := onUpdated(cm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return e.SetUserPolicy(policyCSV)
|
return e.SetUserPolicy(policyCSV)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ const (
|
|||||||
fakeNamespace = "fake-ns"
|
fakeNamespace = "fake-ns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
noOpUpdate = func(cm *apiv1.ConfigMap) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func fakeConfigMap(policy ...string) *apiv1.ConfigMap {
|
func fakeConfigMap(policy ...string) *apiv1.ConfigMap {
|
||||||
cm := apiv1.ConfigMap{
|
cm := apiv1.ConfigMap{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
@@ -42,7 +48,7 @@ func fakeConfigMap(policy ...string) *apiv1.ConfigMap {
|
|||||||
func TestBuiltinPolicyEnforcer(t *testing.T) {
|
func TestBuiltinPolicyEnforcer(t *testing.T) {
|
||||||
kubeclientset := fake.NewSimpleClientset()
|
kubeclientset := fake.NewSimpleClientset()
|
||||||
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
||||||
err := enf.syncUpdate(fakeConfigMap())
|
err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
// Without setting builtin policy, this should fail
|
// Without setting builtin policy, this should fail
|
||||||
@@ -85,7 +91,9 @@ func TestPolicyInformer(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
go enf.runInformer(ctx)
|
go enf.runInformer(ctx, func(cm *apiv1.ConfigMap) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
loaded := false
|
loaded := false
|
||||||
for i := 1; i <= 20; i++ {
|
for i := 1; i <= 20; i++ {
|
||||||
@@ -99,7 +107,7 @@ func TestPolicyInformer(t *testing.T) {
|
|||||||
|
|
||||||
// update the configmap and update policy
|
// update the configmap and update policy
|
||||||
delete(cm.Data, ConfigMapPolicyCSVKey)
|
delete(cm.Data, ConfigMapPolicyCSVKey)
|
||||||
err := enf.syncUpdate(cm)
|
err := enf.syncUpdate(cm, noOpUpdate)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.False(t, enf.Enforce("admin", "applications", "delete", "foo/bar"))
|
assert.False(t, enf.Enforce("admin", "applications", "delete", "foo/bar"))
|
||||||
}
|
}
|
||||||
@@ -207,7 +215,7 @@ g, alice, role:foo-readonly
|
|||||||
func TestDefaultRole(t *testing.T) {
|
func TestDefaultRole(t *testing.T) {
|
||||||
kubeclientset := fake.NewSimpleClientset()
|
kubeclientset := fake.NewSimpleClientset()
|
||||||
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
||||||
err := enf.syncUpdate(fakeConfigMap())
|
err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
||||||
|
|
||||||
@@ -221,7 +229,7 @@ func TestDefaultRole(t *testing.T) {
|
|||||||
func TestURLAsObjectName(t *testing.T) {
|
func TestURLAsObjectName(t *testing.T) {
|
||||||
kubeclientset := fake.NewSimpleClientset()
|
kubeclientset := fake.NewSimpleClientset()
|
||||||
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
||||||
err := enf.syncUpdate(fakeConfigMap())
|
err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
policy := `
|
policy := `
|
||||||
p, alice, repositories, *, foo/*, allow
|
p, alice, repositories, *, foo/*, allow
|
||||||
@@ -321,7 +329,7 @@ func TestClaimsEnforcerFunc(t *testing.T) {
|
|||||||
func TestDefaultRoleWithRuntimePolicy(t *testing.T) {
|
func TestDefaultRoleWithRuntimePolicy(t *testing.T) {
|
||||||
kubeclientset := fake.NewSimpleClientset()
|
kubeclientset := fake.NewSimpleClientset()
|
||||||
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
||||||
err := enf.syncUpdate(fakeConfigMap())
|
err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
runtimePolicy := assets.BuiltinPolicyCSV
|
runtimePolicy := assets.BuiltinPolicyCSV
|
||||||
assert.False(t, enf.EnforceRuntimePolicy(runtimePolicy, "bob", "applications", "get", "foo/bar"))
|
assert.False(t, enf.EnforceRuntimePolicy(runtimePolicy, "bob", "applications", "get", "foo/bar"))
|
||||||
@@ -334,7 +342,7 @@ func TestDefaultRoleWithRuntimePolicy(t *testing.T) {
|
|||||||
func TestClaimsEnforcerFuncWithRuntimePolicy(t *testing.T) {
|
func TestClaimsEnforcerFuncWithRuntimePolicy(t *testing.T) {
|
||||||
kubeclientset := fake.NewSimpleClientset()
|
kubeclientset := fake.NewSimpleClientset()
|
||||||
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
||||||
err := enf.syncUpdate(fakeConfigMap())
|
err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
runtimePolicy := assets.BuiltinPolicyCSV
|
runtimePolicy := assets.BuiltinPolicyCSV
|
||||||
claims := jwt.StandardClaims{
|
claims := jwt.StandardClaims{
|
||||||
@@ -352,7 +360,7 @@ func TestInvalidRuntimePolicy(t *testing.T) {
|
|||||||
cm := fakeConfigMap()
|
cm := fakeConfigMap()
|
||||||
kubeclientset := fake.NewSimpleClientset(cm)
|
kubeclientset := fake.NewSimpleClientset(cm)
|
||||||
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
||||||
err := enf.syncUpdate(fakeConfigMap())
|
err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
_ = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
||||||
assert.True(t, enf.EnforceRuntimePolicy("", "admin", "applications", "update", "foo/bar"))
|
assert.True(t, enf.EnforceRuntimePolicy("", "admin", "applications", "update", "foo/bar"))
|
||||||
@@ -383,7 +391,7 @@ func TestValidatePolicy(t *testing.T) {
|
|||||||
func TestEnforceErrorMessage(t *testing.T) {
|
func TestEnforceErrorMessage(t *testing.T) {
|
||||||
kubeclientset := fake.NewSimpleClientset()
|
kubeclientset := fake.NewSimpleClientset()
|
||||||
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
enf := NewEnforcer(kubeclientset, fakeNamespace, fakeConfgMapName, nil)
|
||||||
err := enf.syncUpdate(fakeConfigMap())
|
err := enf.syncUpdate(fakeConfigMap(), noOpUpdate)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
err = enf.EnforceErr("admin", "applications", "get", "foo/bar")
|
err = enf.EnforceErr("admin", "applications", "get", "foo/bar")
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ExcludedResource struct {
|
type FilteredResource struct {
|
||||||
APIGroups []string `json:"apiGroups,omitempty"`
|
APIGroups []string `json:"apiGroups,omitempty"`
|
||||||
Kinds []string `json:"kinds,omitempty"`
|
Kinds []string `json:"kinds,omitempty"`
|
||||||
Clusters []string `json:"clusters,omitempty"`
|
Clusters []string `json:"clusters,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ExcludedResource) matchGroup(apiGroup string) bool {
|
func (r FilteredResource) matchGroup(apiGroup string) bool {
|
||||||
for _, excludedApiGroup := range r.APIGroups {
|
for _, excludedApiGroup := range r.APIGroups {
|
||||||
if match(excludedApiGroup, apiGroup) {
|
if match(excludedApiGroup, apiGroup) {
|
||||||
return true
|
return true
|
||||||
@@ -29,7 +29,7 @@ func match(pattern, text string) bool {
|
|||||||
return compiledGlob.Match(text)
|
return compiledGlob.Match(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ExcludedResource) matchKind(kind string) bool {
|
func (r FilteredResource) matchKind(kind string) bool {
|
||||||
for _, excludedKind := range r.Kinds {
|
for _, excludedKind := range r.Kinds {
|
||||||
if excludedKind == "*" || excludedKind == kind {
|
if excludedKind == "*" || excludedKind == kind {
|
||||||
return true
|
return true
|
||||||
@@ -38,7 +38,7 @@ func (r ExcludedResource) matchKind(kind string) bool {
|
|||||||
return len(r.Kinds) == 0
|
return len(r.Kinds) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ExcludedResource) matchCluster(cluster string) bool {
|
func (r FilteredResource) matchCluster(cluster string) bool {
|
||||||
for _, excludedCluster := range r.Clusters {
|
for _, excludedCluster := range r.Clusters {
|
||||||
if match(excludedCluster, cluster) {
|
if match(excludedCluster, cluster) {
|
||||||
return true
|
return true
|
||||||
@@ -47,6 +47,6 @@ func (r ExcludedResource) matchCluster(cluster string) bool {
|
|||||||
return len(r.Clusters) == 0
|
return len(r.Clusters) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ExcludedResource) Match(apiGroup, kind, cluster string) bool {
|
func (r FilteredResource) Match(apiGroup, kind, cluster string) bool {
|
||||||
return r.matchGroup(apiGroup) && r.matchKind(kind) && r.matchCluster(cluster)
|
return r.matchGroup(apiGroup) && r.matchKind(kind) && r.matchCluster(cluster)
|
||||||
}
|
}
|
||||||
@@ -12,28 +12,28 @@ func TestExcludeResource(t *testing.T) {
|
|||||||
cluster := "baz.com"
|
cluster := "baz.com"
|
||||||
|
|
||||||
// matches with missing values
|
// matches with missing values
|
||||||
assert.True(t, ExcludedResource{Kinds: []string{kind}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
assert.True(t, FilteredResource{Kinds: []string{kind}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
||||||
assert.True(t, ExcludedResource{APIGroups: []string{apiGroup}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
assert.True(t, FilteredResource{APIGroups: []string{apiGroup}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
||||||
assert.True(t, ExcludedResource{APIGroups: []string{apiGroup}, Kinds: []string{"*"}}.Match(apiGroup, kind, cluster))
|
assert.True(t, FilteredResource{APIGroups: []string{apiGroup}, Kinds: []string{"*"}}.Match(apiGroup, kind, cluster))
|
||||||
|
|
||||||
// simple matches
|
// simple matches
|
||||||
assert.True(t, ExcludedResource{APIGroups: []string{apiGroup}, Kinds: []string{kind}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
assert.True(t, FilteredResource{APIGroups: []string{apiGroup}, Kinds: []string{kind}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
||||||
assert.True(t, ExcludedResource{APIGroups: []string{"*.com"}, Kinds: []string{kind}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
assert.True(t, FilteredResource{APIGroups: []string{"*.com"}, Kinds: []string{kind}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
||||||
assert.True(t, ExcludedResource{APIGroups: []string{apiGroup}, Kinds: []string{"*"}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
assert.True(t, FilteredResource{APIGroups: []string{apiGroup}, Kinds: []string{"*"}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
||||||
assert.True(t, ExcludedResource{APIGroups: []string{apiGroup}, Kinds: []string{kind}, Clusters: []string{"*.com"}}.Match(apiGroup, kind, cluster))
|
assert.True(t, FilteredResource{APIGroups: []string{apiGroup}, Kinds: []string{kind}, Clusters: []string{"*.com"}}.Match(apiGroup, kind, cluster))
|
||||||
|
|
||||||
// negative matches
|
// negative matches
|
||||||
assert.False(t, ExcludedResource{APIGroups: []string{""}, Kinds: []string{kind}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
assert.False(t, FilteredResource{APIGroups: []string{""}, Kinds: []string{kind}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
||||||
assert.False(t, ExcludedResource{APIGroups: []string{apiGroup}, Kinds: []string{""}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
assert.False(t, FilteredResource{APIGroups: []string{apiGroup}, Kinds: []string{""}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
||||||
assert.False(t, ExcludedResource{APIGroups: []string{apiGroup}, Kinds: []string{kind}, Clusters: []string{""}}.Match(apiGroup, kind, cluster))
|
assert.False(t, FilteredResource{APIGroups: []string{apiGroup}, Kinds: []string{kind}, Clusters: []string{""}}.Match(apiGroup, kind, cluster))
|
||||||
|
|
||||||
// complex matches
|
// complex matches
|
||||||
assert.True(t, ExcludedResource{APIGroups: []string{apiGroup, apiGroup}, Kinds: []string{kind}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
assert.True(t, FilteredResource{APIGroups: []string{apiGroup, apiGroup}, Kinds: []string{kind}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
||||||
assert.True(t, ExcludedResource{APIGroups: []string{apiGroup}, Kinds: []string{kind, kind}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
assert.True(t, FilteredResource{APIGroups: []string{apiGroup}, Kinds: []string{kind, kind}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
||||||
assert.True(t, ExcludedResource{APIGroups: []string{apiGroup}, Kinds: []string{kind}, Clusters: []string{cluster, cluster}}.Match(apiGroup, kind, cluster))
|
assert.True(t, FilteredResource{APIGroups: []string{apiGroup}, Kinds: []string{kind}, Clusters: []string{cluster, cluster}}.Match(apiGroup, kind, cluster))
|
||||||
|
|
||||||
// rubbish patterns
|
// rubbish patterns
|
||||||
assert.False(t, ExcludedResource{APIGroups: []string{"["}, Kinds: []string{""}, Clusters: []string{""}}.Match("", "", ""))
|
assert.False(t, FilteredResource{APIGroups: []string{"["}, Kinds: []string{""}, Clusters: []string{""}}.Match("", "", ""))
|
||||||
assert.False(t, ExcludedResource{APIGroups: []string{""}, Kinds: []string{"["}, Clusters: []string{""}}.Match("", "", ""))
|
assert.False(t, FilteredResource{APIGroups: []string{""}, Kinds: []string{"["}, Clusters: []string{""}}.Match("", "", ""))
|
||||||
assert.False(t, ExcludedResource{APIGroups: []string{""}, Kinds: []string{""}, Clusters: []string{"["}}.Match("", "", ""))
|
assert.False(t, FilteredResource{APIGroups: []string{""}, Kinds: []string{""}, Clusters: []string{"["}}.Match("", "", ""))
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user