mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 17:48:47 +01:00
Compare commits
16 Commits
commit-ser
...
v1.0.0-rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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
|
||||
|
||||
### Changes since v0.12.1
|
||||
|
||||
#### 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)
|
||||
### Changes since v0.12.2
|
||||
|
||||
#### Enhancements
|
||||
|
||||
* `argocd app wait` should have `--resource` flag like sync #1206
|
||||
* Adds support for `kustomize edit set image`. Closes #1275 (#1324)
|
||||
* Allow wait to return on health or suspended (#1392)
|
||||
* 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
|
||||
* 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)
|
||||
* Issue #357 - Expose application nodes networking information (#1333)
|
||||
* Issue #1404 - App controller unnecessary set namespace to cluster level resources (#1405)
|
||||
* 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)
|
||||
* Remove deprecated componentParameterOverrides field #1372
|
||||
* 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
|
||||
* Support for customizable resource actions as Lua scripts #86
|
||||
* UI / API Errors Truncated, Time Out #1386
|
||||
* 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
|
||||
|
||||
#### 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)
|
||||
- Dropdown menu should not have sync item for unmanaged resources #1357
|
||||
- Fixes goroutine leak. Closes #1381 (#1457)
|
||||
- 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)
|
||||
- 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
|
||||
- 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
|
||||
|
||||
## v0.12.2 (2019-04-22)
|
||||
|
||||
3
Makefile
3
Makefile
@@ -148,6 +148,7 @@ test-e2e: cli
|
||||
|
||||
.PHONY: start-e2e
|
||||
start-e2e: cli
|
||||
killall goreman || true
|
||||
kubectl create ns argocd-e2e || true
|
||||
kubens argocd-e2e
|
||||
kustomize build test/manifests/base | kubectl apply -f -
|
||||
@@ -178,4 +179,4 @@ release-precheck: manifests
|
||||
@if [ "$(GIT_TAG)" != "v`cat VERSION`" ]; then echo 'VERSION does not match git tag'; exit 1; fi
|
||||
|
||||
.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"
|
||||
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"
|
||||
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
|
||||
|
||||
@@ -37,14 +37,15 @@ const (
|
||||
|
||||
func newCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
appResyncPeriod int64
|
||||
repoServerAddress string
|
||||
statusProcessors int
|
||||
operationProcessors int
|
||||
logLevel string
|
||||
glogLevel int
|
||||
cacheSrc func() (*cache.Cache, error)
|
||||
clientConfig clientcmd.ClientConfig
|
||||
appResyncPeriod int64
|
||||
repoServerAddress string
|
||||
repoServerTimeoutSeconds int
|
||||
statusProcessors int
|
||||
operationProcessors int
|
||||
logLevel string
|
||||
glogLevel int
|
||||
cacheSrc func() (*cache.Cache, error)
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -65,7 +66,7 @@ func newCommand() *cobra.Command {
|
||||
errors.CheckError(err)
|
||||
|
||||
resyncDuration := time.Duration(appResyncPeriod) * time.Second
|
||||
repoClientset := reposerver.NewRepoServerClientset(repoServerAddress)
|
||||
repoClientset := reposerver.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@@ -98,6 +99,7 @@ func newCommand() *cobra.Command {
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
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().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(&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")
|
||||
|
||||
@@ -57,7 +57,7 @@ func NewCommand() *cobra.Command {
|
||||
|
||||
kubeclientset := kubernetes.NewForConfigOrDie(config)
|
||||
appclientset := appclientset.NewForConfigOrDie(config)
|
||||
repoclientset := reposerver.NewRepoServerClientset(repoServerAddress)
|
||||
repoclientset := reposerver.NewRepoServerClientset(repoServerAddress, 0)
|
||||
|
||||
argoCDOpts := server.ArgoCDServerOpts{
|
||||
Insecure: insecure,
|
||||
|
||||
@@ -957,6 +957,7 @@ func formatConditionsSummary(app argoappv1.Application) string {
|
||||
const (
|
||||
resourceFieldDelimiter = ":"
|
||||
resourceFieldCount = 3
|
||||
labelFieldDelimiter = "="
|
||||
)
|
||||
|
||||
func parseSelectedResources(resources []string) []argoappv1.SyncOperationResource {
|
||||
@@ -979,6 +980,21 @@ func parseSelectedResources(resources []string) []argoappv1.SyncOperationResourc
|
||||
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
|
||||
func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
@@ -1073,6 +1089,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
var (
|
||||
revision string
|
||||
resources []string
|
||||
labels []string
|
||||
prune bool
|
||||
dryRun bool
|
||||
timeout uint
|
||||
@@ -1091,8 +1108,51 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
conn, appIf := acdClient.NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
|
||||
selectedResources := parseSelectedResources(resources)
|
||||
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{
|
||||
Name: &appName,
|
||||
DryRun: dryRun,
|
||||
@@ -1117,18 +1177,21 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
app, err := waitOnApplicationStatus(acdClient, appName, timeout, false, false, true, false, selectedResources)
|
||||
errors.CheckError(err)
|
||||
|
||||
pruningRequired := 0
|
||||
for _, resDetails := range app.Status.OperationState.SyncResult.Resources {
|
||||
if resDetails.Status == argoappv1.ResultCodePruneSkipped {
|
||||
pruningRequired++
|
||||
// Only get resources to be pruned if sync was application-wide
|
||||
if len(selectedResources) == 0 {
|
||||
pruningRequired := 0
|
||||
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 {
|
||||
os.Exit(1)
|
||||
if !app.Status.OperationState.Phase.Successful() && !dryRun {
|
||||
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().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(&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().StringVar(&strategy, "strategy", "", "Sync strategy (one of: apply|hook)")
|
||||
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)
|
||||
|
||||
}
|
||||
@@ -110,18 +110,19 @@ func NewApplicationController(
|
||||
}
|
||||
appInformer, appLister := ctrl.newApplicationInformerAndLister()
|
||||
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.metricsServer = metrics.NewMetricsServer(metricsAddr, appLister)
|
||||
stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settings, kubectlCmd, ctrl.metricsServer, func(appName string, fullRefresh bool) {
|
||||
ctrl.requestAppRefresh(appName, fullRefresh)
|
||||
ctrl.appRefreshQueue.Add(fmt.Sprintf("%s/%s", ctrl.namespace, appName))
|
||||
})
|
||||
appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectlCmd, ctrl.settings, stateCache, projInformer)
|
||||
appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectlCmd, ctrl.settings, stateCache, projInformer, ctrl.metricsServer)
|
||||
ctrl.appInformer = appInformer
|
||||
ctrl.appLister = appLister
|
||||
ctrl.projInformer = projInformer
|
||||
ctrl.appStateManager = appStateManager
|
||||
ctrl.stateCache = stateCache
|
||||
metricsAddr := fmt.Sprintf("0.0.0.0:%d", common.PortArgoCDMetrics)
|
||||
ctrl.metricsServer = metrics.NewMetricsServer(metricsAddr, ctrl.appLister)
|
||||
|
||||
return &ctrl, nil
|
||||
}
|
||||
|
||||
@@ -349,9 +350,16 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic
|
||||
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 {
|
||||
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 {
|
||||
return err
|
||||
@@ -688,7 +696,7 @@ func (ctrl *ApplicationController) refreshAppConditions(app *appv1.Application)
|
||||
})
|
||||
}
|
||||
} 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 {
|
||||
conditions = append(conditions, appv1.ApplicationCondition{
|
||||
Type: appv1.ApplicationConditionUnknownError,
|
||||
@@ -927,6 +935,7 @@ func (ctrl *ApplicationController) watchSettings(ctx context.Context) {
|
||||
ctrl.settingsMgr.Subscribe(updateCh)
|
||||
prevAppLabelKey := ctrl.settings.GetAppInstanceLabelKey()
|
||||
prevResourceExclusions := ctrl.settings.ResourceExclusions
|
||||
prevResourceInclusions := ctrl.settings.ResourceInclusions
|
||||
done := false
|
||||
for !done {
|
||||
select {
|
||||
@@ -939,10 +948,15 @@ func (ctrl *ApplicationController) watchSettings(ctx context.Context) {
|
||||
prevAppLabelKey = newAppLabelKey
|
||||
}
|
||||
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()
|
||||
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():
|
||||
done = true
|
||||
}
|
||||
|
||||
52
controller/cache/cache.go
vendored
52
controller/cache/cache.go
vendored
@@ -10,6 +10,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"github.com/argoproj/argo-cd/controller/metrics"
|
||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/db"
|
||||
@@ -25,8 +26,6 @@ type LiveStateCache interface {
|
||||
GetManagedLiveObjs(a *appv1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error)
|
||||
// Starts watching resources of each controlled cluster.
|
||||
Run(ctx context.Context)
|
||||
// Deletes specified resource from cluster.
|
||||
Delete(server string, obj *unstructured.Unstructured) error
|
||||
// Invalidate invalidates the entire cluster state cache
|
||||
Invalidate()
|
||||
}
|
||||
@@ -42,26 +41,35 @@ func GetTargetObjKey(a *appv1.Application, un *unstructured.Unstructured, isName
|
||||
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 func(appName string, fullRefresh bool)) LiveStateCache {
|
||||
|
||||
return &liveStateCache{
|
||||
appInformer: appInformer,
|
||||
db: db,
|
||||
clusters: make(map[string]*clusterInfo),
|
||||
lock: &sync.Mutex{},
|
||||
onAppUpdated: onAppUpdated,
|
||||
kubectl: kubectl,
|
||||
settings: settings,
|
||||
appInformer: appInformer,
|
||||
db: db,
|
||||
clusters: make(map[string]*clusterInfo),
|
||||
lock: &sync.Mutex{},
|
||||
onAppUpdated: onAppUpdated,
|
||||
kubectl: kubectl,
|
||||
settings: settings,
|
||||
metricsServer: metricsServer,
|
||||
}
|
||||
}
|
||||
|
||||
type liveStateCache struct {
|
||||
db db.ArgoDB
|
||||
clusters map[string]*clusterInfo
|
||||
lock *sync.Mutex
|
||||
appInformer cache.SharedIndexInformer
|
||||
onAppUpdated func(appName string, fullRefresh bool)
|
||||
kubectl kube.Kubectl
|
||||
settings *settings.ArgoCDSettings
|
||||
db db.ArgoDB
|
||||
clusters map[string]*clusterInfo
|
||||
lock *sync.Mutex
|
||||
appInformer cache.SharedIndexInformer
|
||||
onAppUpdated func(appName string, fullRefresh bool)
|
||||
kubectl kube.Kubectl
|
||||
settings *settings.ArgoCDSettings
|
||||
metricsServer *metrics.MetricsServer
|
||||
}
|
||||
|
||||
func (c *liveStateCache) getCluster(server string) (*clusterInfo, error) {
|
||||
@@ -116,14 +124,6 @@ func (c *liveStateCache) Invalidate() {
|
||||
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) {
|
||||
clusterInfo, err := c.getSyncedCluster(server)
|
||||
if err != nil {
|
||||
@@ -146,7 +146,7 @@ func (c *liveStateCache) GetManagedLiveObjs(a *appv1.Application, targetObjs []*
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clusterInfo.getManagedLiveObjs(a, targetObjs)
|
||||
return clusterInfo.getManagedLiveObjs(a, targetObjs, c.metricsServer)
|
||||
}
|
||||
|
||||
func isClusterHasApps(apps []interface{}, cluster *appv1.Cluster) bool {
|
||||
|
||||
25
controller/cache/cluster.go
vendored
25
controller/cache/cluster.go
vendored
@@ -7,6 +7,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/controller/metrics"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -351,7 +353,7 @@ func (c *clusterInfo) isNamespaced(obj *unstructured.Unstructured) bool {
|
||||
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()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
@@ -362,6 +364,7 @@ func (c *clusterInfo) getManagedLiveObjs(a *appv1.Application, targetObjs []*uns
|
||||
managedObjs[key] = o.resource
|
||||
}
|
||||
}
|
||||
config := metrics.AddMetricsTransportWrapper(metricsServer, a, c.cluster.RESTConfig())
|
||||
// iterate target objects and identify ones that already exist in the cluster,\
|
||||
// but are simply missing our label
|
||||
lock := &sync.Mutex{}
|
||||
@@ -378,7 +381,7 @@ func (c *clusterInfo) getManagedLiveObjs(a *appv1.Application, targetObjs []*uns
|
||||
managedObj = existingObj.resource
|
||||
} else {
|
||||
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 errors.IsNotFound(err) {
|
||||
return nil
|
||||
@@ -390,9 +393,19 @@ func (c *clusterInfo) getManagedLiveObjs(a *appv1.Application, targetObjs []*uns
|
||||
}
|
||||
|
||||
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 {
|
||||
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()
|
||||
managedObjs[key] = managedObj
|
||||
@@ -407,10 +420,6 @@ func (c *clusterInfo) getManagedLiveObjs(a *appv1.Application, targetObjs []*uns
|
||||
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 {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
6
controller/cache/cluster_test.go
vendored
6
controller/cache/cluster_test.go
vendored
@@ -107,6 +107,10 @@ var (
|
||||
serviceName: helm-guestbook
|
||||
servicePort: 443
|
||||
path: /
|
||||
- backend:
|
||||
serviceName: helm-guestbook
|
||||
servicePort: https
|
||||
path: /
|
||||
status:
|
||||
loadBalancer:
|
||||
ingress:
|
||||
@@ -225,7 +229,7 @@ metadata:
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
}, []*unstructured.Unstructured{targetDeploy})
|
||||
}, []*unstructured.Unstructured{targetDeploy}, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, managedObjs, map[kube.ResourceKey]*unstructured.Unstructured{
|
||||
kube.NewResourceKey("apps", "Deployment", "default", "helm-guestbook"): testDeploy,
|
||||
|
||||
18
controller/cache/info.go
vendored
18
controller/cache/info.go
vendored
@@ -63,14 +63,14 @@ func populateServiceInfo(un *unstructured.Unstructured, node *node) {
|
||||
}
|
||||
|
||||
func populateIngressInfo(un *unstructured.Unstructured, node *node) {
|
||||
targets := make([]v1alpha1.ResourceRef, 0)
|
||||
targetsMap := make(map[v1alpha1.ResourceRef]bool)
|
||||
if backend, ok, err := unstructured.NestedMap(un.Object, "spec", "backend"); ok && err == nil {
|
||||
targets = append(targets, v1alpha1.ResourceRef{
|
||||
targetsMap[v1alpha1.ResourceRef{
|
||||
Group: "",
|
||||
Kind: kube.ServiceKind,
|
||||
Namespace: un.GetNamespace(),
|
||||
Name: fmt.Sprintf("%s", backend["serviceName"]),
|
||||
})
|
||||
}] = true
|
||||
}
|
||||
urlsSet := make(map[string]bool)
|
||||
if rules, ok, err := unstructured.NestedSlice(un.Object, "spec", "rules"); ok && err == nil {
|
||||
@@ -91,19 +91,19 @@ func populateIngressInfo(un *unstructured.Unstructured, node *node) {
|
||||
}
|
||||
|
||||
if serviceName, ok, err := unstructured.NestedString(path, "backend", "serviceName"); ok && err == nil {
|
||||
targets = append(targets, v1alpha1.ResourceRef{
|
||||
targetsMap[v1alpha1.ResourceRef{
|
||||
Group: "",
|
||||
Kind: kube.ServiceKind,
|
||||
Namespace: un.GetNamespace(),
|
||||
Name: serviceName,
|
||||
})
|
||||
}] = true
|
||||
}
|
||||
|
||||
if port, ok, err := unstructured.NestedFieldNoCopy(path, "backend", "servicePort"); ok && err == nil && host != "" {
|
||||
switch fmt.Sprintf("%v", port) {
|
||||
case "80":
|
||||
case "80", "http":
|
||||
urlsSet[fmt.Sprintf("http://%s", host)] = true
|
||||
case "443":
|
||||
case "443", "https":
|
||||
urlsSet[fmt.Sprintf("https://%s", host)] = true
|
||||
default:
|
||||
urlsSet[fmt.Sprintf("http://%s:%s", host, port)] = true
|
||||
@@ -112,6 +112,10 @@ func populateIngressInfo(un *unstructured.Unstructured, node *node) {
|
||||
}
|
||||
}
|
||||
}
|
||||
targets := make([]v1alpha1.ResourceRef, 0)
|
||||
for target := range targetsMap {
|
||||
targets = append(targets, target)
|
||||
}
|
||||
urls := make([]string, 0)
|
||||
for url := range urlsSet {
|
||||
urls = append(urls, url)
|
||||
|
||||
@@ -2,6 +2,7 @@ package metrics
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
type MetricsServer struct {
|
||||
*http.Server
|
||||
syncCounter *prometheus.CounterVec
|
||||
k8sRequestCounter *prometheus.CounterVec
|
||||
reconcileHistogram *prometheus.HistogramVec
|
||||
}
|
||||
|
||||
@@ -72,6 +74,14 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister) *Metri
|
||||
append(descAppDefaultLabels, "phase"),
|
||||
)
|
||||
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(
|
||||
prometheus.HistogramOpts{
|
||||
@@ -91,6 +101,7 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister) *Metri
|
||||
Handler: mux,
|
||||
},
|
||||
syncCounter: syncCounter,
|
||||
k8sRequestCounter: k8sRequestCounter,
|
||||
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()
|
||||
}
|
||||
|
||||
// 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
|
||||
func (m *MetricsServer) IncReconcile(app *argoappv1.Application, duration time.Duration) {
|
||||
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"
|
||||
statecache "github.com/argoproj/argo-cd/controller/cache"
|
||||
"github.com/argoproj/argo-cd/controller/metrics"
|
||||
"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"
|
||||
@@ -73,6 +74,7 @@ type comparisonResult struct {
|
||||
|
||||
// appStateManager allows to compare applications to git
|
||||
type appStateManager struct {
|
||||
metricsServer *metrics.MetricsServer
|
||||
db db.ArgoDB
|
||||
settings *settings.ArgoCDSettings
|
||||
appclientset appclientset.Interface
|
||||
@@ -379,6 +381,7 @@ func NewAppStateManager(
|
||||
settings *settings.ArgoCDSettings,
|
||||
liveStateCache statecache.LiveStateCache,
|
||||
projInformer cache.SharedIndexInformer,
|
||||
metricsServer *metrics.MetricsServer,
|
||||
) AppStateManager {
|
||||
return &appStateManager{
|
||||
liveStateCache: liveStateCache,
|
||||
@@ -389,5 +392,6 @@ func NewAppStateManager(
|
||||
namespace: namespace,
|
||||
settings: settings,
|
||||
projInformer: projInformer,
|
||||
metricsServer: metricsServer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"github.com/argoproj/argo-cd/controller/metrics"
|
||||
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/util/argo"
|
||||
@@ -116,7 +117,7 @@ func (m *appStateManager) SyncAppState(app *appv1.Application, state *appv1.Oper
|
||||
return
|
||||
}
|
||||
|
||||
restConfig := clst.RESTConfig()
|
||||
restConfig := metrics.AddMetricsTransportWrapper(m.metricsServer, app, clst.RESTConfig())
|
||||
dynamicIf, err := dynamic.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
state.Phase = appv1.OperationError
|
||||
|
||||
@@ -150,6 +150,88 @@ data:
|
||||
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
|
||||
|
||||
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:
|
||||
- name: argoproj/argocd
|
||||
newName: argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v1.0.0-rc2
|
||||
- name: argoproj/argocd-ui
|
||||
newName: argoproj/argocd-ui
|
||||
newTag: latest
|
||||
newTag: v1.0.0-rc2
|
||||
|
||||
@@ -17,7 +17,7 @@ patchesStrategicMerge:
|
||||
images:
|
||||
- name: argoproj/argocd
|
||||
newName: argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v1.0.0-rc2
|
||||
- name: argoproj/argocd-ui
|
||||
newName: argoproj/argocd-ui
|
||||
newTag: latest
|
||||
newTag: v1.0.0-rc2
|
||||
|
||||
@@ -185,10 +185,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -419,10 +415,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources for
|
||||
kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository containing
|
||||
@@ -488,6 +480,12 @@ spec:
|
||||
- message
|
||||
type: object
|
||||
type: array
|
||||
externalURLs:
|
||||
description: ExternalURLs holds all external URLs of application child
|
||||
resources.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
health:
|
||||
properties:
|
||||
message:
|
||||
@@ -633,10 +631,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -670,9 +664,6 @@ spec:
|
||||
- source
|
||||
type: object
|
||||
type: array
|
||||
ingress:
|
||||
items: {}
|
||||
type: array
|
||||
observedAt: {}
|
||||
operationState:
|
||||
description: OperationState contains information about state of currently
|
||||
@@ -853,10 +844,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to
|
||||
resources for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -1092,10 +1079,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -1312,10 +1295,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -2138,7 +2117,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
@@ -2185,7 +2164,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2240,7 +2219,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: argocd-repo-server
|
||||
ports:
|
||||
@@ -2297,7 +2276,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: argocd-server
|
||||
ports:
|
||||
@@ -2318,7 +2297,7 @@ spec:
|
||||
- -r
|
||||
- /app
|
||||
- /shared
|
||||
image: argoproj/argocd-ui:latest
|
||||
image: argoproj/argocd-ui:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: ui
|
||||
volumeMounts:
|
||||
|
||||
@@ -185,10 +185,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -419,10 +415,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources for
|
||||
kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository containing
|
||||
@@ -488,6 +480,12 @@ spec:
|
||||
- message
|
||||
type: object
|
||||
type: array
|
||||
externalURLs:
|
||||
description: ExternalURLs holds all external URLs of application child
|
||||
resources.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
health:
|
||||
properties:
|
||||
message:
|
||||
@@ -633,10 +631,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -670,9 +664,6 @@ spec:
|
||||
- source
|
||||
type: object
|
||||
type: array
|
||||
ingress:
|
||||
items: {}
|
||||
type: array
|
||||
observedAt: {}
|
||||
operationState:
|
||||
description: OperationState contains information about state of currently
|
||||
@@ -853,10 +844,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to
|
||||
resources for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -1092,10 +1079,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -1312,10 +1295,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -2053,7 +2032,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
@@ -2100,7 +2079,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2155,7 +2134,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: argocd-repo-server
|
||||
ports:
|
||||
@@ -2212,7 +2191,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: argocd-server
|
||||
ports:
|
||||
@@ -2233,7 +2212,7 @@ spec:
|
||||
- -r
|
||||
- /app
|
||||
- /shared
|
||||
image: argoproj/argocd-ui:latest
|
||||
image: argoproj/argocd-ui:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: ui
|
||||
volumeMounts:
|
||||
|
||||
@@ -185,10 +185,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -419,10 +415,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources for
|
||||
kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository containing
|
||||
@@ -488,6 +480,12 @@ spec:
|
||||
- message
|
||||
type: object
|
||||
type: array
|
||||
externalURLs:
|
||||
description: ExternalURLs holds all external URLs of application child
|
||||
resources.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
health:
|
||||
properties:
|
||||
message:
|
||||
@@ -633,10 +631,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -670,9 +664,6 @@ spec:
|
||||
- source
|
||||
type: object
|
||||
type: array
|
||||
ingress:
|
||||
items: {}
|
||||
type: array
|
||||
observedAt: {}
|
||||
operationState:
|
||||
description: OperationState contains information about state of currently
|
||||
@@ -853,10 +844,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to
|
||||
resources for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -1092,10 +1079,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -1312,10 +1295,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -1902,7 +1881,7 @@ spec:
|
||||
- "20"
|
||||
- --operation-processors
|
||||
- "10"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
@@ -1949,7 +1928,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2012,7 +1991,7 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: argocd-repo-server
|
||||
ports:
|
||||
@@ -2046,7 +2025,7 @@ spec:
|
||||
- argocd-server
|
||||
- --staticassets
|
||||
- /shared/app
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: argocd-server
|
||||
ports:
|
||||
@@ -2067,7 +2046,7 @@ spec:
|
||||
- -r
|
||||
- /app
|
||||
- /shared
|
||||
image: argoproj/argocd-ui:latest
|
||||
image: argoproj/argocd-ui:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: ui
|
||||
volumeMounts:
|
||||
|
||||
@@ -185,10 +185,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -419,10 +415,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources for
|
||||
kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository containing
|
||||
@@ -488,6 +480,12 @@ spec:
|
||||
- message
|
||||
type: object
|
||||
type: array
|
||||
externalURLs:
|
||||
description: ExternalURLs holds all external URLs of application child
|
||||
resources.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
health:
|
||||
properties:
|
||||
message:
|
||||
@@ -633,10 +631,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -670,9 +664,6 @@ spec:
|
||||
- source
|
||||
type: object
|
||||
type: array
|
||||
ingress:
|
||||
items: {}
|
||||
type: array
|
||||
observedAt: {}
|
||||
operationState:
|
||||
description: OperationState contains information about state of currently
|
||||
@@ -853,10 +844,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to
|
||||
resources for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -1092,10 +1079,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -1312,10 +1295,6 @@ spec:
|
||||
description: NamePrefix is a prefix appended to resources
|
||||
for kustomize apps
|
||||
type: string
|
||||
required:
|
||||
- namePrefix
|
||||
- imageTags
|
||||
- images
|
||||
type: object
|
||||
path:
|
||||
description: Path is a directory path within the repository
|
||||
@@ -1817,7 +1796,7 @@ spec:
|
||||
- "20"
|
||||
- --operation-processors
|
||||
- "10"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
@@ -1864,7 +1843,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -1927,7 +1906,7 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: argocd-repo-server
|
||||
ports:
|
||||
@@ -1961,7 +1940,7 @@ spec:
|
||||
- argocd-server
|
||||
- --staticassets
|
||||
- /shared/app
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: argocd-server
|
||||
ports:
|
||||
@@ -1982,7 +1961,7 @@ spec:
|
||||
- -r
|
||||
- /app
|
||||
- /shared
|
||||
image: argoproj/argocd-ui:latest
|
||||
image: argoproj/argocd-ui:v1.0.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: ui
|
||||
volumeMounts:
|
||||
|
||||
@@ -104,6 +104,7 @@ func schema_pkg_apis_application_v1alpha1_AWSAuthConfig(ref common.ReferenceCall
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,6 +358,7 @@ func schema_pkg_apis_application_v1alpha1_ApplicationCondition(ref common.Refere
|
||||
Required: []string{"type", "message"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,6 +386,7 @@ func schema_pkg_apis_application_v1alpha1_ApplicationDestination(ref common.Refe
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,6 +712,7 @@ func schema_pkg_apis_application_v1alpha1_ApplicationSourcePlugin(ref common.Ref
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1076,6 +1080,7 @@ func schema_pkg_apis_application_v1alpha1_Command(ref common.ReferenceCallback)
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1134,6 +1139,7 @@ func schema_pkg_apis_application_v1alpha1_ComponentParameter(ref common.Referenc
|
||||
Required: []string{"name", "value"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1223,6 +1229,7 @@ func schema_pkg_apis_application_v1alpha1_HealthStatus(ref common.ReferenceCallb
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1250,6 +1257,7 @@ func schema_pkg_apis_application_v1alpha1_HelmParameter(ref common.ReferenceCall
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1305,6 +1313,7 @@ func schema_pkg_apis_application_v1alpha1_HelmRepository(ref common.ReferenceCal
|
||||
Required: []string{"url", "name"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1332,6 +1341,7 @@ func schema_pkg_apis_application_v1alpha1_InfoItem(ref common.ReferenceCallback)
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1358,6 +1368,7 @@ func schema_pkg_apis_application_v1alpha1_JWTToken(ref common.ReferenceCallback)
|
||||
Required: []string{"iat"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1390,6 +1401,7 @@ func schema_pkg_apis_application_v1alpha1_JsonnetVar(ref common.ReferenceCallbac
|
||||
Required: []string{"name", "value"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1422,6 +1434,7 @@ func schema_pkg_apis_application_v1alpha1_KsonnetParameter(ref common.ReferenceC
|
||||
Required: []string{"name", "value"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1449,6 +1462,7 @@ func schema_pkg_apis_application_v1alpha1_KustomizeImageTag(ref common.Reference
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1735,6 +1749,7 @@ func schema_pkg_apis_application_v1alpha1_ResourceActionDefinition(ref common.Re
|
||||
Required: []string{"name", "action.lua"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1771,6 +1786,7 @@ func schema_pkg_apis_application_v1alpha1_ResourceActionParam(ref common.Referen
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1859,6 +1875,7 @@ func schema_pkg_apis_application_v1alpha1_ResourceDiff(ref common.ReferenceCallb
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1910,6 +1927,7 @@ func schema_pkg_apis_application_v1alpha1_ResourceIgnoreDifferences(ref common.R
|
||||
Required: []string{"group", "kind", "jsonPointers"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1924,7 +1942,6 @@ func schema_pkg_apis_application_v1alpha1_ResourceNetworkingInfo(ref common.Refe
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
AdditionalProperties: &spec.SchemaOrBool{
|
||||
Allows: true,
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
@@ -1950,7 +1967,6 @@ func schema_pkg_apis_application_v1alpha1_ResourceNetworkingInfo(ref common.Refe
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
AdditionalProperties: &spec.SchemaOrBool{
|
||||
Allows: true,
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
@@ -2120,6 +2136,7 @@ func schema_pkg_apis_application_v1alpha1_ResourceOverride(ref common.ReferenceC
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2163,6 +2180,7 @@ func schema_pkg_apis_application_v1alpha1_ResourceRef(ref common.ReferenceCallba
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2231,6 +2249,7 @@ func schema_pkg_apis_application_v1alpha1_ResourceResult(ref common.ReferenceCal
|
||||
Required: []string{"group", "version", "kind", "namespace", "name"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2424,6 +2443,7 @@ func schema_pkg_apis_application_v1alpha1_SyncOperationResource(ref common.Refer
|
||||
Required: []string{"kind", "name"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2507,6 +2527,7 @@ func schema_pkg_apis_application_v1alpha1_SyncPolicyAutomated(ref common.Referen
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2587,6 +2608,7 @@ func schema_pkg_apis_application_v1alpha1_SyncStrategyApply(ref common.Reference
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2657,6 +2679,7 @@ func schema_pkg_apis_application_v1alpha1_TLSClientConfig(ref common.ReferenceCa
|
||||
Required: []string{"insecure"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2677,5 +2700,6 @@ func schema_pkg_apis_application_v1alpha1_objectMeta(ref common.ReferenceCallbac
|
||||
Required: []string{"Name"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -756,6 +756,17 @@ type Repository struct {
|
||||
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.
|
||||
type RepositoryList struct {
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ import (
|
||||
"time"
|
||||
|
||||
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
"github.com/argoproj/argo-cd/reposerver/repository"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
argogrpc "github.com/argoproj/argo-cd/util/grpc"
|
||||
)
|
||||
|
||||
// Clientset represets repository server api clients
|
||||
@@ -20,7 +20,8 @@ type Clientset interface {
|
||||
}
|
||||
|
||||
type clientSet struct {
|
||||
address string
|
||||
address string
|
||||
timeoutSeconds int
|
||||
}
|
||||
|
||||
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.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.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 {
|
||||
log.Errorf("Unable to connect to repository service with address %s", c.address)
|
||||
return nil, nil, err
|
||||
@@ -40,6 +45,6 @@ func (c *clientSet) NewRepoServerClient() (util.Closer, repository.RepoServerSer
|
||||
}
|
||||
|
||||
// NewRepoServerClientset creates new instance of repo server Clientset
|
||||
func NewRepoServerClientset(address string) Clientset {
|
||||
return &clientSet{address: address}
|
||||
func NewRepoServerClientset(address string, timeoutSeconds int) Clientset {
|
||||
return &clientSet{address: address, timeoutSeconds: timeoutSeconds}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ discoveryTests:
|
||||
- inputPath: testdata/paused_rollout.yaml
|
||||
result:
|
||||
- name: resume
|
||||
- inputPath: testdata/v0.2_paused_rollout.yaml
|
||||
result:
|
||||
- name: resume
|
||||
- inputPath: testdata/not_paused_rollout.yaml
|
||||
result: []
|
||||
- inputPath: testdata/nil_paused_rollout.yaml
|
||||
@@ -10,4 +13,6 @@ actionTests:
|
||||
- action: resume
|
||||
inputPath: testdata/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 = {}
|
||||
|
||||
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"] = {}
|
||||
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
|
||||
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
|
||||
@@ -16,7 +16,14 @@ if obj.status ~= nil then
|
||||
return hs
|
||||
end
|
||||
|
||||
if 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
|
||||
hs.status = "Suspended"
|
||||
if obj.status.blueGreen.previewSelector ~= nil and obj.status.blueGreen.previewSelector == obj.status.currentPodHash then
|
||||
hs.message = "The preview Service is serving traffic to the current pod spec"
|
||||
|
||||
@@ -7,6 +7,10 @@ tests:
|
||||
status: Suspended
|
||||
message: The preview Service is serving traffic to the current pod spec
|
||||
inputPath: testdata/suspended_servingPreviewService.yaml
|
||||
- healthStatus:
|
||||
status: Suspended
|
||||
message: The preview Service is serving traffic to the current pod spec
|
||||
inputPath: testdata/v0.2_suspended_servingPreviewService.yaml
|
||||
- healthStatus:
|
||||
status: Progressing
|
||||
message: "Waiting for roll out to finish: More replicas need to be updated"
|
||||
|
||||
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
|
||||
@@ -501,7 +501,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 {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -406,6 +406,7 @@ func TestEdgeCasesApplicationResources(t *testing.T) {
|
||||
"DeprecatedExtensions": "deprecated-extensions",
|
||||
"CRDs": "crd-creation",
|
||||
"DuplicatedResources": "duplicated-resources",
|
||||
"FailedConversion": "failed-conversion",
|
||||
}
|
||||
|
||||
for name, appPath := range apps {
|
||||
@@ -516,3 +517,75 @@ func TestResourceAction(t *testing.T) {
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
// 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 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
|
||||
// * ksonnet: the specified environment exists
|
||||
func GetSpecErrors(
|
||||
ctx context.Context,
|
||||
spec *argoappv1.ApplicationSpec,
|
||||
proj *argoappv1.AppProject,
|
||||
repoClientset reposerver.Clientset,
|
||||
db db.ArgoDB,
|
||||
) ([]argoappv1.ApplicationCondition, argoappv1.ApplicationSourceType, error) {
|
||||
func ValidateRepo(ctx context.Context, spec *argoappv1.ApplicationSpec, repoClientset reposerver.Clientset, db db.ArgoDB) ([]argoappv1.ApplicationCondition, argoappv1.ApplicationSourceType, 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
|
||||
}
|
||||
|
||||
// Test the repo
|
||||
conn, repoClient, err := repoClientset.NewRepoServerClient()
|
||||
@@ -218,18 +203,31 @@ func GetSpecErrors(
|
||||
conditions = append(conditions, helmConditions...)
|
||||
}
|
||||
case argoappv1.ApplicationSourceTypeDirectory, argoappv1.ApplicationSourceTypeKustomize:
|
||||
maniDirConditions := verifyGenerateManifests(ctx, repoRes, []*argoappv1.HelmRepository{}, spec, repoClient)
|
||||
if len(maniDirConditions) > 0 {
|
||||
conditions = append(conditions, maniDirConditions...)
|
||||
mainDirConditions := verifyGenerateManifests(ctx, repoRes, []*argoappv1.HelmRepository{}, spec, repoClient)
|
||||
if len(mainDirConditions) > 0 {
|
||||
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) {
|
||||
conditions = append(conditions, argoappv1.ApplicationCondition{
|
||||
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 +239,7 @@ func GetSpecErrors(
|
||||
})
|
||||
}
|
||||
// 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 errStatus, ok := status.FromError(err); ok && errStatus.Code() == codes.NotFound {
|
||||
conditions = append(conditions, argoappv1.ApplicationCondition{
|
||||
@@ -249,11 +247,11 @@ func GetSpecErrors(
|
||||
Message: fmt.Sprintf("cluster '%s' has not been configured", spec.Destination.Server),
|
||||
})
|
||||
} else {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return conditions, appSourceType, nil
|
||||
return conditions, nil
|
||||
}
|
||||
|
||||
// GetAppProject returns a project from an application
|
||||
@@ -340,6 +338,7 @@ func verifyAppYAML(ctx context.Context, repoRes *argoappv1.Repository, spec *arg
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
var conditions []argoappv1.ApplicationCondition
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
@@ -14,10 +14,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
noMatchingPathError = "Unable to remove nonexistent key: not-matching-path"
|
||||
)
|
||||
|
||||
type normalizerPatch struct {
|
||||
groupKind schema.GroupKind
|
||||
namespace string
|
||||
@@ -103,7 +99,8 @@ func (n *normalizer) Normalize(un *unstructured.Unstructured) error {
|
||||
for _, patch := range matched {
|
||||
patchedData, err := patch.patch.Apply(docData)
|
||||
if err != nil {
|
||||
if err.Error() == noMatchingPathError {
|
||||
// bug?
|
||||
if strings.HasPrefix(err.Error(), "Unable to remove nonexistent key") {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
|
||||
@@ -73,3 +73,17 @@ func TestNormalizeMatchedResourceOverrides(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, has)
|
||||
}
|
||||
|
||||
func TestNormalizeMissingJsonPointer(t *testing.T) {
|
||||
normalizer, err := NewDiffNormalizer([]v1alpha1.ResourceIgnoreDifferences{}, map[string]v1alpha1.ResourceOverride{
|
||||
"apps/Deployment": {
|
||||
IgnoreDifferences: `jsonPointers: ["/garbage"]`,
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
deployment := kube.MustToUnstructured(test.DemoDeployment())
|
||||
|
||||
err = normalizer.Normalize(deployment)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -80,10 +81,14 @@ func TestCreateExistingRepository(t *testing.T) {
|
||||
assert.Equal(t, codes.AlreadyExists, status.Convert(err).Code())
|
||||
}
|
||||
|
||||
func TestDeleteRepositoryManagedSecrets(t *testing.T) {
|
||||
func TestGetRepository(t *testing.T) {
|
||||
config := map[string]string{
|
||||
"repositories": `
|
||||
- url: https://github.com/argoproj/argocd-example-apps
|
||||
- url: https://known/repo
|
||||
- url: https://secured/repo
|
||||
`,
|
||||
"repository.credentials": `
|
||||
- url: https://secured
|
||||
usernameSecret:
|
||||
name: managed-secret
|
||||
key: username
|
||||
@@ -91,7 +96,47 @@ func TestDeleteRepositoryManagedSecrets(t *testing.T) {
|
||||
name: managed-secret
|
||||
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
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "TestUnknownRepo",
|
||||
repoURL: "http://unknown/repo",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
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)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("db.GetHydratedRepository() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("db.GetHydratedRepository() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newManagedSecret() *v1.Secret {
|
||||
return &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "managed-secret",
|
||||
Namespace: testNamespace,
|
||||
@@ -103,7 +148,21 @@ func TestDeleteRepositoryManagedSecrets(t *testing.T) {
|
||||
username: []byte("test-username"),
|
||||
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)
|
||||
|
||||
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
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
@@ -45,7 +46,7 @@ func (db *db) CreateRepository(ctx context.Context, r *appsv1.Repository) (*apps
|
||||
return nil, err
|
||||
}
|
||||
|
||||
index := getRepoCredIndex(s, r.Repo)
|
||||
index := getRepositoryIndex(s, r.Repo)
|
||||
if index > -1 {
|
||||
return nil, status.Errorf(codes.AlreadyExists, "repository '%s' already exists", r.Repo)
|
||||
}
|
||||
@@ -85,25 +86,47 @@ func (db *db) GetRepository(ctx context.Context, repoURL string) (*appsv1.Reposi
|
||||
return nil, err
|
||||
}
|
||||
|
||||
index := getRepoCredIndex(s, repoURL)
|
||||
index := getRepositoryIndex(s, repoURL)
|
||||
if index < 0 {
|
||||
return nil, status.Errorf(codes.NotFound, "repo '%s' not found", repoURL)
|
||||
}
|
||||
repoInfo := s.Repositories[index]
|
||||
|
||||
repo, err := db.credentialsToRepository(s.Repositories[index])
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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: repoInfo.URL,
|
||||
InsecureIgnoreHostKey: repoInfo.InsecureIgnoreHostKey,
|
||||
}
|
||||
|
||||
err = db.unmarshalFromSecretsStr(map[*string]*apiv1.SecretKeySelector{
|
||||
err := db.unmarshalFromSecretsStr(map[*string]*apiv1.SecretKeySelector{
|
||||
&repo.Username: repoInfo.UsernameSecret,
|
||||
&repo.Password: repoInfo.PasswordSecret,
|
||||
&repo.SSHPrivateKey: repoInfo.SSHPrivateKeySecret,
|
||||
}, make(map[string]*apiv1.Secret))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return repo, nil
|
||||
|
||||
return repo, err
|
||||
}
|
||||
|
||||
// UpdateRepository updates a repository
|
||||
@@ -113,7 +136,7 @@ func (db *db) UpdateRepository(ctx context.Context, r *appsv1.Repository) (*apps
|
||||
return nil, err
|
||||
}
|
||||
|
||||
index := getRepoCredIndex(s, r.Repo)
|
||||
index := getRepositoryIndex(s, r.Repo)
|
||||
if index < 0 {
|
||||
return nil, status.Errorf(codes.NotFound, "repo '%s' not found", r.Repo)
|
||||
}
|
||||
@@ -138,7 +161,7 @@ func (db *db) DeleteRepository(ctx context.Context, repoURL string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
index := getRepoCredIndex(s, repoURL)
|
||||
index := getRepositoryIndex(s, repoURL)
|
||||
if index < 0 {
|
||||
return status.Errorf(codes.NotFound, "repo '%s' not found", repoURL)
|
||||
}
|
||||
@@ -243,9 +266,20 @@ func (db *db) upsertSecret(name string, data map[string][]byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRepoCredIndex(s *settings.ArgoCDSettings, repoURL string) int {
|
||||
for i, cred := range s.Repositories {
|
||||
if git.SameURL(cred.URL, repoURL) {
|
||||
func getRepositoryIndex(s *settings.ArgoCDSettings, repoURL string) int {
|
||||
for i, repo := range s.Repositories {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package db
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
)
|
||||
|
||||
func TestRepoURLToSecretName(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,3 +149,12 @@ func TestTLS(address string) (*TLSTestResult, error) {
|
||||
}
|
||||
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...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,9 +80,11 @@ func filterAPIResources(config *rest.Config, resourceFilter ResourceFilter, filt
|
||||
gv = schema.GroupVersion{}
|
||||
}
|
||||
for _, apiResource := range apiResourcesList.APIResources {
|
||||
|
||||
if resourceFilter.IsExcludedResource(gv.Group, apiResource.Kind, config.Host) {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := isObsoleteExtensionsGroupKind(gv.Group, apiResource.Kind); ok &&
|
||||
// Edge case for deprecated Ingress kind.
|
||||
!(gv.Group == "extensions" && apiResource.Kind == IngressKind && version.LessThan(ingressDeprecationVersion)) {
|
||||
|
||||
@@ -30,4 +30,4 @@ func TestConvertToVersion(t *testing.T) {
|
||||
gvk = newObj.GroupVersionKind()
|
||||
assert.Equal(t, "apps", gvk.Group)
|
||||
assert.Equal(t, "v1", gvk.Version)
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,13 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type ExcludedResource struct {
|
||||
type FilteredResource struct {
|
||||
APIGroups []string `json:"apiGroups,omitempty"`
|
||||
Kinds []string `json:"kinds,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 {
|
||||
if match(excludedApiGroup, apiGroup) {
|
||||
return true
|
||||
@@ -29,7 +29,7 @@ func match(pattern, text string) bool {
|
||||
return compiledGlob.Match(text)
|
||||
}
|
||||
|
||||
func (r ExcludedResource) matchKind(kind string) bool {
|
||||
func (r FilteredResource) matchKind(kind string) bool {
|
||||
for _, excludedKind := range r.Kinds {
|
||||
if excludedKind == "*" || excludedKind == kind {
|
||||
return true
|
||||
@@ -38,7 +38,7 @@ func (r ExcludedResource) matchKind(kind string) bool {
|
||||
return len(r.Kinds) == 0
|
||||
}
|
||||
|
||||
func (r ExcludedResource) matchCluster(cluster string) bool {
|
||||
func (r FilteredResource) matchCluster(cluster string) bool {
|
||||
for _, excludedCluster := range r.Clusters {
|
||||
if match(excludedCluster, cluster) {
|
||||
return true
|
||||
@@ -47,6 +47,6 @@ func (r ExcludedResource) matchCluster(cluster string) bool {
|
||||
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)
|
||||
}
|
||||
@@ -12,28 +12,28 @@ func TestExcludeResource(t *testing.T) {
|
||||
cluster := "baz.com"
|
||||
|
||||
// matches with missing values
|
||||
assert.True(t, ExcludedResource{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, ExcludedResource{APIGroups: []string{apiGroup}, Kinds: []string{"*"}}.Match(apiGroup, kind, cluster))
|
||||
assert.True(t, FilteredResource{Kinds: []string{kind}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
||||
assert.True(t, FilteredResource{APIGroups: []string{apiGroup}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
||||
assert.True(t, FilteredResource{APIGroups: []string{apiGroup}, Kinds: []string{"*"}}.Match(apiGroup, kind, cluster))
|
||||
|
||||
// simple matches
|
||||
assert.True(t, ExcludedResource{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, ExcludedResource{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{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, FilteredResource{APIGroups: []string{apiGroup}, Kinds: []string{"*"}, Clusters: []string{cluster}}.Match(apiGroup, kind, cluster))
|
||||
assert.True(t, FilteredResource{APIGroups: []string{apiGroup}, Kinds: []string{kind}, Clusters: []string{"*.com"}}.Match(apiGroup, kind, cluster))
|
||||
|
||||
// negative matches
|
||||
assert.False(t, ExcludedResource{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, ExcludedResource{APIGroups: []string{apiGroup}, Kinds: []string{kind}, Clusters: []string{""}}.Match(apiGroup, kind, cluster))
|
||||
assert.False(t, FilteredResource{APIGroups: []string{""}, Kinds: []string{kind}, 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, FilteredResource{APIGroups: []string{apiGroup}, Kinds: []string{kind}, Clusters: []string{""}}.Match(apiGroup, kind, cluster))
|
||||
|
||||
// complex matches
|
||||
assert.True(t, ExcludedResource{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, ExcludedResource{APIGroups: []string{apiGroup}, Kinds: []string{kind}, Clusters: []string{cluster, 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, FilteredResource{APIGroups: []string{apiGroup}, Kinds: []string{kind, kind}, Clusters: []string{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
|
||||
assert.False(t, ExcludedResource{APIGroups: []string{"["}, Kinds: []string{""}, Clusters: []string{""}}.Match("", "", ""))
|
||||
assert.False(t, ExcludedResource{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, FilteredResource{APIGroups: []string{""}, Kinds: []string{"["}, Clusters: []string{""}}.Match("", "", ""))
|
||||
assert.False(t, FilteredResource{APIGroups: []string{""}, Kinds: []string{""}, Clusters: []string{"["}}.Match("", "", ""))
|
||||
}
|
||||
@@ -60,6 +60,8 @@ type ArgoCDSettings struct {
|
||||
Secrets map[string]string `json:"secrets,omitempty"`
|
||||
// Repositories holds list of configured git repositories
|
||||
Repositories []RepoCredentials
|
||||
// Repositories holds list of repo credentials
|
||||
RepositoryCredentials []RepoCredentials
|
||||
// Repositories holds list of configured helm repositories
|
||||
HelmRepositories []HelmRepoCredentials
|
||||
// AppInstanceLabelKey is the configured application instance label key used to label apps. May be empty
|
||||
@@ -70,7 +72,9 @@ type ArgoCDSettings struct {
|
||||
// (e.g. argoproj.io/rollout) for the resource that is being overridden
|
||||
ResourceOverrides map[string]v1alpha1.ResourceOverride
|
||||
// ResourceExclusions holds the api groups, kinds per cluster to exclude from Argo CD's watch
|
||||
ResourceExclusions []ExcludedResource
|
||||
ResourceExclusions []FilteredResource
|
||||
// ResourceInclusions holds the only api groups, kinds per cluster that Argo CD will watch
|
||||
ResourceInclusions []FilteredResource
|
||||
}
|
||||
|
||||
type OIDCConfig struct {
|
||||
@@ -114,6 +118,8 @@ const (
|
||||
settingURLKey = "url"
|
||||
// repositoriesKey designates the key where ArgoCDs repositories list is set
|
||||
repositoriesKey = "repositories"
|
||||
// repositoryCredentialsKey designates the key where ArgoCDs repositories credentials list is set
|
||||
repositoryCredentialsKey = "repository.credentials"
|
||||
// helmRepositoriesKey designates the key where list of helm repositories is set
|
||||
helmRepositoriesKey = "helm.repositories"
|
||||
// settingDexConfigKey designates the key for the dex config
|
||||
@@ -132,6 +138,8 @@ const (
|
||||
resourceCustomizationsKey = "resource.customizations"
|
||||
// resourceExclusions is the key to the list of excluded resources
|
||||
resourceExclusionsKey = "resource.exclusions"
|
||||
// resourceInclusions is the key to the list of explicitly watched resources
|
||||
resourceInclusionsKey = "resource.inclusions"
|
||||
// configManagementPluginsKey is the key to the list of config management plugins
|
||||
configManagementPluginsKey = "configManagementPlugins"
|
||||
)
|
||||
@@ -336,6 +344,7 @@ func updateSettingsFromConfigMap(settings *ArgoCDSettings, argoCDCM *apiv1.Confi
|
||||
settings.URL = argoCDCM.Data[settingURLKey]
|
||||
settings.AppInstanceLabelKey = argoCDCM.Data[settingsApplicationInstanceLabelKey]
|
||||
repositoriesStr := argoCDCM.Data[repositoriesKey]
|
||||
repositoryCredentialsStr := argoCDCM.Data[repositoryCredentialsKey]
|
||||
var errors []error
|
||||
if repositoriesStr != "" {
|
||||
repositories := make([]RepoCredentials, 0)
|
||||
@@ -346,6 +355,15 @@ func updateSettingsFromConfigMap(settings *ArgoCDSettings, argoCDCM *apiv1.Confi
|
||||
settings.Repositories = repositories
|
||||
}
|
||||
}
|
||||
if repositoryCredentialsStr != "" {
|
||||
repositoryCredentials := make([]RepoCredentials, 0)
|
||||
err := yaml.Unmarshal([]byte(repositoryCredentialsStr), &repositoryCredentials)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
} else {
|
||||
settings.RepositoryCredentials = repositoryCredentials
|
||||
}
|
||||
}
|
||||
helmRepositoriesStr := argoCDCM.Data[helmRepositoriesKey]
|
||||
if helmRepositoriesStr != "" {
|
||||
helmRepositories := make([]HelmRepoCredentials, 0)
|
||||
@@ -364,8 +382,18 @@ func updateSettingsFromConfigMap(settings *ArgoCDSettings, argoCDCM *apiv1.Confi
|
||||
settings.ResourceOverrides = resourceOverrides
|
||||
}
|
||||
|
||||
if value, ok := argoCDCM.Data[resourceInclusionsKey]; ok {
|
||||
includedResources := make([]FilteredResource, 0)
|
||||
err := yaml.Unmarshal([]byte(value), &includedResources)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
} else {
|
||||
settings.ResourceInclusions = includedResources
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := argoCDCM.Data[resourceExclusionsKey]; ok {
|
||||
excludedResources := make([]ExcludedResource, 0)
|
||||
excludedResources := make([]FilteredResource, 0)
|
||||
err := yaml.Unmarshal([]byte(value), &excludedResources)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
@@ -501,6 +529,15 @@ func (mgr *SettingsManager) SaveSettings(settings *ArgoCDSettings) error {
|
||||
} else {
|
||||
delete(argoCDCM.Data, repositoriesKey)
|
||||
}
|
||||
if len(settings.RepositoryCredentials) > 0 {
|
||||
yamlStr, err := yaml.Marshal(settings.RepositoryCredentials)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
argoCDCM.Data[repositoryCredentialsKey] = string(yamlStr)
|
||||
} else {
|
||||
delete(argoCDCM.Data, repositoryCredentialsKey)
|
||||
}
|
||||
if settings.AppInstanceLabelKey != "" {
|
||||
argoCDCM.Data[settingsApplicationInstanceLabelKey] = settings.AppInstanceLabelKey
|
||||
} else {
|
||||
@@ -517,6 +554,16 @@ func (mgr *SettingsManager) SaveSettings(settings *ArgoCDSettings) error {
|
||||
delete(argoCDCM.Data, resourceCustomizationsKey)
|
||||
}
|
||||
|
||||
if len(settings.ResourceInclusions) > 0 {
|
||||
yamlBytes, err := yaml.Marshal(settings.ResourceInclusions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
argoCDCM.Data[resourceInclusionsKey] = string(yamlBytes)
|
||||
} else {
|
||||
delete(argoCDCM.Data, resourceInclusionsKey)
|
||||
}
|
||||
|
||||
if len(settings.ResourceExclusions) > 0 {
|
||||
yamlBytes, err := yaml.Marshal(settings.ResourceExclusions)
|
||||
if err != nil {
|
||||
@@ -837,21 +884,64 @@ func (a *ArgoCDSettings) GetAppInstanceLabelKey() string {
|
||||
return a.AppInstanceLabelKey
|
||||
}
|
||||
|
||||
func (a *ArgoCDSettings) getExcludedResources() []ExcludedResource {
|
||||
coreExcludedResources := []ExcludedResource{
|
||||
func (a *ArgoCDSettings) getExcludedResources() []FilteredResource {
|
||||
coreExcludedResources := []FilteredResource{
|
||||
{APIGroups: []string{"events.k8s.io", "metrics.k8s.io"}},
|
||||
{APIGroups: []string{""}, Kinds: []string{"Event"}},
|
||||
}
|
||||
return append(coreExcludedResources, a.ResourceExclusions...)
|
||||
}
|
||||
|
||||
func (a *ArgoCDSettings) IsExcludedResource(apiGroup, kind, cluster string) bool {
|
||||
func (a *ArgoCDSettings) checkResourcePresence(apiGroup, kind, cluster string, filteredResources []FilteredResource) bool {
|
||||
|
||||
for _, excludedResource := range a.getExcludedResources() {
|
||||
if excludedResource.Match(apiGroup, kind, cluster) {
|
||||
for _, includedResource := range filteredResources {
|
||||
if includedResource.Match(apiGroup, kind, cluster) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *ArgoCDSettings) isIncludedResource(apiGroup, kind, cluster string) bool {
|
||||
return a.checkResourcePresence(apiGroup, kind, cluster, a.ResourceInclusions)
|
||||
}
|
||||
|
||||
func (a *ArgoCDSettings) isExcludedResource(apiGroup, kind, cluster string) bool {
|
||||
return a.checkResourcePresence(apiGroup, kind, cluster, a.getExcludedResources())
|
||||
}
|
||||
|
||||
// Behavior of this function is as follows:
|
||||
// +-------------+-------------+-------------+
|
||||
// | Inclusions | Exclusions | Result |
|
||||
// +-------------+-------------+-------------+
|
||||
// | Empty | Empty | Allowed |
|
||||
// +-------------+-------------+-------------+
|
||||
// | Present | Empty | Allowed |
|
||||
// +-------------+-------------+-------------+
|
||||
// | Not Present | Empty | Not Allowed |
|
||||
// +-------------+-------------+-------------+
|
||||
// | Empty | Present | Not Allowed |
|
||||
// +-------------+-------------+-------------+
|
||||
// | Empty | Not Present | Allowed |
|
||||
// +-------------+-------------+-------------+
|
||||
// | Present | Not Present | Allowed |
|
||||
// +-------------+-------------+-------------+
|
||||
// | Not Present | Present | Not Allowed |
|
||||
// +-------------+-------------+-------------+
|
||||
// | Not Present | Not Present | Not Allowed |
|
||||
// +-------------+-------------+-------------+
|
||||
// | Present | Present | Not Allowed |
|
||||
// +-------------+-------------+-------------+
|
||||
//
|
||||
func (a *ArgoCDSettings) IsExcludedResource(apiGroup, kind, cluster string) bool {
|
||||
if len(a.ResourceInclusions) > 0 {
|
||||
if a.isIncludedResource(apiGroup, kind, cluster) {
|
||||
return a.isExcludedResource(apiGroup, kind, cluster)
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return a.isExcludedResource(apiGroup, kind, cluster)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,8 @@ package settings
|
||||
import (
|
||||
"testing"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestArgoCDSettings_IsExcludedResource(t *testing.T) {
|
||||
@@ -15,7 +14,69 @@ func TestArgoCDSettings_IsExcludedResource(t *testing.T) {
|
||||
assert.False(t, settings.IsExcludedResource("rubbish.io", "", ""))
|
||||
}
|
||||
|
||||
func TestUpdateSettingsFromConfigMapResourceExclusions(t *testing.T) {
|
||||
func Test_updateSettingsFromConfigMap(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key string
|
||||
value string
|
||||
wantErr bool
|
||||
get func(settings ArgoCDSettings) interface{}
|
||||
want interface{}
|
||||
}{
|
||||
{
|
||||
name: "TestResourceExclusions",
|
||||
key: "resource.exclusions",
|
||||
value: "\n - apiGroups: []\n kinds: []\n clusters: []\n",
|
||||
get: func(settings ArgoCDSettings) interface{} {
|
||||
return settings.ResourceExclusions
|
||||
},
|
||||
want: []FilteredResource{{APIGroups: []string{}, Kinds: []string{}, Clusters: []string{}}},
|
||||
},
|
||||
{
|
||||
name: "TestResourceInclusion",
|
||||
key: "resource.inclusions",
|
||||
value: "\n - apiGroups: []\n kinds: [managed_only]\n clusters: []\n",
|
||||
get: func(settings ArgoCDSettings) interface{} {
|
||||
return settings.ResourceInclusions
|
||||
},
|
||||
want: []FilteredResource{{APIGroups: []string{}, Kinds: []string{"managed_only"}, Clusters: []string{}}},
|
||||
},
|
||||
{
|
||||
name: "TestRepositories",
|
||||
key: "repositories",
|
||||
value: "\n - url: http://foo\n",
|
||||
get: func(settings ArgoCDSettings) interface{} {
|
||||
return settings.Repositories
|
||||
},
|
||||
want: []RepoCredentials{{URL: "http://foo"}},
|
||||
},
|
||||
{
|
||||
name: "TestRepositoryCredentials",
|
||||
key: "repository.credentials",
|
||||
value: "\n - url: http://foo\n",
|
||||
get: func(settings ArgoCDSettings) interface{} {
|
||||
return settings.RepositoryCredentials
|
||||
},
|
||||
want: []RepoCredentials{{URL: "http://foo"}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
settings := ArgoCDSettings{}
|
||||
configMap := v1.ConfigMap{
|
||||
Data: map[string]string{
|
||||
tt.key: tt.value,
|
||||
},
|
||||
}
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := updateSettingsFromConfigMap(&settings, &configMap); (err != nil) != tt.wantErr {
|
||||
t.Errorf("updateSettingsFromConfigMap() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
assert.Equal(t, tt.want, tt.get(settings))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceInclusions(t *testing.T) {
|
||||
|
||||
settings := ArgoCDSettings{}
|
||||
configMap := v1.ConfigMap{}
|
||||
@@ -25,11 +86,59 @@ func TestUpdateSettingsFromConfigMapResourceExclusions(t *testing.T) {
|
||||
assert.Nil(t, settings.ResourceExclusions)
|
||||
|
||||
configMap.Data = map[string]string{
|
||||
"resource.exclusions": "\n - apiGroups: []\n kinds: []\n clusters: []\n",
|
||||
"resource.inclusions": "\n - apiGroups: [\"whitelisted-resource\"]\n kinds: []\n clusters: []\n",
|
||||
}
|
||||
|
||||
err = updateSettingsFromConfigMap(&settings, &configMap)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []ExcludedResource{{APIGroups: []string{}, Kinds: []string{}, Clusters: []string{}}}, settings.ResourceExclusions)
|
||||
assert.Equal(t, []FilteredResource{{APIGroups: []string{"whitelisted-resource"}, Kinds: []string{}, Clusters: []string{}}}, settings.ResourceInclusions)
|
||||
|
||||
assert.True(t, settings.IsExcludedResource("non-whitelisted-resource", "", ""))
|
||||
assert.False(t, settings.IsExcludedResource("whitelisted-resource", "", ""))
|
||||
}
|
||||
|
||||
func TestResourceInclusionsExclusionNonMutex(t *testing.T) {
|
||||
|
||||
settings := ArgoCDSettings{}
|
||||
configMap := v1.ConfigMap{}
|
||||
err := updateSettingsFromConfigMap(&settings, &configMap)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, settings.ResourceExclusions)
|
||||
|
||||
configMap.Data = map[string]string{
|
||||
"resource.inclusions": "\n - apiGroups: [\"whitelisted-resource\"]\n kinds: []\n clusters: []\n",
|
||||
"resource.exclusions": "\n - apiGroups: [\"whitelisted-resource\"]\n kinds: [\"blacklisted-kind\"]\n clusters: []\n",
|
||||
}
|
||||
|
||||
err = updateSettingsFromConfigMap(&settings, &configMap)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.True(t, settings.IsExcludedResource("whitelisted-resource", "blacklisted-kind", ""))
|
||||
assert.False(t, settings.IsExcludedResource("whitelisted-resource", "", ""))
|
||||
assert.False(t, settings.IsExcludedResource("whitelisted-resource", "non-blacklisted-kind", ""))
|
||||
|
||||
configMap.Data = map[string]string{
|
||||
"resource.inclusions": "\n - apiGroups: [\"whitelisted-resource\"]\n kinds: [\"whitelisted-kind\"]\n clusters: []\n",
|
||||
"resource.exclusions": "\n - apiGroups: [\"whitelisted-resource\"]\n kinds: []\n clusters: []\n",
|
||||
}
|
||||
|
||||
err = updateSettingsFromConfigMap(&settings, &configMap)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.True(t, settings.IsExcludedResource("whitelisted-resource", "whitelisted-kind", ""))
|
||||
assert.True(t, settings.IsExcludedResource("whitelisted-resource", "", ""))
|
||||
assert.True(t, settings.IsExcludedResource("whitelisted-resource", "non-whitelisted-kind", ""))
|
||||
|
||||
configMap.Data = map[string]string{
|
||||
"resource.inclusions": "\n - apiGroups: [\"foo-bar\"]\n kinds: [\"whitelisted-kind\"]\n clusters: []\n",
|
||||
"resource.exclusions": "\n - apiGroups: [\"whitelisted-resource\"]\n kinds: []\n clusters: []\n",
|
||||
}
|
||||
|
||||
err = updateSettingsFromConfigMap(&settings, &configMap)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.True(t, settings.IsExcludedResource("not-whitelisted-resource", "whitelisted-kind", ""))
|
||||
assert.True(t, settings.IsExcludedResource("not-whitelisted-resource", "", ""))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user