Compare commits

...

40 Commits

Author SHA1 Message Date
Alexander Matyushentsev
1aaf76f230 Update manifests to v1.2.2 2019-09-24 07:53:42 -07:00
Gregor Krmelj
71ad60e89f Add cache-control HTTP header to badge response (#2328)
Since we serve the badge as an image using HTTP GET, cache systems
(incl. GitHub's CDN - Fastly) like to cache the image thus the
badge becomes stale rendering it useless. Adding the appropriate
Cache-Control HTTP header we direct cache systems and web browsers
not to cache the contents of the response.
2019-09-24 07:45:06 -07:00
Gustav Paul
9c46f77bb1 util/localconfig: prefer HOME env var over os/user (#2326)
* util/localconfig: prefer HOME env var over os/user

The os/user package requires that the current user be in /etc/passwd.
That complicates executing the argocd command in a docker container
when the UID:GID of the executing user is overridden.

This is often done in order to have files generated inside a docker
container have their ownership set to match the uid/gid of the host
user.

For example,

```sh
docker run -ti -u "$(id -u "${USER}"):$(id -g "${USER}")" argocd:latest ...
```

* Makefile: use pinned dev image dependencies to run make lint
2019-09-24 07:21:52 -07:00
Alexander Matyushentsev
b6256aae9f Codegen (#2343) 2019-09-23 14:22:49 -07:00
Alexander Matyushentsev
f802190a24 Issue #2339 - Make sure controller uses latest git version if app reconciliation result expired (#2346) 2019-09-23 14:22:36 -07:00
Alexander Matyushentsev
e07c1edece Don't fix imports in auto-generated files (#2342) 2019-09-23 14:22:31 -07:00
Alex Collins
ed15da5271 Adds support for Github Enterprise URLs (#2344) 2019-09-23 13:24:52 -07:00
Jesse Suen
d34dbeac0d Add restart action to Deployment/StatefulSet/DaemonSet (#2300) 2019-09-13 02:57:25 -07:00
Alexander Matyushentsev
c4d3a54126 Merge branch 'release-1.2' of github.com:argoproj/argo-cd into release-1.2 2019-09-12 10:26:23 -07:00
Alex Collins
622671ece4 removed e2e tests that do not work and I should not have merged 2019-09-12 10:08:49 -07:00
Alex Collins
cf6a7abd30 ported FailOnErr from master 2019-09-12 09:56:43 -07:00
Alexander Matyushentsev
a6a394ba93 Update manifests to v1.2.1 2019-09-12 09:56:15 -07:00
Alex Collins
d315814020 Fixes issue diffing secrets (#2271)
# Conflicts:
#	test/e2e/app_management_test.go
2019-09-12 09:55:15 -07:00
Seiya Muramatsu
d46872d7e8 Add --self-heal flag to argocd cli (#2296) 2019-09-12 09:45:14 -07:00
Alexander Matyushentsev
ba7f0fcb47 Issue #2290 - Fix nil pointer dereference in application controller (#2291) 2019-09-12 09:44:59 -07:00
Alexander Matyushentsev
5fcfc22298 Issue #2022 - Support limiting number of concurrent kubectl fork/execs (#2264) 2019-09-12 09:43:35 -07:00
Mitz Amano
9e486dfad4 Fix degraded proxy support for http(s) git repository (#2243) (#2249) 2019-09-05 15:38:45 -07:00
Alexander Matyushentsev
674978cd58 Update manifests to v1.2.0 2019-09-04 13:51:54 -07:00
Alexander Matyushentsev
020d284a00 Add missing pending method 2019-09-04 11:49:13 -07:00
Alex Collins
09b874613d If there is only one wave and no pre/post hooks, we should be synced.… (#2217) 2019-08-27 08:57:42 -07:00
Alex Collins
44cb2ce51a codegen 2019-08-26 13:56:25 -07:00
Alex Collins
6eaed1e64e Fix for displaying hooks in app diff view. Fixes #2215 (#2218)
* Duct tape fix for displaying hooks in app diff view. Fixes #2215

* ""operationId": "ListMixin7"," to swagger.json

* "for _, item := range items {" to app.go
2019-08-26 13:51:18 -07:00
Alex Collins
2a63b44af0 Redact secrets using "+" rather than "*" as this is base 64 compatiba… (#2119) 2019-08-23 13:46:34 -07:00
Alexander Matyushentsev
5571cf1333 Update codegen 2019-08-21 08:20:45 -07:00
Alexander Matyushentsev
ed0add3087 Update manifests to v1.2.0-rc2 2019-08-20 15:45:24 -07:00
Alex Collins
d27849cdc4 Adds a floating action button with help and chat links to every page.… (#2125) 2019-08-20 10:35:31 -07:00
Alexander Matyushentsev
3a3f490abf Issue #2174 - Fix git repo url parsing on application list view (#2175) 2019-08-20 09:30:33 -07:00
Alexander Matyushentsev
2dc95fffb7 Issue #2146 - Fix nil pointer dereference error during app reconciliation (#2170) 2019-08-20 08:56:43 -07:00
Alexander Matyushentsev
9cf978c168 Temporary disable Git LFS test to unblock release (#2172) 2019-08-20 08:20:10 -07:00
Alex Collins
3891b29d82 Determine the manifest version from the VERSION file when on release branch (#2166) 2019-08-20 08:20:06 -07:00
Alexander Matyushentsev
ed916702d6 Issue #2114 - Fix history api fallback implementation to support app names with dots (#2168) 2019-08-20 08:20:02 -07:00
Alex Collins
c4eba32f0e Enhances cookie warning with actual length to help users fix their co… (#2134) 2019-08-16 14:08:03 -07:00
Alex Collins
046a62420e Fixes some code issues related to Kustomize build options. See #2146 (#2151) 2019-08-16 12:56:06 -07:00
Simon Behar
1b393bc473 Added 'SyncFail' to possible HookTypes in UI (#2153) 2019-08-14 14:29:48 -07:00
jannfis
d8c38bb45b Fix and enhance end-to-end testing for SSH repositories (#2101)
* Fix and enhance end-to-end testing for SSH repositories
2019-08-14 10:42:23 -07:00
Alex Collins
249ce9317f Adds checks around valid paths for apps (#2133) 2019-08-09 14:26:31 -07:00
Alex Collins
a094d5abb8 Minor CLI bug fixes (#2132) 2019-08-09 13:27:02 -07:00
Alexander Matyushentsev
3f31224a6e Issue #2060 - Enpoint incorrectly considered top level managed resource (#2129) 2019-08-09 11:52:55 -07:00
jannfis
649b1b7b75 Allow adding certs for hostnames ending on a dot (fixes #2116) (#2120) 2019-08-08 17:14:33 -07:00
Alexander Matyushentsev
2c691a874b Update manifests to v1.2.0-rc1 2019-08-06 10:40:47 -07:00
109 changed files with 2725 additions and 1051 deletions

View File

@@ -60,10 +60,8 @@ commands:
name: Install Go deps
command: |
set -x
go get github.com/golangci/golangci-lint/cmd/golangci-lint
go get github.com/jstemmer/go-junit-report
go get github.com/mattn/goreman
go get golang.org/x/tools/cmd/goimports
install_tools:
steps:
- run:
@@ -71,7 +69,14 @@ commands:
command: mkdir -p /tmp/dl
- restore_cache:
keys:
- dl-v6
- dl-v7
- run:
name: Install Kubectl v1.14.0
command: |
set -x
[ -e /tmp/dl/kubectl ] || curl -sLf -C - -o /tmp/dl/kubectl https://storage.googleapis.com/kubernetes-release/release/v1.14.0/bin/linux/amd64/kubectl
sudo cp /tmp/dl/kubectl /usr/local/bin/kubectl
sudo chmod +x /usr/local/bin/kubectl
- run:
name: Install Kubectx v0.6.3
command: |
@@ -118,17 +123,8 @@ commands:
sudo cp /tmp/dl/kustomize_${VER} /usr/local/go/bin/kustomize
sudo chmod +x /usr/local/go/bin/kustomize
kustomize version
- run:
name: Install Git LFS plugin
command: |
set -x
curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
sleep 5
sudo killall -9 apt-get || true
sudo apt-get update
sudo apt-get install -y git-lfs openssh-client
- save_cache:
key: dl-v6
key: dl-v7
paths:
- /tmp/dl
save_go_cache:
@@ -217,14 +213,13 @@ jobs:
name: Create namespace
command: |
set -x
cat /etc/rancher/k3s/k3s.yaml | sed "s/localhost/`hostname`/" | tee ~/.kube/config
echo "127.0.0.1 `hostname`" | sudo tee -a /etc/hosts
kubectl create ns argocd-e2e
kubens argocd-e2e
# install the certificates (not 100% sure we need this)
sudo cp /var/lib/rancher/k3s/server/tls/token-ca.crt /usr/local/share/ca-certificates/k3s.crt
sudo update-ca-certificates
# create the kubecfg, again - not sure we need this
cat /etc/rancher/k3s/k3s.yaml | sed "s/localhost/`hostname`/" | tee ~/.kube/config
echo "127.0.0.1 `hostname`" | sudo tee -a /etc/hosts
- run:
name: Apply manifests
command: kustomize build test/manifests/base | kubectl apply -f -

10
Gopkg.lock generated
View File

@@ -89,6 +89,14 @@
pruneopts = ""
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
[[projects]]
digest = "1:a6ee710e45210bafe11f2f28963571be2ac8809f9a7b675a6d2c02302a1ce1a9"
name = "github.com/bouk/monkey"
packages = ["."]
pruneopts = ""
revision = "5df1f207ff77e025801505ae4d903133a0b4353f"
version = "v1.0.0"
[[projects]]
digest = "1:e04162bd6a6d4950541bae744c968108e14913b1cebccf29f7650b573f44adb3"
name = "github.com/casbin/casbin"
@@ -1589,6 +1597,7 @@
"github.com/argoproj/pkg/errors",
"github.com/argoproj/pkg/exec",
"github.com/argoproj/pkg/time",
"github.com/bouk/monkey",
"github.com/casbin/casbin",
"github.com/casbin/casbin/model",
"github.com/casbin/casbin/persist",
@@ -1646,6 +1655,7 @@
"github.com/yuin/gopher-lua",
"golang.org/x/crypto/bcrypt",
"golang.org/x/crypto/ssh",
"golang.org/x/crypto/ssh/knownhosts",
"golang.org/x/crypto/ssh/terminal",
"golang.org/x/net/context",
"golang.org/x/oauth2",

View File

@@ -18,7 +18,7 @@ PATH:=$(PATH):$(PWD)/hack
# docker image publishing options
DOCKER_PUSH?=false
IMAGE_TAG?=latest
IMAGE_TAG?=
# perform static compilation
STATIC_BUILD?=true
# build development images
@@ -153,12 +153,16 @@ builder-image:
dep-ensure:
dep ensure -no-vendor
.PHONY: lint
lint:
.PHONY: lint-local
lint-local: build
# golangci-lint does not do a good job of formatting imports
goimports -local github.com/argoproj/argo-cd -w `find . ! -path './vendor/*' ! -path './pkg/client/*' -type f -name '*.go'`
goimports -local github.com/argoproj/argo-cd -w `find . ! -path './vendor/*' ! -path './pkg/client/*' ! -path '*.pb.go' ! -path '*.gw.go' -type f -name '*.go'`
GOGC=$(LINT_GOGC) golangci-lint run --fix --verbose --concurrency $(LINT_CONCURRENCY) --deadline $(LINT_DEADLINE)
.PHONY: lint
lint: dev-tools-image
$(call run-in-dev-tool,make lint-local LINT_CONCURRENCY=$(LINT_CONCURRENCY) LINT_DEADLINE=$(LINT_DEADLINE) LINT_GOGC=$(LINT_GOGC))
.PHONY: build
build:
go build -v `go list ./... | grep -v 'resource_customizations\|test/e2e'`

View File

@@ -2,6 +2,6 @@ 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:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --staticassets ui/dist/app"
dex: sh -c "go run ./cmd/argocd-util/main.go gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml quay.io/dexidp/dex:v2.14.0 serve /dex.yaml"
redis: docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:5.0.3-alpine --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}
repo-server: sh -c "FORCE_LOG_COLORS=1 go run ./cmd/argocd-repo-server/main.go --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}"
repo-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true go run ./cmd/argocd-repo-server/main.go --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}"
ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start'
git-server: test/fixture/testrepos/start-git.sh
git-server: test/fixture/testrepos/start-git.sh

View File

@@ -1 +1 @@
1.1.2
1.2.2

View File

@@ -1740,6 +1740,20 @@
}
}
},
"clusterHelp": {
"type": "object",
"title": "Help settings",
"properties": {
"chatText": {
"type": "string",
"title": "the text for getting chat help, defaults to \"Chat now!\""
},
"chatUrl": {
"type": "string",
"title": "the URL for getting chat help, this will typically be your Slack channel for support"
}
}
},
"clusterOIDCConfig": {
"type": "object",
"properties": {
@@ -1775,6 +1789,9 @@
"googleAnalytics": {
"$ref": "#/definitions/clusterGoogleAnalyticsConfig"
},
"help": {
"$ref": "#/definitions/clusterHelp"
},
"kustomizeOptions": {
"$ref": "#/definitions/v1alpha1KustomizeOptions"
},
@@ -3380,6 +3397,10 @@
"group": {
"type": "string"
},
"hook": {
"type": "boolean",
"format": "boolean"
},
"kind": {
"type": "string"
},

View File

@@ -46,6 +46,7 @@ func newCommand() *cobra.Command {
logLevel string
glogLevel int
metricsPort int
kubectlParallelismLimit int64
cacheSrc func() (*cache.Cache, error)
)
var command = cobra.Command{
@@ -84,7 +85,8 @@ func newCommand() *cobra.Command {
cache,
resyncDuration,
time.Duration(selfHealTimeoutSeconds)*time.Second,
metricsPort)
metricsPort,
kubectlParallelismLimit)
errors.CheckError(err)
log.Infof("Application Controller (version: %s) starting (namespace: %s)", common.GetVersion(), namespace)
@@ -109,6 +111,7 @@ func newCommand() *cobra.Command {
command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level")
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDMetrics, "Start metrics server on given port")
command.Flags().IntVar(&selfHealTimeoutSeconds, "self-heal-timeout-seconds", 5, "Specifies timeout between application self heal attempts")
command.Flags().Int64Var(&kubectlParallelismLimit, "kubectl-parallelism-limit", 0, "Number of allowed concurrent kubectl fork/execs.")
cacheSrc = cache.AddCacheFlagsToCmd(&command)
return &command

View File

@@ -5,11 +5,8 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/url"
"os"
"os/exec"
"path"
"path/filepath"
"reflect"
"regexp"
@@ -20,7 +17,6 @@ import (
"time"
"github.com/ghodss/yaml"
"github.com/google/shlex"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@@ -444,6 +440,12 @@ func setAppOptions(flags *pflag.FlagSet, app *argoappv1.Application, appOpts *ap
}
app.Spec.SyncPolicy.Automated.Prune = appOpts.autoPrune
}
if flags.Changed("self-heal") {
if app.Spec.SyncPolicy == nil || app.Spec.SyncPolicy.Automated == nil {
log.Fatal("Cannot set --self-helf: application not configured with automatic sync")
}
app.Spec.SyncPolicy.Automated.SelfHeal = appOpts.selfHeal
}
return visited
}
@@ -544,6 +546,7 @@ type appOptions struct {
project string
syncPolicy string
autoPrune bool
selfHeal bool
namePrefix string
directoryRecurse bool
configManagementPlugin string
@@ -565,6 +568,7 @@ func addAppFlags(command *cobra.Command, opts *appOptions) {
command.Flags().StringVar(&opts.project, "project", "", "Application project name")
command.Flags().StringVar(&opts.syncPolicy, "sync-policy", "", "Set the sync policy (one of: automated, none)")
command.Flags().BoolVar(&opts.autoPrune, "auto-prune", false, "Set automatic pruning when sync is automated")
command.Flags().BoolVar(&opts.selfHeal, "self-heal", false, "Set self healing when sync is automated")
command.Flags().StringVar(&opts.namePrefix, "nameprefix", "", "Kustomize nameprefix")
command.Flags().BoolVar(&opts.directoryRecurse, "directory-recurse", false, "Recurse directory")
command.Flags().StringVar(&opts.configManagementPlugin, "config-management-plugin", "", "Config management plugin name")
@@ -744,7 +748,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
Short: shortDesc,
Long: shortDesc + "\nUses 'diff' to render the difference. KUBECTL_EXTERNAL_DIFF environment variable can be used to select your own diff tool.\nReturns the following exit codes: 2 on general errors, 1 when a diff is found, and 0 when no diff is found",
Run: func(c *cobra.Command, args []string) {
if len(args) == 0 {
if len(args) != 1 {
c.HelpFunc()(c, args)
os.Exit(2)
}
@@ -844,8 +848,10 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
}
foundDiffs := false
for i := range items {
item := items[i]
for _, item := range items {
if item.target != nil && hook.IsHook(item.target) || item.live != nil && hook.IsHook(item.live) {
continue
}
overrides := make(map[string]argoappv1.ResourceOverride)
for k := range argoSettings.ResourceOverrides {
val := argoSettings.ResourceOverrides[k]
@@ -869,7 +875,8 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
}
foundDiffs = true
printDiff(item.key.Name, target, live)
err = diff.PrintDiff(item.key.Name, target, live)
errors.CheckError(err)
}
}
if foundDiffs {
@@ -884,43 +891,6 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
return command
}
func printDiff(name string, live *unstructured.Unstructured, target *unstructured.Unstructured) {
tempDir, err := ioutil.TempDir("", "argocd-diff")
errors.CheckError(err)
targetFile := path.Join(tempDir, name)
targetData := []byte("")
if target != nil {
targetData, err = yaml.Marshal(target)
errors.CheckError(err)
}
err = ioutil.WriteFile(targetFile, targetData, 0644)
errors.CheckError(err)
liveFile := path.Join(tempDir, fmt.Sprintf("%s-live.yaml", name))
liveData := []byte("")
if live != nil {
liveData, err = yaml.Marshal(live)
errors.CheckError(err)
}
err = ioutil.WriteFile(liveFile, liveData, 0644)
errors.CheckError(err)
cmdBinary := "diff"
var args []string
if envDiff := os.Getenv("KUBECTL_EXTERNAL_DIFF"); envDiff != "" {
parts, err := shlex.Split(envDiff)
errors.CheckError(err)
cmdBinary = parts[0]
args = parts[1:]
}
cmd := exec.Command(cmdBinary, append(args, liveFile, targetFile)...)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
_ = cmd.Run()
}
// NewApplicationDeleteCommand returns a new instance of an `argocd app delete` command
func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
@@ -1213,7 +1183,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
var localObjsStrings []string
if local != "" {
app, err := appIf.Get(context.Background(), &applicationpkg.ApplicationQuery{Name: &appName})
errors.CheckError(err)
if app.Spec.SyncPolicy != nil && app.Spec.SyncPolicy.Automated != nil {
log.Fatal("Cannot use local sync when Automatic Sync Policy is enabled")
}

View File

@@ -12,6 +12,7 @@ import (
"time"
log "github.com/sirupsen/logrus"
"golang.org/x/sync/semaphore"
v1 "k8s.io/api/core/v1"
apierr "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -85,6 +86,7 @@ type ApplicationController struct {
refreshRequestedApps map[string]CompareWith
refreshRequestedAppsMutex *sync.Mutex
metricsServer *metrics.MetricsServer
kubectlSemaphore *semaphore.Weighted
}
type ApplicationControllerConfig struct {
@@ -103,6 +105,7 @@ func NewApplicationController(
appResyncPeriod time.Duration,
selfHealTimeout time.Duration,
metricsPort int,
kubectlParallelismLimit int64,
) (*ApplicationController, error) {
db := db.NewDB(namespace, settingsMgr, kubeClientset)
kubectlCmd := kube.KubectlCmd{}
@@ -123,7 +126,12 @@ func NewApplicationController(
settingsMgr: settingsMgr,
selfHealTimeout: selfHealTimeout,
}
if kubectlParallelismLimit > 0 {
ctrl.kubectlSemaphore = semaphore.NewWeighted(kubectlParallelismLimit)
}
kubectlCmd.OnKubectlRun = ctrl.onKubectlRun
appInformer, appLister := ctrl.newApplicationInformerAndLister()
projInformer := v1alpha1.NewAppProjectInformer(applicationClientset, namespace, appResyncPeriod, cache.Indexers{})
metricsAddr := fmt.Sprintf("0.0.0.0:%d", metricsPort)
ctrl.metricsServer = metrics.NewMetricsServer(metricsAddr, appLister, func() error {
@@ -141,6 +149,23 @@ func NewApplicationController(
return &ctrl, nil
}
func (ctrl *ApplicationController) onKubectlRun(command string) (util.Closer, error) {
ctrl.metricsServer.IncKubectlExec(command)
if ctrl.kubectlSemaphore != nil {
if err := ctrl.kubectlSemaphore.Acquire(context.Background(), 1); err != nil {
return nil, err
}
ctrl.metricsServer.IncKubectlExecPending(command)
}
return util.NewCloser(func() error {
if ctrl.kubectlSemaphore != nil {
ctrl.kubectlSemaphore.Release(1)
ctrl.metricsServer.DecKubectlExecPending(command)
}
return nil
}), nil
}
func isSelfReferencedApp(app *appv1.Application, ref v1.ObjectReference) bool {
gvk := ref.GroupVersionKind()
return ref.UID == app.UID &&
@@ -233,6 +258,7 @@ func (ctrl *ApplicationController) managedResources(comparisonResult *comparison
Name: res.Name,
Group: res.Group,
Kind: res.Kind,
Hook: res.Hook,
}
target := res.Target
@@ -682,7 +708,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
}
var localManifests []string
if opState := app.Status.OperationState; opState != nil {
if opState := app.Status.OperationState; opState != nil && opState.Operation.Sync != nil {
localManifests = opState.Operation.Sync.Manifests
}
@@ -690,13 +716,13 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
if comparisonLevel == CompareWithRecent {
revision = app.Status.Sync.Revision
}
compareResult, err := ctrl.appStateManager.CompareAppState(app, revision, app.Spec.Source, refreshType == appv1.RefreshTypeHard, localManifests)
if err != nil {
conditions = append(conditions, appv1.ApplicationCondition{Type: appv1.ApplicationConditionComparisonError, Message: err.Error()})
} else {
ctrl.normalizeApplication(origApp, app, compareResult.appSourceType)
conditions = append(conditions, compareResult.conditions...)
}
compareResult := ctrl.appStateManager.CompareAppState(app, revision, app.Spec.Source, refreshType == appv1.RefreshTypeHard, localManifests)
ctrl.normalizeApplication(origApp, app, compareResult.appSourceType)
conditions = append(conditions, compareResult.conditions...)
tree, err := ctrl.setAppManagedResources(app, compareResult)
if err != nil {
logCtx.Errorf("Failed to cache app resources: %v", err)
@@ -730,9 +756,13 @@ func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application,
compareWith := CompareWithLatest
refreshType := appv1.RefreshTypeNormal
expired := app.Status.ReconciledAt == nil || app.Status.ReconciledAt.Add(statusRefreshTimeout).Before(time.Now().UTC())
if requestedType, ok := app.IsRefreshRequested(); ok {
refreshType = requestedType
reason = fmt.Sprintf("%s refresh requested", refreshType)
if requestedType, ok := app.IsRefreshRequested(); ok || expired {
if ok {
refreshType = requestedType
reason = fmt.Sprintf("%s refresh requested", refreshType)
} else if expired {
reason = fmt.Sprintf("comparison expired. reconciledAt: %v, expiry: %v", app.Status.ReconciledAt, statusRefreshTimeout)
}
} else if requested, level := ctrl.isRefreshRequested(app.Name); requested {
compareWith = level
reason = fmt.Sprintf("controller refresh requested")
@@ -742,8 +772,6 @@ func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application,
reason = "spec.source differs"
} else if !app.Spec.Destination.Equals(app.Status.Sync.ComparedTo.Destination) {
reason = "spec.destination differs"
} else if expired {
reason = fmt.Sprintf("comparison expired. reconciledAt: %v, expiry: %v", app.Status.ReconciledAt, statusRefreshTimeout)
}
if reason != "" {
logCtx.Infof("Refreshing app status (%s), level (%d)", reason, compareWith)

View File

@@ -35,6 +35,7 @@ type fakeData struct {
apps []runtime.Object
manifestResponse *apiclient.ManifestResponse
managedLiveObjs map[kube.ResourceKey]*unstructured.Unstructured
configMapData map[string]string
}
func newFakeController(data *fakeData) *ApplicationController {
@@ -68,7 +69,7 @@ func newFakeController(data *fakeData) *ApplicationController {
"app.kubernetes.io/part-of": "argocd",
},
},
Data: nil,
Data: data.configMapData,
}
kubeClient := fake.NewSimpleClientset(&clust, &cm, &secret)
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClient, test.FakeArgoCDNamespace)
@@ -82,6 +83,7 @@ func newFakeController(data *fakeData) *ApplicationController {
time.Minute,
time.Minute,
common.DefaultPortArgoCDMetrics,
0,
)
if err != nil {
panic(err)
@@ -523,13 +525,29 @@ func TestNeedRefreshAppStatus(t *testing.T) {
assert.Equal(t, argoappv1.RefreshTypeNormal, refreshType)
assert.Equal(t, CompareWithLatest, compareWith)
// execute hard refresh if app has refresh annotation
app.Annotations = map[string]string{
common.AnnotationKeyRefresh: string(argoappv1.RefreshTypeHard),
{
// refresh app using the 'latest' level if comparison expired
app := app.DeepCopy()
ctrl.requestAppRefresh(app.Name, CompareWithRecent)
reconciledAt := metav1.NewTime(time.Now().UTC().Add(-1 * time.Hour))
app.Status.ReconciledAt = &reconciledAt
needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 1*time.Minute)
assert.True(t, needRefresh)
assert.Equal(t, argoappv1.RefreshTypeNormal, refreshType)
assert.Equal(t, CompareWithLatest, compareWith)
}
needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 1*time.Hour)
assert.True(t, needRefresh)
assert.Equal(t, argoappv1.RefreshTypeHard, refreshType)
assert.Equal(t, CompareWithLatest, compareWith)
{
app := app.DeepCopy()
// execute hard refresh if app has refresh annotation
reconciledAt := metav1.NewTime(time.Now().UTC().Add(-1 * time.Hour))
app.Status.ReconciledAt = &reconciledAt
app.Annotations = map[string]string{
common.AnnotationKeyRefresh: string(argoappv1.RefreshTypeHard),
}
needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 1*time.Hour)
assert.True(t, needRefresh)
assert.Equal(t, argoappv1.RefreshTypeHard, refreshType)
assert.Equal(t, CompareWithLatest, compareWith)
}
}

View File

@@ -87,6 +87,14 @@ func (c *clusterInfo) replaceResourceCache(gk schema.GroupKind, resourceVersion
func (c *clusterInfo) createObjInfo(un *unstructured.Unstructured, appInstanceLabel string) *node {
ownerRefs := un.GetOwnerReferences()
// Special case for endpoint. Remove after https://github.com/kubernetes/kubernetes/issues/28483 is fixed
if un.GroupVersionKind().Group == "" && un.GetKind() == kube.EndpointsKind && len(un.GetOwnerReferences()) == 0 {
ownerRefs = append(ownerRefs, metav1.OwnerReference{
Name: un.GetName(),
Kind: kube.ServiceKind,
APIVersion: "v1",
})
}
nodeInfo := &node{
resourceVersion: un.GetResourceVersion(),
ref: kube.GetObjectRef(un),

View File

@@ -87,6 +87,7 @@ var (
name: helm-guestbook
namespace: default
resourceVersion: "123"
uid: "4"
spec:
selector:
app: guestbook
@@ -102,6 +103,7 @@ var (
metadata:
name: helm-guestbook
namespace: default
uid: "4"
spec:
backend:
serviceName: not-found-service

View File

@@ -35,12 +35,15 @@ func (n *node) resourceKey() kube.ResourceKey {
}
func (n *node) isParentOf(child *node) bool {
// Special case for endpoint. Remove after https://github.com/kubernetes/kubernetes/issues/28483 is fixed
if len(child.ownerRefs) == 0 && child.ref.APIVersion == "v1" && child.ref.Kind == kube.EndpointsKind && n.ref.APIVersion == "v1" && n.ref.Kind == kube.ServiceKind && n.ref.Name == child.ref.Name {
child.ownerRefs = []metav1.OwnerReference{{Name: n.ref.Name, Kind: n.ref.Kind, APIVersion: n.ref.APIVersion, UID: n.ref.UID}}
}
for i, ownerRef := range child.ownerRefs {
// backfill UID of inferred owner child references
if ownerRef.UID == "" && n.ref.Kind == ownerRef.Kind && n.ref.APIVersion == ownerRef.APIVersion && n.ref.Name == ownerRef.Name {
ownerRef.UID = n.ref.UID
child.ownerRefs[i] = ownerRef
return true
}
for _, ownerRef := range child.ownerRefs {
if n.ref.UID == ownerRef.UID {
return true
}

View File

@@ -51,5 +51,6 @@ metadata:
parent := c.createObjInfo(testService, "")
assert.True(t, parent.isParentOf(matchingNameEndPoint))
assert.Equal(t, parent.ref.UID, matchingNameEndPoint.ownerRefs[0].UID)
assert.False(t, parent.isParentOf(nonMatchingNameEndPoint))
}

View File

@@ -18,9 +18,11 @@ import (
type MetricsServer struct {
*http.Server
syncCounter *prometheus.CounterVec
k8sRequestCounter *prometheus.CounterVec
reconcileHistogram *prometheus.HistogramVec
syncCounter *prometheus.CounterVec
k8sRequestCounter *prometheus.CounterVec
kubectlExecCounter *prometheus.CounterVec
kubectlExecPendingGauge *prometheus.GaugeVec
reconcileHistogram *prometheus.HistogramVec
}
const (
@@ -76,6 +78,16 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister, health
append(descAppDefaultLabels, "phase"),
)
appRegistry.MustRegister(syncCounter)
kubectlExecCounter := prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "argocd_kubectl_exec_total",
Help: "Number of kubectl executions",
}, []string{"command"})
appRegistry.MustRegister(kubectlExecCounter)
kubectlExecPendingGauge := prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "argocd_kubectl_exec_pending",
Help: "Number of pending kubectl executions",
}, []string{"command"})
appRegistry.MustRegister(kubectlExecPendingGauge)
k8sRequestCounter := prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "argocd_app_k8s_request_total",
@@ -102,9 +114,11 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister, health
Addr: addr,
Handler: mux,
},
syncCounter: syncCounter,
k8sRequestCounter: k8sRequestCounter,
reconcileHistogram: reconcileHistogram,
syncCounter: syncCounter,
k8sRequestCounter: k8sRequestCounter,
reconcileHistogram: reconcileHistogram,
kubectlExecCounter: kubectlExecCounter,
kubectlExecPendingGauge: kubectlExecPendingGauge,
}
}
@@ -126,6 +140,18 @@ func (m *MetricsServer) IncReconcile(app *argoappv1.Application, duration time.D
m.reconcileHistogram.WithLabelValues(app.Namespace, app.Name, app.Spec.GetProject()).Observe(duration.Seconds())
}
func (m *MetricsServer) IncKubectlExec(command string) {
m.kubectlExecCounter.WithLabelValues(command).Inc()
}
func (m *MetricsServer) IncKubectlExecPending(command string) {
m.kubectlExecPendingGauge.WithLabelValues(command).Inc()
}
func (m *MetricsServer) DecKubectlExecPending(command string) {
m.kubectlExecPendingGauge.WithLabelValues(command).Dec()
}
type appCollector struct {
store applister.ApplicationLister
}

View File

@@ -56,7 +56,7 @@ type ResourceInfoProvider interface {
// AppStateManager defines methods which allow to compare application spec and actual application state.
type AppStateManager interface {
CompareAppState(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, noCache bool, localObjects []string) (*comparisonResult, error)
CompareAppState(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, noCache bool, localObjects []string) *comparisonResult
SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState)
}
@@ -135,10 +135,11 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
if err != nil {
return nil, nil, nil, err
}
targetObjs, hooks, nil := unmarshalManifests(manifestInfo.Manifests)
targetObjs, hooks, err := unmarshalManifests(manifestInfo.Manifests)
if err != nil {
return nil, nil, nil, err
}
return targetObjs, hooks, manifestInfo, nil
}
func unmarshalManifests(manifests []string) ([]*unstructured.Unstructured, []*unstructured.Unstructured, error) {
@@ -234,28 +235,48 @@ func dedupLiveResources(targetObjs []*unstructured.Unstructured, liveObjsByKey m
}
}
// CompareAppState compares application git state to the live app state, using the specified
// revision and supplied source. If revision or overrides are empty, then compares against
// revision and overrides in the app spec.
func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, noCache bool, localManifests []string) (*comparisonResult, error) {
func (m *appStateManager) getComparisonSettings(app *appv1.Application) (string, map[string]v1alpha1.ResourceOverride, diff.Normalizer, error) {
resourceOverrides, err := m.settingsMgr.GetResourceOverrides()
if err != nil {
return nil, err
return "", nil, nil, err
}
appLabelKey, err := m.settingsMgr.GetAppInstanceLabelKey()
if err != nil {
return nil, err
return "", nil, nil, err
}
diffNormalizer, err := argo.NewDiffNormalizer(app.Spec.IgnoreDifferences, resourceOverrides)
if err != nil {
return nil, err
return "", nil, nil, err
}
logCtx := log.WithField("application", app.Name)
logCtx.Infof("Comparing app state (cluster: %s, namespace: %s)", app.Spec.Destination.Server, app.Spec.Destination.Namespace)
observedAt := metav1.Now()
return appLabelKey, resourceOverrides, diffNormalizer, nil
}
// CompareAppState compares application git state to the live app state, using the specified
// revision and supplied source. If revision or overrides are empty, then compares against
// revision and overrides in the app spec.
func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource, noCache bool, localManifests []string) *comparisonResult {
reconciledAt := metav1.Now()
appLabelKey, resourceOverrides, diffNormalizer, err := m.getComparisonSettings(app)
// return unknown comparison result if basic comparison settings cannot be loaded
if err != nil {
return &comparisonResult{
reconciledAt: reconciledAt,
syncStatus: &v1alpha1.SyncStatus{
ComparedTo: appv1.ComparedTo{Source: source, Destination: app.Spec.Destination},
Status: appv1.SyncStatusCodeUnknown,
},
healthStatus: &appv1.HealthStatus{Status: appv1.HealthStatusUnknown},
}
}
// do best effort loading live and target state to present as much information about app state as possible
failedToLoadObjs := false
conditions := make([]v1alpha1.ApplicationCondition, 0)
logCtx := log.WithField("application", app.Name)
logCtx.Infof("Comparing app state (cluster: %s, namespace: %s)", app.Spec.Destination.Server, app.Spec.Destination.Namespace)
var targetObjs []*unstructured.Unstructured
var hooks []*unstructured.Unstructured
var manifestInfo *apiclient.ManifestResponse
@@ -348,7 +369,9 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st
// Do the actual comparison
diffResults, err := diff.DiffArray(targetObjs, managedLiveObj, diffNormalizer)
if err != nil {
return nil, err
diffResults = &diff.DiffResultList{}
failedToLoadObjs = true
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error()})
}
syncCode := v1alpha1.SyncStatusCodeSynced
@@ -429,7 +452,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st
}
compRes := comparisonResult{
reconciledAt: observedAt,
reconciledAt: reconciledAt,
syncStatus: &syncStatus,
healthStatus: healthStatus,
resources: resourceSummaries,
@@ -441,7 +464,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st
if manifestInfo != nil {
compRes.appSourceType = v1alpha1.ApplicationSourceType(manifestInfo.SourceType)
}
return &compRes, nil
return &compRes
}
func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revision string, source v1alpha1.ApplicationSource) error {

View File

@@ -30,8 +30,7 @@ func TestCompareAppStateEmpty(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes, err := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NoError(t, err)
compRes := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
assert.Equal(t, 0, len(compRes.resources))
@@ -53,8 +52,7 @@ func TestCompareAppStateMissing(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes, err := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NoError(t, err)
compRes := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
assert.Equal(t, 1, len(compRes.resources))
@@ -80,8 +78,7 @@ func TestCompareAppStateExtra(t *testing.T) {
},
}
ctrl := newFakeController(&data)
compRes, err := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NoError(t, err)
compRes := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
assert.Equal(t, 1, len(compRes.resources))
@@ -107,8 +104,7 @@ func TestCompareAppStateHook(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes, err := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NoError(t, err)
compRes := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
assert.Equal(t, 0, len(compRes.resources))
@@ -134,9 +130,8 @@ func TestCompareAppStateCompareOptionIgnoreExtraneous(t *testing.T) {
}
ctrl := newFakeController(&data)
compRes, err := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NoError(t, err)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
assert.Len(t, compRes.resources, 0)
@@ -163,8 +158,8 @@ func TestCompareAppStateExtraHook(t *testing.T) {
},
}
ctrl := newFakeController(&data)
compRes, err := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NoError(t, err)
compRes := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
assert.Equal(t, 1, len(compRes.resources))
@@ -200,8 +195,8 @@ func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) {
},
}
ctrl := newFakeController(&data)
compRes, err := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NoError(t, err)
compRes := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.Contains(t, compRes.conditions, argoappv1.ApplicationCondition{
Message: "Resource /Pod/fake-dest-ns/my-pod appeared 2 times among application resources.",
@@ -251,8 +246,7 @@ func TestSetHealth(t *testing.T) {
},
})
compRes, err := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NoError(t, err)
compRes := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.Equal(t, compRes.healthStatus.Status, argoappv1.HealthStatusHealthy)
}
@@ -284,8 +278,7 @@ func TestSetHealthSelfReferencedApp(t *testing.T) {
},
})
compRes, err := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.NoError(t, err)
compRes := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false, nil)
assert.Equal(t, compRes.healthStatus.Status, argoappv1.HealthStatusHealthy)
}

View File

@@ -101,12 +101,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
revision = syncOp.Revision
}
compareResult, err := m.CompareAppState(app, revision, source, false, syncOp.Manifests)
if err != nil {
state.Phase = v1alpha1.OperationError
state.Message = err.Error()
return
}
compareResult := m.CompareAppState(app, revision, source, false, syncOp.Manifests)
// If there are any error conditions, do not perform the operation
errConditions := make([]v1alpha1.ApplicationCondition, 0)
@@ -264,8 +259,11 @@ func (sc *syncContext) sync() {
}
}
// any running tasks, lets wait...
if tasks.Any(func(t *syncTask) bool { return t.running() }) {
// if (a) we are multi-step and we have any running tasks,
// or (b) there are any running hooks,
// then wait...
multiStep := tasks.multiStep()
if tasks.Any(func(t *syncTask) bool { return (multiStep || t.isHook()) && t.running() }) {
sc.setOperationPhase(v1alpha1.OperationRunning, "one or more tasks are running")
return
}
@@ -279,9 +277,9 @@ func (sc *syncContext) sync() {
return
}
sc.log.WithFields(log.Fields{"tasks": tasks}).Debug("filtering out completed tasks")
sc.log.WithFields(log.Fields{"tasks": tasks}).Debug("filtering out non-pending tasks")
// remove tasks that are completed, we can assume that there are no running tasks
tasks = tasks.Filter(func(t *syncTask) bool { return !t.completed() })
tasks = tasks.Filter(func(t *syncTask) bool { return t.pending() })
// If no sync tasks were generated (e.g., in case all application manifests have been removed),
// the sync operation is successful.

View File

@@ -94,6 +94,10 @@ func (t *syncTask) namespace() string {
return t.obj().GetNamespace()
}
func (t *syncTask) pending() bool {
return t.operationState == ""
}
func (t *syncTask) running() bool {
return t.operationState == v1alpha1.OperationRunning
}

View File

@@ -165,3 +165,21 @@ func (s syncTasks) wave() int {
}
return 0
}
func (s syncTasks) lastPhase() v1alpha1.SyncPhase {
if len(s) > 0 {
return s[len(s)-1].phase
}
return ""
}
func (s syncTasks) lastWave() int {
if len(s) > 0 {
return s[len(s)-1].wave()
}
return 0
}
func (s syncTasks) multiStep() bool {
return s.wave() != s.lastWave() || s.phase() != s.lastPhase()
}

View File

@@ -8,7 +8,9 @@ import (
apiv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/argoproj/argo-cd/common"
. "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
. "github.com/argoproj/argo-cd/test"
)
func Test_syncTasks_kindOrder(t *testing.T) {
@@ -366,3 +368,25 @@ func TestSyncNamespaceAgainstCRD(t *testing.T) {
assert.Equal(t, syncTasks{namespace, crd}, unsorted)
}
func Test_syncTasks_multiStep(t *testing.T) {
t.Run("Single", func(t *testing.T) {
tasks := syncTasks{{liveObj: Annotate(NewPod(), common.AnnotationSyncWave, "-1"), phase: SyncPhaseSync}}
assert.Equal(t, SyncPhaseSync, tasks.phase())
assert.Equal(t, -1, tasks.wave())
assert.Equal(t, SyncPhaseSync, tasks.lastPhase())
assert.Equal(t, -1, tasks.lastWave())
assert.False(t, tasks.multiStep())
})
t.Run("Double", func(t *testing.T) {
tasks := syncTasks{
{liveObj: Annotate(NewPod(), common.AnnotationSyncWave, "-1"), phase: SyncPhasePreSync},
{liveObj: Annotate(NewPod(), common.AnnotationSyncWave, "1"), phase: SyncPhasePostSync},
}
assert.Equal(t, SyncPhasePreSync, tasks.phase())
assert.Equal(t, -1, tasks.wave())
assert.Equal(t, SyncPhasePostSync, tasks.lastPhase())
assert.Equal(t, 1, tasks.lastWave())
assert.True(t, tasks.multiStep())
})
}

View File

@@ -91,4 +91,37 @@ Argo CD automatically sets the `app.kubernetes.io/instance` label and uses it to
!!! note When you make this change your applications will become out of sync and will need re-syncing.
See [#1482](https://github.com/argoproj/argo-cd/issues/1482).
See [#1482](https://github.com/argoproj/argo-cd/issues/1482).
# How Do I Fix "invalid cookie, longer than max length 4093"?
Argo CD uses a JWT as the auth token. You likely are part of many groups and have gone over the 4KB limit which is set for cookies.
You can get the list of groups by opening "developer tools -> network"
* Click log in
* Find the call to `<argocd_instance>/auth/callback?code=<random_string>`
Decode the token at https://jwt.io/. That will provide the list of teams that you can remove yourself from.
See [#2165](https://github.com/argoproj/argo-cd/issues/2165).
## Why Am I Getting `rpc error: code = Unavailable desc = transport is closing` When Using The CLI?
Maybe you're behind a proxy that does not support HTTP 2? Try the `--grcp-web` flag.:
```bash
argocd ... --grcp-web
```
## Why Am I Getting `x509: certificate signed by unknown authority` When Using The CLI?
Your not running your server with correct certs.
If you're not running in a production system (e.g. you're testing Argo CD out), try the `--insecure` flag:
```bash
argocd ... --insecure
```
!!! warning "Do not use `--insecure` in production"

View File

@@ -18,6 +18,11 @@ data:
# Unless set to 'false' then user ids are hashed before sending to google analytics
ga.anonymizeusers: 'false'
# the URL for getting chat help, this will typically be your Slack channel for support
help.chatUrl: 'https://mycorp.slack.com/argo-cd'
# the text for getting chat help, defaults to "Chat now!"
help.chatText: 'Chat now!'
# A dex connector configuration (optional). See SSO configuration documentation:
# https://github.com/argoproj/argo-cd/blob/master/docs/sso.md
# https://github.com/dexidp/dex/tree/master/Documentation/connectors

View File

@@ -11,6 +11,10 @@ func CheckError(err error) {
}
}
func FailOnErr(_ interface{}, err error) {
// panics if there is an error.
// This returns the first value so you can use it if you cast it:
// text := FailOrErr(Foo)).(string)
func FailOnErr(v interface{}, err error) interface{} {
CheckError(err)
return v
}

View File

@@ -10,7 +10,20 @@ AUTOGENMSG="# This is an auto-generated file. DO NOT EDIT"
cd ${SRCROOT}/manifests/ha/base/redis-ha && ./generate.sh
IMAGE_NAMESPACE="${IMAGE_NAMESPACE:-argoproj}"
IMAGE_TAG="${IMAGE_TAG:-latest}"
IMAGE_TAG="${IMAGE_TAG:-}"
# if the tag has not been declared, and we are on a release branch, use the VERSION file.
if [ "$IMAGE_TAG" = "" ]; then
branch=$(git rev-parse --abbrev-ref HEAD)
if [[ $branch = release-* ]]; then
pwd
IMAGE_TAG=v$(cat $SRCROOT/VERSION)
fi
fi
# otherwise, use latest
if [ "$IMAGE_TAG" = "" ]; then
IMAGE_TAG=latest
fi
cd ${SRCROOT}/manifests/base && kustomize edit set image argoproj/argocd=${IMAGE_NAMESPACE}/argocd:${IMAGE_TAG} argoproj/argocd-ui=${IMAGE_NAMESPACE}/argocd-ui:${IMAGE_TAG}
cd ${SRCROOT}/manifests/ha/base && kustomize edit set image argoproj/argocd=${IMAGE_NAMESPACE}/argocd:${IMAGE_TAG} argoproj/argocd-ui=${IMAGE_NAMESPACE}/argocd-ui:${IMAGE_TAG}

View File

@@ -12,7 +12,7 @@ bases:
images:
- name: argoproj/argocd
newName: argoproj/argocd
newTag: latest
newTag: v1.2.2
- name: argoproj/argocd-ui
newName: argoproj/argocd-ui
newTag: latest
newTag: v1.2.2

View File

@@ -18,7 +18,7 @@ bases:
images:
- name: argoproj/argocd
newName: argoproj/argocd
newTag: latest
newTag: v1.2.2
- name: argoproj/argocd-ui
newName: argoproj/argocd-ui
newTag: latest
newTag: v1.2.2

View File

@@ -2901,7 +2901,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2955,7 +2955,7 @@ spec:
- cp
- /usr/local/bin/argocd-util
- /shared
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -3010,7 +3010,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
livenessProbe:
initialDelaySeconds: 5
@@ -3084,7 +3084,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -2816,7 +2816,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2870,7 +2870,7 @@ spec:
- cp
- /usr/local/bin/argocd-util
- /shared
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -2925,7 +2925,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
livenessProbe:
initialDelaySeconds: 5
@@ -2999,7 +2999,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -2665,7 +2665,7 @@ spec:
- "20"
- --operation-processors
- "10"
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2719,7 +2719,7 @@ spec:
- cp
- /usr/local/bin/argocd-util
- /shared
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -2782,7 +2782,7 @@ spec:
- argocd-repo-server
- --redis
- argocd-redis:6379
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
livenessProbe:
initialDelaySeconds: 5
@@ -2833,7 +2833,7 @@ spec:
- argocd-server
- --staticassets
- /shared/app
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -2580,7 +2580,7 @@ spec:
- "20"
- --operation-processors
- "10"
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2634,7 +2634,7 @@ spec:
- cp
- /usr/local/bin/argocd-util
- /shared
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -2697,7 +2697,7 @@ spec:
- argocd-repo-server
- --redis
- argocd-redis:6379
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
livenessProbe:
initialDelaySeconds: 5
@@ -2748,7 +2748,7 @@ spec:
- argocd-server
- --staticassets
- /shared/app
image: argoproj/argocd:latest
image: argoproj/argocd:v1.2.2
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -36,6 +36,7 @@ import (
versionpkg "github.com/argoproj/argo-cd/pkg/apiclient/version"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util"
grpc_util "github.com/argoproj/argo-cd/util/grpc"
"github.com/argoproj/argo-cd/util/localconfig"
oidcutil "github.com/argoproj/argo-cd/util/oidc"
@@ -387,7 +388,7 @@ func (c *client) newConn() (*grpc.ClientConn, io.Closer, error) {
}
conn, e := grpc_util.BlockingDial(context.Background(), network, serverAddr, creds, dialOpts...)
closers = append(closers, conn)
return conn, &inlineCloser{close: func() error {
return conn, util.NewCloser(func() error {
var firstErr error
for i := range closers {
err := closers[i].Close()
@@ -396,7 +397,7 @@ func (c *client) newConn() (*grpc.ClientConn, io.Closer, error) {
}
}
return firstErr
}}, e
}), e
}
func (c *client) tlsConfig() (*tls.Config, error) {

View File

@@ -43,14 +43,6 @@ func (noopCodec) String() string {
return "bytes"
}
type inlineCloser struct {
close func() error
}
func (c *inlineCloser) Close() error {
return c.close()
}
func toFrame(msg []byte) []byte {
frame := append([]byte{0, 0, 0, 0}, msg...)
binary.BigEndian.PutUint32(frame, uint32(len(msg)))
@@ -185,7 +177,7 @@ func (c *client) useGRPCProxy() (net.Addr, io.Closer, error) {
}
c.proxyUsersCount = c.proxyUsersCount + 1
return c.proxyListener.Addr(), &inlineCloser{close: func() error {
return c.proxyListener.Addr(), util.NewCloser(func() error {
c.proxyMutex.Lock()
defer c.proxyMutex.Unlock()
c.proxyUsersCount = c.proxyUsersCount - 1
@@ -196,5 +188,5 @@ func (c *client) useGRPCProxy() (net.Addr, io.Closer, error) {
return nil
}
return nil
}}, nil
}), nil
}

View File

@@ -43,7 +43,7 @@ func (m *SettingsQuery) Reset() { *m = SettingsQuery{} }
func (m *SettingsQuery) String() string { return proto.CompactTextString(m) }
func (*SettingsQuery) ProtoMessage() {}
func (*SettingsQuery) Descriptor() ([]byte, []int) {
return fileDescriptor_settings_7350a88adeab0893, []int{0}
return fileDescriptor_settings_ab575d97e47167cb, []int{0}
}
func (m *SettingsQuery) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -73,24 +73,26 @@ func (m *SettingsQuery) XXX_DiscardUnknown() {
var xxx_messageInfo_SettingsQuery proto.InternalMessageInfo
type Settings struct {
URL string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
DexConfig *DexConfig `protobuf:"bytes,2,opt,name=dexConfig" json:"dexConfig,omitempty"`
OIDCConfig *OIDCConfig `protobuf:"bytes,3,opt,name=oidcConfig" json:"oidcConfig,omitempty"`
AppLabelKey string `protobuf:"bytes,4,opt,name=appLabelKey,proto3" json:"appLabelKey,omitempty"`
ResourceOverrides map[string]*v1alpha1.ResourceOverride `protobuf:"bytes,5,rep,name=resourceOverrides" json:"resourceOverrides,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
StatusBadgeEnabled bool `protobuf:"varint,6,opt,name=statusBadgeEnabled,proto3" json:"statusBadgeEnabled,omitempty"`
GoogleAnalytics *GoogleAnalyticsConfig `protobuf:"bytes,7,opt,name=googleAnalytics" json:"googleAnalytics,omitempty"`
KustomizeOptions *v1alpha1.KustomizeOptions `protobuf:"bytes,8,opt,name=kustomizeOptions" json:"kustomizeOptions,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
URL string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
DexConfig *DexConfig `protobuf:"bytes,2,opt,name=dexConfig" json:"dexConfig,omitempty"`
OIDCConfig *OIDCConfig `protobuf:"bytes,3,opt,name=oidcConfig" json:"oidcConfig,omitempty"`
AppLabelKey string `protobuf:"bytes,4,opt,name=appLabelKey,proto3" json:"appLabelKey,omitempty"`
ResourceOverrides map[string]*v1alpha1.ResourceOverride `protobuf:"bytes,5,rep,name=resourceOverrides" json:"resourceOverrides,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
StatusBadgeEnabled bool `protobuf:"varint,6,opt,name=statusBadgeEnabled,proto3" json:"statusBadgeEnabled,omitempty"`
GoogleAnalytics *GoogleAnalyticsConfig `protobuf:"bytes,7,opt,name=googleAnalytics" json:"googleAnalytics,omitempty"`
KustomizeOptions *v1alpha1.KustomizeOptions `protobuf:"bytes,8,opt,name=kustomizeOptions" json:"kustomizeOptions,omitempty"`
// Help settings
Help *Help `protobuf:"bytes,9,opt,name=help" json:"help,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Settings) Reset() { *m = Settings{} }
func (m *Settings) String() string { return proto.CompactTextString(m) }
func (*Settings) ProtoMessage() {}
func (*Settings) Descriptor() ([]byte, []int) {
return fileDescriptor_settings_7350a88adeab0893, []int{1}
return fileDescriptor_settings_ab575d97e47167cb, []int{1}
}
func (m *Settings) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -175,6 +177,13 @@ func (m *Settings) GetKustomizeOptions() *v1alpha1.KustomizeOptions {
return nil
}
func (m *Settings) GetHelp() *Help {
if m != nil {
return m.Help
}
return nil
}
type GoogleAnalyticsConfig struct {
TrackingID string `protobuf:"bytes,1,opt,name=trackingID,proto3" json:"trackingID,omitempty"`
AnonymizeUsers bool `protobuf:"varint,2,opt,name=anonymizeUsers,proto3" json:"anonymizeUsers,omitempty"`
@@ -187,7 +196,7 @@ func (m *GoogleAnalyticsConfig) Reset() { *m = GoogleAnalyticsConfig{} }
func (m *GoogleAnalyticsConfig) String() string { return proto.CompactTextString(m) }
func (*GoogleAnalyticsConfig) ProtoMessage() {}
func (*GoogleAnalyticsConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_settings_7350a88adeab0893, []int{2}
return fileDescriptor_settings_ab575d97e47167cb, []int{2}
}
func (m *GoogleAnalyticsConfig) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -230,6 +239,64 @@ func (m *GoogleAnalyticsConfig) GetAnonymizeUsers() bool {
return false
}
// Help settings
type Help struct {
// the URL for getting chat help, this will typically be your Slack channel for support
ChatUrl string `protobuf:"bytes,1,opt,name=chatUrl,proto3" json:"chatUrl,omitempty"`
// the text for getting chat help, defaults to "Chat now!"
ChatText string `protobuf:"bytes,2,opt,name=chatText,proto3" json:"chatText,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Help) Reset() { *m = Help{} }
func (m *Help) String() string { return proto.CompactTextString(m) }
func (*Help) ProtoMessage() {}
func (*Help) Descriptor() ([]byte, []int) {
return fileDescriptor_settings_ab575d97e47167cb, []int{3}
}
func (m *Help) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Help) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Help.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *Help) XXX_Merge(src proto.Message) {
xxx_messageInfo_Help.Merge(dst, src)
}
func (m *Help) XXX_Size() int {
return m.Size()
}
func (m *Help) XXX_DiscardUnknown() {
xxx_messageInfo_Help.DiscardUnknown(m)
}
var xxx_messageInfo_Help proto.InternalMessageInfo
func (m *Help) GetChatUrl() string {
if m != nil {
return m.ChatUrl
}
return ""
}
func (m *Help) GetChatText() string {
if m != nil {
return m.ChatText
}
return ""
}
type DexConfig struct {
Connectors []*Connector `protobuf:"bytes,1,rep,name=connectors" json:"connectors,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@@ -241,7 +308,7 @@ func (m *DexConfig) Reset() { *m = DexConfig{} }
func (m *DexConfig) String() string { return proto.CompactTextString(m) }
func (*DexConfig) ProtoMessage() {}
func (*DexConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_settings_7350a88adeab0893, []int{3}
return fileDescriptor_settings_ab575d97e47167cb, []int{4}
}
func (m *DexConfig) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -289,7 +356,7 @@ func (m *Connector) Reset() { *m = Connector{} }
func (m *Connector) String() string { return proto.CompactTextString(m) }
func (*Connector) ProtoMessage() {}
func (*Connector) Descriptor() ([]byte, []int) {
return fileDescriptor_settings_7350a88adeab0893, []int{4}
return fileDescriptor_settings_ab575d97e47167cb, []int{5}
}
func (m *Connector) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -347,7 +414,7 @@ func (m *OIDCConfig) Reset() { *m = OIDCConfig{} }
func (m *OIDCConfig) String() string { return proto.CompactTextString(m) }
func (*OIDCConfig) ProtoMessage() {}
func (*OIDCConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_settings_7350a88adeab0893, []int{5}
return fileDescriptor_settings_ab575d97e47167cb, []int{6}
}
func (m *OIDCConfig) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -416,6 +483,7 @@ func init() {
proto.RegisterType((*Settings)(nil), "cluster.Settings")
proto.RegisterMapType((map[string]*v1alpha1.ResourceOverride)(nil), "cluster.Settings.ResourceOverridesEntry")
proto.RegisterType((*GoogleAnalyticsConfig)(nil), "cluster.GoogleAnalyticsConfig")
proto.RegisterType((*Help)(nil), "cluster.Help")
proto.RegisterType((*DexConfig)(nil), "cluster.DexConfig")
proto.RegisterType((*Connector)(nil), "cluster.Connector")
proto.RegisterType((*OIDCConfig)(nil), "cluster.OIDCConfig")
@@ -621,6 +689,16 @@ func (m *Settings) MarshalTo(dAtA []byte) (int, error) {
}
i += n5
}
if m.Help != nil {
dAtA[i] = 0x4a
i++
i = encodeVarintSettings(dAtA, i, uint64(m.Help.Size()))
n6, err := m.Help.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n6
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
@@ -664,6 +742,39 @@ func (m *GoogleAnalyticsConfig) MarshalTo(dAtA []byte) (int, error) {
return i, nil
}
func (m *Help) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Help) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if len(m.ChatUrl) > 0 {
dAtA[i] = 0xa
i++
i = encodeVarintSettings(dAtA, i, uint64(len(m.ChatUrl)))
i += copy(dAtA[i:], m.ChatUrl)
}
if len(m.ChatText) > 0 {
dAtA[i] = 0x12
i++
i = encodeVarintSettings(dAtA, i, uint64(len(m.ChatText)))
i += copy(dAtA[i:], m.ChatText)
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func (m *DexConfig) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -851,6 +962,10 @@ func (m *Settings) Size() (n int) {
l = m.KustomizeOptions.Size()
n += 1 + l + sovSettings(uint64(l))
}
if m.Help != nil {
l = m.Help.Size()
n += 1 + l + sovSettings(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
@@ -873,6 +988,23 @@ func (m *GoogleAnalyticsConfig) Size() (n int) {
return n
}
func (m *Help) Size() (n int) {
var l int
_ = l
l = len(m.ChatUrl)
if l > 0 {
n += 1 + l + sovSettings(uint64(l))
}
l = len(m.ChatText)
if l > 0 {
n += 1 + l + sovSettings(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *DexConfig) Size() (n int) {
var l int
_ = l
@@ -1362,6 +1494,39 @@ func (m *Settings) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
case 9:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Help", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowSettings
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthSettings
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Help == nil {
m.Help = &Help{}
}
if err := m.Help.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipSettings(dAtA[iNdEx:])
@@ -1484,6 +1649,115 @@ func (m *GoogleAnalyticsConfig) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *Help) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowSettings
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Help: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Help: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ChatUrl", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowSettings
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthSettings
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ChatUrl = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ChatText", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowSettings
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthSettings
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ChatText = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipSettings(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthSettings
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *DexConfig) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@@ -1977,53 +2251,56 @@ var (
)
func init() {
proto.RegisterFile("server/settings/settings.proto", fileDescriptor_settings_7350a88adeab0893)
proto.RegisterFile("server/settings/settings.proto", fileDescriptor_settings_ab575d97e47167cb)
}
var fileDescriptor_settings_7350a88adeab0893 = []byte{
// 699 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xcd, 0x6e, 0xd3, 0x40,
0x10, 0x96, 0x9b, 0xfe, 0x24, 0x13, 0x68, 0xda, 0x05, 0x2a, 0x13, 0xa1, 0x24, 0xca, 0x01, 0xe5,
0x82, 0x4d, 0xd2, 0x0b, 0xe2, 0x02, 0x24, 0xa9, 0x4a, 0x68, 0xa5, 0x8a, 0xad, 0xca, 0x01, 0x09,
0x55, 0x1b, 0x7b, 0x71, 0x4d, 0xdc, 0x5d, 0x6b, 0x77, 0x1d, 0x08, 0x47, 0xde, 0x00, 0x71, 0xe6,
0x11, 0x78, 0x0f, 0x8e, 0x48, 0xdc, 0x23, 0x14, 0xf1, 0x20, 0xc8, 0xeb, 0x9f, 0x84, 0x24, 0x07,
0x24, 0x6e, 0xe3, 0xef, 0xdb, 0x6f, 0x76, 0x66, 0xfd, 0xcd, 0x40, 0x4d, 0x52, 0x31, 0xa6, 0xc2,
0x96, 0x54, 0x29, 0x9f, 0x79, 0x32, 0x0f, 0xac, 0x50, 0x70, 0xc5, 0xd1, 0x8e, 0x13, 0x44, 0x52,
0x51, 0x51, 0xbd, 0xed, 0x71, 0x8f, 0x6b, 0xcc, 0x8e, 0xa3, 0x84, 0xae, 0xde, 0xf3, 0x38, 0xf7,
0x02, 0x6a, 0x93, 0xd0, 0xb7, 0x09, 0x63, 0x5c, 0x11, 0xe5, 0x73, 0x96, 0x8a, 0xab, 0x03, 0xcf,
0x57, 0x57, 0xd1, 0xd0, 0x72, 0xf8, 0xb5, 0x4d, 0x84, 0x96, 0xbf, 0xd3, 0xc1, 0x03, 0xc7, 0xb5,
0xc3, 0x91, 0x17, 0xcb, 0xa4, 0x4d, 0xc2, 0x30, 0xf0, 0x1d, 0x2d, 0xb4, 0xc7, 0x6d, 0x12, 0x84,
0x57, 0xa4, 0x6d, 0x7b, 0x94, 0x51, 0x41, 0x14, 0x75, 0x93, 0x54, 0xcd, 0x0a, 0xdc, 0x3c, 0x4f,
0x2b, 0x7b, 0x19, 0x51, 0x31, 0x69, 0x7e, 0xdd, 0x82, 0x62, 0x86, 0xa0, 0xbb, 0x50, 0x88, 0x44,
0x60, 0x1a, 0x0d, 0xa3, 0x55, 0xea, 0xee, 0xcc, 0xa6, 0xf5, 0xc2, 0x05, 0x3e, 0xc5, 0x31, 0x86,
0x1e, 0x42, 0xc9, 0xa5, 0x1f, 0x7a, 0x9c, 0xbd, 0xf5, 0x3d, 0x73, 0xa3, 0x61, 0xb4, 0xca, 0x1d,
0x64, 0xa5, 0x4d, 0x59, 0xfd, 0x8c, 0xc1, 0xf3, 0x43, 0xa8, 0x07, 0xc0, 0x7d, 0xd7, 0x49, 0x25,
0x05, 0x2d, 0xb9, 0x95, 0x4b, 0xce, 0x06, 0xfd, 0x5e, 0x42, 0x75, 0x77, 0x67, 0xd3, 0x3a, 0xcc,
0xbf, 0xf1, 0x82, 0x0c, 0x35, 0xa0, 0x4c, 0xc2, 0xf0, 0x94, 0x0c, 0x69, 0x70, 0x42, 0x27, 0xe6,
0x66, 0x5c, 0x19, 0x5e, 0x84, 0xd0, 0x2b, 0xd8, 0x17, 0x54, 0xf2, 0x48, 0x38, 0xf4, 0x6c, 0x4c,
0x85, 0xf0, 0x5d, 0x2a, 0xcd, 0xad, 0x46, 0xa1, 0x55, 0xee, 0xb4, 0xf2, 0xdb, 0xb2, 0x0e, 0x2d,
0xbc, 0x7c, 0xf4, 0x88, 0x29, 0x31, 0xc1, 0xab, 0x29, 0x90, 0x05, 0x48, 0x2a, 0xa2, 0x22, 0xd9,
0x25, 0xae, 0x47, 0x8f, 0x18, 0x19, 0x06, 0xd4, 0x35, 0xb7, 0x1b, 0x46, 0xab, 0x88, 0xd7, 0x30,
0xe8, 0x39, 0x54, 0x92, 0x9f, 0xf8, 0x8c, 0x91, 0x60, 0xa2, 0x7c, 0x47, 0x9a, 0x3b, 0xba, 0xe7,
0x5a, 0x5e, 0xc5, 0xf1, 0xdf, 0x7c, 0xda, 0xee, 0xb2, 0x0c, 0xbd, 0x87, 0xbd, 0x51, 0x24, 0x15,
0xbf, 0xf6, 0x3f, 0xd2, 0xb3, 0x50, 0x1b, 0xc1, 0x2c, 0xea, 0x54, 0x27, 0xd6, 0xdc, 0x09, 0x56,
0xe6, 0x04, 0x1d, 0x5c, 0x3a, 0xae, 0x15, 0x8e, 0x3c, 0x2b, 0x76, 0x82, 0xb5, 0xe0, 0x04, 0x2b,
0x73, 0x82, 0x75, 0xb2, 0x94, 0x12, 0xaf, 0x5c, 0x52, 0xfd, 0x6c, 0xc0, 0xc1, 0xfa, 0x07, 0x42,
0x7b, 0x50, 0x18, 0xd1, 0x49, 0xe2, 0x0c, 0x1c, 0x87, 0x88, 0xc0, 0xd6, 0x98, 0x04, 0x11, 0x4d,
0xcd, 0xf0, 0x3f, 0xa5, 0x2d, 0xdf, 0x89, 0x93, 0xcc, 0x8f, 0x37, 0x1e, 0x19, 0xcd, 0x4b, 0xb8,
0xb3, 0xf6, 0xd9, 0x50, 0x0d, 0x40, 0x09, 0xe2, 0x8c, 0x7c, 0xe6, 0x0d, 0xfa, 0x69, 0x61, 0x0b,
0x08, 0xba, 0x0f, 0xbb, 0x84, 0x71, 0x36, 0x89, 0x1b, 0xbc, 0x90, 0x54, 0x48, 0x5d, 0x68, 0x11,
0x2f, 0xa1, 0xcd, 0x27, 0x50, 0xca, 0xed, 0x8b, 0x3a, 0x00, 0x0e, 0x67, 0x8c, 0x3a, 0x8a, 0x0b,
0x69, 0x1a, 0xda, 0x45, 0x73, 0x9b, 0xf7, 0x32, 0x0a, 0x2f, 0x9c, 0x6a, 0x1e, 0x42, 0x29, 0x27,
0x10, 0x82, 0x4d, 0x46, 0xae, 0x69, 0x5a, 0x8f, 0x8e, 0x63, 0x4c, 0x4d, 0xc2, 0xe4, 0xa1, 0x4a,
0x58, 0xc7, 0xcd, 0x6f, 0x06, 0x2c, 0x58, 0x7e, 0xad, 0xec, 0x00, 0xb6, 0x7d, 0x29, 0x23, 0x2a,
0x52, 0x61, 0xfa, 0x85, 0x5a, 0x50, 0x74, 0x02, 0x9f, 0x32, 0x35, 0xe8, 0xeb, 0xa9, 0x2a, 0x75,
0x6f, 0xcc, 0xa6, 0xf5, 0x62, 0x2f, 0xc5, 0x70, 0xce, 0xa2, 0x36, 0x94, 0x9d, 0xc0, 0xcf, 0x88,
0x64, 0x78, 0xba, 0x95, 0xd9, 0xb4, 0x5e, 0xee, 0x9d, 0x0e, 0xf2, 0xf3, 0x8b, 0x67, 0xe2, 0x4b,
0xa5, 0xc3, 0xc3, 0x74, 0x84, 0x4a, 0x38, 0xfd, 0xea, 0xbc, 0x81, 0x4a, 0x36, 0x43, 0xe7, 0x54,
0x8c, 0x7d, 0x87, 0xa2, 0x17, 0x50, 0x38, 0xa6, 0x0a, 0x1d, 0xac, 0x0c, 0x99, 0x5e, 0x2c, 0xd5,
0xfd, 0x15, 0xbc, 0x69, 0x7e, 0xfa, 0xf9, 0xfb, 0xcb, 0x06, 0x42, 0x7b, 0x7a, 0xcf, 0x8d, 0xdb,
0xf9, 0x92, 0xec, 0x3e, 0xfd, 0x3e, 0xab, 0x19, 0x3f, 0x66, 0x35, 0xe3, 0xd7, 0xac, 0x66, 0xbc,
0xee, 0xfc, 0xc3, 0xbe, 0x4b, 0x9a, 0xcc, 0x33, 0x0c, 0xb7, 0xf5, 0x7e, 0x3b, 0xfc, 0x13, 0x00,
0x00, 0xff, 0xff, 0x5b, 0xcc, 0xdb, 0x51, 0x89, 0x05, 0x00, 0x00,
var fileDescriptor_settings_ab575d97e47167cb = []byte{
// 750 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xcd, 0x6e, 0x23, 0x45,
0x10, 0xd6, 0xc4, 0xf9, 0xf1, 0x94, 0xd9, 0x4d, 0xb6, 0x81, 0x68, 0xb0, 0x90, 0x63, 0xe6, 0x80,
0x7c, 0x61, 0x86, 0x78, 0x2f, 0x08, 0x21, 0x01, 0x76, 0x56, 0xbb, 0x26, 0x91, 0x22, 0x7a, 0x09,
0x07, 0x24, 0xb4, 0xea, 0xf4, 0x14, 0xe3, 0xc1, 0x93, 0xee, 0x51, 0x77, 0x8f, 0x59, 0x73, 0xe4,
0x0d, 0x10, 0xcf, 0xc1, 0x3b, 0x70, 0xe4, 0x88, 0xc4, 0xdd, 0x42, 0x16, 0x0f, 0x82, 0xa6, 0xe7,
0xc7, 0xc6, 0xf6, 0x01, 0x89, 0x5b, 0x55, 0x7d, 0xf5, 0xd5, 0x4f, 0x77, 0x55, 0x41, 0x4f, 0xa3,
0x9a, 0xa3, 0x0a, 0x35, 0x1a, 0x93, 0x88, 0x58, 0x37, 0x42, 0x90, 0x29, 0x69, 0x24, 0x39, 0xe1,
0x69, 0xae, 0x0d, 0xaa, 0xee, 0x5b, 0xb1, 0x8c, 0xa5, 0xb5, 0x85, 0x85, 0x54, 0xc2, 0xdd, 0x77,
0x63, 0x29, 0xe3, 0x14, 0x43, 0x96, 0x25, 0x21, 0x13, 0x42, 0x1a, 0x66, 0x12, 0x29, 0x2a, 0x72,
0x77, 0x12, 0x27, 0x66, 0x9a, 0xdf, 0x07, 0x5c, 0x3e, 0x84, 0x4c, 0x59, 0xfa, 0xf7, 0x56, 0xf8,
0x80, 0x47, 0x61, 0x36, 0x8b, 0x0b, 0x9a, 0x0e, 0x59, 0x96, 0xa5, 0x09, 0xb7, 0xc4, 0x70, 0x7e,
0xc9, 0xd2, 0x6c, 0xca, 0x2e, 0xc3, 0x18, 0x05, 0x2a, 0x66, 0x30, 0x2a, 0x43, 0xf9, 0xa7, 0xf0,
0xe8, 0x65, 0x55, 0xd9, 0x97, 0x39, 0xaa, 0x85, 0xff, 0xdb, 0x11, 0xb4, 0x6b, 0x0b, 0x79, 0x07,
0x5a, 0xb9, 0x4a, 0x3d, 0xa7, 0xef, 0x0c, 0xdc, 0xd1, 0xc9, 0x6a, 0x79, 0xd1, 0xba, 0xa3, 0x37,
0xb4, 0xb0, 0x91, 0x0f, 0xc1, 0x8d, 0xf0, 0xf5, 0x58, 0x8a, 0xef, 0x92, 0xd8, 0x3b, 0xe8, 0x3b,
0x83, 0xce, 0x90, 0x04, 0x55, 0x53, 0xc1, 0x55, 0x8d, 0xd0, 0xb5, 0x13, 0x19, 0x03, 0xc8, 0x24,
0xe2, 0x15, 0xa5, 0x65, 0x29, 0x6f, 0x36, 0x94, 0xdb, 0xc9, 0xd5, 0xb8, 0x84, 0x46, 0x8f, 0x57,
0xcb, 0x0b, 0x58, 0xeb, 0x74, 0x83, 0x46, 0xfa, 0xd0, 0x61, 0x59, 0x76, 0xc3, 0xee, 0x31, 0xbd,
0xc6, 0x85, 0x77, 0x58, 0x54, 0x46, 0x37, 0x4d, 0xe4, 0x6b, 0x78, 0xa2, 0x50, 0xcb, 0x5c, 0x71,
0xbc, 0x9d, 0xa3, 0x52, 0x49, 0x84, 0xda, 0x3b, 0xea, 0xb7, 0x06, 0x9d, 0xe1, 0xa0, 0xc9, 0x56,
0x77, 0x18, 0xd0, 0x6d, 0xd7, 0x67, 0xc2, 0xa8, 0x05, 0xdd, 0x0d, 0x41, 0x02, 0x20, 0xda, 0x30,
0x93, 0xeb, 0x11, 0x8b, 0x62, 0x7c, 0x26, 0xd8, 0x7d, 0x8a, 0x91, 0x77, 0xdc, 0x77, 0x06, 0x6d,
0xba, 0x07, 0x21, 0x2f, 0xe0, 0xb4, 0xfc, 0xc4, 0xcf, 0x05, 0x4b, 0x17, 0x26, 0xe1, 0xda, 0x3b,
0xb1, 0x3d, 0xf7, 0x9a, 0x2a, 0x9e, 0xff, 0x1b, 0xaf, 0xda, 0xdd, 0xa6, 0x91, 0x1f, 0xe0, 0x6c,
0x96, 0x6b, 0x23, 0x1f, 0x92, 0x1f, 0xf1, 0x36, 0xb3, 0x83, 0xe0, 0xb5, 0x6d, 0xa8, 0xeb, 0x60,
0x3d, 0x09, 0x41, 0x3d, 0x09, 0x56, 0x78, 0xc5, 0xa3, 0x20, 0x9b, 0xc5, 0x41, 0x31, 0x09, 0xc1,
0xc6, 0x24, 0x04, 0xf5, 0x24, 0x04, 0xd7, 0x5b, 0x21, 0xe9, 0x4e, 0x12, 0xf2, 0x1e, 0x1c, 0x4e,
0x31, 0xcd, 0x3c, 0xd7, 0x26, 0x7b, 0xd4, 0xd4, 0xfd, 0x02, 0xd3, 0x8c, 0x5a, 0xa8, 0xfb, 0xb3,
0x03, 0xe7, 0xfb, 0xdf, 0x90, 0x9c, 0x41, 0x6b, 0x86, 0x8b, 0x72, 0x78, 0x68, 0x21, 0x12, 0x06,
0x47, 0x73, 0x96, 0xe6, 0x58, 0xcd, 0xcb, 0xff, 0xa9, 0x7e, 0x3b, 0x27, 0x2d, 0x23, 0x7f, 0x7c,
0xf0, 0x91, 0xe3, 0xbf, 0x82, 0xb7, 0xf7, 0xbe, 0x2c, 0xe9, 0x01, 0x18, 0xc5, 0xf8, 0x2c, 0x11,
0xf1, 0xe4, 0xaa, 0x2a, 0x6c, 0xc3, 0x42, 0xde, 0x87, 0xc7, 0x4c, 0x48, 0xb1, 0x28, 0xde, 0xe0,
0x4e, 0xa3, 0xd2, 0xb6, 0xd0, 0x36, 0xdd, 0xb2, 0xfa, 0x9f, 0xc0, 0x61, 0xf1, 0x04, 0xc4, 0x83,
0x13, 0x3e, 0x65, 0xe6, 0xae, 0x5e, 0x11, 0x5a, 0xab, 0xa4, 0x0b, 0xed, 0x42, 0xfc, 0x0a, 0x5f,
0x1b, 0x1b, 0xc3, 0xa5, 0x8d, 0xee, 0x7f, 0x0a, 0x6e, 0xb3, 0x1f, 0x64, 0x08, 0xc0, 0xa5, 0x10,
0xc8, 0x8d, 0x54, 0xda, 0x73, 0xec, 0x98, 0xae, 0xf7, 0x68, 0x5c, 0x43, 0x74, 0xc3, 0xcb, 0x7f,
0x0a, 0x6e, 0x03, 0x10, 0x02, 0x87, 0x82, 0x3d, 0x60, 0x55, 0x80, 0x95, 0x0b, 0x9b, 0x59, 0x64,
0x58, 0x65, 0xb6, 0xb2, 0xff, 0xab, 0x03, 0x1b, 0x3b, 0xb5, 0x97, 0x76, 0x0e, 0xc7, 0x89, 0xd6,
0x39, 0xaa, 0x8a, 0x58, 0x69, 0x64, 0x00, 0x6d, 0x9e, 0x26, 0x28, 0xcc, 0xe4, 0xca, 0xae, 0xad,
0x3b, 0x7a, 0x63, 0xb5, 0xbc, 0x68, 0x8f, 0x2b, 0x1b, 0x6d, 0x50, 0x72, 0x09, 0x1d, 0x9e, 0x26,
0x35, 0x50, 0x6e, 0xe7, 0xe8, 0x74, 0xb5, 0xbc, 0xe8, 0x8c, 0x6f, 0x26, 0x8d, 0xff, 0xa6, 0x4f,
0x91, 0x54, 0x73, 0x99, 0x55, 0x3b, 0xea, 0xd2, 0x4a, 0x1b, 0x7e, 0x0b, 0xa7, 0xf5, 0x92, 0xbe,
0x44, 0x35, 0x4f, 0x38, 0x92, 0x2f, 0xa0, 0xf5, 0x1c, 0x0d, 0x39, 0xdf, 0xd9, 0x62, 0x7b, 0xb9,
0xba, 0x4f, 0x76, 0xec, 0xbe, 0xf7, 0xd3, 0x9f, 0x7f, 0xff, 0x72, 0x40, 0xc8, 0x99, 0x3d, 0xa4,
0xf3, 0xcb, 0xe6, 0x0a, 0x8f, 0x3e, 0xfb, 0x7d, 0xd5, 0x73, 0xfe, 0x58, 0xf5, 0x9c, 0xbf, 0x56,
0x3d, 0xe7, 0x9b, 0xe1, 0x7f, 0x38, 0xa8, 0x65, 0x93, 0x4d, 0x84, 0xfb, 0x63, 0x7b, 0x40, 0x9f,
0xfe, 0x13, 0x00, 0x00, 0xff, 0xff, 0xc4, 0xa4, 0xe4, 0xcb, 0xea, 0x05, 0x00, 0x00,
}

File diff suppressed because it is too large Load Diff

View File

@@ -584,6 +584,8 @@ message ResourceDiff {
optional string liveState = 6;
optional string diff = 7;
optional bool hook = 8;
}
// ResourceIgnoreDifferences contains resource filter and list of json paths which should be ignored during comparison with live state.

View File

@@ -2097,6 +2097,12 @@ func schema_pkg_apis_application_v1alpha1_ResourceDiff(ref common.ReferenceCallb
Format: "",
},
},
"hook": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
},
},
},

View File

@@ -786,6 +786,7 @@ type ResourceDiff struct {
TargetState string `json:"targetState,omitempty" protobuf:"bytes,5,opt,name=targetState"`
LiveState string `json:"liveState,omitempty" protobuf:"bytes,6,opt,name=liveState"`
Diff string `json:"diff,omitempty" protobuf:"bytes,7,opt,name=diff"`
Hook bool `json:"hook,omitempty" protobuf:"bytes,8,opt,name=hook"`
}
// ConnectionStatus represents connection status

View File

@@ -189,28 +189,33 @@ func (s *Service) GenerateManifest(c context.Context, q *apiclient.ManifestReque
return &res, nil
}
func checkPath(root, path string) error {
info, err := os.Stat(filepath.Join(root, path))
func appPath(root, path string) (string, error) {
if filepath.IsAbs(path) {
return "", fmt.Errorf("%s: app path is absolute", path)
}
appPath := filepath.Join(root, path)
if !strings.HasPrefix(appPath, filepath.Clean(root)) {
return "", fmt.Errorf("%s: app path outside repo", path)
}
info, err := os.Stat(appPath)
if os.IsNotExist(err) {
return fmt.Errorf("%s: app path does not exist", path)
return "", fmt.Errorf("%s: app path does not exist", path)
}
if err != nil {
return err
return "", err
}
if !info.IsDir() {
return fmt.Errorf("%s: app path is not a directory", path)
return "", fmt.Errorf("%s: app path is not a directory", path)
}
return nil
return appPath, nil
}
// GenerateManifests generates manifests from a path
func GenerateManifests(root, path string, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) {
err := checkPath(root, path)
appPath, err := appPath(root, path)
if err != nil {
return nil, err
}
appPath := filepath.Join(root, path)
var targetObjs []*unstructured.Unstructured
var dest *v1alpha1.ApplicationDestination

View File

@@ -90,16 +90,51 @@ func TestRecurseManifestsInDir(t *testing.T) {
assert.Equal(t, 2, len(res1.Manifests))
}
func TestPathRoot(t *testing.T) {
_, err := appPath("./testdata", "/")
assert.EqualError(t, err, "/: app path is absolute")
}
func TestPathAbsolute(t *testing.T) {
_, err := appPath("./testdata", "/etc/passwd")
assert.EqualError(t, err, "/etc/passwd: app path is absolute")
}
func TestPathDotDot(t *testing.T) {
_, err := appPath("./testdata", "..")
assert.EqualError(t, err, "..: app path outside repo")
}
func TestPathDotDotSlash(t *testing.T) {
_, err := appPath("./testdata", "../")
assert.EqualError(t, err, "../: app path outside repo")
}
func TestPathDot(t *testing.T) {
_, err := appPath("./testdata", ".")
assert.NoError(t, err)
}
func TestPathDotSlash(t *testing.T) {
_, err := appPath("./testdata", "./")
assert.NoError(t, err)
}
func TestNonExistentPath(t *testing.T) {
_, err := GenerateManifests("./testdata", "does-not-exist", nil)
_, err := appPath("./testdata", "does-not-exist")
assert.EqualError(t, err, "does-not-exist: app path does not exist")
}
func TestPathNotDir(t *testing.T) {
_, err := GenerateManifests("./testdata", "file.txt", nil)
_, err := appPath("./testdata", "file.txt")
assert.EqualError(t, err, "file.txt: app path is not a directory")
}
func TestGenerateManifests_NonExistentPath(t *testing.T) {
_, err := GenerateManifests("./testdata", "does-not-exist", nil)
assert.EqualError(t, err, "does-not-exist: app path does not exist")
}
func TestGenerateJsonnetManifestInDir(t *testing.T) {
q := apiclient.ManifestRequest{
ApplicationSource: &argoappv1.ApplicationSource{

View File

@@ -0,0 +1,4 @@
actionTests:
- action: restart
inputPath: testdata/daemonset.yaml
expectedOutputPath: testdata/daemonset-restarted.yaml

View File

@@ -0,0 +1,3 @@
actions = {}
actions["restart"] = {}
return actions

View File

@@ -0,0 +1,9 @@
local os = require("os")
if obj.spec.template.metadata == nil then
obj.spec.template.metadata = {}
end
if obj.spec.template.metadata.annotations == nil then
obj.spec.template.metadata.annotations = {}
end
obj.spec.template.metadata.annotations["kubectl.kubernetes.io/restartedAt"] = os.date("!%Y-%m-%dT%XZ")
return obj

View File

@@ -0,0 +1,50 @@
apiVersion: apps/v1
kind: DaemonSet
metadata:
annotations:
deprecated.daemonset.template.generation: "3"
creationTimestamp: "2019-09-13T08:52:50Z"
generation: 3
labels:
app.kubernetes.io/instance: extensions
name: daemonset
namespace: statefulset
resourceVersion: "7472656"
selfLink: /apis/apps/v1/namespaces/statefulset/daemonsets/daemonset
uid: de04d075-d603-11e9-9e69-42010aa8005f
spec:
revisionHistoryLimit: 10
selector:
matchLabels:
name: daemonset
template:
metadata:
annotations:
kubectl.kubernetes.io/restartedAt: "0001-01-01T00:00:00Z"
labels:
name: daemonset
spec:
containers:
- image: k8s.gcr.io/nginx-slim:0.8
imagePullPolicy: IfNotPresent
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
updateStrategy:
rollingUpdate:
maxUnavailable: 1
type: RollingUpdate
status:
currentNumberScheduled: 4
desiredNumberScheduled: 4
numberAvailable: 4
numberMisscheduled: 0
numberReady: 4
observedGeneration: 3
updatedNumberScheduled: 4

View File

@@ -0,0 +1,48 @@
apiVersion: apps/v1
kind: DaemonSet
metadata:
annotations:
deprecated.daemonset.template.generation: "3"
creationTimestamp: "2019-09-13T08:52:50Z"
generation: 3
labels:
app.kubernetes.io/instance: extensions
name: daemonset
namespace: statefulset
resourceVersion: "7472656"
selfLink: /apis/apps/v1/namespaces/statefulset/daemonsets/daemonset
uid: de04d075-d603-11e9-9e69-42010aa8005f
spec:
revisionHistoryLimit: 10
selector:
matchLabels:
name: daemonset
template:
metadata:
labels:
name: daemonset
spec:
containers:
- image: k8s.gcr.io/nginx-slim:0.8
imagePullPolicy: IfNotPresent
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
updateStrategy:
rollingUpdate:
maxUnavailable: 1
type: RollingUpdate
status:
currentNumberScheduled: 4
desiredNumberScheduled: 4
numberAvailable: 4
numberMisscheduled: 0
numberReady: 4
observedGeneration: 3
updatedNumberScheduled: 4

View File

@@ -0,0 +1,4 @@
actionTests:
- action: restart
inputPath: testdata/deployment.yaml
expectedOutputPath: testdata/deployment-restarted.yaml

View File

@@ -0,0 +1,3 @@
actions = {}
actions["restart"] = {}
return actions

View File

@@ -0,0 +1,9 @@
local os = require("os")
if obj.spec.template.metadata == nil then
obj.spec.template.metadata = {}
end
if obj.spec.template.metadata.annotations == nil then
obj.spec.template.metadata.annotations = {}
end
obj.spec.template.metadata.annotations["kubectl.kubernetes.io/restartedAt"] = os.date("!%Y-%m-%dT%XZ")
return obj

View File

@@ -0,0 +1,63 @@
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
creationTimestamp: "2019-09-12T01:33:53Z"
generation: 1
name: nginx-deploy
namespace: default
resourceVersion: "6897444"
selfLink: /apis/apps/v1/namespaces/default/deployments/nginx-deploy
uid: 61689d6d-d4fd-11e9-9e69-42010aa8005f
spec:
progressDeadlineSeconds: 600
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: nginx
annotations:
kubectl.kubernetes.io/restartedAt: "0001-01-01T00:00:00Z"
spec:
containers:
- image: nginx:latest
imagePullPolicy: Always
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
availableReplicas: 2
conditions:
- lastTransitionTime: "2019-09-12T01:33:53Z"
lastUpdateTime: "2019-09-12T01:33:53Z"
message: Deployment does not have minimum availability.
reason: MinimumReplicasUnavailable
status: "False"
type: Available
- lastTransitionTime: "2019-09-12T01:33:53Z"
lastUpdateTime: "2019-09-12T01:34:05Z"
message: ReplicaSet "nginx-deploy-9cb4784bd" is progressing.
reason: ReplicaSetUpdated
status: "True"
type: Progressing
observedGeneration: 1
readyReplicas: 2
replicas: 3
unavailableReplicas: 1
updatedReplicas: 3

View File

@@ -0,0 +1,61 @@
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
creationTimestamp: "2019-09-12T01:33:53Z"
generation: 1
name: nginx-deploy
namespace: default
resourceVersion: "6897444"
selfLink: /apis/apps/v1/namespaces/default/deployments/nginx-deploy
uid: 61689d6d-d4fd-11e9-9e69-42010aa8005f
spec:
progressDeadlineSeconds: 600
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:latest
imagePullPolicy: Always
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
availableReplicas: 2
conditions:
- lastTransitionTime: "2019-09-12T01:33:53Z"
lastUpdateTime: "2019-09-12T01:33:53Z"
message: Deployment does not have minimum availability.
reason: MinimumReplicasUnavailable
status: "False"
type: Available
- lastTransitionTime: "2019-09-12T01:33:53Z"
lastUpdateTime: "2019-09-12T01:34:05Z"
message: ReplicaSet "nginx-deploy-9cb4784bd" is progressing.
reason: ReplicaSetUpdated
status: "True"
type: Progressing
observedGeneration: 1
readyReplicas: 2
replicas: 3
unavailableReplicas: 1
updatedReplicas: 3

View File

@@ -0,0 +1,4 @@
actionTests:
- action: restart
inputPath: testdata/statefulset.yaml
expectedOutputPath: testdata/statefulset-restarted.yaml

View File

@@ -0,0 +1,3 @@
actions = {}
actions["restart"] = {}
return actions

View File

@@ -0,0 +1,9 @@
local os = require("os")
if obj.spec.template.metadata == nil then
obj.spec.template.metadata = {}
end
if obj.spec.template.metadata.annotations == nil then
obj.spec.template.metadata.annotations = {}
end
obj.spec.template.metadata.annotations["kubectl.kubernetes.io/restartedAt"] = os.date("!%Y-%m-%dT%XZ")
return obj

View File

@@ -0,0 +1,52 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
creationTimestamp: "2019-09-13T08:52:54Z"
generation: 2
labels:
app.kubernetes.io/instance: extensions
name: statefulset
namespace: statefulset
resourceVersion: "7471813"
selfLink: /apis/apps/v1/namespaces/statefulset/statefulsets/statefulset
uid: dfe8fadf-d603-11e9-9e69-42010aa8005f
spec:
podManagementPolicy: OrderedReady
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
app: statefulset
serviceName: statefulset
template:
metadata:
labels:
app: statefulset
annotations:
kubectl.kubernetes.io/restartedAt: "0001-01-01T00:00:00Z"
spec:
containers:
- image: k8s.gcr.io/nginx-slim:0.8
imagePullPolicy: IfNotPresent
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
updateStrategy:
rollingUpdate:
partition: 0
type: RollingUpdate
status:
collisionCount: 0
currentReplicas: 3
currentRevision: statefulset-85b7f767c6
observedGeneration: 2
readyReplicas: 3
replicas: 3
updateRevision: statefulset-85b7f767c6
updatedReplicas: 3

View File

@@ -0,0 +1,50 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
creationTimestamp: "2019-09-13T08:52:54Z"
generation: 2
labels:
app.kubernetes.io/instance: extensions
name: statefulset
namespace: statefulset
resourceVersion: "7471813"
selfLink: /apis/apps/v1/namespaces/statefulset/statefulsets/statefulset
uid: dfe8fadf-d603-11e9-9e69-42010aa8005f
spec:
podManagementPolicy: OrderedReady
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
app: statefulset
serviceName: statefulset
template:
metadata:
labels:
app: statefulset
spec:
containers:
- image: k8s.gcr.io/nginx-slim:0.8
imagePullPolicy: IfNotPresent
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
updateStrategy:
rollingUpdate:
partition: 0
type: RollingUpdate
status:
collisionCount: 0
currentReplicas: 3
currentRevision: statefulset-85b7f767c6
observedGeneration: 2
readyReplicas: 3
replicas: 3
updateRevision: statefulset-85b7f767c6
updatedReplicas: 3

View File

@@ -7,13 +7,13 @@ import (
"path/filepath"
"strings"
"testing"
"time"
"github.com/bouk/monkey"
"github.com/ghodss/yaml"
"github.com/stretchr/testify/assert"
"github.com/yudai/gojsondiff/formatter"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/argoproj/argo-cd/errors"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/diff"
"github.com/argoproj/argo-cd/util/lua"
@@ -47,14 +47,14 @@ func TestLuaResourceActionsScript(t *testing.T) {
if !strings.Contains(path, "action_test.yaml") {
return nil
}
errors.CheckError(err)
assert.NoError(t, err)
dir := filepath.Dir(path)
//TODO: Change to path
yamlBytes, err := ioutil.ReadFile(dir + "/action_test.yaml")
errors.CheckError(err)
assert.NoError(t, err)
var resourceTest ActionTestStructure
err = yaml.Unmarshal(yamlBytes, &resourceTest)
errors.CheckError(err)
assert.NoError(t, err)
for i := range resourceTest.DiscoveryTests {
test := resourceTest.DiscoveryTests[i]
testName := fmt.Sprintf("discovery/%s", test.InputPath)
@@ -64,9 +64,9 @@ func TestLuaResourceActionsScript(t *testing.T) {
}
obj := getObj(filepath.Join(dir, test.InputPath))
discoveryLua, err := vm.GetResourceActionDiscovery(obj)
errors.CheckError(err)
assert.NoError(t, err)
result, err := vm.ExecuteResourceActionDiscovery(obj, discoveryLua)
errors.CheckError(err)
assert.NoError(t, err)
assert.Equal(t, test.Result, result)
})
}
@@ -75,20 +75,28 @@ func TestLuaResourceActionsScript(t *testing.T) {
testName := fmt.Sprintf("actions/%s/%s", test.Action, test.InputPath)
t.Run(testName, func(t *testing.T) {
vm := lua.VM{
UseOpenLibs: true,
// Uncomment the following line if you need to use lua libraries debugging
// purposes. Otherwise, leave this false to ensure tests reflect the same
// privileges that API server has.
//UseOpenLibs: true,
}
obj := getObj(filepath.Join(dir, test.InputPath))
action, err := vm.GetResourceAction(obj, test.Action)
errors.CheckError(err)
assert.NoError(t, err)
// freeze time so that lua test has predictable time output (will return 0001-01-01T00:00:00Z)
patch := monkey.Patch(time.Now, func() time.Time { return time.Time{} })
result, err := vm.ExecuteResourceAction(obj, action.ActionLua)
errors.CheckError(err)
patch.Unpatch()
assert.NoError(t, err)
expectedObj := getObj(filepath.Join(dir, test.ExpectedOutputPath))
// Ideally, we would use a assert.Equal to detect the difference, but the Lua VM returns a object with float64 instead of the originial int32. As a result, the assert.Equal is never true despite that the change has been applied.
// Ideally, we would use a assert.Equal to detect the difference, but the Lua VM returns a object with float64 instead of the original int32. As a result, the assert.Equal is never true despite that the change has been applied.
diffResult := diff.Diff(expectedObj, result, testNormalizer{})
if diffResult.Modified {
output, err := diffResult.ASCIIFormat(expectedObj, formatter.AsciiFormatterConfig{})
errors.CheckError(err)
assert.Fail(t, "Output does not match input:", output)
t.Error("Output does not match input:")
err = diff.PrintDiff(test.Action, expectedObj, result)
assert.NoError(t, err)
}
})
}

View File

@@ -0,0 +1,4 @@
actionTests:
- action: restart
inputPath: testdata/daemonset.yaml
expectedOutputPath: testdata/daemonset-restarted.yaml

View File

@@ -0,0 +1,3 @@
actions = {}
actions["restart"] = {}
return actions

View File

@@ -0,0 +1,9 @@
local os = require("os")
if obj.spec.template.metadata == nil then
obj.spec.template.metadata = {}
end
if obj.spec.template.metadata.annotations == nil then
obj.spec.template.metadata.annotations = {}
end
obj.spec.template.metadata.annotations["kubectl.kubernetes.io/restartedAt"] = os.date("!%Y-%m-%dT%XZ")
return obj

View File

@@ -0,0 +1,47 @@
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
annotations:
creationTimestamp: "2019-09-13T08:52:50Z"
generation: 2
labels:
app.kubernetes.io/instance: extensions
name: extensions-daemonset
namespace: statefulset
resourceVersion: "7471358"
selfLink: /apis/extensions/v1beta1/namespaces/statefulset/daemonsets/extensions-daemonset
uid: de09964a-d603-11e9-9e69-42010aa8005f
spec:
revisionHistoryLimit: 10
selector:
matchLabels:
name: extensions-daemonset
template:
metadata:
annotations:
kubectl.kubernetes.io/restartedAt: "0001-01-01T00:00:00Z"
labels:
name: extensions-daemonset
spec:
containers:
- image: k8s.gcr.io/nginx-slim:0.8
imagePullPolicy: IfNotPresent
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
templateGeneration: 2
updateStrategy:
type: OnDelete
status:
currentNumberScheduled: 4
desiredNumberScheduled: 4
numberAvailable: 4
numberMisscheduled: 0
numberReady: 4
observedGeneration: 2

View File

@@ -0,0 +1,45 @@
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
annotations:
creationTimestamp: "2019-09-13T08:52:50Z"
generation: 2
labels:
app.kubernetes.io/instance: extensions
name: extensions-daemonset
namespace: statefulset
resourceVersion: "7471358"
selfLink: /apis/extensions/v1beta1/namespaces/statefulset/daemonsets/extensions-daemonset
uid: de09964a-d603-11e9-9e69-42010aa8005f
spec:
revisionHistoryLimit: 10
selector:
matchLabels:
name: extensions-daemonset
template:
metadata:
labels:
name: extensions-daemonset
spec:
containers:
- image: k8s.gcr.io/nginx-slim:0.8
imagePullPolicy: IfNotPresent
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
templateGeneration: 2
updateStrategy:
type: OnDelete
status:
currentNumberScheduled: 4
desiredNumberScheduled: 4
numberAvailable: 4
numberMisscheduled: 0
numberReady: 4
observedGeneration: 2

View File

@@ -0,0 +1,4 @@
actionTests:
- action: restart
inputPath: testdata/deployment.yaml
expectedOutputPath: testdata/deployment-restarted.yaml

View File

@@ -0,0 +1,3 @@
actions = {}
actions["restart"] = {}
return actions

View File

@@ -0,0 +1,9 @@
local os = require("os")
if obj.spec.template.metadata == nil then
obj.spec.template.metadata = {}
end
if obj.spec.template.metadata.annotations == nil then
obj.spec.template.metadata.annotations = {}
end
obj.spec.template.metadata.annotations["kubectl.kubernetes.io/restartedAt"] = os.date("!%Y-%m-%dT%XZ")
return obj

View File

@@ -0,0 +1,63 @@
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
creationTimestamp: "2019-09-12T01:33:53Z"
generation: 1
name: nginx-deploy
namespace: default
resourceVersion: "6897444"
selfLink: /apis/apps/v1/namespaces/default/deployments/nginx-deploy
uid: 61689d6d-d4fd-11e9-9e69-42010aa8005f
spec:
progressDeadlineSeconds: 600
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: nginx
annotations:
kubectl.kubernetes.io/restartedAt: "0001-01-01T00:00:00Z"
spec:
containers:
- image: nginx:latest
imagePullPolicy: Always
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
availableReplicas: 2
conditions:
- lastTransitionTime: "2019-09-12T01:33:53Z"
lastUpdateTime: "2019-09-12T01:33:53Z"
message: Deployment does not have minimum availability.
reason: MinimumReplicasUnavailable
status: "False"
type: Available
- lastTransitionTime: "2019-09-12T01:33:53Z"
lastUpdateTime: "2019-09-12T01:34:05Z"
message: ReplicaSet "nginx-deploy-9cb4784bd" is progressing.
reason: ReplicaSetUpdated
status: "True"
type: Progressing
observedGeneration: 1
readyReplicas: 2
replicas: 3
unavailableReplicas: 1
updatedReplicas: 3

View File

@@ -0,0 +1,61 @@
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
creationTimestamp: "2019-09-12T01:33:53Z"
generation: 1
name: nginx-deploy
namespace: default
resourceVersion: "6897444"
selfLink: /apis/apps/v1/namespaces/default/deployments/nginx-deploy
uid: 61689d6d-d4fd-11e9-9e69-42010aa8005f
spec:
progressDeadlineSeconds: 600
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:latest
imagePullPolicy: Always
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
availableReplicas: 2
conditions:
- lastTransitionTime: "2019-09-12T01:33:53Z"
lastUpdateTime: "2019-09-12T01:33:53Z"
message: Deployment does not have minimum availability.
reason: MinimumReplicasUnavailable
status: "False"
type: Available
- lastTransitionTime: "2019-09-12T01:33:53Z"
lastUpdateTime: "2019-09-12T01:34:05Z"
message: ReplicaSet "nginx-deploy-9cb4784bd" is progressing.
reason: ReplicaSetUpdated
status: "True"
type: Progressing
observedGeneration: 1
readyReplicas: 2
replicas: 3
unavailableReplicas: 1
updatedReplicas: 3

View File

@@ -121,6 +121,9 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
badge = replaceFirstGroupSubMatch(rightText1Pattern, badge, rightText)
badge = replaceFirstGroupSubMatch(rightText2Pattern, badge, rightText)
w.Header().Set("Content-Type", "image/svg+xml")
//Ask cache's to not cache the contents in order prevent the badge from becoming stale
w.Header().Set("Cache-Control", "private, no-store")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(badge))
}

View File

@@ -54,6 +54,8 @@ func TestHandlerFeatureIsEnabled(t *testing.T) {
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
response := rr.Body.String()
assert.Equal(t, success, leftPathColorPattern.FindStringSubmatch(response)[1])
assert.Equal(t, success, rightPathColorPattern.FindStringSubmatch(response)[1])
@@ -74,6 +76,8 @@ func TestHandlerFeatureIsDisabled(t *testing.T) {
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
response := rr.Body.String()
assert.Equal(t, unknown, leftPathColorPattern.FindStringSubmatch(response)[1])
assert.Equal(t, unknown, rightPathColorPattern.FindStringSubmatch(response)[1])

View File

@@ -660,6 +660,14 @@ func newAPIServerMetricsServer(port int) *http.Server {
}
}
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
// newStaticAssetsHandler returns an HTTP handler to serve UI static assets
func newStaticAssetsHandler(dir string, baseHRef string) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
@@ -670,7 +678,7 @@ func newStaticAssetsHandler(dir string, baseHRef string) func(http.ResponseWrite
break
}
}
fileRequest := r.URL.Path != "/index.html" && strings.Contains(r.URL.Path, ".")
fileRequest := r.URL.Path != "/index.html" && fileExists(path.Join(dir, r.URL.Path))
// serve index.html for non file requests to support HTML5 History API
if acceptHTML && !fileRequest && (r.Method == "GET" || r.Method == "HEAD") {

View File

@@ -39,6 +39,10 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin
if err != nil {
return nil, err
}
help, err := s.mgr.GetHelp()
if err != nil {
return nil, err
}
overrides := make(map[string]*v1alpha1.ResourceOverride)
for k := range resourceOverrides {
@@ -58,6 +62,10 @@ func (s *Server) Get(ctx context.Context, q *settingspkg.SettingsQuery) (*settin
TrackingID: gaSettings.TrackingID,
AnonymizeUsers: gaSettings.AnonymizeUsers,
},
Help: &settingspkg.Help{
ChatUrl: help.ChatURL,
ChatText: help.ChatText,
},
}
if argoCDSettings.DexConfig != "" {
var cfg settingspkg.DexConfig

View File

@@ -23,6 +23,8 @@ message Settings {
bool statusBadgeEnabled = 6;
GoogleAnalyticsConfig googleAnalytics = 7;
github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.KustomizeOptions kustomizeOptions = 8;
// Help settings
Help help = 9;
}
message GoogleAnalyticsConfig {
@@ -30,6 +32,14 @@ message GoogleAnalyticsConfig {
bool anonymizeUsers = 2;
}
// Help settings
message Help {
// the URL for getting chat help, this will typically be your Slack channel for support
string chatUrl = 1;
// the text for getting chat help, defaults to "Chat now!"
string chatText = 2;
}
message DexConfig {
repeated Connector connectors = 1;
}

View File

@@ -16,13 +16,13 @@ func TestDeletingAppStuckInSync(t *testing.T) {
Path("hook").
When().
PatchFile("hook.yaml", `[{"op": "replace", "path": "/spec/containers/0/command", "value": ["sh", "-c", "until ls /tmp/done; do sleep 0.1; done"]}]`).
PatchFile("pod.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-wave": "1"}}]`).
Create().
Sync().
Then().
// stuck in running state
Expect(OperationPhaseIs(OperationRunning)).
Expect(SyncStatusIs(SyncStatusCodeSynced)).
Expect(ResourceResultNumbering(2)).
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
When().
Delete(true).
Then().

View File

@@ -17,12 +17,12 @@ import (
"k8s.io/apimachinery/pkg/types"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/errors"
. "github.com/argoproj/argo-cd/errors"
applicationpkg "github.com/argoproj/argo-cd/pkg/apiclient/application"
repositorypkg "github.com/argoproj/argo-cd/pkg/apiclient/repository"
. "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/reposerver/apiclient"
"github.com/argoproj/argo-cd/test/e2e/fixture"
. "github.com/argoproj/argo-cd/test/e2e/fixture"
. "github.com/argoproj/argo-cd/test/e2e/fixture/app"
"github.com/argoproj/argo-cd/util"
. "github.com/argoproj/argo-cd/util/argo"
@@ -44,18 +44,18 @@ func TestAppCreation(t *testing.T) {
Then().
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
And(func(app *Application) {
assert.Equal(t, fixture.Name(), app.Name)
assert.Equal(t, fixture.RepoURL(fixture.RepoURLTypeFile), app.Spec.Source.RepoURL)
assert.Equal(t, Name(), app.Name)
assert.Equal(t, RepoURL(RepoURLTypeFile), app.Spec.Source.RepoURL)
assert.Equal(t, guestbookPath, app.Spec.Source.Path)
assert.Equal(t, fixture.DeploymentNamespace(), app.Spec.Destination.Namespace)
assert.Equal(t, DeploymentNamespace(), app.Spec.Destination.Namespace)
assert.Equal(t, common.KubernetesInternalAPIServerAddr, app.Spec.Destination.Server)
}).
Expect(Event(EventReasonResourceCreated, "create")).
And(func(_ *Application) {
// app should be listed
output, err := fixture.RunCli("app", "list")
output, err := RunCli("app", "list")
assert.NoError(t, err)
assert.Contains(t, output, fixture.Name())
assert.Contains(t, output, Name())
})
}
@@ -83,9 +83,9 @@ func TestAppDeletion(t *testing.T) {
Expect(DoesNotExist()).
Expect(Event(EventReasonResourceDeleted, "delete"))
output, err := fixture.RunCli("app", "list")
output, err := RunCli("app", "list")
assert.NoError(t, err)
assert.NotContains(t, output, fixture.Name())
assert.NotContains(t, output, Name())
}
func TestTrackAppStateAndSyncApp(t *testing.T) {
@@ -95,10 +95,10 @@ func TestTrackAppStateAndSyncApp(t *testing.T) {
Create().
Sync().
Then().
Expect(Success(fmt.Sprintf("apps Deployment %s guestbook-ui OutOfSync Missing", fixture.DeploymentNamespace()))).
Expect(Success(fmt.Sprintf("Service %s guestbook-ui OutOfSync Missing", fixture.DeploymentNamespace()))).
Expect(Success(fmt.Sprintf("Service %s guestbook-ui Synced Healthy service/guestbook-ui created", fixture.DeploymentNamespace()))).
Expect(Success(fmt.Sprintf("apps Deployment %s guestbook-ui Synced Healthy deployment.apps/guestbook-ui created", fixture.DeploymentNamespace()))).
Expect(Success(fmt.Sprintf("apps Deployment %s guestbook-ui OutOfSync Missing", DeploymentNamespace()))).
Expect(Success(fmt.Sprintf("Service %s guestbook-ui OutOfSync Missing", DeploymentNamespace()))).
Expect(Success(fmt.Sprintf("Service %s guestbook-ui Synced Healthy service/guestbook-ui created", DeploymentNamespace()))).
Expect(Success(fmt.Sprintf("apps Deployment %s guestbook-ui Synced Healthy deployment.apps/guestbook-ui created", DeploymentNamespace()))).
Expect(OperationPhaseIs(OperationSucceeded)).
Expect(SyncStatusIs(SyncStatusCodeSynced)).
Expect(Event(EventReasonResourceUpdated, "sync")).
@@ -134,11 +134,11 @@ func TestAppRollbackSuccessful(t *testing.T) {
patch, _, err := diff.CreateTwoWayMergePatch(app, appWithHistory, &Application{})
assert.NoError(t, err)
app, err = fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.ArgoCDNamespace).Patch(app.Name, types.MergePatchType, patch)
app, err = AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Patch(app.Name, types.MergePatchType, patch)
assert.NoError(t, err)
// sync app and make sure it reaches InSync state
_, err = fixture.RunCli("app", "rollback", app.Name, "1")
_, err = RunCli("app", "rollback", app.Name, "1")
assert.NoError(t, err)
}).
@@ -184,7 +184,7 @@ func TestManipulateApplicationResources(t *testing.T) {
Then().
Expect(SyncStatusIs(SyncStatusCodeSynced)).
And(func(app *Application) {
manifests, err := fixture.RunCli("app", "manifests", app.Name, "--source", "live")
manifests, err := RunCli("app", "manifests", app.Name, "--source", "live")
assert.NoError(t, err)
resources, err := kube.SplitYAML(manifests)
assert.NoError(t, err)
@@ -201,7 +201,7 @@ func TestManipulateApplicationResources(t *testing.T) {
deployment := resources[index]
closer, client, err := fixture.ArgoCDClientset.NewApplicationClient()
closer, client, err := ArgoCDClientset.NewApplicationClient()
assert.NoError(t, err)
defer util.Close(closer)
@@ -243,7 +243,7 @@ func assetSecretDataHidden(t *testing.T, manifest string) {
}
func TestAppWithSecrets(t *testing.T) {
closer, client, err := fixture.ArgoCDClientset.NewApplicationClient()
closer, client, err := ArgoCDClientset.NewApplicationClient()
assert.NoError(t, err)
defer util.Close(closer)
@@ -255,49 +255,45 @@ func TestAppWithSecrets(t *testing.T) {
Then().
Expect(SyncStatusIs(SyncStatusCodeSynced)).
And(func(app *Application) {
res, err := client.GetResource(context.Background(), &applicationpkg.ApplicationResourceRequest{
res := FailOnErr(client.GetResource(context.Background(), &applicationpkg.ApplicationResourceRequest{
Namespace: app.Spec.Destination.Namespace,
Kind: kube.SecretKind,
Group: "",
Name: &app.Name,
Version: "v1",
ResourceName: "test-secret",
})
assert.NoError(t, err)
})).(*applicationpkg.ApplicationResourceResponse)
assetSecretDataHidden(t, res.Manifest)
diffOutput, err := fixture.RunCli("app", "diff", app.Name)
assert.NoError(t, err)
diffOutput := FailOnErr(RunCli("app", "diff", app.Name)).(string)
assert.Empty(t, diffOutput)
// patch secret and make sure app is out of sync and diff detects the change
_, err = fixture.KubeClientset.CoreV1().Secrets(fixture.DeploymentNamespace()).Patch(
"test-secret", types.JSONPatchType, []byte(`[{"op": "remove", "path": "/data/username"}]`))
assert.NoError(t, err)
FailOnErr(KubeClientset.CoreV1().Secrets(DeploymentNamespace()).Patch(
"test-secret", types.JSONPatchType, []byte(`[
{"op": "remove", "path": "/data/username"},
{"op": "add", "path": "/stringData", "value": {"password": "foo"}}
]`)))
}).
When().
Refresh(RefreshTypeNormal).
Then().
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
And(func(app *Application) {
diffOutput, err := fixture.RunCli("app", "diff", app.Name)
diffOutput, err := RunCli("app", "diff", app.Name)
assert.Error(t, err)
assert.Contains(t, diffOutput, "username: '*********'")
assert.Contains(t, diffOutput, "username: ++++++++")
assert.Contains(t, diffOutput, "password: ++++++++++++")
// local diff should ignore secrets
diffOutput, err = fixture.RunCli("app", "diff", app.Name, "--local", "testdata/secrets")
assert.NoError(t, err)
diffOutput = FailOnErr(RunCli("app", "diff", app.Name, "--local", "testdata/secrets")).(string)
assert.Empty(t, diffOutput)
// ignore missing field and make sure diff shows no difference
app.Spec.IgnoreDifferences = []ResourceIgnoreDifferences{{
Kind: kube.SecretKind, JSONPointers: []string{"/data/username"},
Kind: kube.SecretKind, JSONPointers: []string{"/data/username", "/data/password"},
}}
_, err = client.UpdateSpec(context.Background(), &applicationpkg.ApplicationUpdateSpecRequest{Name: &app.Name, Spec: app.Spec})
assert.NoError(t, err)
FailOnErr(client.UpdateSpec(context.Background(), &applicationpkg.ApplicationUpdateSpecRequest{Name: &app.Name, Spec: app.Spec}))
}).
When().
Refresh(RefreshTypeNormal).
@@ -305,8 +301,7 @@ func TestAppWithSecrets(t *testing.T) {
Expect(OperationPhaseIs(OperationSucceeded)).
Expect(SyncStatusIs(SyncStatusCodeSynced)).
And(func(app *Application) {
diffOutput, err := fixture.RunCli("app", "diff", app.Name)
assert.NoError(t, err)
diffOutput := FailOnErr(RunCli("app", "diff", app.Name)).(string)
assert.Empty(t, diffOutput)
})
}
@@ -321,7 +316,7 @@ func TestResourceDiffing(t *testing.T) {
Expect(SyncStatusIs(SyncStatusCodeSynced)).
And(func(app *Application) {
// Patch deployment
_, err := fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Patch(
_, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Patch(
"guestbook-ui", types.JSONPatchType, []byte(`[{ "op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "test" }]`))
assert.NoError(t, err)
}).
@@ -330,9 +325,9 @@ func TestResourceDiffing(t *testing.T) {
Then().
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
And(func(app *Application) {
diffOutput, err := fixture.RunCli("app", "diff", app.Name, "--local", "testdata/guestbook")
diffOutput, err := RunCli("app", "diff", app.Name, "--local", "testdata/guestbook")
assert.Error(t, err)
assert.Contains(t, diffOutput, fmt.Sprintf("===== apps/Deployment %s/guestbook-ui ======", fixture.DeploymentNamespace()))
assert.Contains(t, diffOutput, fmt.Sprintf("===== apps/Deployment %s/guestbook-ui ======", DeploymentNamespace()))
}).
Given().
ResourceOverrides(map[string]ResourceOverride{"apps/Deployment": {IgnoreDifferences: ` jsonPointers: ["/spec/template/spec/containers/0/image"]`}}).
@@ -341,7 +336,7 @@ func TestResourceDiffing(t *testing.T) {
Then().
Expect(SyncStatusIs(SyncStatusCodeSynced)).
And(func(app *Application) {
diffOutput, err := fixture.RunCli("app", "diff", app.Name, "--local", "testdata/guestbook")
diffOutput, err := RunCli("app", "diff", app.Name, "--local", "testdata/guestbook")
assert.NoError(t, err)
assert.Empty(t, diffOutput)
})
@@ -366,7 +361,7 @@ func TestConfigMap(t *testing.T) {
func TestFailedConversion(t *testing.T) {
defer func() {
errors.FailOnErr(fixture.Run("", "kubectl", "delete", "apiservice", "v1beta1.metrics.k8s.io"))
FailOnErr(Run("", "kubectl", "delete", "apiservice", "v1beta1.metrics.k8s.io"))
}()
testEdgeCasesApplicationResources(t, "failed-conversion", HealthStatusProgressing)
@@ -383,7 +378,7 @@ func testEdgeCasesApplicationResources(t *testing.T, appPath string, statusCode
Expect(SyncStatusIs(SyncStatusCodeSynced)).
Expect(HealthIs(statusCode)).
And(func(app *Application) {
diffOutput, err := fixture.RunCli("app", "diff", app.Name, "--local", path.Join("testdata", appPath))
diffOutput, err := RunCli("app", "diff", app.Name, "--local", path.Join("testdata", appPath))
assert.Empty(t, diffOutput)
assert.NoError(t, err)
})
@@ -401,7 +396,7 @@ func TestKsonnetApp(t *testing.T) {
Sync().
Then().
And(func(app *Application) {
closer, client, err := fixture.ArgoCDClientset.NewRepoClient()
closer, client, err := ArgoCDClientset.NewRepoClient()
assert.NoError(t, err)
defer util.Close(closer)
@@ -440,7 +435,7 @@ func TestResourceAction(t *testing.T) {
Then().
And(func(app *Application) {
closer, client, err := fixture.ArgoCDClientset.NewApplicationClient()
closer, client, err := ArgoCDClientset.NewApplicationClient()
assert.NoError(t, err)
defer util.Close(closer)
@@ -449,7 +444,7 @@ func TestResourceAction(t *testing.T) {
Group: "apps",
Kind: "Deployment",
Version: "v1",
Namespace: fixture.DeploymentNamespace(),
Namespace: DeploymentNamespace(),
ResourceName: "guestbook-ui",
})
assert.NoError(t, err)
@@ -459,13 +454,13 @@ func TestResourceAction(t *testing.T) {
Group: "apps",
Kind: "Deployment",
Version: "v1",
Namespace: fixture.DeploymentNamespace(),
Namespace: DeploymentNamespace(),
ResourceName: "guestbook-ui",
Action: "sample",
})
assert.NoError(t, err)
deployment, err := fixture.KubeClientset.AppsV1().Deployments(fixture.DeploymentNamespace()).Get("guestbook-ui", metav1.GetOptions{})
deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get("guestbook-ui", metav1.GetOptions{})
assert.NoError(t, err)
assert.Equal(t, "test", deployment.Labels["sample"])
@@ -480,11 +475,11 @@ func TestSyncResourceByLabel(t *testing.T) {
Sync().
Then().
And(func(app *Application) {
_, _ = fixture.RunCli("app", "sync", app.Name, "--label", fmt.Sprintf("app.kubernetes.io/instance=%s", app.Name))
_, _ = RunCli("app", "sync", app.Name, "--label", fmt.Sprintf("app.kubernetes.io/instance=%s", app.Name))
}).
Expect(SyncStatusIs(SyncStatusCodeSynced)).
And(func(app *Application) {
_, err := fixture.RunCli("app", "sync", app.Name, "--label", "this-label=does-not-exist")
_, err := RunCli("app", "sync", app.Name, "--label", "this-label=does-not-exist")
assert.Error(t, err)
assert.Contains(t, err.Error(), "level=fatal")
})
@@ -498,7 +493,7 @@ func TestLocalManifestSync(t *testing.T) {
Sync().
Then().
And(func(app *Application) {
res, _ := fixture.RunCli("app", "manifests", app.Name)
res, _ := RunCli("app", "manifests", app.Name)
assert.Contains(t, res, "containerPort: 80")
assert.Contains(t, res, "image: gcr.io/heptio-images/ks-guestbook-demo:0.2")
}).
@@ -509,7 +504,7 @@ func TestLocalManifestSync(t *testing.T) {
Then().
Expect(SyncStatusIs(SyncStatusCodeSynced)).
And(func(app *Application) {
res, _ := fixture.RunCli("app", "manifests", app.Name)
res, _ := RunCli("app", "manifests", app.Name)
assert.Contains(t, res, "containerPort: 81")
assert.Contains(t, res, "image: gcr.io/heptio-images/ks-guestbook-demo:0.3")
}).
@@ -520,7 +515,7 @@ func TestLocalManifestSync(t *testing.T) {
Then().
Expect(SyncStatusIs(SyncStatusCodeSynced)).
And(func(app *Application) {
res, _ := fixture.RunCli("app", "manifests", app.Name)
res, _ := RunCli("app", "manifests", app.Name)
assert.Contains(t, res, "containerPort: 80")
assert.Contains(t, res, "image: gcr.io/heptio-images/ks-guestbook-demo:0.2")
})
@@ -534,10 +529,10 @@ func TestNoLocalSyncWithAutosyncEnabled(t *testing.T) {
Sync().
Then().
And(func(app *Application) {
_, err := fixture.RunCli("app", "set", app.Name, "--sync-policy", "automated")
_, err := RunCli("app", "set", app.Name, "--sync-policy", "automated")
assert.NoError(t, err)
_, err = fixture.RunCli("app", "sync", app.Name, "--local", guestbookPathLocal)
_, err = RunCli("app", "sync", app.Name, "--local", guestbookPathLocal)
assert.Error(t, err)
})
}
@@ -556,44 +551,44 @@ func TestSyncAsync(t *testing.T) {
}
func TestPermissions(t *testing.T) {
fixture.EnsureCleanState(t)
appName := fixture.Name()
_, err := fixture.RunCli("proj", "create", "test")
EnsureCleanState(t)
appName := Name()
_, err := RunCli("proj", "create", "test")
assert.NoError(t, err)
// make sure app cannot be created without permissions in project
_, err = fixture.RunCli("app", "create", appName, "--repo", fixture.RepoURL(fixture.RepoURLTypeFile),
"--path", guestbookPath, "--project", "test", "--dest-server", common.KubernetesInternalAPIServerAddr, "--dest-namespace", fixture.DeploymentNamespace())
_, err = RunCli("app", "create", appName, "--repo", RepoURL(RepoURLTypeFile),
"--path", guestbookPath, "--project", "test", "--dest-server", common.KubernetesInternalAPIServerAddr, "--dest-namespace", DeploymentNamespace())
assert.Error(t, err)
sourceError := fmt.Sprintf("application repo %s is not permitted in project 'test'", fixture.RepoURL(fixture.RepoURLTypeFile))
destinationError := fmt.Sprintf("application destination {%s %s} is not permitted in project 'test'", common.KubernetesInternalAPIServerAddr, fixture.DeploymentNamespace())
sourceError := fmt.Sprintf("application repo %s is not permitted in project 'test'", RepoURL(RepoURLTypeFile))
destinationError := fmt.Sprintf("application destination {%s %s} is not permitted in project 'test'", common.KubernetesInternalAPIServerAddr, DeploymentNamespace())
assert.Contains(t, err.Error(), sourceError)
assert.Contains(t, err.Error(), destinationError)
proj, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Get("test", metav1.GetOptions{})
proj, err := AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Get("test", metav1.GetOptions{})
assert.NoError(t, err)
proj.Spec.Destinations = []ApplicationDestination{{Server: "*", Namespace: "*"}}
proj.Spec.SourceRepos = []string{"*"}
proj, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Update(proj)
proj, err = AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Update(proj)
assert.NoError(t, err)
// make sure controller report permissions issues in conditions
_, err = fixture.RunCli("app", "create", appName, "--repo", fixture.RepoURL(fixture.RepoURLTypeFile),
"--path", guestbookPath, "--project", "test", "--dest-server", common.KubernetesInternalAPIServerAddr, "--dest-namespace", fixture.DeploymentNamespace())
_, err = RunCli("app", "create", appName, "--repo", RepoURL(RepoURLTypeFile),
"--path", guestbookPath, "--project", "test", "--dest-server", common.KubernetesInternalAPIServerAddr, "--dest-namespace", DeploymentNamespace())
assert.NoError(t, err)
defer func() {
err = fixture.AppClientset.ArgoprojV1alpha1().Applications(fixture.ArgoCDNamespace).Delete(appName, &metav1.DeleteOptions{})
err = AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Delete(appName, &metav1.DeleteOptions{})
assert.NoError(t, err)
}()
proj.Spec.Destinations = []ApplicationDestination{}
proj.Spec.SourceRepos = []string{}
_, err = fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.ArgoCDNamespace).Update(proj)
_, err = AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Update(proj)
assert.NoError(t, err)
time.Sleep(1 * time.Second)
closer, client, err := fixture.ArgoCDClientset.NewApplicationClient()
closer, client, err := ArgoCDClientset.NewApplicationClient()
assert.NoError(t, err)
defer util.Close(closer)
@@ -697,7 +692,7 @@ func TestSelfManagedApps(t *testing.T) {
Given(t).
Path("self-managed-app").
When().
PatchFile("resources.yaml", fmt.Sprintf(`[{"op": "replace", "path": "/spec/source/repoURL", "value": "%s"}]`, fixture.RepoURL(fixture.RepoURLTypeFile))).
PatchFile("resources.yaml", fmt.Sprintf(`[{"op": "replace", "path": "/spec/source/repoURL", "value": "%s"}]`, RepoURL(RepoURLTypeFile))).
Create().
Sync().
Then().
@@ -709,7 +704,7 @@ func TestSelfManagedApps(t *testing.T) {
reconciledCount := 0
var lastReconciledAt *metav1.Time
for event := range fixture.ArgoCDClientset.WatchApplicationWithRetry(ctx, a.Name) {
for event := range ArgoCDClientset.WatchApplicationWithRetry(ctx, a.Name) {
reconciledAt := event.Application.Status.ReconciledAt
if reconciledAt == nil {
reconciledAt = &metav1.Time{}

View File

@@ -19,11 +19,12 @@ func TestCliAppCommand(t *testing.T) {
output, err := RunCli("app", "sync", Name(), "--timeout", "90")
assert.NoError(t, err)
vars := map[string]interface{}{"Name": Name(), "Namespace": DeploymentNamespace()}
assert.Contains(t, NormalizeOutput(output), Tmpl(`Pod {{.Namespace}} pod Synced Healthy pod/pod created`, vars))
assert.Contains(t, NormalizeOutput(output), Tmpl(`Pod {{.Namespace}} pod Synced Progressing pod/pod created`, vars))
assert.Contains(t, NormalizeOutput(output), Tmpl(`Pod {{.Namespace}} hook Succeeded Sync pod/hook created`, vars))
}).
Then().
Expect(OperationPhaseIs(OperationSucceeded)).
Expect(HealthIs(HealthStatusHealthy)).
And(func(_ *Application) {
output, err := RunCli("app", "list")
assert.NoError(t, err)

View File

@@ -43,6 +43,11 @@ func (c *Context) CustomCACertAdded() *Context {
return c
}
func (c *Context) CustomSSHKnownHostsAdded() *Context {
certs.AddCustomSSHKnownHostsKeys()
return c
}
func (c *Context) HTTPSRepoURLAdded() *Context {
repos.AddHTTPSRepo(false)
return c

View File

@@ -1,12 +1,15 @@
package certs
import (
"io/ioutil"
"path/filepath"
"github.com/argoproj/argo-cd/errors"
"github.com/argoproj/argo-cd/test/e2e/fixture"
)
// Add a custom CA certificate to the test and also create the certificate file
// on the file system, so argocd-server and argocd-repo-server can use it.
func AddCustomCACert() {
caCertPath, err := filepath.Abs("../fixture/certs/argocd-test-ca.crt")
errors.CheckError(err)
@@ -14,4 +17,23 @@ func AddCustomCACert() {
errors.FailOnErr(fixture.RunCli(args...))
args = []string{"cert", "add-tls", "127.0.0.1", "--from", caCertPath}
errors.FailOnErr(fixture.RunCli(args...))
certData, err := ioutil.ReadFile(caCertPath)
errors.CheckError(err)
err = ioutil.WriteFile(fixture.TmpDir+"/app/config/tls/localhost", certData, 0644)
errors.CheckError(err)
err = ioutil.WriteFile(fixture.TmpDir+"/app/config/tls/127.0.0.1", certData, 0644)
errors.CheckError(err)
}
func AddCustomSSHKnownHostsKeys() {
knownHostsPath, err := filepath.Abs("../fixture/certs/ssh_known_hosts")
errors.CheckError(err)
args := []string{"cert", "add-ssh", "--batch", "--from", knownHostsPath}
errors.FailOnErr(fixture.RunCli(args...))
knownHostsData, err := ioutil.ReadFile(knownHostsPath)
errors.CheckError(err)
err = ioutil.WriteFile(fixture.TmpDir+"/app/config/ssh/ssh_known_hosts", knownHostsData, 0644)
errors.CheckError(err)
}

View File

@@ -40,7 +40,7 @@ const (
ArgoCDNamespace = "argocd-e2e"
// ensure all repos are in one directory tree, so we can easily clean them up
tmpDir = "/tmp/argo-e2e"
TmpDir = "/tmp/argo-e2e"
repoDir = "testdata.git"
GuestbookPath = "guestbook"
@@ -127,7 +127,7 @@ func Name() string {
}
func repoDirectory() string {
return path.Join(tmpDir, repoDir)
return path.Join(TmpDir, repoDir)
}
func RepoURL(urlType RepoURLType) string {
@@ -336,7 +336,7 @@ func EnsureCleanState(t *testing.T) {
SetTLSCerts()
// remove tmp dir
CheckError(os.RemoveAll(tmpDir))
CheckError(os.RemoveAll(TmpDir))
// name based on test name
name = dnsFriendly(t.Name())
@@ -344,11 +344,11 @@ func EnsureCleanState(t *testing.T) {
id = name + "-" + strings.ToLower(rand.RandString(5))
// create tmp dir
FailOnErr(Run("", "mkdir", "-p", tmpDir))
FailOnErr(Run("", "mkdir", "-p", TmpDir))
// create TLS and SSH certificate directories
FailOnErr(Run("", "mkdir", "-p", tmpDir+"/app/config/tls"))
FailOnErr(Run("", "mkdir", "-p", tmpDir+"/app/config/ssh"))
FailOnErr(Run("", "mkdir", "-p", TmpDir+"/app/config/tls"))
FailOnErr(Run("", "mkdir", "-p", TmpDir+"/app/config/ssh"))
// set-up tmp repo, must have unique name
FailOnErr(Run("", "cp", "-Rf", "testdata", repoDirectory()))

View File

@@ -45,6 +45,21 @@ func testHookSuccessful(t *testing.T, hookType HookType, podHookPhase OperationP
Expect(ResourceResultIs(ResourceResult{Version: "v1", Kind: "Pod", Namespace: DeploymentNamespace(), Name: "hook", Message: "pod/hook created", HookType: hookType, HookPhase: OperationSucceeded, SyncPhase: SyncPhase(hookType)}))
}
// make sure that that hooks do not appear in "argocd app diff"
func TestHookDiff(t *testing.T) {
Given(t).
Path("hook").
When().
Create().
Then().
And(func(_ *Application) {
output, err := RunCli("app", "diff", Name())
assert.Error(t, err)
assert.Contains(t, output, "name: pod")
assert.NotContains(t, output, "name: hook")
})
}
// make sure that if pre-sync fails, we fail the app and we do not create the pod
func TestPreSyncHookFailure(t *testing.T) {
Given(t).
@@ -67,7 +82,7 @@ func TestPreSyncHookFailure(t *testing.T) {
Expect(ResourceSyncStatusIs("Pod", "pod", SyncStatusCodeOutOfSync))
}
// make sure that if pre-sync fails, we fail the app and we did create the pod
// make sure that if sync fails, we fail the app and we did create the pod
func TestSyncHookFailure(t *testing.T) {
Given(t).
Path("hook").
@@ -85,6 +100,19 @@ func TestSyncHookFailure(t *testing.T) {
Expect(ResourceSyncStatusIs("Pod", "pod", SyncStatusCodeSynced))
}
// make sure that if the deployments fails, we still get success and synced
func TestSyncHookResourceFailure(t *testing.T) {
Given(t).
Path("hook-and-deployment").
When().
Create().
Sync().
Then().
Expect(OperationPhaseIs(OperationSucceeded)).
Expect(SyncStatusIs(SyncStatusCodeSynced)).
Expect(HealthIs(HealthStatusProgressing))
}
// make sure that if post-sync fails, we fail the app and we did not create the pod
func TestPostSyncHookFailure(t *testing.T) {
Given(t).

View File

@@ -9,7 +9,7 @@ import (
. "github.com/argoproj/argo-cd/test/e2e/fixture/app"
)
func TestCanAccessSSHRepo(t *testing.T) {
func TestCanAccessInsecureSSHRepo(t *testing.T) {
Given(t).
SSHInsecureRepoURLAdded().
RepoURLType(fixture.RepoURLTypeSSH).
@@ -20,3 +20,16 @@ func TestCanAccessSSHRepo(t *testing.T) {
Then().
Expect(OperationPhaseIs(OperationSucceeded))
}
func TestCanAccessSSHRepo(t *testing.T) {
Given(t).
CustomSSHKnownHostsAdded().
SSHRepoURLAdded().
RepoURLType(fixture.RepoURLTypeSSH).
Path("config-map").
When().
Create().
Sync().
Then().
Expect(OperationPhaseIs(OperationSucceeded))
}

View File

@@ -0,0 +1,28 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: main
image: nginx:latest
imagePullPolicy: IfNotPresent
readinessProbe:
failureThreshold: 3
httpGet:
path: /does-not-exist
port: 8080
scheme: HTTP
initialDelaySeconds: 5
periodSeconds: 5
successThreshold: 3
timeoutSeconds: 1

View File

@@ -0,0 +1,16 @@
apiVersion: batch/v1
kind: Job
metadata:
name: my-hook
annotations:
argocd.argoproj.io/hook: Sync
spec:
template:
spec:
containers:
- command:
- "true"
image: "alpine:latest"
imagePullPolicy: IfNotPresent
name: main
restartPolicy: Never

View File

@@ -0,0 +1,3 @@
[localhost]:2222 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLle3IiLWy+Cwz6/JT3K8PSGAEZAJnaxiWk0u9wkAvbZ9wHTffctg25coBa8J4Oo1l5GTIkezib2C4PjCE01BZM=
[localhost]:2222 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDhRWyu6rg0Kd0ugLxNGZ8gzUjasF4Z0oT16RUC/L9EkJWATAu4TkkoozZ5AcejlS29jUZXTkKt0La4dmIooeMDNd8b5vg1dWzSDDHwxd8Wa/4XZsUlL6zkUFrnqOPaFc/7EwM3I30064zT/Gt0BVvQUxKoT/TTea2KhQqeLmlWh4cVWJBuhZ8YODUf2VD4TSYfvpcqW/jVw2oG8Pj3WIaaG2+Bcp4Q4sJS2K+2kkiqmZ/hiPK1X/UbMRN2zWQBp5UPWFY2ctuC9B8yhLwAyMkHzuWLfB39dNEdn1jTjDsOUWbC3kDsWHsY5gtBxN30NizBWC+83NpaWbrzAlGb0JV1
[localhost]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG2t7Tcavp5oUqbbSwEKRaGwEq94b8BFK16AEBbgRCTp

View File

@@ -1,15 +1,30 @@
import { Layout, NavigationManager, Notifications, NotificationsManager, PageContext, Popup, PopupManager, PopupProps } from 'argo-ui';
import {
DataLoader,
Layout,
NavigationManager,
Notifications,
NotificationsManager,
PageContext,
Popup,
PopupManager,
PopupProps,
} from 'argo-ui';
import * as cookie from 'cookie';
import { createBrowserHistory } from 'history';
import {createBrowserHistory} from 'history';
import * as jwtDecode from 'jwt-decode';
import * as PropTypes from 'prop-types';
import * as React from 'react';
import { Helmet } from 'react-helmet';
import { Redirect, Route, RouteComponentProps, Router, Switch } from 'react-router';
import {Helmet} from 'react-helmet';
import {Redirect, Route, RouteComponentProps, Router, Switch} from 'react-router';
import { services } from './shared/services';
import applications from './applications';
import help from './help';
import login from './login';
import settings from './settings';
import {Provider} from './shared/context';
import {services} from './shared/services';
import requests from './shared/services/requests';
import { hashCode } from './shared/utils';
import {hashCode} from './shared/utils';
services.viewPreferences.init();
const bases = document.getElementsByTagName('base');
@@ -17,12 +32,6 @@ const base = bases.length > 0 ? bases[0].getAttribute('href') || '/' : '/';
export const history = createBrowserHistory({ basename: base });
requests.setApiRoot(`${base}api/v1`);
import applications from './applications';
import help from './help';
import login from './login';
import settings from './settings';
import { Provider } from './shared/context';
const routes: {[path: string]: { component: React.ComponentType<RouteComponentProps<any>>, noLayout?: boolean } } = {
'/login': { component: login.component as any, noLayout: true },
'/applications': { component: applications.component },
@@ -165,6 +174,13 @@ export class App extends React.Component<{}, { popupProps: PopupProps, error: Er
<Redirect path='*' to='/'/>
</Switch>
</Router>
<DataLoader load={() => services.authService.settings()}>{(s) => (
s.help && s.help.chatUrl && <div style={{position: 'fixed', right: 10, bottom: 10}}>
<a href={s.help.chatUrl} className='argo-button argo-button--special'>
<i className='fas fa-comment-alt'/> {s.help.chatText}
</a>
</div> || null
)}</DataLoader>
</Provider>
</PageContext.Provider>
<Notifications notifications={this.notificationsManager.notifications}/>

View File

@@ -33,10 +33,13 @@ export const ApplicationResourcesDiff = (props: ApplicationResourcesDiffProps) =
return {
a: live ? jsYaml.safeDump(live, {indent: 2}) : '',
b: target ? jsYaml.safeDump(target, {indent: 2}) : '',
hook: state.hook,
// doubles as sort order
name: (state.group || '') + '/' + state.kind + '/' + state.namespace + '/' + state.name,
};
}).filter((i) => i.a !== i.b)
})
.filter((i) => !i.hook)
.filter((i) => i.a !== i.b)
.map((i) => {
const context = pref.appDetails.compactDiff ? 2 : Number.MAX_SAFE_INTEGER;
// react-diff-view, awesome as it is, does not accept unidiff format, you must add a git header section

View File

@@ -1,5 +1,6 @@
import { DropDownMenu, Tooltip } from 'argo-ui';
import * as React from 'react';
const GitUrlParse = require('git-url-parse');
import { Cluster } from '../../../shared/components';
import { Consumer } from '../../../shared/context';
@@ -10,7 +11,7 @@ import * as AppUtils from '../utils';
require('./applications-table.scss');
function shortRepo(repo: string) {
const url = new URL(repo);
const url = GitUrlParse(repo);
return <Tooltip content={repo}><span>{url.pathname.slice(1)}</span></Tooltip>;
}

View File

@@ -2,9 +2,12 @@ import {repoUrl, revisionUrl} from './urls';
function testExample(http: string, ssl: string, revision: string, expectedRepoUrl: string, expectedRevisionUrl: string) {
expect(repoUrl(http)).toBe(expectedRepoUrl);
expect(repoUrl(ssl)).toBe(expectedRepoUrl);
expect(revisionUrl(http, revision)).toBe(expectedRevisionUrl);
expect(revisionUrl(ssl, revision)).toBe(expectedRevisionUrl);
expect(repoUrl(http)).toBe(expectedRepoUrl);
expect(revisionUrl(http, revision)).toBe(expectedRevisionUrl);
expect(revisionUrl(ssl, revision)).toBe(expectedRevisionUrl);
}
test('github.com', () => {
@@ -16,6 +19,15 @@ test('github.com', () => {
'https://github.com/argoproj/argo-cd/commit/024dee09f543ce7bb5af7ca50260504d89dfda94');
});
// for enterprise github installations
test('github.my-enterprise.com', () => {
testExample(
'https://github.my-enterprise.com/my-org/my-repo.git',
'git@github.my-enterprise.com:my-org/my-repo.git',
'a06f2be80a4da89abb8ced904beab75b3ec6db0e',
'https://github.my-enterprise.com/my-org/my-repo',
'https://github.my-enterprise.com/my-org/my-repo/commit/a06f2be80a4da89abb8ced904beab75b3ec6db0e');
});
test('gitlab.com', () => {
testExample(

View File

@@ -1,7 +1,9 @@
import {GitUrl} from 'git-url-parse';
const GitUrlParse = require('git-url-parse');
function supportedSource(source: string): boolean {
return ['github.com', 'gitlab.com', 'bitbucket.org'].indexOf(source) >= 0;
function supportedSource(parsed: GitUrl): boolean {
return parsed.resource.startsWith('github') || ['gitlab.com', 'bitbucket.org'].indexOf(parsed.source) >= 0;
}
function protocol(proto: string): string {
@@ -11,7 +13,7 @@ function protocol(proto: string): string {
export function repoUrl(url: string): string {
const parsed = GitUrlParse(url);
if (!supportedSource(parsed.source)) {
if (!supportedSource(parsed)) {
return null;
}
@@ -22,7 +24,7 @@ export function revisionUrl(url: string, revision: string): string {
const parsed = GitUrlParse(url);
if (!supportedSource(parsed.source)) {
if (!supportedSource(parsed)) {
return null;
}

View File

@@ -61,7 +61,7 @@ export interface OperationState {
finishedAt: models.Time;
}
export type HookType = 'PreSync' | 'Sync' | 'PostSync' | 'Skip';
export type HookType = 'PreSync' | 'Sync' | 'PostSync' | 'SyncFail' | 'Skip';
export interface RevisionMetadata {
author: string;
@@ -278,6 +278,7 @@ export interface ResourceDiff {
targetState: State;
liveState: State;
diff: string;
hook: boolean;
}
export interface SyncStatus {
@@ -328,6 +329,10 @@ export interface AuthSettings {
oidcConfig: {
name: string;
};
help: {
chatUrl: string;
chatText: string;
};
}
export type ConnectionStatus = 'Unknown' | 'Successful' | 'Failed';

View File

@@ -65,10 +65,10 @@ const (
)
// Regular expression that matches a valid hostname
var validHostNameRegexp = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]))*$`)
var validHostNameRegexp = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]))*(\.){0,1}$`)
// Regular expression that matches a valid FQDN
var validFQDNRegexp = regexp.MustCompile(`^([a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$`)
var validFQDNRegexp = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]))*(\.){1}$`)
// Can be used to test whether a given string represents a valid hostname
// If fqdn is true, given string must also be a FQDN representation.

View File

@@ -357,31 +357,58 @@ func Test_SSHFingerPrintSHA256FromString(t *testing.T) {
}
func Test_ServerNameWithoutPort(t *testing.T) {
hostNameList := []string{"localhost", "localhost:9443", "localhost:", "localhost:abc"}
for _, hostName := range hostNameList {
assert.Equal(t, "localhost", ServerNameWithoutPort(hostName))
hostNames := map[string]string{
"localhost": "localhost",
"localhost:9443": "localhost",
"localhost:": "localhost",
"localhost:abc": "localhost",
"localhost.:22": "localhost.",
"foo.example.com:443": "foo.example.com",
"foo.example.com.:443": "foo.example.com.",
}
for inp, res := range hostNames {
assert.Equal(t, res, ServerNameWithoutPort(inp))
}
}
func Test_ValidHostnames(t *testing.T) {
hostNameList := []string{"localhost", "localhost.localdomain", "foo.example.com", "argocd-server.svc.kubernetes.local"}
for idx, hostName := range hostNameList {
assert.Equal(t, true, IsValidHostname(hostName, false))
if idx != 0 {
assert.Equal(t, true, IsValidHostname(hostName, true))
} else {
assert.Equal(t, false, IsValidHostname(hostName, true))
}
hostNames := map[string]bool{
"localhost": true,
"localhost.localdomain": true,
"foo.example.com": true,
"argocd-server.svc.kubernetes.local": true,
"localhost.": true,
"github.com.": true,
"localhost..": false,
"localhost..localdomain": false,
".localhost": false,
"local_host": false,
"localhost.local_domain": false,
}
for hostName, valid := range hostNames {
assert.Equal(t, valid, IsValidHostname(hostName, false))
}
}
func Test_InvalidHostnames(t *testing.T) {
hostNameList := []string{"localhost.a", "localhost.", "localhost..localdomain", ".foo.example.com", "argocd_server.svc.kubernetes.local"}
for idx, hostName := range hostNameList {
if idx == 0 {
assert.Equal(t, false, IsValidHostname(hostName, true))
} else {
assert.Equal(t, false, IsValidHostname(hostName, false))
}
func Test_ValidFQDNs(t *testing.T) {
hostNames := map[string]bool{
"localhost": false,
"localhost.localdomain": false,
"foo.example.com.": true,
"argocd-server.svc.kubernetes.local": false,
"localhost.": true,
"github.com.": true,
"localhost..": false,
"localhost..localdomain": false,
"localhost..localdomain.": false,
".localhost": false,
"local_host": false,
"localhost.local_domain": false,
"localhost.local_domain.": false,
}
for hostName, valid := range hostNames {
assert.Equal(t, valid, IsValidHostname(hostName, true))
}
}

View File

@@ -2,11 +2,14 @@ package db
import (
"fmt"
"regexp"
"strings"
"golang.org/x/crypto/ssh"
"golang.org/x/net/context"
log "github.com/sirupsen/logrus"
"github.com/argoproj/argo-cd/common"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
certutil "github.com/argoproj/argo-cd/util/cert"
@@ -169,9 +172,24 @@ func (db *db) CreateRepoCertificate(ctx context.Context, certificates *appsv1.Re
// Each request can contain multiple certificates of different types, so we
// make sure to handle each request accordingly.
for _, certificate := range certificates.Items {
// Ensure valid repo server name was given
if !certutil.IsValidHostname(certificate.ServerName, false) {
// Ensure valid repo server name was given only for https certificates.
// For SSH known host entries, we let Go's ssh library do the validation
// later on.
if certificate.CertType == "https" && !certutil.IsValidHostname(certificate.ServerName, false) {
return nil, fmt.Errorf("Invalid hostname in request: %s", certificate.ServerName)
} else if certificate.CertType == "ssh" {
// Matches "[hostname]:port" format
reExtract := regexp.MustCompile(`^\[(.*)\]\:[0-9]+$`)
matches := reExtract.FindStringSubmatch(certificate.ServerName)
var hostnameToCheck string
if len(matches) == 0 {
hostnameToCheck = certificate.ServerName
} else {
hostnameToCheck = matches[1]
}
if !certutil.IsValidHostname(hostnameToCheck, false) {
return nil, fmt.Errorf("Invalid hostname in request: %s", hostnameToCheck)
}
}
if certificate.CertType == "ssh" {
@@ -201,14 +219,18 @@ func (db *db) CreateRepoCertificate(ctx context.Context, certificates *appsv1.Re
}
// Make sure that we received a valid public host key by parsing it
_, _, rawKeyData, _, _, err := ssh.ParseKnownHosts([]byte(fmt.Sprintf("%s %s %s", certificate.ServerName, certificate.CertSubType, certificate.CertData)))
_, hostnames, rawKeyData, _, _, err := ssh.ParseKnownHosts([]byte(fmt.Sprintf("%s %s %s", certificate.ServerName, certificate.CertSubType, certificate.CertData)))
if err != nil {
return nil, err
}
if len(hostnames) == 0 {
log.Errorf("Could not parse hostname for key from token %s", certificate.ServerName)
}
if newEntry {
sshKnownHostsList = append(sshKnownHostsList, &SSHKnownHostsEntry{
Host: certificate.ServerName,
Host: hostnames[0],
Data: string(certificate.CertData),
SubType: certificate.CertSubType,
})

View File

@@ -292,9 +292,9 @@ func Test_ListCertificate(t *testing.T) {
HostNamePattern: "*",
CertType: "ssh",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, len(certList.Items), Test_NumSSHKnownHostsExpected)
assert.Len(t, certList.Items, Test_NumSSHKnownHostsExpected)
for idx, entry := range certList.Items {
assert.Equal(t, entry.ServerName, Test_SSH_Hostname_Entries[idx])
assert.Equal(t, entry.CertSubType, Test_SSH_Subtypes[idx])
@@ -306,9 +306,9 @@ func Test_ListCertificate(t *testing.T) {
HostNamePattern: "*",
CertType: "https",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, len(certList.Items), Test_NumTLSCertificatesExpected)
assert.Len(t, certList.Items, Test_NumTLSCertificatesExpected)
// List all certificates using selector
// Expected: List of 10 entries
@@ -316,16 +316,16 @@ func Test_ListCertificate(t *testing.T) {
HostNamePattern: "*",
CertType: "*",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, len(certList.Items), Test_NumTLSCertificatesExpected+Test_NumSSHKnownHostsExpected)
assert.Len(t, certList.Items, Test_NumTLSCertificatesExpected+Test_NumSSHKnownHostsExpected)
// List all certificates using nil selector
// Expected: List of 10 entries
certList, err = db.ListRepoCertificates(context.Background(), nil)
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, len(certList.Items), Test_NumTLSCertificatesExpected+Test_NumSSHKnownHostsExpected)
assert.Len(t, certList.Items, Test_NumTLSCertificatesExpected+Test_NumSSHKnownHostsExpected)
// List all certificates matching a host name pattern
// Expected: List of 4 entries, all with servername gitlab.com
@@ -333,9 +333,9 @@ func Test_ListCertificate(t *testing.T) {
HostNamePattern: "gitlab.com",
CertType: "*",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 4, len(certList.Items))
assert.Len(t, certList.Items, 4)
for _, entry := range certList.Items {
assert.Equal(t, "gitlab.com", entry.ServerName)
}
@@ -345,9 +345,9 @@ func Test_ListCertificate(t *testing.T) {
HostNamePattern: "gitlab.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
assert.Len(t, certList.Items, 1)
assert.Equal(t, "gitlab.com", certList.Items[0].ServerName)
assert.Equal(t, "https", certList.Items[0].CertType)
}
@@ -367,9 +367,23 @@ func Test_CreateSSHKnownHostEntries(t *testing.T) {
},
},
}, false)
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
assert.Len(t, certList.Items, 1)
// Valid known hosts entry
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "[foo.example.com]:2222",
CertType: "ssh",
CertData: []byte("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDioSMcGxdVkHaQzRjP71nY4mgVHXjuZiYN9NBiUxNZ0DYGjTIENI3uV45XxrS6PQfoyekUlVlHK2jwpcPrqAg6rlAdMD5WIxzvCnFjCuPA6Ljk8p0ZmYbvriDcgtj+UfGEdyUTgxH2gch6KwTY0eAbLue15IuXtoNzpLxk29iGRi5ZXNAbSBjeB3hm2PKLa6LnDqdkvc+nqoYqn1Fvx7ZJIh0apBCJpOtHPON4rnl7QQvNg9pWulZ5GKcpYMRfTpvHyFTEyrsVT5GH38l9s355GqU7GxQ/i6Tj1D0MKrIB2WmdjOnujM/ELLsrkYspMhn8ZRpCphN/LTcrOWsb0AM69drvYlhc6cnNAtC4UXp0GUy1HsBiJCsUm9/1Gz23VLDRvWop8yE8+PE3Ho5eL7ad9wmOG0mSOYEqVvAstmd8vzbD6oRuY8qV8X3tt9ph2tMAve0Qbo0NN3c51c9OfdXtJaSyckjEjaK7zjnArnYfladZZVlf2Tv8FsV0sJmfSAE="),
},
},
}, false)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Len(t, certList.Items, 1)
// Invalid hostname
// Result: Error
@@ -382,7 +396,7 @@ func Test_CreateSSHKnownHostEntries(t *testing.T) {
},
},
}, false)
assert.NotNil(t, err)
assert.Error(t, err)
assert.Nil(t, certList)
// Check if it really was added
@@ -391,9 +405,9 @@ func Test_CreateSSHKnownHostEntries(t *testing.T) {
HostNamePattern: "foo.example.com",
CertType: "ssh",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
assert.Len(t, certList.Items, 1)
// Existing cert, same data, no upsert
// Result: no error, should return 0 added certificates
@@ -406,9 +420,9 @@ func Test_CreateSSHKnownHostEntries(t *testing.T) {
},
},
}, false)
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
assert.Len(t, certList.Items, 0)
// Existing cert, different data, no upsert
// Result: Error
@@ -421,7 +435,7 @@ func Test_CreateSSHKnownHostEntries(t *testing.T) {
},
},
}, false)
assert.NotNil(t, err)
assert.Error(t, err)
assert.Nil(t, certList)
// Existing cert, different data, upsert
@@ -434,9 +448,9 @@ func Test_CreateSSHKnownHostEntries(t *testing.T) {
},
},
}, true)
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
assert.Len(t, certList.Items, 1)
// Invalid known hosts entry, case 1: key sub type missing
// Result: Error
@@ -449,7 +463,7 @@ func Test_CreateSSHKnownHostEntries(t *testing.T) {
},
},
}, false)
assert.NotNil(t, err)
assert.Error(t, err)
assert.Nil(t, certList)
// Invalid known hosts entry, case 2: invalid base64 data
@@ -463,7 +477,7 @@ func Test_CreateSSHKnownHostEntries(t *testing.T) {
},
},
}, false)
assert.NotNil(t, err)
assert.Error(t, err)
assert.Nil(t, certList)
}
@@ -483,22 +497,22 @@ func Test_CreateTLSCertificates(t *testing.T) {
},
},
}, false)
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
assert.Len(t, certList.Items, 1)
// Invalid hostname
// Result: Error
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "foo.example.",
ServerName: "foo..example",
CertType: "https",
CertData: []byte(Test_TLSValidSingleCert),
},
},
}, false)
assert.NotNil(t, err)
assert.Error(t, err)
assert.Nil(t, certList)
// Check if it really was added
@@ -507,9 +521,9 @@ func Test_CreateTLSCertificates(t *testing.T) {
HostNamePattern: "foo.example.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
assert.Len(t, certList.Items, 1)
// Valid TLS certificates, multiple PEMs in data
// Expected: List of 2 entry
@@ -522,9 +536,9 @@ func Test_CreateTLSCertificates(t *testing.T) {
},
},
}, false)
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 2, len(certList.Items))
assert.Len(t, certList.Items, 2)
// Check if it really was added
// Result: Return new certificate
@@ -532,9 +546,9 @@ func Test_CreateTLSCertificates(t *testing.T) {
HostNamePattern: "bar.example.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 2, len(certList.Items))
assert.Len(t, certList.Items, 2)
// Valid TLS certificate, existing cert, same data, no upsert
// Expected: List of 0 entry
@@ -547,9 +561,9 @@ func Test_CreateTLSCertificates(t *testing.T) {
},
},
}, false)
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
assert.Len(t, certList.Items, 0)
// Valid TLS certificate, existing cert, different data, no upsert
// Expected: Error
@@ -562,7 +576,7 @@ func Test_CreateTLSCertificates(t *testing.T) {
},
},
}, false)
assert.NotNil(t, err)
assert.Error(t, err)
assert.Nil(t, certList)
// Valid TLS certificate, existing cert, different data, upsert
@@ -576,9 +590,9 @@ func Test_CreateTLSCertificates(t *testing.T) {
},
},
}, true)
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 2, len(certList.Items))
assert.Len(t, certList.Items, 2)
// Check if upsert was successful
// Expected: List of 2 entries, matching hostnames & cert types
@@ -586,9 +600,9 @@ func Test_CreateTLSCertificates(t *testing.T) {
HostNamePattern: "foo.example.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 2, len(certList.Items))
assert.Len(t, certList.Items, 2)
for _, entry := range certList.Items {
assert.Equal(t, "foo.example.com", entry.ServerName)
assert.Equal(t, "https", entry.CertType)
@@ -605,7 +619,7 @@ func Test_CreateTLSCertificates(t *testing.T) {
},
},
}, false)
assert.NotNil(t, err)
assert.Error(t, err)
assert.Nil(t, certList)
// Valid PEM data, new cert, but invalid certificate
@@ -619,7 +633,7 @@ func Test_CreateTLSCertificates(t *testing.T) {
},
},
}, false)
assert.NotNil(t, err)
assert.Error(t, err)
assert.Nil(t, certList)
// Invalid PEM data, existing cert, upsert
@@ -633,7 +647,7 @@ func Test_CreateTLSCertificates(t *testing.T) {
},
},
}, true)
assert.NotNil(t, err)
assert.Error(t, err)
assert.Nil(t, certList)
// Valid PEM data, existing cert, but invalid certificate, upsert
@@ -647,7 +661,7 @@ func Test_CreateTLSCertificates(t *testing.T) {
},
},
}, true)
assert.NotNil(t, err)
assert.Error(t, err)
assert.Nil(t, certList)
}
@@ -663,9 +677,9 @@ func Test_RemoveSSHKnownHosts(t *testing.T) {
HostNamePattern: "github.com",
CertType: "ssh",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
assert.Len(t, certList.Items, 1)
// Check whether entry was really removed
// Expected: List of 0 entries
@@ -673,9 +687,9 @@ func Test_RemoveSSHKnownHosts(t *testing.T) {
HostNamePattern: "github.com",
CertType: "ssh",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
assert.Len(t, certList.Items, 0)
// Remove single SSH known hosts entry by sub type
// Expected: List of 1 entry
@@ -683,9 +697,9 @@ func Test_RemoveSSHKnownHosts(t *testing.T) {
CertType: "ssh",
CertSubType: "ssh-ed25519",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
assert.Len(t, certList.Items, 1)
// Check whether entry was really removed
// Expected: List of 0 entries
@@ -693,27 +707,27 @@ func Test_RemoveSSHKnownHosts(t *testing.T) {
CertType: "ssh",
CertSubType: "ssh-ed25519",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
assert.Len(t, certList.Items, 0)
// Remove all remaining SSH known hosts entries
// Expected: List of 5 entry
certList, err = db.RemoveRepoCertificates(context.Background(), &CertificateListSelector{
CertType: "ssh",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 5, len(certList.Items))
assert.Len(t, certList.Items, 5)
// Check whether the entries were really removed
// Expected: List of 0 entries
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
CertType: "ssh",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
assert.Len(t, certList.Items, 0)
}
func Test_RemoveTLSCertificates(t *testing.T) {
@@ -727,9 +741,9 @@ func Test_RemoveTLSCertificates(t *testing.T) {
HostNamePattern: "gitlab.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
assert.Len(t, certList.Items, 1)
// Check whether entry was really removed
// Expected: List of 0 entries
@@ -737,9 +751,9 @@ func Test_RemoveTLSCertificates(t *testing.T) {
HostNamePattern: "gitlab.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
assert.Len(t, certList.Items, 0)
// Remove all TLS certificate entry for hostname
// Expected: List of 2 entry
@@ -747,9 +761,9 @@ func Test_RemoveTLSCertificates(t *testing.T) {
HostNamePattern: "test.example.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 2, len(certList.Items))
assert.Len(t, certList.Items, 2)
// Check whether entries were really removed
// Expected: List of 0 entries
@@ -757,8 +771,8 @@ func Test_RemoveTLSCertificates(t *testing.T) {
HostNamePattern: "test.example.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NoError(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
assert.Len(t, certList.Items, 0)
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"hash/fnv"
"net/url"
"reflect"
"strings"
"golang.org/x/net/context"
@@ -141,8 +142,12 @@ func (db *db) WatchClusters(ctx context.Context, callback func(*ClusterEvent)) e
next.Type = watch.Modified
cluster = &localCluster
} else if next.Type == watch.Added {
localCls = cluster
next.Type = watch.Modified
if !reflect.DeepEqual(localCls.Config, cluster.Config) {
localCls = cluster
next.Type = watch.Modified
} else {
continue
}
} else {
localCls = cluster
}

View File

@@ -4,8 +4,14 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"reflect"
"github.com/ghodss/yaml"
"github.com/google/shlex"
log "github.com/sirupsen/logrus"
"github.com/yudai/gojsondiff"
"github.com/yudai/gojsondiff/formatter"
@@ -387,7 +393,8 @@ func HideSecretData(target *unstructured.Unstructured, live *unstructured.Unstru
}
for k := range keys {
nextReplacement := "*********"
// we use "+" rather than the more common "*"
nextReplacement := "++++++++"
valToReplacement := make(map[string]string)
for _, obj := range []*unstructured.Unstructured{target, live, orig} {
var data map[string]interface{}
@@ -409,7 +416,7 @@ func HideSecretData(target *unstructured.Unstructured, live *unstructured.Unstru
replacement, ok := valToReplacement[val]
if !ok {
replacement = nextReplacement
nextReplacement = nextReplacement + "*"
nextReplacement = nextReplacement + "++++"
valToReplacement[val] = replacement
}
data[k] = replacement
@@ -476,3 +483,50 @@ func remarshal(obj *unstructured.Unstructured) *unstructured.Unstructured {
unstrBody = jsonutil.RemoveMapFields(obj.Object, unstrBody)
return &unstructured.Unstructured{Object: unstrBody}
}
// PrintDiff prints a diff between two unstructured objects to stdout using an external diff utility
// Honors the diff utility set in the KUBECTL_EXTERNAL_DIFF environment variable
func PrintDiff(name string, live *unstructured.Unstructured, target *unstructured.Unstructured) error {
tempDir, err := ioutil.TempDir("", "argocd-diff")
if err != nil {
return err
}
targetFile := path.Join(tempDir, name)
targetData := []byte("")
if target != nil {
targetData, err = yaml.Marshal(target)
if err != nil {
return err
}
}
err = ioutil.WriteFile(targetFile, targetData, 0644)
if err != nil {
return err
}
liveFile := path.Join(tempDir, fmt.Sprintf("%s-live.yaml", name))
liveData := []byte("")
if live != nil {
liveData, err = yaml.Marshal(live)
if err != nil {
return err
}
}
err = ioutil.WriteFile(liveFile, liveData, 0644)
if err != nil {
return err
}
cmdBinary := "diff"
var args []string
if envDiff := os.Getenv("KUBECTL_EXTERNAL_DIFF"); envDiff != "" {
parts, err := shlex.Split(envDiff)
if err != nil {
return err
}
cmdBinary = parts[0]
args = parts[1:]
}
cmd := exec.Command(cmdBinary, append(args, liveFile, targetFile)...)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
return cmd.Run()
}

View File

@@ -533,10 +533,10 @@ func secretData(obj *unstructured.Unstructured) map[string]interface{} {
return data
}
const (
replacement1 = "*********"
replacement2 = "**********"
replacement3 = "***********"
var (
replacement1 = strings.Repeat("+", 8)
replacement2 = strings.Repeat("+", 12)
replacement3 = strings.Repeat("+", 16)
)
func TestHideSecretDataSameKeysDifferentValues(t *testing.T) {

View File

@@ -14,6 +14,7 @@ import (
argoexec "github.com/argoproj/pkg/exec"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/knownhosts"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
@@ -93,7 +94,14 @@ func (f *factory) NewClient(rawRepoURL string, path string, creds Creds, insecur
// - Otherwise (and on non-fatal errors), a default HTTP client is returned.
func GetRepoHTTPClient(repoURL string, insecure bool, creds Creds) *http.Client {
// Default HTTP client
var customHTTPClient *http.Client = &http.Client{}
var customHTTPClient *http.Client = &http.Client{
// 15 second timeout
Timeout: 15 * time.Second,
// don't follow redirect
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
// Callback function to return any configured client certificate
// We never return err, but an empty cert instead.
@@ -121,19 +129,11 @@ func GetRepoHTTPClient(repoURL string, insecure bool, creds Creds) *http.Client
}
if insecure {
customHTTPClient = &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
GetClientCertificate: clientCertFunc,
},
},
// 15 second timeout
Timeout: 15 * time.Second,
// don't follow redirect
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
customHTTPClient.Transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
GetClientCertificate: clientCertFunc,
},
}
} else {
@@ -146,33 +146,19 @@ func GetRepoHTTPClient(repoURL string, insecure bool, creds Creds) *http.Client
return customHTTPClient
} else if len(serverCertificatePem) > 0 {
certPool := certutil.GetCertPoolFromPEMData(serverCertificatePem)
customHTTPClient = &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: certPool,
GetClientCertificate: clientCertFunc,
},
},
// 15 second timeout
Timeout: 15 * time.Second,
// don't follow redirect
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
customHTTPClient.Transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
RootCAs: certPool,
GetClientCertificate: clientCertFunc,
},
}
} else {
// else no custom certificate stored.
customHTTPClient = &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
GetClientCertificate: clientCertFunc,
},
},
// 15 second timeout
Timeout: 15 * time.Second,
// don't follow redirect
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
customHTTPClient.Transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{
GetClientCertificate: clientCertFunc,
},
}
}
@@ -195,6 +181,13 @@ func newAuth(repoURL string, creds Creds) (transport.AuthMethod, error) {
auth := &ssh2.PublicKeys{User: sshUser, Signer: signer}
if creds.insecure {
auth.HostKeyCallback = ssh.InsecureIgnoreHostKey()
} else {
// Set up validation of SSH known hosts for using our ssh_known_hosts
// file.
auth.HostKeyCallback, err = knownhosts.New(certutil.GetSSHKnownHostsDataPath())
if err != nil {
log.Errorf("Could not set-up SSH known hosts callback: %v", err)
}
}
return auth, nil
case HTTPSCreds:

View File

@@ -10,6 +10,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/argoproj/argo-cd/util"
certutil "github.com/argoproj/argo-cd/util/cert"
)
type Creds interface {
@@ -171,6 +172,9 @@ func (c SSHCreds) Environ() (io.Closer, []string, error) {
// StrictHostKeyChecking will add the host to the knownhosts file, we don't want that - a security issue really,
// UserKnownHostsFile=/dev/null is therefore used so we write the new insecure host to /dev/null
args = append(args, "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null")
} else {
knownHostsFile := certutil.GetSSHKnownHostsDataPath()
args = append(args, "-o", "StrictHostKeyChecking=yes", "-o", fmt.Sprintf("UserKnownHostsFile=%s", knownHostsFile))
}
env = append(env, []string{fmt.Sprintf("GIT_SSH_COMMAND=%s", strings.Join(args, " "))}...)
return sshPrivateKeyFile(file.Name()), env, nil

View File

@@ -209,7 +209,9 @@ func TestLsRemote(t *testing.T) {
// Running this test requires git-lfs to be installed on your machine.
func TestLFSClient(t *testing.T) {
test.CIOnly(t)
// temporary disable LFS test
// TODO(alexmt): dockerize tests in and enabled it
t.Skip()
tempDir, err := ioutil.TempDir("", "git-client-lfs-test-")
assert.NoError(t, err)

View File

@@ -18,8 +18,9 @@ func MakeCookieMetadata(key, value string, flags ...string) (string, error) {
header := strings.Join(components, "; ")
const maxLength = 4093
if len(header) > maxLength {
return "", fmt.Errorf("invalid cookie, longer than max length %v", maxLength)
headerLength := len(header)
if headerLength > maxLength {
return "", fmt.Errorf("invalid cookie, at %d long it is longer than the max length of %d", headerLength, maxLength)
}
return header, nil
}

View File

@@ -14,6 +14,6 @@ func TestCookieMaxLength(t *testing.T) {
assert.Equal(t, "foo=bar", cookie)
cookie, err = MakeCookieMetadata("foo", strings.Repeat("_", 4093-3))
assert.Error(t, err)
assert.EqualError(t, err, "invalid cookie, at 4094 long it is longer than the max length of 4093")
assert.Equal(t, "", cookie)
}

Some files were not shown because too many files have changed in this diff Show More