mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 09:38:49 +01:00
Compare commits
77 Commits
refresh-do
...
v1.8.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb3d1fb84b | ||
|
|
e97b643526 | ||
|
|
b51ba85aae | ||
|
|
be72e7cc42 | ||
|
|
f6e9e41d7b | ||
|
|
087b8b2fd5 | ||
|
|
6dbbb18aa9 | ||
|
|
62b9b3aeb5 | ||
|
|
31110cde4d | ||
|
|
d6c5c72eb4 | ||
|
|
0b3333ef4b | ||
|
|
d0f8edfec8 | ||
|
|
b1ff29fdf9 | ||
|
|
785bb9ecce | ||
|
|
b57579c4ae | ||
|
|
6b53ac785e | ||
|
|
e38920f570 | ||
|
|
28aea3dfde | ||
|
|
fe59190a96 | ||
|
|
0a04a491d9 | ||
|
|
701ce05393 | ||
|
|
965825f752 | ||
|
|
bd73326b8a | ||
|
|
502b8944c4 | ||
|
|
f5b0db240b | ||
|
|
ce43b7a438 | ||
|
|
ebcfea64ff | ||
|
|
14c3dd2c59 | ||
|
|
4359d345a0 | ||
|
|
7081068a2d | ||
|
|
0f9c684278 | ||
|
|
3ea3c13665 | ||
|
|
13fed83ec6 | ||
|
|
94017f2c8d | ||
|
|
3c53ea6cff | ||
|
|
7b2946962d | ||
|
|
df3422798d | ||
|
|
f44855fa4d | ||
|
|
1f4a052da3 | ||
|
|
74f5eae750 | ||
|
|
36a9465d85 | ||
|
|
9e6c04700e | ||
|
|
8abe96ad9a | ||
|
|
10de6e7cfc | ||
|
|
e57071a150 | ||
|
|
41db5fc010 | ||
|
|
31f257e957 | ||
|
|
5fd93a7db5 | ||
|
|
c2547dca95 | ||
|
|
522ed90f38 | ||
|
|
7a0266f0fb | ||
|
|
30348417a9 | ||
|
|
910eddbbf3 | ||
|
|
f150ba18fb | ||
|
|
97d030b4b2 | ||
|
|
5df269b3fa | ||
|
|
775f9711e7 | ||
|
|
3f974825a6 | ||
|
|
1d55439f7f | ||
|
|
41daf71851 | ||
|
|
f4796398a8 | ||
|
|
0f0b6ce278 | ||
|
|
aee4dfaa1e | ||
|
|
74a92c6031 | ||
|
|
4237c6f00f | ||
|
|
0f3d74fa58 | ||
|
|
868516f0bd | ||
|
|
e50c43fb3f | ||
|
|
9b6a0dc3cd | ||
|
|
1f63e99b78 | ||
|
|
589ad5d2ac | ||
|
|
4464f99df7 | ||
|
|
65a2d9f1ff | ||
|
|
bcaabc51a1 | ||
|
|
9140fea0fb | ||
|
|
d83e0ddb56 | ||
|
|
25ca589bff |
8
.github/workflows/release.yaml
vendored
8
.github/workflows/release.yaml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
# Target version must match major.minor.patch and optional -rcX suffix
|
||||
# where X must be a number.
|
||||
TARGET_VERSION=${SOURCE_TAG#*release-v}
|
||||
if ! echo ${TARGET_VERSION} | egrep '^[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)*$'; then
|
||||
if ! echo "${TARGET_VERSION}" | egrep '^[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)*$'; then
|
||||
echo "::error::Target version '${TARGET_VERSION}' is malformed, refusing to continue." >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -101,16 +101,16 @@ jobs:
|
||||
# Whatever is in commit history for the tag, we only want that
|
||||
# annotation from our tag. We discard everything else.
|
||||
if test "$begin" = "false"; then
|
||||
if echo $line | grep -q "tag ${SOURCE_TAG#refs/tags/}"; then begin="true"; fi
|
||||
if echo "$line" | grep -q "tag ${SOURCE_TAG#refs/tags/}"; then begin="true"; fi
|
||||
continue
|
||||
fi
|
||||
if test "$prefix" = "true"; then
|
||||
if test -z "$line"; then prefix=false; fi
|
||||
else
|
||||
if echo $line | egrep -q '^commit [0-9a-f]+'; then
|
||||
if echo "$line" | egrep -q '^commit [0-9a-f]+'; then
|
||||
break
|
||||
fi
|
||||
echo $line >> ${RELEASE_NOTES}
|
||||
echo "$line" >> ${RELEASE_NOTES}
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
7
.readthedocs.yml
Normal file
7
.readthedocs.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
version: 2
|
||||
formats: all
|
||||
mkdocs:
|
||||
fail_on_warning: false
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
58
CHANGELOG.md
58
CHANGELOG.md
@@ -1,5 +1,63 @@
|
||||
# Changelog
|
||||
|
||||
## v1.8.0 (Unreleased)
|
||||
|
||||
### Mono-Repository Improvements
|
||||
|
||||
Enhanced performance during manifest generation from mono-repository - the repository that represents the
|
||||
desired state of the whole cluster and contains hundreds of applications. The improved argocd-repo-server
|
||||
now able to concurrently generate manifests from the same repository and for the same commit SHA. This
|
||||
might provide 10x performance improvement of manifests generation.
|
||||
|
||||
### Annotation Based Path Detection
|
||||
|
||||
The feature that allows specifying which source repository directories influence the application manifest generation
|
||||
using the `argocd.argoproj.io/manifest-generate-paths` annotation. The annotation improves the Git webhook handler
|
||||
behavior. The webhook avoids related applications reconciliation if no related files have been changed by the Git commit
|
||||
and even allows to skip manifests generation for new commit by re-using generation manifests for the previous commit.
|
||||
|
||||
### Horizontal Controller Scaling
|
||||
|
||||
This release allows scaling the `argocd-application-controller` horizontally. This allows you to manage as many Kubernetes clusters
|
||||
as needed using a single Argo CD instance.
|
||||
|
||||
## New Core Functionality Features
|
||||
|
||||
Besides performance improvements, Argo CD got a lot of usability enhancements and new features:
|
||||
|
||||
* Namespace and CRD creation [#4354](https://github.com/argoproj/argo-cd/issues/4354)
|
||||
* Unknown fields of built-in K8S types [#1787](https://github.com/argoproj/argo-cd/issues/1787)
|
||||
* Endpoints Diffing [#1816](https://github.com/argoproj/argo-cd/issues/1816)
|
||||
* Better compatibility with Helm Hooks [#1816](https://github.com/argoproj/argo-cd/issues/1816)
|
||||
* App-of-Apps Health Assessment [#3781](https://github.com/argoproj/argo-cd/issues/3781)
|
||||
|
||||
## Global Projects
|
||||
|
||||
This release makes it easy to manage an Argo CD that has hundreds of Projects. Instead of duplicating the same organization-wide rules in all projects
|
||||
you can put such rules into one project and make this project “global” for all other projects. Rules defined in the global project are inherited by all
|
||||
other projects and therefore don’t have to be duplicated. The sample below demonstrates how you can create a global project and specify which project should
|
||||
inherit global project rules using Kubernetes labels.
|
||||
|
||||
## User Interface Improvements
|
||||
|
||||
The Argo CD user interface is an important part of a project and we keep working hard on improving the user experience. Here is an incomplete list of implemented improvements:
|
||||
|
||||
* Improved Applications Filters [#4622](https://github.com/argoproj/argo-cd/issues/4622)
|
||||
* Git tags and branches autocompletion [#4713](https://github.com/argoproj/argo-cd/issues/4713)
|
||||
* Project Details Page [#4400](https://github.com/argoproj/argo-cd/issues/4400)
|
||||
* New version information panel [#4376](https://github.com/argoproj/argo-cd/issues/4376)
|
||||
* Progress Indicators [#4411](https://github.com/argoproj/argo-cd/issues/4411)
|
||||
* External links annotations [#4380](https://github.com/argoproj/argo-cd/issues/4380) and more!
|
||||
|
||||
## Config Management Tools Enhancements
|
||||
|
||||
* OCI Based Repositories [#4018](https://github.com/argoproj/argo-cd/issues/4018)
|
||||
* Configurable Helm Versions [#4111](https://github.com/argoproj/argo-cd/issues/4111)
|
||||
|
||||
## Bug fixes and under the hood changes
|
||||
|
||||
In addition to new features and enhancements, we’ve fixed more than 50 bugs and upgraded third-party components and libraries that Argo CD relies on.
|
||||
|
||||
## v1.7.9 (2020-11-17)
|
||||
|
||||
- fix: improve commit verification tolerance (#4825)
|
||||
|
||||
2
Procfile
2
Procfile
@@ -1,6 +1,6 @@
|
||||
controller: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} go run ./cmd/argocd-application-controller/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
|
||||
api-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} go run ./cmd/argocd-server/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --staticassets ui/dist/app"
|
||||
dex: sh -c "go run github.com/argoproj/argo-cd/cmd/argocd-util 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.25.0 serve /dex.yaml"
|
||||
dex: sh -c "go run github.com/argoproj/argo-cd/cmd/argocd-util gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.27.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.10-alpine --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}
|
||||
repo-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} 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'
|
||||
|
||||
47
SECURITY.md
Normal file
47
SECURITY.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Security Policy for Argo CD
|
||||
|
||||
Version: **v1.0 (2020-02-26)**
|
||||
|
||||
## Preface
|
||||
|
||||
As a deployment tool, Argo CD needs to have production access which makes
|
||||
security a very important topic. The Argoproj team takes security very
|
||||
seriously and is continuously working on improving it.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We currently support the most recent release (`N`, e.g. `1.8`) and the release
|
||||
previous to the most recent one (`N-1`, e.g. `1.7`). With the release of
|
||||
`N+1`, `N-1` drops out of support and `N` becomes `N-1`.
|
||||
|
||||
We regularly perform patch releases (e.g. `1.8.5` and `1.7.12`) for the
|
||||
supported versions, which will contain fixes for security vulnerabilities and
|
||||
important bugs. Prior releases might receive critical security fixes on a best
|
||||
effort basis, however, it cannot be guaranteed that security fixes get
|
||||
back-ported to these unsupported versions.
|
||||
|
||||
In rare cases, where a security fix needs complex re-design of a feature or is
|
||||
otherwise very intrusive, and there's a workaround available, we may decide to
|
||||
provide a forward-fix only, e.g. to be released the next minor release, instead
|
||||
of releasing it within a patch branch for the currently supported releases.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you find a security related bug in ArgoCD, we kindly ask you for responsible
|
||||
disclosure and for giving us appropriate time to react, analyze and develop a
|
||||
fix to mitigate the found security vulnerability.
|
||||
|
||||
We will do our best to react quickly on your inquiry, and to coordinate a fix
|
||||
and disclosure with you. Sometimes, it might take a little longer for us to
|
||||
react (e.g. out of office conditions), so please bear with us in these cases.
|
||||
|
||||
We will publish security advisiories using the Git Hub SA feature to keep our
|
||||
community well informed, and will credit you for your findings (unless you
|
||||
prefer to stay anonymous, of course).
|
||||
|
||||
Please report vulnerabilities by e-mail to all of the following people:
|
||||
|
||||
* jfischer@redhat.com
|
||||
* Jesse_Suen@intuit.com
|
||||
* Alexander_Matyushentsev@intuit.com
|
||||
* Edward_Lee@intuit.com
|
||||
@@ -78,6 +78,7 @@ func NewCommand() *cobra.Command {
|
||||
|
||||
cache, err := cacheSrc()
|
||||
errors.CheckError(err)
|
||||
cache.Cache.SetClient(cacheutil.NewTwoLevelClient(cache.Cache.GetClient(), 10*time.Minute))
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(ctx, kubeClient, namespace)
|
||||
kubectl := kubeutil.NewKubectl()
|
||||
|
||||
@@ -12,9 +12,11 @@ import (
|
||||
"github.com/go-redis/redis/v8"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/reposerver"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
reposervercache "github.com/argoproj/argo-cd/reposerver/cache"
|
||||
"github.com/argoproj/argo-cd/reposerver/metrics"
|
||||
"github.com/argoproj/argo-cd/reposerver/repository"
|
||||
@@ -23,6 +25,8 @@ import (
|
||||
"github.com/argoproj/argo-cd/util/env"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/gpg"
|
||||
"github.com/argoproj/argo-cd/util/healthz"
|
||||
ioutil "github.com/argoproj/argo-cd/util/io"
|
||||
"github.com/argoproj/argo-cd/util/tls"
|
||||
)
|
||||
|
||||
@@ -32,8 +36,8 @@ const (
|
||||
gnuPGSourcePath = "/app/config/gpg/source"
|
||||
|
||||
defaultPauseGenerationAfterFailedGenerationAttempts = 3
|
||||
defaultPauseGenerationOnFailureForMinutes = 0
|
||||
defaultPauseGenerationOnFailureForRequests = 12
|
||||
defaultPauseGenerationOnFailureForMinutes = 60
|
||||
defaultPauseGenerationOnFailureForRequests = 0
|
||||
)
|
||||
|
||||
func getGnuPGSourcePath() string {
|
||||
@@ -96,6 +100,28 @@ func NewCommand() *cobra.Command {
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", listenPort))
|
||||
errors.CheckError(err)
|
||||
|
||||
healthz.ServeHealthCheck(http.DefaultServeMux, func(r *http.Request) error {
|
||||
if val, ok := r.URL.Query()["full"]; ok && len(val) > 0 && val[0] == "true" {
|
||||
// connect to itself to make sure repo server is able to serve connection
|
||||
// used by liveness probe to auto restart repo server
|
||||
// see https://github.com/argoproj/argo-cd/issues/5110 for more information
|
||||
conn, err := apiclient.NewConnection(fmt.Sprintf("localhost:%d", listenPort), 60)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ioutil.Close(conn)
|
||||
client := grpc_health_v1.NewHealthClient(conn)
|
||||
res, err := client.Check(r.Context(), &grpc_health_v1.HealthCheckRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.Status != grpc_health_v1.HealthCheckResponse_SERVING {
|
||||
return fmt.Errorf("grpc health check status is '%v'", res.Status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
})
|
||||
http.Handle("/metrics", metricsServer.GetHandler())
|
||||
go func() { errors.CheckError(http.ListenAndServe(fmt.Sprintf(":%d", metricsPort), nil)) }()
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"time"
|
||||
@@ -278,7 +279,7 @@ func reconcileApplications(
|
||||
projLister := appInformerFactory.Argoproj().V1alpha1().AppProjects().Lister()
|
||||
server, err := metrics.NewMetricsServer("", appLister, func(obj interface{}) bool {
|
||||
return true
|
||||
}, func() error {
|
||||
}, func(r *http.Request) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
|
||||
@@ -1901,7 +1901,7 @@ func waitOnApplicationStatus(acdClient apiclient.Client, appName string, timeout
|
||||
selectedResourcesAreReady = checkResourceStatus(watchSync, watchHealth, watchOperation, watchSuspended, string(app.Status.Health.Status), string(app.Status.Sync.Status), appEvent.Application.Operation)
|
||||
}
|
||||
|
||||
if selectedResourcesAreReady && !operationInProgress {
|
||||
if selectedResourcesAreReady && (!operationInProgress || !watchOperation) {
|
||||
app = printFinalStatus(app)
|
||||
return app, nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"html"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
@@ -12,7 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/skratchdot/open-golang/open"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -25,6 +26,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
grpc_util "github.com/argoproj/argo-cd/util/grpc"
|
||||
"github.com/argoproj/argo-cd/util/io"
|
||||
jwtutil "github.com/argoproj/argo-cd/util/jwt"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
oidcutil "github.com/argoproj/argo-cd/util/oidc"
|
||||
"github.com/argoproj/argo-cd/util/rand"
|
||||
@@ -113,7 +115,7 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
}
|
||||
|
||||
parser := &jwt.Parser{
|
||||
SkipClaimsValidation: true,
|
||||
ValidationHelper: jwt.NewValidationHelper(jwt.WithoutClaimsValidation(), jwt.WithoutAudienceValidation()),
|
||||
}
|
||||
claims := jwt.MapClaims{}
|
||||
_, _, err := parser.ParseUnverified(tokenString, &claims)
|
||||
@@ -161,13 +163,13 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
}
|
||||
|
||||
func userDisplayName(claims jwt.MapClaims) string {
|
||||
if email, ok := claims["email"]; ok && email != nil {
|
||||
return email.(string)
|
||||
if email := jwtutil.StringField(claims, "email"); email != "" {
|
||||
return email
|
||||
}
|
||||
if name, ok := claims["name"]; ok && name != nil {
|
||||
return name.(string)
|
||||
if name := jwtutil.StringField(claims, "name"); name != "" {
|
||||
return name
|
||||
}
|
||||
return claims["sub"].(string)
|
||||
return jwtutil.StringField(claims, "sub")
|
||||
}
|
||||
|
||||
// oauth2Login opens a browser, runs a temporary HTTP server to delegate OAuth2 login flow and
|
||||
@@ -190,7 +192,7 @@ func oauth2Login(ctx context.Context, port int, oidcSettings *settingspkg.OIDCCo
|
||||
var refreshToken string
|
||||
|
||||
handleErr := func(w http.ResponseWriter, errMsg string) {
|
||||
http.Error(w, errMsg, http.StatusBadRequest)
|
||||
http.Error(w, html.EscapeString(errMsg), http.StatusBadRequest)
|
||||
completionChan <- errMsg
|
||||
}
|
||||
|
||||
@@ -205,7 +207,7 @@ func oauth2Login(ctx context.Context, port int, oidcSettings *settingspkg.OIDCCo
|
||||
log.Debugf("Callback: %s", r.URL)
|
||||
|
||||
if formErr := r.FormValue("error"); formErr != "" {
|
||||
handleErr(w, formErr+": "+r.FormValue("error_description"))
|
||||
handleErr(w, fmt.Sprintf("%s: %s", formErr, r.FormValue("error_description")))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
31
cmd/argocd/commands/login_test.go
Normal file
31
cmd/argocd/commands/login_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
//
|
||||
|
||||
func Test_userDisplayName_email(t *testing.T) {
|
||||
claims := jwt.MapClaims{"iss": "qux", "sub": "foo", "email": "firstname.lastname@example.com", "groups": []string{"baz"}}
|
||||
actualName := userDisplayName(claims)
|
||||
expectedName := "firstname.lastname@example.com"
|
||||
assert.Equal(t, expectedName, actualName)
|
||||
}
|
||||
|
||||
func Test_userDisplayName_name(t *testing.T) {
|
||||
claims := jwt.MapClaims{"iss": "qux", "sub": "foo", "name": "Firstname Lastname", "groups": []string{"baz"}}
|
||||
actualName := userDisplayName(claims)
|
||||
expectedName := "Firstname Lastname"
|
||||
assert.Equal(t, expectedName, actualName)
|
||||
}
|
||||
|
||||
func Test_userDisplayName_sub(t *testing.T) {
|
||||
claims := jwt.MapClaims{"iss": "qux", "sub": "foo", "groups": []string{"baz"}}
|
||||
actualName := userDisplayName(claims)
|
||||
expectedName := "foo"
|
||||
assert.Equal(t, expectedName, actualName)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
timeutil "github.com/argoproj/pkg/time"
|
||||
jwtgo "github.com/dgrijalva/jwt-go"
|
||||
jwtgo "github.com/dgrijalva/jwt-go/v4"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/io"
|
||||
"github.com/argoproj/argo-cd/util/jwt"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -247,13 +248,10 @@ func NewProjectRoleCreateTokenCommand(clientOpts *argocdclient.ClientOptions) *c
|
||||
}
|
||||
|
||||
claims := token.Claims.(jwtgo.MapClaims)
|
||||
issuedAt := int64(claims["iat"].(float64))
|
||||
expiresAt := int64(0)
|
||||
if expires, ok := claims["exp"]; ok {
|
||||
expiresAt = int64(expires.(float64))
|
||||
}
|
||||
id := claims["jti"].(string)
|
||||
subject := claims["sub"].(string)
|
||||
issuedAt, _ := jwt.IssuedAt(claims)
|
||||
expiresAt := int64(jwt.Float64Field(claims, "exp"))
|
||||
id := jwt.StringField(claims, "jti")
|
||||
subject := jwt.StringField(claims, "sub")
|
||||
|
||||
if !outputTokenOnly {
|
||||
fmt.Printf("Create token succeeded for %s.\n", subject)
|
||||
|
||||
@@ -85,6 +85,8 @@ const (
|
||||
DexAPIEndpoint = "/api/dex"
|
||||
// LoginEndpoint is Argo CD's shorthand login endpoint which redirects to dex's OAuth 2.0 provider's consent page
|
||||
LoginEndpoint = "/auth/login"
|
||||
// LogoutEndpoint is Argo CD's shorthand logout endpoint which invalidates OIDC session after logout
|
||||
LogoutEndpoint = "/auth/logout"
|
||||
// CallbackEndpoint is Argo CD's final callback endpoint we reach after OAuth 2.0 login flow has been completed
|
||||
CallbackEndpoint = "/auth/callback"
|
||||
// DexCallbackEndpoint is Argo CD's final callback endpoint when Dex is configured
|
||||
@@ -180,6 +182,8 @@ const (
|
||||
EnvControllerReplicas = "ARGOCD_CONTROLLER_REPLICAS"
|
||||
// EnvControllerShard is the shard number that should be handled by controller
|
||||
EnvControllerShard = "ARGOCD_CONTROLLER_SHARD"
|
||||
// EnvEnableGRPCTimeHistogramEnv enables gRPC metrics collection
|
||||
EnvEnableGRPCTimeHistogramEnv = "ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -189,7 +193,7 @@ const (
|
||||
MinClientVersion = "1.4.0"
|
||||
// CacheVersion is a objects version cached using util/cache/cache.go.
|
||||
// Number should be bumped in case of backward incompatible change to make sure cache is invalidated after upgrade.
|
||||
CacheVersion = "1.8.1"
|
||||
CacheVersion = "1.8.3"
|
||||
)
|
||||
|
||||
// GetGnuPGHomePath retrieves the path to use for GnuPG home directory, which is either taken from GNUPGHOME environment or a default value
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
@@ -13,12 +14,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
logutils "github.com/argoproj/argo-cd/util/log"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/diff"
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/semaphore"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@@ -26,9 +26,11 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
apiruntime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
@@ -42,7 +44,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application"
|
||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
appinformers "github.com/argoproj/argo-cd/pkg/client/informers/externalversions"
|
||||
"github.com/argoproj/argo-cd/pkg/client/informers/externalversions/application/v1alpha1"
|
||||
applisters "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
@@ -51,6 +52,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/util/db"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/glob"
|
||||
logutils "github.com/argoproj/argo-cd/util/log"
|
||||
settings_util "github.com/argoproj/argo-cd/util/settings"
|
||||
)
|
||||
|
||||
@@ -151,10 +153,7 @@ func NewApplicationController(
|
||||
ctrl.kubectlSemaphore = semaphore.NewWeighted(kubectlParallelismLimit)
|
||||
}
|
||||
kubectl.SetOnKubectlRun(ctrl.onKubectlRun)
|
||||
appInformer, appLister, err := ctrl.newApplicationInformerAndLister()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appInformer, appLister := ctrl.newApplicationInformerAndLister()
|
||||
indexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}
|
||||
projInformer := v1alpha1.NewAppProjectInformer(applicationClientset, namespace, appResyncPeriod, indexers)
|
||||
projInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
@@ -175,7 +174,8 @@ func NewApplicationController(
|
||||
},
|
||||
})
|
||||
metricsAddr := fmt.Sprintf("0.0.0.0:%d", metricsPort)
|
||||
ctrl.metricsServer, err = metrics.NewMetricsServer(metricsAddr, appLister, ctrl.canProcessApp, func() error {
|
||||
var err error
|
||||
ctrl.metricsServer, err = metrics.NewMetricsServer(metricsAddr, appLister, ctrl.canProcessApp, func(r *http.Request) error {
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
@@ -854,6 +854,11 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
|
||||
ctrl.requestAppRefresh(app.Name, CompareWithLatest.Pointer(), &retryAfter)
|
||||
return
|
||||
} else {
|
||||
// retrying operation. remove previous failure time in app since it is used as a trigger
|
||||
// that previous failed and operation should be retried
|
||||
state.FinishedAt = nil
|
||||
ctrl.setOperationState(app, state)
|
||||
// Get rid of sync results and null out previous operation completion time
|
||||
state.SyncResult = nil
|
||||
}
|
||||
} else {
|
||||
@@ -944,6 +949,13 @@ func (ctrl *ApplicationController) setOperationState(app *appv1.Application, sta
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if app.Status.OperationState != nil && app.Status.OperationState.FinishedAt != nil && state.FinishedAt == nil {
|
||||
patchJSON, err = jsonpatch.MergeMergePatches(patchJSON, []byte(`{"status": {"operationState": {"finishedAt": null}}}`))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
appClient := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace)
|
||||
_, err = appClient.Patch(context.Background(), app.Name, types.MergePatchType, patchJSON, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
@@ -1392,11 +1404,6 @@ func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool {
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db)
|
||||
if err != nil {
|
||||
ctrl.setAppCondition(app, appv1.ApplicationCondition{Type: appv1.ApplicationConditionInvalidSpecError, Message: err.Error()})
|
||||
return false
|
||||
}
|
||||
if ctrl.clusterFilter != nil {
|
||||
cluster, err := ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
@@ -1408,15 +1415,47 @@ func (ctrl *ApplicationController) canProcessApp(obj interface{}) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.SharedIndexInformer, applisters.ApplicationLister, error) {
|
||||
appInformerFactory := appinformers.NewFilteredSharedInformerFactory(
|
||||
ctrl.applicationClientset,
|
||||
func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.SharedIndexInformer, applisters.ApplicationLister) {
|
||||
informer := cache.NewSharedIndexInformer(
|
||||
&cache.ListWatch{
|
||||
ListFunc: func(options metav1.ListOptions) (apiruntime.Object, error) {
|
||||
return ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace).List(context.TODO(), options)
|
||||
},
|
||||
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
|
||||
return ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace).Watch(context.TODO(), options)
|
||||
},
|
||||
},
|
||||
&appv1.Application{},
|
||||
ctrl.statusRefreshTimeout,
|
||||
ctrl.namespace,
|
||||
func(options *metav1.ListOptions) {},
|
||||
cache.Indexers{
|
||||
cache.NamespaceIndex: func(obj interface{}) ([]string, error) {
|
||||
app, ok := obj.(*appv1.Application)
|
||||
if ok {
|
||||
if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil {
|
||||
ctrl.setAppCondition(app, appv1.ApplicationCondition{Type: appv1.ApplicationConditionInvalidSpecError, Message: err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
return cache.MetaNamespaceIndexFunc(obj)
|
||||
},
|
||||
orphanedIndex: func(obj interface{}) (i []string, e error) {
|
||||
app, ok := obj.(*appv1.Application)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
proj, err := ctrl.getAppProj(app)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
if proj.Spec.OrphanedResources != nil {
|
||||
return []string{app.Spec.Destination.Namespace}, nil
|
||||
}
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
)
|
||||
informer := appInformerFactory.Argoproj().V1alpha1().Applications().Informer()
|
||||
lister := appInformerFactory.Argoproj().V1alpha1().Applications().Lister()
|
||||
lister := applisters.NewApplicationLister(informer.GetIndexer())
|
||||
informer.AddEventHandler(
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
@@ -1461,24 +1500,7 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
|
||||
},
|
||||
},
|
||||
)
|
||||
err := informer.AddIndexers(cache.Indexers{
|
||||
orphanedIndex: func(obj interface{}) (i []string, e error) {
|
||||
app, ok := obj.(*appv1.Application)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
proj, err := ctrl.getAppProj(app)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
if proj.Spec.OrphanedResources != nil {
|
||||
return []string{app.Spec.Destination.Namespace}, nil
|
||||
}
|
||||
return nil, nil
|
||||
},
|
||||
})
|
||||
return informer, lister, err
|
||||
return informer, lister
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) RegisterClusterSecretUpdater(ctx context.Context) {
|
||||
|
||||
@@ -1006,26 +1006,6 @@ func TestUpdateReconciledAt(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestCanProcessApp_DestNameIsValid(t *testing.T) {
|
||||
app := newFakeAppWithDestName()
|
||||
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}})
|
||||
|
||||
ok := ctrl.canProcessApp(app)
|
||||
assert.True(t, ok)
|
||||
assert.Len(t, app.Status.Conditions, 0)
|
||||
}
|
||||
|
||||
func TestCanProcessApp_BothDestNameAndServer(t *testing.T) {
|
||||
app := newFakeAppWithDestMismatch()
|
||||
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}})
|
||||
|
||||
ok := ctrl.canProcessApp(app)
|
||||
assert.False(t, ok)
|
||||
assert.Len(t, app.Status.Conditions, 1)
|
||||
assert.Equal(t, argoappv1.ApplicationConditionInvalidSpecError, app.Status.Conditions[0].Type)
|
||||
assert.Equal(t, "application destination can't have both name and server defined: another-cluster https://localhost:6443", app.Status.Conditions[0].Message)
|
||||
}
|
||||
|
||||
func TestFinalizeProjectDeletion_HasApplications(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
proj := &argoappv1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: test.FakeArgoCDNamespace}}
|
||||
|
||||
2
controller/cache/cache.go
vendored
2
controller/cache/cache.go
vendored
@@ -294,7 +294,7 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
|
||||
c.metricsServer.IncClusterEventsCount(cluster.Server, gvk.Group, gvk.Kind)
|
||||
})
|
||||
|
||||
c.clusters[cluster.Server] = clusterCache
|
||||
c.clusters[server] = clusterCache
|
||||
|
||||
return clusterCache, nil
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ var (
|
||||
)
|
||||
|
||||
// NewMetricsServer returns a new prometheus server which collects application metrics
|
||||
func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFilter func(obj interface{}) bool, healthCheck func() error) (*MetricsServer, error) {
|
||||
func NewMetricsServer(addr string, appLister applister.ApplicationLister, appFilter func(obj interface{}) bool, healthCheck func(r *http.Request) error) (*MetricsServer, error) {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -112,7 +112,7 @@ status:
|
||||
status: Healthy
|
||||
`
|
||||
|
||||
var noOpHealthCheck = func() error {
|
||||
var noOpHealthCheck = func(r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
172
docs/assets/versions.css
Normal file
172
docs/assets/versions.css
Normal file
@@ -0,0 +1,172 @@
|
||||
.md-header__title {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dropdown-caret {
|
||||
display: inline-block !important;
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
}
|
||||
|
||||
.fa .fa-caret-down {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.rst-other-versions {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.rst-other-versions > dl, .rst-other-versions dt, .rst-other-versions small {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rst-other-versions > dl:first-child {
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
line-height: 0px !important;
|
||||
}
|
||||
|
||||
.rst-versions.shift-up .rst-other-versions {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.rst-versions .rst-other-versions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Version Warning */
|
||||
div[data-md-component=announce] {
|
||||
background-color: rgba(255,145,0,.1);
|
||||
}
|
||||
div[data-md-component=announce]>div#announce-msg{
|
||||
color: var(--md-admonition-fg-color);
|
||||
font-size: .8rem;
|
||||
text-align: center;
|
||||
margin: 15px;
|
||||
}
|
||||
div[data-md-component=announce]>div#announce-msg>a{
|
||||
color: var(--md-typeset-a-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* from https://assets.readthedocs.org/static/css/badge_only.css,
|
||||
most styles have to be overriden here */
|
||||
.rst-versions{
|
||||
position: relative !important;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100px !important;
|
||||
background: hsla(173, 100%, 24%, 1) !important;
|
||||
font-family: inherit !important;
|
||||
z-index: 0 !important;
|
||||
}
|
||||
.rst-versions a{
|
||||
color:#2980B9;
|
||||
text-decoration:none
|
||||
}
|
||||
.rst-versions .rst-badge-small{
|
||||
display:none
|
||||
}
|
||||
.rst-versions .rst-current-version{
|
||||
padding:12px;
|
||||
background: hsla(173, 100%, 24%, 1) !important;
|
||||
display:block;
|
||||
text-align:right;
|
||||
font-size:90%;
|
||||
cursor:pointer;
|
||||
color: white !important;
|
||||
*zoom:1
|
||||
}
|
||||
.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{
|
||||
display:table;content:""
|
||||
}
|
||||
.rst-versions .rst-current-version:after{
|
||||
clear:both
|
||||
}
|
||||
.rst-versions .rst-current-version .fa{
|
||||
color:#fcfcfc
|
||||
}
|
||||
.rst-versions .rst-current-version .fa-caret-down{
|
||||
display: none;
|
||||
}
|
||||
.rst-versions.shift-up .rst-other-versions{
|
||||
display:block
|
||||
}
|
||||
.rst-versions .rst-other-versions{
|
||||
font-size:90%;
|
||||
padding:12px;
|
||||
color:gray;
|
||||
display:none
|
||||
}
|
||||
.rst-versions .rst-other-versions hr{
|
||||
display: none !important;
|
||||
height: 0px !important;
|
||||
border: 0px;
|
||||
margin: 0px !important;
|
||||
padding: 0px;
|
||||
border-top: none !important;
|
||||
}
|
||||
.rst-versions .rst-other-versions dd{
|
||||
display:inline-block;
|
||||
margin:0
|
||||
}
|
||||
.rst-versions .rst-other-versions dd a{
|
||||
display:inline-block;
|
||||
padding: 1em 0em !important;
|
||||
color:#fcfcfc;
|
||||
font-size: .6rem !important;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
width: 80px;
|
||||
}
|
||||
.rst-versions .rst-other-versions dd a:hover{
|
||||
font-size: .7rem !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
.rst-versions.rst-badge{
|
||||
display: block !important;
|
||||
width: 100px !important;
|
||||
bottom: 0px !important;
|
||||
right: 0px !important;
|
||||
left:auto;
|
||||
border:none;
|
||||
text-align: center !important;
|
||||
line-height: 0;
|
||||
}
|
||||
.rst-versions.rst-badge .icon-book{
|
||||
display: none;
|
||||
}
|
||||
.rst-versions.rst-badge .fa-book{
|
||||
display: none !important;
|
||||
}
|
||||
.rst-versions.rst-badge.shift-up .rst-current-version{
|
||||
text-align: left !important;
|
||||
}
|
||||
.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{
|
||||
display: none !important;
|
||||
}
|
||||
.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{
|
||||
display: none !important;
|
||||
}
|
||||
.rst-versions.rst-badge .rst-current-version{
|
||||
width: 70px !important;
|
||||
height: 2.4rem !important;
|
||||
line-height:2.4rem !important;
|
||||
padding: 0px 5px !important;
|
||||
display: inline-block !important;
|
||||
font-size: .6rem !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
white-space: nowrap !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
@media screen and (max-width: 768px){
|
||||
.rst-versions{
|
||||
width:85%;
|
||||
display:none
|
||||
}
|
||||
.rst-versions.shift{
|
||||
display:block
|
||||
}
|
||||
}
|
||||
43
docs/assets/versions.js
Normal file
43
docs/assets/versions.js
Normal file
@@ -0,0 +1,43 @@
|
||||
setTimeout(function() {
|
||||
const callbackName = 'callback_' + new Date().getTime();
|
||||
window[callbackName] = function (response) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = response.html;
|
||||
document.querySelector(".md-header__inner > .md-header__title").appendChild(div);
|
||||
const container = div.querySelector('.rst-versions');
|
||||
var caret = document.createElement('div');
|
||||
caret.innerHTML = "<i class='fa fa-caret-down dropdown-caret'></i>"
|
||||
caret.classList.add('dropdown-caret')
|
||||
div.querySelector('.rst-current-version').appendChild(caret);
|
||||
div.querySelector('.rst-current-version').addEventListener('click', function() {
|
||||
const classes = container.className.split(' ');
|
||||
const index = classes.indexOf('shift-up');
|
||||
if (index === -1) {
|
||||
classes.push('shift-up');
|
||||
} else {
|
||||
classes.splice(index, 1);
|
||||
}
|
||||
container.className = classes.join(' ');
|
||||
});
|
||||
}
|
||||
|
||||
var CSSLink = document.createElement('link');
|
||||
CSSLink.rel='stylesheet';
|
||||
CSSLink.href = '/assets/versions.css';
|
||||
document.getElementsByTagName('head')[0].appendChild(CSSLink);
|
||||
|
||||
var script = document.createElement('script');
|
||||
script.src = 'https://argo-cd.readthedocs.io/_/api/v2/footer_html/?'+
|
||||
'callback=' + callbackName + '&project=argo-cd&page=&theme=mkdocs&format=jsonp&docroot=docs&source_suffix=.md&version=' + (window['READTHEDOCS_DATA'] || { version: 'latest' }).version;
|
||||
document.getElementsByTagName('head')[0].appendChild(script);
|
||||
}, 0);
|
||||
|
||||
// VERSION WARNINGS
|
||||
window.addEventListener("DOMContentLoaded", function() {
|
||||
if ((window['READTHEDOCS_DATA']).version === "latest") {
|
||||
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for an unreleased version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>"
|
||||
}
|
||||
else if ((window['READTHEDOCS_DATA']).version !== "stable") {
|
||||
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for a previous version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>"
|
||||
}
|
||||
});
|
||||
@@ -36,6 +36,8 @@ and might fail. To avoid failed syncs use `ARGOCD_GIT_ATTEMPTS_COUNT` environmen
|
||||
|
||||
* `argocd_git_request_total` - Number of git requests. The metric provides two tags: `repo` - Git repo URL; `request_type` - `ls-remote` or `fetch`.
|
||||
|
||||
* `ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM` (v1.8+) - environment variable that enables collecting RPC performance metrics. Enable it if you need to troubleshoot performance issue. Note: metric is expensive to both query and store!
|
||||
|
||||
### argocd-application-controller
|
||||
|
||||
**settings:**
|
||||
@@ -82,6 +84,8 @@ spec:
|
||||
value: "2"
|
||||
```
|
||||
|
||||
* `ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM` (v1.8+)- environment variable that enables collecting RPC performance metrics. Enable it if you need to troubleshoot performance issue. Note: metric is expensive to both query and store!
|
||||
|
||||
**metrics**
|
||||
|
||||
* `argocd_app_reconcile` - reports application reconciliation duration. Can be used to build reconciliation duration heat map to get high-level reconciliation performance picture.
|
||||
@@ -92,6 +96,11 @@ non-preferred version and causes performance issues.
|
||||
|
||||
The `argocd-server` is stateless and probably least likely to cause issues. You might consider increasing number of replicas to 3 or more to ensure there is no downtime during upgrades.
|
||||
|
||||
**settings:**
|
||||
|
||||
* The `ARGOCD_GRPC_MAX_SIZE_MB` environment variable allows specifying the max size of the server response message in megabytes.
|
||||
The default value is 200. You might need to increase for an Argo CD instance that manages 3000+ applications.
|
||||
|
||||
### argocd-dex-server, argocd-redis
|
||||
|
||||
The `argocd-dex-server` uses an in-memory database, and two or more instances would have inconsistent data. `argocd-redis` is pre-configured with the understanding of only three total redis servers/sentinels.
|
||||
|
||||
@@ -28,7 +28,7 @@ Breaking down the permissions definition differs slightly between applications a
|
||||
|
||||
### RBAC Resources and Actions
|
||||
|
||||
Resources: `clusters`, `projects`, `applications`, `repositories`, `certificates`
|
||||
Resources: `clusters`, `projects`, `applications`, `repositories`, `certificates`, `accounts`, `gpgkeys`
|
||||
|
||||
Actions: `get`, `create`, `update`, `delete`, `sync`, `override`, `action`
|
||||
|
||||
|
||||
47
docs/operator-manual/upgrading/1.7-1.8.md
Normal file
47
docs/operator-manual/upgrading/1.7-1.8.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# v1.7 to 1.8
|
||||
|
||||
## The argocd-application-controller converted to StatefulSet
|
||||
|
||||
The `argocd-application-controller` has been converted to StatefulSet. That means you need to manually delete `argocd-application-controller` Deployment after upgrading.
|
||||
Similarly if you decided to rollback to v1.7 don't forget to delete `argocd-application-controller` StatefulSet.
|
||||
|
||||
|
||||
## Health assessement of argoproj.io/Application CRD has been removed
|
||||
|
||||
The health assessement of `argoproj.io/Application` CRD has been removed (see [#3781](https://github.com/argoproj/argo-cd/issues/3781) for more information).
|
||||
You might need to restore it if you are using app-of-apps pattern and orchestrating syncronization using sync waves. Add the following resource customization in
|
||||
`argocd-cm` ConfigMap:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-cm
|
||||
namespace: argocd
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-cm
|
||||
app.kubernetes.io/part-of: argocd
|
||||
data:
|
||||
resource.customizations: |
|
||||
argoproj.io/Application:
|
||||
health.lua: |
|
||||
hs = {}
|
||||
hs.status = "Healthy"
|
||||
hs.message = ""
|
||||
if obj.status ~= nil then
|
||||
if obj.status.health ~= nil then
|
||||
hs.status = obj.status.health.status
|
||||
hs.message = obj.status.health.message
|
||||
end
|
||||
end
|
||||
return hs
|
||||
```
|
||||
|
||||
## gRPC metrics are disabled by default
|
||||
|
||||
The gRPC metrics are not exposed by default by `argocd-server` and `argocd-repo-server` anymore. These metrics appear
|
||||
to be too expensive so we've decided to disable them by default. Metrics can be enabled using
|
||||
`ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM=true` environment variable.
|
||||
|
||||
From here on you can follow the [regular upgrade process](./overview.md).
|
||||
@@ -37,6 +37,7 @@ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/<v
|
||||
|
||||
<hr/>
|
||||
|
||||
* [v1.7 to v1.8](./1.7-1.8.md)
|
||||
* [v1.6 to v1.7](./1.6-1.7.md)
|
||||
* [v1.5 to v1.6](./1.5-1.6.md)
|
||||
* [v1.4 to v1.5](./1.4-1.5.md)
|
||||
|
||||
@@ -263,3 +263,29 @@ For a simple case this can be:
|
||||
oidc.config: |
|
||||
requestedIDTokenClaims: {"groups": {"essential": true}}
|
||||
```
|
||||
### Configuring a custom logout URL for your OIDC provider
|
||||
|
||||
Optionally, if your OIDC provider exposes a logout API and you wish to configure a custom logout URL for the purposes of invalidating
|
||||
any active session post logout, you can do so by specifying it as follows:
|
||||
|
||||
```yaml
|
||||
oidc.config: |
|
||||
name: example-OIDC-provider
|
||||
issuer: https://example-OIDC-provider.com
|
||||
clientID: xxxxxxxxx
|
||||
clientSecret: xxxxxxxxx
|
||||
requestedScopes: ["openid", "profile", "email", "groups"]
|
||||
requestedIDTokenClaims: {"groups": {"essential": true}}
|
||||
logoutURL: https://example-OIDC-provider.com/logout?id_token_hint={{token}}
|
||||
```
|
||||
By default, this would take the user to their OIDC provider's login page after logout. If you also wish to redirect the user back to Argo CD after logout, you can specify the logout URL as follows:
|
||||
|
||||
```yaml
|
||||
...
|
||||
logoutURL: https://example-OIDC-provider.com/logout?id_token_hint={{token}}&post_logout_redirect_uri={{logoutRedirectURL}}
|
||||
```
|
||||
|
||||
You are not required to specify a logoutRedirectURL as this is automatically generated by ArgoCD as your base ArgoCD url + Rootpath
|
||||
|
||||
!!! note
|
||||
The post logout redirect URI may need to be whitelisted against your OIDC provider's client settings for ArgoCD.
|
||||
3
docs/requirements.txt
Normal file
3
docs/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
mkdocs-material
|
||||
markdown_include
|
||||
pygments==2.4
|
||||
@@ -1,5 +1,11 @@
|
||||
# Security Considerations
|
||||
|
||||
!!!warning "Deprecation notice"
|
||||
This page is now deprecated and serves as an archive only. For up-to-date
|
||||
information, please have a look at our
|
||||
[security policy](https://github.com/argoproj/argo-cd/security/policy) and
|
||||
[published security advisories](https://github.com/argoproj/argo-cd/security/advisories).
|
||||
|
||||
As a deployment tool, Argo CD needs to have production access which makes security a very important topic.
|
||||
The Argoproj team takes security very seriously and continuously working on improving it. Learn more about security
|
||||
related features in [Security](./operator-manual/security.md) section.
|
||||
@@ -171,12 +177,6 @@ Upgrade to ArgoCD v1.5.0 or higher. No workaround available
|
||||
|
||||
## Reporting Vulnerabilities
|
||||
|
||||
If you find a security related bug in ArgoCD, we kindly ask you for responsible
|
||||
disclosure and for giving us appropriate time to react, analyze and develop a
|
||||
fix to mitigate the found security vulnerability.
|
||||
|
||||
Please report security vulnerabilities by e-mailing:
|
||||
|
||||
* [Jesse_Suen@intuit.com](mailto:Jesse_Suen@intuit.com)
|
||||
* [Alexander_Matyushentsev@intuit.com](mailto:Alexander_Matyushentsev@intuit.com)
|
||||
* [Edward_Lee@intuit.com](mailto:Edward_Lee@intuit.com)
|
||||
Please have a look at our
|
||||
[security policy](https://github.com/argoproj/argo-cd/security/policy)
|
||||
for more details on how to report security vulnerabilities for Argo CD.
|
||||
|
||||
24
go.mod
24
go.mod
@@ -7,13 +7,13 @@ require (
|
||||
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 // indirect
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible
|
||||
github.com/argoproj/gitops-engine v0.1.3-0.20201113084616-069a5e64fb79
|
||||
github.com/argoproj/gitops-engine v0.2.2
|
||||
github.com/argoproj/pkg v0.2.0
|
||||
github.com/bombsimon/logrusr v1.0.0
|
||||
github.com/casbin/casbin v1.9.1
|
||||
github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1
|
||||
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible
|
||||
@@ -23,19 +23,19 @@ require (
|
||||
github.com/go-openapi/loads v0.19.4
|
||||
github.com/go-openapi/runtime v0.19.4
|
||||
github.com/go-openapi/spec v0.19.3
|
||||
github.com/go-redis/cache/v8 v8.0.0-beta.11
|
||||
github.com/go-redis/redis/v8 v8.0.0-beta.9
|
||||
github.com/go-redis/cache/v8 v8.2.1
|
||||
github.com/go-redis/redis/v8 v8.3.2
|
||||
github.com/gobuffalo/packr v1.11.0
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogits/go-gogs-client v0.0.0-20190616193657-5a05380e4bc2
|
||||
github.com/gogo/protobuf v1.3.1
|
||||
github.com/golang/protobuf v1.4.2
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
||||
github.com/google/go-cmp v0.5.1
|
||||
github.com/google/go-jsonnet v0.16.0
|
||||
github.com/google/go-cmp v0.5.2
|
||||
github.com/google/go-jsonnet v0.17.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/handlers v1.5.0
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
||||
@@ -43,6 +43,7 @@ require (
|
||||
github.com/improbable-eng/grpc-web v0.0.0-20181111100011-16092bd1d58a
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8 // indirect
|
||||
github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf // indirect
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
@@ -57,11 +58,13 @@ require (
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/undefinedlabs/go-mpatch v1.0.6
|
||||
github.com/vmihailenco/msgpack/v5 v5.1.0 // indirect
|
||||
github.com/yuin/gopher-lua v0.0.0-20190115140932-732aa6820ec4
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381
|
||||
golang.org/x/exp v0.0.0-20200821190819-94841d0725da // indirect
|
||||
golang.org/x/net v0.0.0-20201024042810-be3efd7ff127
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013
|
||||
google.golang.org/grpc v1.29.1
|
||||
gopkg.in/go-playground/webhooks.v5 v5.11.0
|
||||
@@ -85,6 +88,7 @@ require (
|
||||
|
||||
replace (
|
||||
github.com/golang/protobuf => github.com/golang/protobuf v1.4.2
|
||||
github.com/gorilla/websocket => github.com/gorilla/websocket v1.4.2
|
||||
github.com/grpc-ecosystem/grpc-gateway => github.com/grpc-ecosystem/grpc-gateway v1.12.2
|
||||
github.com/improbable-eng/grpc-web => github.com/improbable-eng/grpc-web v0.0.0-20181111100011-16092bd1d58a
|
||||
|
||||
|
||||
107
go.sum
107
go.sum
@@ -42,7 +42,6 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
|
||||
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20200415212048-7901bc822317/go.mod h1:DF8FZRxMHMGv/vP2lQP6h+dYzzjpuRn24VeRiYn3qjQ=
|
||||
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
|
||||
@@ -64,8 +63,6 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d h1:WtAMR0fPCOfK7TPGZ8ZpLLY18HRvL7XJ3xcs0wnREgo=
|
||||
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d/go.mod h1:WML6KOYjeU8N6YyusMjj2qRvaPNUEvrQvaxuFcMRFJY=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
@@ -78,14 +75,12 @@ github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMw
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI=
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
|
||||
github.com/argoproj/gitops-engine v0.1.3-0.20201113084616-069a5e64fb79 h1:+VXKYb/FNTag/vnSoH9/Pey/kXK/jBKDyOtwRfnmMMw=
|
||||
github.com/argoproj/gitops-engine v0.1.3-0.20201113084616-069a5e64fb79/go.mod h1:OxXp8YaT73rw9gEBnGBWg55af80nkV/uIjWCbJu1Nw0=
|
||||
github.com/argoproj/gitops-engine v0.2.2 h1:islNwFTaHVVgx2pMvgXqINV93WPCVInWe64DlasFSx4=
|
||||
github.com/argoproj/gitops-engine v0.2.2/go.mod h1:OxXp8YaT73rw9gEBnGBWg55af80nkV/uIjWCbJu1Nw0=
|
||||
github.com/argoproj/pkg v0.2.0 h1:ETgC600kr8WcAi3MEVY5sA1H7H/u1/IysYOobwsZ8No=
|
||||
github.com/argoproj/pkg v0.2.0/go.mod h1:F4TZgInLUEjzsWFB/BTJBsewoEy0ucnKSq6vmQiD/yc=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
@@ -99,7 +94,6 @@ github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7/go.mod h1:
|
||||
github.com/aws/aws-sdk-go v1.6.10/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k=
|
||||
github.com/aws/aws-sdk-go v1.28.2/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.33.16/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -167,6 +161,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -174,6 +170,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
@@ -291,11 +289,10 @@ github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2K
|
||||
github.com/go-openapi/validate v0.19.5 h1:QhCBKRYqZR+SKo4gl1lPhPahope8/RLt6EVgY8X80w0=
|
||||
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
||||
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
|
||||
github.com/go-redis/cache/v8 v8.0.0-beta.11 h1:6QdpDYemSFQ2yrDkDkEofHpXid6Mm3UctqaVUzxGdn8=
|
||||
github.com/go-redis/cache/v8 v8.0.0-beta.11/go.mod h1:4wxD/neK+Uw+SteOR+AXtlyQYMBlI/D1u7UahfDCBAI=
|
||||
github.com/go-redis/redis/v8 v8.0.0-beta.2/go.mod h1:o1M7JtsgfDYyv3o+gBn/jJ1LkqpnCrmil7PSppZGBak=
|
||||
github.com/go-redis/redis/v8 v8.0.0-beta.9 h1:gcRNXZvs4PFi/mptho1D3hXant8JsbQKQxQ+0W3QGPw=
|
||||
github.com/go-redis/redis/v8 v8.0.0-beta.9/go.mod h1:CCWlPAL3uftOyyBOGpJ+A0ps2WpG3+hqo14fVs5aQ6A=
|
||||
github.com/go-redis/cache/v8 v8.2.1 h1:G4CtEQDT3JsiERPob1nUL/KTkiC317rAJvHx6GdWjiM=
|
||||
github.com/go-redis/cache/v8 v8.2.1/go.mod h1:8PFGBZrRqG2nToSHw76mSsozxgSKrn3vsZerq/NJtt8=
|
||||
github.com/go-redis/redis/v8 v8.3.2 h1:1bJscgN2yGtKLW6MsTRosa2LHyeq94j0hnNAgRZzj/M=
|
||||
github.com/go-redis/redis/v8 v8.3.2/go.mod h1:jszGxBCez8QA1HWSmQxJO9Y82kNibbUmeYhKWrBejTU=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
@@ -326,8 +323,6 @@ github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=
|
||||
github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8=
|
||||
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
|
||||
@@ -341,10 +336,11 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-jsonnet v0.16.0 h1:Nb4EEOp+rdeGGyB1rQ5eisgSAqrTnhf9ip+X6lzZbY0=
|
||||
github.com/google/go-jsonnet v0.16.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-jsonnet v0.17.0 h1:/9NIEfhK1NQRKl3sP2536b2+x5HnZMdql7x3yK/l8JY=
|
||||
github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@@ -365,12 +361,11 @@ github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3i
|
||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/handlers v1.5.0 h1:4wjo3sf9azi99c8hTmyaxp9y5S+pFszsy3pP0rAw/lw=
|
||||
github.com/gorilla/handlers v1.5.0/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
@@ -425,8 +420,8 @@ github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.10.5 h1:7q6vHIqubShURwQz8cQK6yIe/xC3IF0Vm7TGfqjewrc=
|
||||
github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.1 h1:bPb7nMRdOZYDrpPMTA3EInUQrdgoBinqUuSwlGdKDdE=
|
||||
github.com/klauspost/compress v1.11.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@@ -487,12 +482,12 @@ github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mmcloughlin/avo v0.0.0-20200504053806-fa88270b07e4 h1:HqABfvSTSz0ipb7ArOwybHX8/5lSzn0eU7BDYiBU/XY=
|
||||
github.com/mmcloughlin/avo v0.0.0-20200504053806-fa88270b07e4/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
|
||||
github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hxQ=
|
||||
github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o=
|
||||
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI=
|
||||
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
|
||||
github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf h1:Un6PNx5oMK6CCwO3QTUyPiK2mtZnPrpDl5UnZ64eCkw=
|
||||
github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -512,18 +507,26 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||
github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
|
||||
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
|
||||
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
@@ -535,7 +538,6 @@ github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
|
||||
github.com/opencontainers/selinux v1.5.2/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
|
||||
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
@@ -654,11 +656,13 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17
|
||||
github.com/vishvananda/netns v0.0.0-20200520041808-52d707b772fe/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94=
|
||||
github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ=
|
||||
github.com/vmihailenco/msgpack/v4 v4.3.11/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
||||
github.com/vmihailenco/msgpack/v5 v5.0.0-alpha.2 h1:0jVpYJSRJzGY7m21n9V5uIkl7Zre64W8DR1dxEKX2g4=
|
||||
github.com/vmihailenco/msgpack/v5 v5.0.0-alpha.2/go.mod h1:LDfrk4wJpSFwkzNOJxrCWiSm8c7Iqw/hXNPT2fzQfE8=
|
||||
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
|
||||
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
github.com/vmihailenco/go-tinylfu v0.1.0 h1:wNwKigNq50gfiyQDPpseEuGK4TZtFyjduJSg0M6gBns=
|
||||
github.com/vmihailenco/go-tinylfu v0.1.0/go.mod h1:qZbD6U3F10Sfuxyy4c5wMq5CM4/t5I3eJJS9yMQoXU0=
|
||||
github.com/vmihailenco/msgpack/v5 v5.0.0-beta.5/go.mod h1:MPECSZPg8yittBek5Gq2MhEDJpB9FrbSzQOSWmJm38A=
|
||||
github.com/vmihailenco/msgpack/v5 v5.1.0 h1:+od5YbEXxW95SPlW6beocmt8nOtlh83zqat5Ip9Hwdc=
|
||||
github.com/vmihailenco/msgpack/v5 v5.1.0/go.mod h1:C5gboKD0TJPqWDTVTtrQNfRbiBwHZGo8UTqP/9/XvLI=
|
||||
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
|
||||
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
@@ -679,14 +683,12 @@ go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/otel v0.5.0/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek=
|
||||
go.opentelemetry.io/otel v0.11.0 h1:IN2tzQa9Gc4ZVKnTaMbPVcHjvzOdg5n9QfnmlqiET7E=
|
||||
go.opentelemetry.io/otel v0.11.0/go.mod h1:G8UCk+KooF2HLkgo8RHX9epABH/aRGYET7gQOqBVdB0=
|
||||
go.opentelemetry.io/otel v0.13.0 h1:2isEnyzjjJZq6r2EKMsFj4TxiQiexsM04AVhwbR/oBA=
|
||||
go.opentelemetry.io/otel v0.13.0/go.mod h1:dlSNewoRYikTkotEnxdmuBHgzT+k/idJSfDv/FxEnOY=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -755,18 +757,20 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201024042810-be3efd7ff127 h1:pZPp9+iYUqwYKLjht0SDBbRCRK/9gAXDy7pz5fRDpjo=
|
||||
golang.org/x/net v0.0.0-20201024042810-be3efd7ff127/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -780,6 +784,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -803,10 +809,10 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -823,9 +829,13 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8=
|
||||
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
@@ -866,8 +876,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8=
|
||||
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -892,8 +900,8 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@@ -903,7 +911,6 @@ google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
@@ -914,6 +921,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -949,7 +958,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -957,6 +965,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# The checksum of this file is used as cache key in our integration toolchain
|
||||
#
|
||||
helm2_version=2.17.0
|
||||
helm3_version=3.4.0
|
||||
helm3_version=3.4.1
|
||||
jq_version=1.6
|
||||
ksonnet_version=0.13.1
|
||||
kubectl_version=1.17.8
|
||||
|
||||
@@ -42,3 +42,18 @@ spec:
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
serviceAccountName: argocd-application-controller
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
topologyKey: kubernetes.io/hostname
|
||||
- weight: 5
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
||||
@@ -26,7 +26,7 @@ spec:
|
||||
name: static-files
|
||||
containers:
|
||||
- name: dex
|
||||
image: quay.io/dexidp/dex:v2.25.0
|
||||
image: ghcr.io/dexidp/dex:v2.27.0
|
||||
imagePullPolicy: Always
|
||||
command: [/shared/argocd-util, rundex]
|
||||
ports:
|
||||
@@ -39,3 +39,12 @@ spec:
|
||||
volumes:
|
||||
- emptyDir: {}
|
||||
name: static-files
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 5
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: argoproj/argocd
|
||||
newName: argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v1.8.7
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -32,3 +32,18 @@ spec:
|
||||
- "no"
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
topologyKey: kubernetes.io/hostname
|
||||
- weight: 5
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
||||
@@ -28,9 +28,17 @@ spec:
|
||||
ports:
|
||||
- containerPort: 8081
|
||||
- containerPort: 8084
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz?full=true
|
||||
port: 8084
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 8081
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8084
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
volumeMounts:
|
||||
@@ -54,3 +62,18 @@ spec:
|
||||
name: argocd-gpg-keys-cm
|
||||
- name: gpg-keyring
|
||||
emptyDir: {}
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
topologyKey: kubernetes.io/hostname
|
||||
- weight: 5
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
||||
@@ -29,6 +29,12 @@ spec:
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
- containerPort: 8083
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz?full=true
|
||||
port: 8080
|
||||
initialDelaySeconds: 3
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
@@ -44,3 +50,18 @@ spec:
|
||||
- name: tls-certs
|
||||
configMap:
|
||||
name: argocd-tls-certs-cm
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-server
|
||||
topologyKey: kubernetes.io/hostname
|
||||
- weight: 5
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
|
||||
@@ -11,7 +11,7 @@ patchesStrategicMerge:
|
||||
images:
|
||||
- name: argoproj/argocd
|
||||
newName: argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v1.8.7
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/dex
|
||||
|
||||
@@ -2835,11 +2835,20 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-dex-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- /shared/argocd-util
|
||||
- rundex
|
||||
image: quay.io/dexidp/dex:v2.25.0
|
||||
image: ghcr.io/dexidp/dex:v2.27.0
|
||||
imagePullPolicy: Always
|
||||
name: dex
|
||||
ports:
|
||||
@@ -2855,7 +2864,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2993,17 +3002,25 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis-ha-haproxy:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /healthz?full=true
|
||||
port: 8084
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 5
|
||||
name: argocd-repo-server
|
||||
ports:
|
||||
- containerPort: 8081
|
||||
- containerPort: 8084
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8084
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
tcpSocket:
|
||||
port: 8081
|
||||
volumeMounts:
|
||||
- mountPath: /app/config/ssh
|
||||
name: ssh-known-hosts
|
||||
@@ -3067,8 +3084,14 @@ spec:
|
||||
env:
|
||||
- name: ARGOCD_API_SERVER_REPLICAS
|
||||
value: "2"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz?full=true
|
||||
port: 8080
|
||||
initialDelaySeconds: 3
|
||||
periodSeconds: 30
|
||||
name: argocd-server
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
@@ -3113,6 +3136,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- argocd-application-controller
|
||||
@@ -3122,7 +3160,7 @@ spec:
|
||||
- "10"
|
||||
- --redis
|
||||
- argocd-redis-ha-haproxy:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -2750,11 +2750,20 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-dex-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- /shared/argocd-util
|
||||
- rundex
|
||||
image: quay.io/dexidp/dex:v2.25.0
|
||||
image: ghcr.io/dexidp/dex:v2.27.0
|
||||
imagePullPolicy: Always
|
||||
name: dex
|
||||
ports:
|
||||
@@ -2770,7 +2779,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2908,17 +2917,25 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis-ha-haproxy:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /healthz?full=true
|
||||
port: 8084
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 5
|
||||
name: argocd-repo-server
|
||||
ports:
|
||||
- containerPort: 8081
|
||||
- containerPort: 8084
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8084
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
tcpSocket:
|
||||
port: 8081
|
||||
volumeMounts:
|
||||
- mountPath: /app/config/ssh
|
||||
name: ssh-known-hosts
|
||||
@@ -2982,8 +2999,14 @@ spec:
|
||||
env:
|
||||
- name: ARGOCD_API_SERVER_REPLICAS
|
||||
value: "2"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz?full=true
|
||||
port: 8080
|
||||
initialDelaySeconds: 3
|
||||
periodSeconds: 30
|
||||
name: argocd-server
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
@@ -3028,6 +3051,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- argocd-application-controller
|
||||
@@ -3037,7 +3075,7 @@ spec:
|
||||
- "10"
|
||||
- --redis
|
||||
- argocd-redis-ha-haproxy:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -2424,11 +2424,20 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-dex-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- /shared/argocd-util
|
||||
- rundex
|
||||
image: quay.io/dexidp/dex:v2.25.0
|
||||
image: ghcr.io/dexidp/dex:v2.27.0
|
||||
imagePullPolicy: Always
|
||||
name: dex
|
||||
ports:
|
||||
@@ -2444,7 +2453,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2472,6 +2481,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- args:
|
||||
- --save
|
||||
@@ -2507,6 +2531,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
automountServiceAccountToken: false
|
||||
containers:
|
||||
- command:
|
||||
@@ -2514,17 +2553,25 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /healthz?full=true
|
||||
port: 8084
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 5
|
||||
name: argocd-repo-server
|
||||
ports:
|
||||
- containerPort: 8081
|
||||
- containerPort: 8084
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8084
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
tcpSocket:
|
||||
port: 8081
|
||||
volumeMounts:
|
||||
- mountPath: /app/config/ssh
|
||||
name: ssh-known-hosts
|
||||
@@ -2564,13 +2611,34 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-server
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- argocd-server
|
||||
- --staticassets
|
||||
- /shared/app
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz?full=true
|
||||
port: 8080
|
||||
initialDelaySeconds: 3
|
||||
periodSeconds: 30
|
||||
name: argocd-server
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
@@ -2616,6 +2684,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- argocd-application-controller
|
||||
@@ -2623,7 +2706,7 @@ spec:
|
||||
- "20"
|
||||
- --operation-processors
|
||||
- "10"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -2339,11 +2339,20 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-dex-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- /shared/argocd-util
|
||||
- rundex
|
||||
image: quay.io/dexidp/dex:v2.25.0
|
||||
image: ghcr.io/dexidp/dex:v2.27.0
|
||||
imagePullPolicy: Always
|
||||
name: dex
|
||||
ports:
|
||||
@@ -2359,7 +2368,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2387,6 +2396,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-redis
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- args:
|
||||
- --save
|
||||
@@ -2422,6 +2446,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-repo-server
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
automountServiceAccountToken: false
|
||||
containers:
|
||||
- command:
|
||||
@@ -2429,17 +2468,25 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /healthz?full=true
|
||||
port: 8084
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 5
|
||||
name: argocd-repo-server
|
||||
ports:
|
||||
- containerPort: 8081
|
||||
- containerPort: 8084
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8084
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
tcpSocket:
|
||||
port: 8081
|
||||
volumeMounts:
|
||||
- mountPath: /app/config/ssh
|
||||
name: ssh-known-hosts
|
||||
@@ -2479,13 +2526,34 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-server
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-server
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- argocd-server
|
||||
- --staticassets
|
||||
- /shared/app
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz?full=true
|
||||
port: 8080
|
||||
initialDelaySeconds: 3
|
||||
periodSeconds: 30
|
||||
name: argocd-server
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
@@ -2531,6 +2599,21 @@ spec:
|
||||
labels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: argocd-application-controller
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/part-of: argocd
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 5
|
||||
containers:
|
||||
- command:
|
||||
- argocd-application-controller
|
||||
@@ -2538,7 +2621,7 @@ spec:
|
||||
- "20"
|
||||
- --operation-processors
|
||||
- "10"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.8.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -9,6 +9,12 @@ theme:
|
||||
text: 'Work Sans'
|
||||
logo: 'assets/logo.png'
|
||||
favicon: 'assets/favicon.png'
|
||||
# language: en-custom
|
||||
custom_dir: overrides
|
||||
extra_javascript:
|
||||
- assets/versions.js
|
||||
extra_css:
|
||||
- assets/versions.css
|
||||
google_analytics:
|
||||
- 'UA-105170809-2'
|
||||
- 'auto'
|
||||
|
||||
3
overrides/partials/language/en-custom.html
Normal file
3
overrides/partials/language/en-custom.html
Normal file
@@ -0,0 +1,3 @@
|
||||
{% macro t(key) %}{{ {
|
||||
"toc.title": "Table of Contents"
|
||||
}[key] }}{% endmacro %}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -16,7 +17,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
"google.golang.org/grpc"
|
||||
@@ -39,6 +40,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/env"
|
||||
grpc_util "github.com/argoproj/argo-cd/util/grpc"
|
||||
argoio "github.com/argoproj/argo-cd/util/io"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
@@ -53,8 +55,13 @@ const (
|
||||
EnvArgoCDServer = "ARGOCD_SERVER"
|
||||
// EnvArgoCDAuthToken is the environment variable to look for an Argo CD auth token
|
||||
EnvArgoCDAuthToken = "ARGOCD_AUTH_TOKEN"
|
||||
// EnvArgoCDgRPCMaxSizeMB is the environment variable to look for a max gRPC message size
|
||||
EnvArgoCDgRPCMaxSizeMB = "ARGOCD_GRPC_MAX_SIZE_MB"
|
||||
)
|
||||
|
||||
var (
|
||||
// MaxGRPCMessageSize contains max grpc message size
|
||||
MaxGRPCMessageSize = 100 * 1024 * 1024
|
||||
MaxGRPCMessageSize = env.ParseNumFromEnv(EnvArgoCDgRPCMaxSizeMB, 200, 0, math.MaxInt32) * 1024 * 1024
|
||||
)
|
||||
|
||||
// Client defines an interface for interaction with an Argo CD server.
|
||||
@@ -321,14 +328,14 @@ func (c *client) refreshAuthToken(localCfg *localconfig.LocalConfig, ctxName, co
|
||||
return err
|
||||
}
|
||||
parser := &jwt.Parser{
|
||||
SkipClaimsValidation: true,
|
||||
ValidationHelper: jwt.NewValidationHelper(jwt.WithoutClaimsValidation(), jwt.WithoutAudienceValidation()),
|
||||
}
|
||||
var claims jwt.StandardClaims
|
||||
_, _, err = parser.ParseUnverified(configCtx.User.AuthToken, &claims)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if claims.Valid() == nil {
|
||||
if claims.Valid(parser.ValidationHelper) == nil {
|
||||
// token is still valid
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -170,10 +170,7 @@ func (a *ApplicationSource) IsHelmOci() bool {
|
||||
if a.Chart == "" {
|
||||
return false
|
||||
}
|
||||
if _, _, ok := helm.IsHelmOci(a.Chart); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return helm.IsHelmOciChart(a.Chart)
|
||||
}
|
||||
|
||||
func (a *ApplicationSource) IsZero() bool {
|
||||
@@ -946,6 +943,17 @@ type ApplicationTree struct {
|
||||
OrphanedNodes []ResourceNode `json:"orphanedNodes,omitempty" protobuf:"bytes,2,rep,name=orphanedNodes"`
|
||||
}
|
||||
|
||||
// Normalize sorts application tree nodes and hosts. The persistent order allows to
|
||||
// effectively compare previously cached app tree and allows to unnecessary Redis requests.
|
||||
func (t *ApplicationTree) Normalize() {
|
||||
sort.Slice(t.Nodes, func(i, j int) bool {
|
||||
return t.Nodes[i].FullName() < t.Nodes[j].FullName()
|
||||
})
|
||||
sort.Slice(t.OrphanedNodes, func(i, j int) bool {
|
||||
return t.OrphanedNodes[i].FullName() < t.OrphanedNodes[j].FullName()
|
||||
})
|
||||
}
|
||||
|
||||
type ApplicationSummary struct {
|
||||
// ExternalURLs holds all external URLs of application child resources.
|
||||
ExternalURLs []string `json:"externalURLs,omitempty" protobuf:"bytes,1,opt,name=externalURLs"`
|
||||
@@ -1014,6 +1022,11 @@ type ResourceNode struct {
|
||||
CreatedAt *metav1.Time `json:"createdAt,omitempty" protobuf:"bytes,8,opt,name=createdAt"`
|
||||
}
|
||||
|
||||
// FullName returns node full name
|
||||
func (n *ResourceNode) FullName() string {
|
||||
return fmt.Sprintf("%s/%s/%s/%s", n.Group, n.Kind, n.Namespace, n.Name)
|
||||
}
|
||||
|
||||
func (n *ResourceNode) GroupKindVersion() schema.GroupVersionKind {
|
||||
return schema.GroupVersionKind{
|
||||
Group: n.Group,
|
||||
@@ -1059,6 +1072,11 @@ type ResourceDiff struct {
|
||||
PredictedLiveState string `json:"predictedLiveState,omitempty" protobuf:"bytes,10,opt,name=predictedLiveState"`
|
||||
}
|
||||
|
||||
// FullName returns full name of a node that was used for diffing
|
||||
func (r *ResourceDiff) FullName() string {
|
||||
return fmt.Sprintf("%s/%s/%s/%s", r.Group, r.Kind, r.Namespace, r.Name)
|
||||
}
|
||||
|
||||
// ConnectionStatus represents connection status
|
||||
type ConnectionStatus = string
|
||||
|
||||
@@ -2398,6 +2416,17 @@ func (source *ApplicationSource) ExplicitType() (*ApplicationSourceType, error)
|
||||
|
||||
// Equals compares two instances of ApplicationDestination and return true if instances are equal.
|
||||
func (dest ApplicationDestination) Equals(other ApplicationDestination) bool {
|
||||
// ignore destination cluster name and isServerInferred fields during comparison
|
||||
// since server URL is inferred from cluster name
|
||||
if dest.isServerInferred {
|
||||
dest.Server = ""
|
||||
dest.isServerInferred = false
|
||||
}
|
||||
|
||||
if other.isServerInferred {
|
||||
other.Server = ""
|
||||
other.isServerInferred = false
|
||||
}
|
||||
return reflect.DeepEqual(dest, other)
|
||||
}
|
||||
|
||||
@@ -2435,13 +2464,19 @@ func (proj AppProject) IsGroupKindPermitted(gk schema.GroupKind, namespaced bool
|
||||
res := metav1.GroupKind{Group: gk.Group, Kind: gk.Kind}
|
||||
|
||||
if namespaced {
|
||||
isWhiteListed = len(proj.Spec.NamespaceResourceWhitelist) == 0 || isResourceInList(res, proj.Spec.NamespaceResourceWhitelist)
|
||||
isBlackListed = isResourceInList(res, proj.Spec.NamespaceResourceBlacklist)
|
||||
namespaceWhitelist := proj.Spec.NamespaceResourceWhitelist
|
||||
namespaceBlacklist := proj.Spec.NamespaceResourceBlacklist
|
||||
|
||||
isWhiteListed = namespaceWhitelist == nil || len(namespaceWhitelist) != 0 && isResourceInList(res, namespaceWhitelist)
|
||||
isBlackListed = len(namespaceBlacklist) != 0 && isResourceInList(res, namespaceBlacklist)
|
||||
return isWhiteListed && !isBlackListed
|
||||
}
|
||||
|
||||
isWhiteListed = len(proj.Spec.ClusterResourceWhitelist) == 0 || isResourceInList(res, proj.Spec.ClusterResourceWhitelist)
|
||||
isBlackListed = isResourceInList(res, proj.Spec.ClusterResourceBlacklist)
|
||||
clusterWhitelist := proj.Spec.ClusterResourceWhitelist
|
||||
clusterBlacklist := proj.Spec.ClusterResourceBlacklist
|
||||
|
||||
isWhiteListed = len(clusterWhitelist) != 0 && isResourceInList(res, clusterWhitelist)
|
||||
isBlackListed = len(clusterBlacklist) != 0 && isResourceInList(res, clusterBlacklist)
|
||||
return isWhiteListed && !isBlackListed
|
||||
}
|
||||
|
||||
@@ -2699,8 +2734,11 @@ func (proj *AppProject) NormalizeJWTTokens() bool {
|
||||
}
|
||||
|
||||
func syncJWTTokenBetweenStatusAndSpec(proj *AppProject) bool {
|
||||
existingRole := map[string]bool{}
|
||||
needSync := false
|
||||
for roleIndex, role := range proj.Spec.Roles {
|
||||
existingRole[role.Name] = true
|
||||
|
||||
tokensInSpec := role.JWTTokens
|
||||
tokensInStatus := []JWTToken{}
|
||||
if proj.Status.JWTTokensByRole == nil {
|
||||
@@ -2723,8 +2761,16 @@ func syncJWTTokenBetweenStatusAndSpec(proj *AppProject) bool {
|
||||
|
||||
proj.Spec.Roles[roleIndex].JWTTokens = tokens
|
||||
proj.Status.JWTTokensByRole[role.Name] = JWTTokens{Items: tokens}
|
||||
|
||||
}
|
||||
if proj.Status.JWTTokensByRole != nil {
|
||||
for role := range proj.Status.JWTTokensByRole {
|
||||
if !existingRole[role] {
|
||||
delete(proj.Status.JWTTokensByRole, role)
|
||||
needSync = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return needSync
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ func TestAppProject_IsGroupKindPermitted(t *testing.T) {
|
||||
NamespaceResourceBlacklist: []metav1.GroupKind{{Group: "apps", Kind: "Deployment"}},
|
||||
},
|
||||
}
|
||||
assert.True(t, proj.IsGroupKindPermitted(schema.GroupKind{Group: "apps", Kind: "ReplicaSet"}, true))
|
||||
assert.False(t, proj.IsGroupKindPermitted(schema.GroupKind{Group: "apps", Kind: "ReplicaSet"}, true))
|
||||
assert.False(t, proj.IsGroupKindPermitted(schema.GroupKind{Group: "apps", Kind: "Deployment"}, true))
|
||||
|
||||
proj2 := AppProject{
|
||||
@@ -161,6 +161,21 @@ func TestAppProject_IsGroupKindPermitted(t *testing.T) {
|
||||
}
|
||||
assert.False(t, proj4.IsGroupKindPermitted(schema.GroupKind{Group: "", Kind: "Namespace"}, false))
|
||||
assert.True(t, proj4.IsGroupKindPermitted(schema.GroupKind{Group: "apps", Kind: "Action"}, true))
|
||||
|
||||
proj5 := AppProject{
|
||||
Spec: AppProjectSpec{
|
||||
ClusterResourceWhitelist: []metav1.GroupKind{},
|
||||
NamespaceResourceWhitelist: []metav1.GroupKind{{Group: "*", Kind: "*"}},
|
||||
},
|
||||
}
|
||||
assert.False(t, proj5.IsGroupKindPermitted(schema.GroupKind{Group: "", Kind: "Namespace"}, false))
|
||||
assert.True(t, proj5.IsGroupKindPermitted(schema.GroupKind{Group: "apps", Kind: "Action"}, true))
|
||||
|
||||
proj6 := AppProject{
|
||||
Spec: AppProjectSpec{},
|
||||
}
|
||||
assert.False(t, proj6.IsGroupKindPermitted(schema.GroupKind{Group: "", Kind: "Namespace"}, false))
|
||||
assert.True(t, proj6.IsGroupKindPermitted(schema.GroupKind{Group: "apps", Kind: "Action"}, true))
|
||||
}
|
||||
|
||||
func TestAppProject_GetRoleByName(t *testing.T) {
|
||||
@@ -429,6 +444,21 @@ func TestAppDestinationEquality(t *testing.T) {
|
||||
assert.False(t, left.Equals(*right))
|
||||
}
|
||||
|
||||
func TestAppDestinationEquality_InferredServerURL(t *testing.T) {
|
||||
left := ApplicationDestination{
|
||||
Name: "in-cluster",
|
||||
Namespace: "default",
|
||||
}
|
||||
right := ApplicationDestination{
|
||||
Name: "in-cluster",
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "default",
|
||||
isServerInferred: true,
|
||||
}
|
||||
assert.True(t, left.Equals(right))
|
||||
assert.True(t, right.Equals(left))
|
||||
}
|
||||
|
||||
func TestAppProjectSpec_DestinationClusters(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -30,13 +30,21 @@ type clientSet struct {
|
||||
}
|
||||
|
||||
func (c *clientSet) NewRepoServerClient() (io.Closer, RepoServerServiceClient, error) {
|
||||
conn, err := NewConnection(c.address, c.timeoutSeconds)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return conn, NewRepoServerServiceClient(conn), nil
|
||||
}
|
||||
|
||||
func NewConnection(address string, timeoutSeconds int) (*grpc.ClientConn, error) {
|
||||
retryOpts := []grpc_retry.CallOption{
|
||||
grpc_retry.WithMax(3),
|
||||
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(1000 * time.Millisecond)),
|
||||
}
|
||||
unaryInterceptors := []grpc.UnaryClientInterceptor{grpc_retry.UnaryClientInterceptor(retryOpts...)}
|
||||
if c.timeoutSeconds > 0 {
|
||||
unaryInterceptors = append(unaryInterceptors, argogrpc.WithTimeout(time.Duration(c.timeoutSeconds)*time.Second))
|
||||
if timeoutSeconds > 0 {
|
||||
unaryInterceptors = append(unaryInterceptors, argogrpc.WithTimeout(time.Duration(timeoutSeconds)*time.Second))
|
||||
}
|
||||
opts := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})),
|
||||
@@ -45,12 +53,12 @@ func (c *clientSet) NewRepoServerClient() (io.Closer, RepoServerServiceClient, e
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(MaxGRPCMessageSize), grpc.MaxCallSendMsgSize(MaxGRPCMessageSize)),
|
||||
}
|
||||
|
||||
conn, err := grpc.Dial(c.address, opts...)
|
||||
conn, err := grpc.Dial(address, opts...)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to connect to repository service with address %s", c.address)
|
||||
return nil, nil, err
|
||||
log.Errorf("Unable to connect to repository service with address %s", address)
|
||||
return nil, err
|
||||
}
|
||||
return conn, NewRepoServerServiceClient(conn), nil
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// NewRepoServerClientset creates new instance of repo server Clientset
|
||||
|
||||
4
reposerver/cache/cache.go
vendored
4
reposerver/cache/cache.go
vendored
@@ -127,11 +127,11 @@ func appDetailsCacheKey(revision string, appSrc *appv1.ApplicationSource) string
|
||||
return fmt.Sprintf("appdetails|%s|%d", revision, appSourceKey(appSrc))
|
||||
}
|
||||
|
||||
func (c *Cache) GetAppDetails(revision string, appSrc *appv1.ApplicationSource, res interface{}) error {
|
||||
func (c *Cache) GetAppDetails(revision string, appSrc *appv1.ApplicationSource, res *apiclient.RepoAppDetailsResponse) error {
|
||||
return c.cache.GetItem(appDetailsCacheKey(revision, appSrc), res)
|
||||
}
|
||||
|
||||
func (c *Cache) SetAppDetails(revision string, appSrc *appv1.ApplicationSource, res interface{}) error {
|
||||
func (c *Cache) SetAppDetails(revision string, appSrc *appv1.ApplicationSource, res *apiclient.RepoAppDetailsResponse) error {
|
||||
return c.cache.SetItem(appDetailsCacheKey(revision, appSrc), res, c.repoCacheExpiration, res == nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -164,6 +164,23 @@ type operationSettings struct {
|
||||
allowConcurrent bool
|
||||
}
|
||||
|
||||
// operationContext contains request values which are generated by runRepoOperation (on demand) by a call to the
|
||||
// provided operationContextSrc function.
|
||||
type operationContext struct {
|
||||
|
||||
// application path or helm chart path
|
||||
appPath string
|
||||
|
||||
// output of 'git verify-(tag/commit)', if signature verifiction is enabled (otherwise "")
|
||||
verificationResult string
|
||||
}
|
||||
|
||||
// The 'operation' function parameter of 'runRepoOperation' may call this function to retrieve
|
||||
// the appPath or GPG verificationResult.
|
||||
// Failure to generate either of these values will return an error which may be cached by
|
||||
// the calling function (for example, 'runManifestGen')
|
||||
type operationContextSrc = func() (*operationContext, error)
|
||||
|
||||
// runRepoOperation downloads either git folder or helm chart and executes specified operation
|
||||
// - Returns a value from the cache if present (by calling getCached(...)); if no value is present, the
|
||||
// provide operation(...) is called. The specific return type of this function is determined by the
|
||||
@@ -175,13 +192,12 @@ func (s *Service) runRepoOperation(
|
||||
source *v1alpha1.ApplicationSource,
|
||||
verifyCommit bool,
|
||||
getCached func(cacheKey string, firstInvocation bool) (bool, interface{}, error),
|
||||
operation func(appPath, repoRoot, commitSHA, cacheKey, verifyResult string) (interface{}, error),
|
||||
operation func(repoRoot, commitSHA, cacheKey string, ctxSrc operationContextSrc) (interface{}, error),
|
||||
settings operationSettings) (interface{}, error) {
|
||||
|
||||
var gitClient git.Client
|
||||
var helmClient helm.Client
|
||||
var err error
|
||||
var signature string
|
||||
revision = textutils.FirstNonEmpty(revision, source.TargetRevision)
|
||||
if source.IsHelm() {
|
||||
helmClient, revision, err = s.newHelmClientResolveRevision(repo, revision, source.Chart)
|
||||
@@ -229,7 +245,9 @@ func (s *Service) runRepoOperation(
|
||||
return nil, err
|
||||
}
|
||||
defer io.Close(closer)
|
||||
return operation(chartPath, chartPath, revision, revision, "")
|
||||
return operation(chartPath, revision, revision, func() (*operationContext, error) {
|
||||
return &operationContext{chartPath, ""}, nil
|
||||
})
|
||||
} else {
|
||||
closer, err := s.repoLock.Lock(gitClient.Root(), revision, settings.allowConcurrent, func() error {
|
||||
return checkoutRevision(gitClient, revision)
|
||||
@@ -253,19 +271,22 @@ func (s *Service) runRepoOperation(
|
||||
return obj, err
|
||||
}
|
||||
}
|
||||
if verifyCommit {
|
||||
signature, err = gitClient.VerifyCommitSignature(revision)
|
||||
// Here commitSHA refers to the SHA of the actual commit, whereas revision refers to the branch/tag name etc
|
||||
// We use the commitSHA to generate manifests and store them in cache, and revision to retrieve them from cache
|
||||
return operation(gitClient.Root(), commitSHA, revision, func() (*operationContext, error) {
|
||||
var signature string
|
||||
if verifyCommit {
|
||||
signature, err = gitClient.VerifyCommitSignature(revision)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
appPath, err := argopath.Path(gitClient.Root(), source.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
appPath, err := argopath.Path(gitClient.Root(), source.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Here commitSHA refers to the SHA of the actual commit, whereas revision refers to the branch/tag name etc
|
||||
// We use the commitSHA to generate manifests and store them in cache, and revision to retrieve them from cache
|
||||
return operation(appPath, gitClient.Root(), commitSHA, revision, signature)
|
||||
return &operationContext{appPath, signature}, nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,8 +294,8 @@ func (s *Service) GenerateManifest(ctx context.Context, q *apiclient.ManifestReq
|
||||
resultUncast, err := s.runRepoOperation(ctx, q.Revision, q.Repo, q.ApplicationSource, q.VerifySignature,
|
||||
func(cacheKey string, firstInvocation bool) (bool, interface{}, error) {
|
||||
return s.getManifestCacheEntry(cacheKey, q, firstInvocation)
|
||||
}, func(appPath, repoRoot, commitSHA, cacheKey, verifyResult string) (interface{}, error) {
|
||||
return s.runManifestGen(appPath, repoRoot, commitSHA, cacheKey, verifyResult, q)
|
||||
}, func(repoRoot, commitSHA, cacheKey string, ctxSrc operationContextSrc) (interface{}, error) {
|
||||
return s.runManifestGen(repoRoot, commitSHA, cacheKey, ctxSrc, q)
|
||||
}, operationSettings{sem: s.parallelismLimitSemaphore, noCache: q.NoCache, allowConcurrent: q.ApplicationSource.AllowsConcurrentProcessing()})
|
||||
result, ok := resultUncast.(*apiclient.ManifestResponse)
|
||||
if result != nil && !ok {
|
||||
@@ -289,8 +310,12 @@ func (s *Service) GenerateManifest(ctx context.Context, q *apiclient.ManifestReq
|
||||
// - or, the cache does contain a value for this key, but it is an expired manifest generation entry
|
||||
// - or, NoCache is true
|
||||
// Returns a ManifestResponse, or an error, but not both
|
||||
func (s *Service) runManifestGen(appPath, repoRoot, commitSHA, cacheKey, verifyResult string, q *apiclient.ManifestRequest) (interface{}, error) {
|
||||
manifestGenResult, err := GenerateManifests(appPath, repoRoot, commitSHA, q, false)
|
||||
func (s *Service) runManifestGen(repoRoot, commitSHA, cacheKey string, ctxSrc operationContextSrc, q *apiclient.ManifestRequest) (interface{}, error) {
|
||||
var manifestGenResult *apiclient.ManifestResponse
|
||||
ctx, err := ctxSrc()
|
||||
if err == nil {
|
||||
manifestGenResult, err = GenerateManifests(ctx.appPath, repoRoot, commitSHA, q, false)
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
// If manifest generation error caching is enabled
|
||||
@@ -332,7 +357,7 @@ func (s *Service) runManifestGen(appPath, repoRoot, commitSHA, cacheKey, verifyR
|
||||
MostRecentError: "",
|
||||
}
|
||||
manifestGenResult.Revision = commitSHA
|
||||
manifestGenResult.VerifyResult = verifyResult
|
||||
manifestGenResult.VerifyResult = ctx.verificationResult
|
||||
err = s.cache.SetManifests(cacheKey, q.ApplicationSource, q.Namespace, q.AppLabelKey, q.AppLabelValue, &manifestGenCacheEntry)
|
||||
if err != nil {
|
||||
log.Warnf("manifest cache set error %s/%s: %v", q.ApplicationSource.String(), cacheKey, err)
|
||||
@@ -742,7 +767,7 @@ func mergeSourceParameters(source *v1alpha1.ApplicationSource, path string) erro
|
||||
func GetAppSourceType(source *v1alpha1.ApplicationSource, path string) (v1alpha1.ApplicationSourceType, error) {
|
||||
err := mergeSourceParameters(source, path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error while parsing .argocd-app.yaml: %v", err)
|
||||
return "", fmt.Errorf("error while parsing .argocd-source.yaml: %v", err)
|
||||
}
|
||||
|
||||
appSourceType, err := source.ExplicitType()
|
||||
@@ -837,23 +862,12 @@ func findManifests(appPath string, repoRoot string, env *v1alpha1.Env, directory
|
||||
return nil
|
||||
}
|
||||
|
||||
out, err := utfutil.ReadFile(path, utfutil.UTF8)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.HasSuffix(f.Name(), ".json") {
|
||||
var obj unstructured.Unstructured
|
||||
err = json.Unmarshal(out, &obj)
|
||||
if err != nil {
|
||||
return status.Errorf(codes.FailedPrecondition, "Failed to unmarshal %q: %v", f.Name(), err)
|
||||
}
|
||||
objs = append(objs, &obj)
|
||||
} else if strings.HasSuffix(f.Name(), ".jsonnet") {
|
||||
if strings.HasSuffix(f.Name(), ".jsonnet") {
|
||||
vm, err := makeJsonnetVm(appPath, repoRoot, directory.Jsonnet, env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
jsonStr, err := vm.EvaluateSnippet(path, string(out))
|
||||
jsonStr, err := vm.EvaluateFile(path)
|
||||
if err != nil {
|
||||
return status.Errorf(codes.FailedPrecondition, "Failed to evaluate jsonnet %q: %v", f.Name(), err)
|
||||
}
|
||||
@@ -872,24 +886,37 @@ func findManifests(appPath string, repoRoot string, env *v1alpha1.Env, directory
|
||||
objs = append(objs, &jsonObj)
|
||||
}
|
||||
} else {
|
||||
yamlObjs, err := kube.SplitYAML(out)
|
||||
out, err := utfutil.ReadFile(path, utfutil.UTF8)
|
||||
if err != nil {
|
||||
if len(yamlObjs) > 0 {
|
||||
// If we get here, we had a multiple objects in a single YAML file which had some
|
||||
// valid k8s objects, but errors parsing others (within the same file). It's very
|
||||
// likely the user messed up a portion of the YAML, so report on that.
|
||||
return status.Errorf(codes.FailedPrecondition, "Failed to unmarshal %q: %v", f.Name(), err)
|
||||
}
|
||||
// Otherwise, let's see if it looks like a resource, if yes, we return error
|
||||
if bytes.Contains(out, []byte("apiVersion:")) &&
|
||||
bytes.Contains(out, []byte("kind:")) &&
|
||||
bytes.Contains(out, []byte("metadata:")) {
|
||||
return status.Errorf(codes.FailedPrecondition, "Failed to unmarshal %q: %v", f.Name(), err)
|
||||
}
|
||||
// Otherwise, it might be a unrelated YAML file which we will ignore
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
if strings.HasSuffix(f.Name(), ".json") {
|
||||
var obj unstructured.Unstructured
|
||||
err = json.Unmarshal(out, &obj)
|
||||
if err != nil {
|
||||
return status.Errorf(codes.FailedPrecondition, "Failed to unmarshal %q: %v", f.Name(), err)
|
||||
}
|
||||
objs = append(objs, &obj)
|
||||
} else {
|
||||
yamlObjs, err := kube.SplitYAML(out)
|
||||
if err != nil {
|
||||
if len(yamlObjs) > 0 {
|
||||
// If we get here, we had a multiple objects in a single YAML file which had some
|
||||
// valid k8s objects, but errors parsing others (within the same file). It's very
|
||||
// likely the user messed up a portion of the YAML, so report on that.
|
||||
return status.Errorf(codes.FailedPrecondition, "Failed to unmarshal %q: %v", f.Name(), err)
|
||||
}
|
||||
// Otherwise, let's see if it looks like a resource, if yes, we return error
|
||||
if bytes.Contains(out, []byte("apiVersion:")) &&
|
||||
bytes.Contains(out, []byte("kind:")) &&
|
||||
bytes.Contains(out, []byte("metadata:")) {
|
||||
return status.Errorf(codes.FailedPrecondition, "Failed to unmarshal %q: %v", f.Name(), err)
|
||||
}
|
||||
// Otherwise, it might be a unrelated YAML file which we will ignore
|
||||
return nil
|
||||
}
|
||||
objs = append(objs, yamlObjs...)
|
||||
}
|
||||
objs = append(objs, yamlObjs...)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -999,7 +1026,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD
|
||||
|
||||
getCached := func(revision string, _ bool) (bool, interface{}, error) {
|
||||
res := &apiclient.RepoAppDetailsResponse{}
|
||||
err := s.cache.GetAppDetails(revision, q.Source, &res)
|
||||
err := s.cache.GetAppDetails(revision, q.Source, res)
|
||||
if err == nil {
|
||||
log.Infof("app details cache hit: %s/%s", revision, q.Source.Path)
|
||||
return true, res, nil
|
||||
@@ -1014,7 +1041,12 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD
|
||||
|
||||
}
|
||||
|
||||
resultUncast, err := s.runRepoOperation(ctx, q.Source.TargetRevision, q.Repo, q.Source, false, getCached, func(appPath, repoRoot, commitSHA, revision, verifyResult string) (interface{}, error) {
|
||||
resultUncast, err := s.runRepoOperation(ctx, q.Source.TargetRevision, q.Repo, q.Source, false, getCached, func(repoRoot, commitSHA, revision string, ctxSrc operationContextSrc) (interface{}, error) {
|
||||
ctx, err := ctxSrc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appPath := ctx.appPath
|
||||
|
||||
res := &apiclient.RepoAppDetailsResponse{}
|
||||
appSourceType, err := GetAppSourceType(q.Source, appPath)
|
||||
@@ -1245,10 +1277,13 @@ func (s *Service) newClientResolveRevision(repo *v1alpha1.Repository, revision s
|
||||
}
|
||||
|
||||
func (s *Service) newHelmClientResolveRevision(repo *v1alpha1.Repository, revision string, chart string) (helm.Client, string, error) {
|
||||
helmClient := s.newHelmClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI)
|
||||
helmClient := s.newHelmClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI || helm.IsHelmOciChart(chart))
|
||||
if helm.IsVersion(revision) {
|
||||
return helmClient, revision, nil
|
||||
}
|
||||
if repo.EnableOCI {
|
||||
return nil, "", errors.New("OCI helm registers don't support semver ranges. Exact revision must be specified.")
|
||||
}
|
||||
constraints, err := semver.NewConstraint(revision)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("invalid revision '%s': %v", revision, err)
|
||||
|
||||
@@ -2,7 +2,19 @@ package reposerver
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"os"
|
||||
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
|
||||
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/health"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
"google.golang.org/grpc/reflection"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
versionpkg "github.com/argoproj/argo-cd/pkg/apiclient/version"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
reposervercache "github.com/argoproj/argo-cd/reposerver/cache"
|
||||
@@ -11,14 +23,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/server/version"
|
||||
grpc_util "github.com/argoproj/argo-cd/util/grpc"
|
||||
tlsutil "github.com/argoproj/argo-cd/util/tls"
|
||||
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
|
||||
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/reflection"
|
||||
)
|
||||
|
||||
// ArgoCDRepoServer is the repo server implementation
|
||||
@@ -49,7 +53,9 @@ func NewServer(metricsServer *metrics.MetricsServer, cache *reposervercache.Cach
|
||||
tlsConfig := &tls.Config{Certificates: []tls.Certificate{*cert}}
|
||||
tlsConfCustomizer(tlsConfig)
|
||||
|
||||
grpc_prometheus.EnableHandlingTimeHistogram()
|
||||
if os.Getenv(common.EnvEnableGRPCTimeHistogramEnv) == "true" {
|
||||
grpc_prometheus.EnableHandlingTimeHistogram()
|
||||
}
|
||||
|
||||
serverLog := log.NewEntry(log.StandardLogger())
|
||||
streamInterceptors := []grpc.StreamServerInterceptor{grpc_logrus.StreamServerInterceptor(serverLog), grpc_prometheus.StreamServerInterceptor, grpc_util.PanicLoggerStreamServerInterceptor(serverLog)}
|
||||
@@ -73,10 +79,15 @@ func NewServer(metricsServer *metrics.MetricsServer, cache *reposervercache.Cach
|
||||
// CreateGRPC creates new configured grpc server
|
||||
func (a *ArgoCDRepoServer) CreateGRPC() *grpc.Server {
|
||||
server := grpc.NewServer(a.opts...)
|
||||
versionpkg.RegisterVersionServiceServer(server, &version.Server{})
|
||||
versionpkg.RegisterVersionServiceServer(server, version.NewServer(nil, func() (bool, error) {
|
||||
return true, nil
|
||||
}))
|
||||
manifestService := repository.NewService(a.metricsServer, a.cache, a.initConstants)
|
||||
apiclient.RegisterRepoServerServiceServer(server, manifestService)
|
||||
|
||||
healthService := health.NewServer()
|
||||
grpc_health_v1.RegisterHealthServer(server, healthService)
|
||||
|
||||
// Register reflection service on gRPC server.
|
||||
reflection.Register(server)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ actions["retry"] = {["disabled"] = fullyPromoted or not(obj.status.abort)}
|
||||
actions["promote-full"] = {["disabled"] = true}
|
||||
if obj.status ~= nil and not(fullyPromoted) then
|
||||
generation = tonumber(obj.status.observedGeneration)
|
||||
if generation == nil then
|
||||
if generation == nil or generation > obj.metadata.generation then
|
||||
-- rollouts v0.9 - full promotion only supported for canary
|
||||
actions["promote-full"] = {["disabled"] = obj.spec.strategy.blueGreen ~= nil}
|
||||
else
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
if obj.status ~= nil then
|
||||
generation = tonumber(obj.status.observedGeneration)
|
||||
if generation == nil then
|
||||
if generation == nil or generation > obj.metadata.generation then
|
||||
-- rollouts v0.9 and below
|
||||
obj.status.abort = nil
|
||||
if obj.spec.strategy.canary.steps ~= nil then
|
||||
|
||||
@@ -51,15 +51,23 @@ function checkPaused(obj)
|
||||
return nil
|
||||
end
|
||||
|
||||
hs = {}
|
||||
if obj.status == nil or obj.status.observedGeneration == nil then
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Waiting for rollout spec update to be observed"
|
||||
return hs
|
||||
-- isGenerationObserved determines if the rollout spec has been observed by the controller. This
|
||||
-- only applies to v0.10 rollout which uses a numeric status.observedGeneration. For v0.9 rollouts
|
||||
-- and below this function always returns true.
|
||||
function isGenerationObserved(obj)
|
||||
if obj.status == nil then
|
||||
return false
|
||||
end
|
||||
observedGeneration = tonumber(obj.status.observedGeneration)
|
||||
if observedGeneration == nil or observedGeneration > obj.metadata.generation then
|
||||
-- if we get here, the rollout is a v0.9 rollout
|
||||
return true
|
||||
end
|
||||
return observedGeneration == obj.metadata.generation
|
||||
end
|
||||
|
||||
generation = tonumber(obj.status.observedGeneration)
|
||||
if generation ~= nil and generation ~= obj.metadata.generation then
|
||||
hs = {}
|
||||
if not isGenerationObserved(obj) then
|
||||
hs.status = "Progressing"
|
||||
hs.message = "Waiting for rollout spec update to be observed"
|
||||
return hs
|
||||
|
||||
@@ -11,6 +11,10 @@ tests:
|
||||
status: Healthy
|
||||
message: ""
|
||||
inputPath: testdata/healthy_legacy_v0.9_observedGeneration.yaml
|
||||
- healthStatus:
|
||||
status: Healthy
|
||||
message: ""
|
||||
inputPath: testdata/healthy_legacy_v0.9_observedGeneration_numeric.yaml
|
||||
- healthStatus:
|
||||
status: Degraded
|
||||
message: "The Rollout \"basic\" is invalid: spec.strategy.strategy: Required value: Rollout has missing field '.spec.strategy.canary or .spec.strategy.blueGreen'"
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
creationTimestamp: "2020-11-13T00:44:55Z"
|
||||
generation: 1
|
||||
name: basic
|
||||
namespace: argocd-e2e
|
||||
resourceVersion: "182108"
|
||||
selfLink: /apis/argoproj.io/v1alpha1/namespaces/argocd-e2e/rollouts/basic
|
||||
uid: 34e4bbfc-222c-4968-bd60-2b30ae81110d
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: basic
|
||||
strategy:
|
||||
canary:
|
||||
steps:
|
||||
- setWeight: 50
|
||||
- pause: {}
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: basic
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.19-alpine
|
||||
name: basic
|
||||
resources:
|
||||
requests:
|
||||
cpu: 1m
|
||||
memory: 16Mi
|
||||
status:
|
||||
HPAReplicas: 1
|
||||
availableReplicas: 1
|
||||
blueGreen: {}
|
||||
canary: {}
|
||||
conditions:
|
||||
- lastTransitionTime: "2020-11-13T00:48:20Z"
|
||||
lastUpdateTime: "2020-11-13T00:48:22Z"
|
||||
message: ReplicaSet "basic-754cb84d5" has successfully progressed.
|
||||
reason: NewReplicaSetAvailable
|
||||
status: "True"
|
||||
type: Progressing
|
||||
- lastTransitionTime: "2020-11-13T00:48:22Z"
|
||||
lastUpdateTime: "2020-11-13T00:48:22Z"
|
||||
message: Rollout has minimum availability
|
||||
reason: AvailableReason
|
||||
status: "True"
|
||||
type: Available
|
||||
currentPodHash: 754cb84d5
|
||||
currentStepHash: 757f5f97b
|
||||
currentStepIndex: 2
|
||||
observedGeneration: "8575574967" ## <---- uses legacy observedGeneration hash which are numbers
|
||||
readyReplicas: 1
|
||||
replicas: 1
|
||||
selector: app=basic
|
||||
stableRS: 754cb84d5
|
||||
updatedReplicas: 1
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient/account"
|
||||
sessionpkg "github.com/argoproj/argo-cd/pkg/apiclient/session"
|
||||
"github.com/argoproj/argo-cd/server/session"
|
||||
"github.com/argoproj/argo-cd/test"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/password"
|
||||
"github.com/argoproj/argo-cd/util/rbac"
|
||||
@@ -63,7 +64,7 @@ func newTestAccountServerExt(ctx context.Context, enforceFn rbac.ClaimsEnforcerF
|
||||
}
|
||||
kubeclientset := fake.NewSimpleClientset(cm, secret)
|
||||
settingsMgr := settings.NewSettingsManager(ctx, kubeclientset, testNamespace)
|
||||
sessionMgr := sessionutil.NewSessionManager(settingsMgr, "", sessionutil.NewInMemoryUserStateStorage())
|
||||
sessionMgr := sessionutil.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", sessionutil.NewInMemoryUserStateStorage())
|
||||
enforcer := rbac.NewEnforcer(kubeclientset, testNamespace, common.ArgoCDRBACConfigMapName, nil)
|
||||
enforcer.SetClaimsEnforcerFunc(enforceFn)
|
||||
|
||||
@@ -89,7 +90,7 @@ func ssoAdminContext(ctx context.Context, iat time.Time) context.Context {
|
||||
return context.WithValue(ctx, "claims", &jwt.StandardClaims{
|
||||
Subject: "admin",
|
||||
Issuer: "https://myargocdhost.com/api/dex",
|
||||
IssuedAt: iat.Unix(),
|
||||
IssuedAt: jwt.At(iat),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -576,11 +576,12 @@ func (s *Server) Patch(ctx context.Context, q *application.ApplicationPatchReque
|
||||
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Patch type '%s' is not supported", q.PatchType))
|
||||
}
|
||||
|
||||
err = json.Unmarshal(patchApp, &app)
|
||||
newApp := &v1alpha1.Application{}
|
||||
err = json.Unmarshal(patchApp, newApp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.validateAndUpdateApp(ctx, app, false, true)
|
||||
return s.validateAndUpdateApp(ctx, newApp, false, true)
|
||||
}
|
||||
|
||||
// Delete removes an application and all associated resources
|
||||
@@ -747,12 +748,12 @@ func (s *Server) validateAndNormalizeApp(ctx context.Context, app *appv1.Applica
|
||||
return err
|
||||
}
|
||||
|
||||
if err := argo.ValidateDestination(ctx, &app.Spec.Destination, s.db); err != nil {
|
||||
return status.Errorf(codes.InvalidArgument, "application destination spec is invalid: %s", err.Error())
|
||||
}
|
||||
|
||||
var conditions []appv1.ApplicationCondition
|
||||
if validate {
|
||||
if err := argo.ValidateDestination(ctx, &app.Spec.Destination, s.db); err != nil {
|
||||
return status.Errorf(codes.InvalidArgument, "application destination spec is invalid: %s", err.Error())
|
||||
}
|
||||
|
||||
conditions, err = argo.ValidateRepo(ctx, app, s.repoClientset, s.db, kustomizeOptions, plugins, s.kubectl)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -898,6 +899,10 @@ func (s *Server) PatchResource(ctx context.Context, q *application.ApplicationRe
|
||||
|
||||
manifest, err := s.kubectl.PatchResource(ctx, config, res.GroupKindVersion(), res.Name, res.Namespace, types.PatchType(q.PatchType), []byte(q.Patch))
|
||||
if err != nil {
|
||||
// don't expose real error for secrets since it might contain secret data
|
||||
if res.Kind == kube.SecretKind && res.Group == "" {
|
||||
return nil, fmt.Errorf("failed to patch Secret %s/%s", res.Namespace, res.Name)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
manifest, err = replaceSecretValues(manifest)
|
||||
@@ -1292,7 +1297,7 @@ func (s *Server) resolveRevision(ctx context.Context, app *appv1.Application, sy
|
||||
if helm.IsVersion(ambiguousRevision) {
|
||||
return ambiguousRevision, ambiguousRevision, nil
|
||||
}
|
||||
client := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI)
|
||||
client := helm.NewClient(repo.Repo, repo.GetHelmCreds(), repo.EnableOCI || app.Spec.Source.IsHelmOci())
|
||||
index, err := client.GetIndex()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest"
|
||||
"github.com/argoproj/pkg/sync"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
@@ -215,6 +215,26 @@ spec:
|
||||
name: fake-cluster
|
||||
`
|
||||
|
||||
const fakeAppWithAnnotations = `
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: test-app
|
||||
namespace: default
|
||||
annotations:
|
||||
test.annotation: test
|
||||
spec:
|
||||
source:
|
||||
path: some/path
|
||||
repoURL: https://github.com/argoproj/argocd-example-apps.git
|
||||
targetRevision: HEAD
|
||||
ksonnet:
|
||||
environment: default
|
||||
destination:
|
||||
namespace: ` + test.FakeDestNamespace + `
|
||||
server: https://cluster-api.com
|
||||
`
|
||||
|
||||
func newTestAppWithDestName(opts ...func(app *appsv1.Application)) *appsv1.Application {
|
||||
return createTestApp(fakeAppWithDestName, opts...)
|
||||
}
|
||||
@@ -223,6 +243,10 @@ func newTestApp(opts ...func(app *appsv1.Application)) *appsv1.Application {
|
||||
return createTestApp(fakeApp, opts...)
|
||||
}
|
||||
|
||||
func newTestAppWithAnnotations(opts ...func(app *appsv1.Application)) *appsv1.Application {
|
||||
return createTestApp(fakeAppWithAnnotations, opts...)
|
||||
}
|
||||
|
||||
func createTestApp(testApp string, opts ...func(app *appsv1.Application)) *appsv1.Application {
|
||||
var app appsv1.Application
|
||||
err := yaml.Unmarshal([]byte(testApp), &app)
|
||||
@@ -499,7 +523,7 @@ p, admin, applications, update, my-proj/test-app, allow
|
||||
}
|
||||
|
||||
func TestAppJsonPatch(t *testing.T) {
|
||||
testApp := newTestApp()
|
||||
testApp := newTestAppWithAnnotations()
|
||||
ctx := context.Background()
|
||||
// nolint:staticcheck
|
||||
ctx = context.WithValue(ctx, "claims", &jwt.StandardClaims{Subject: "admin"})
|
||||
@@ -517,6 +541,10 @@ func TestAppJsonPatch(t *testing.T) {
|
||||
app, err = appServer.Patch(ctx, &application.ApplicationPatchRequest{Name: &testApp.Name, Patch: `[{"op": "replace", "path": "/spec/source/path", "value": "foo"}]`})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "foo", app.Spec.Source.Path)
|
||||
|
||||
app, err = appServer.Patch(ctx, &application.ApplicationPatchRequest{Name: &testApp.Name, Patch: `[{"op": "remove", "path": "/metadata/annotations/test.annotation"}]`})
|
||||
assert.NoError(t, err)
|
||||
assert.NotContains(t, app.Annotations, "test.annotation")
|
||||
}
|
||||
|
||||
func TestAppMergePatch(t *testing.T) {
|
||||
|
||||
97
server/logout/logout.go
Normal file
97
server/logout/logout.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package logout
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/util/session"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
|
||||
jwtutil "github.com/argoproj/argo-cd/util/jwt"
|
||||
)
|
||||
|
||||
//NewHandler creates handler serving to do api/logout endpoint
|
||||
func NewHandler(appClientset versioned.Interface, settingsMrg *settings.SettingsManager, sessionMgr *session.SessionManager, rootPath, namespace string) *Handler {
|
||||
return &Handler{
|
||||
appClientset: appClientset,
|
||||
namespace: namespace,
|
||||
settingsMgr: settingsMrg,
|
||||
rootPath: rootPath,
|
||||
verifyToken: sessionMgr.VerifyToken,
|
||||
}
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
namespace string
|
||||
appClientset versioned.Interface
|
||||
settingsMgr *settings.SettingsManager
|
||||
rootPath string
|
||||
verifyToken func(tokenString string) (jwt.Claims, error)
|
||||
}
|
||||
|
||||
var (
|
||||
tokenPattern = regexp.MustCompile(`{{token}}`)
|
||||
logoutRedirectURLPattern = regexp.MustCompile(`{{logoutRedirectURL}}`)
|
||||
)
|
||||
|
||||
func constructLogoutURL(logoutURL, token, logoutRedirectURL string) string {
|
||||
constructedLogoutURL := tokenPattern.ReplaceAllString(logoutURL, token)
|
||||
return logoutRedirectURLPattern.ReplaceAllString(constructedLogoutURL, logoutRedirectURL)
|
||||
}
|
||||
|
||||
// ServeHTTP is the logout handler for ArgoCD and constructs OIDC logout URL and redirects to it for OIDC issued sessions,
|
||||
// and redirects user to '/login' for argocd issued sessions
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var tokenString string
|
||||
var oidcConfig *settings.OIDCConfig
|
||||
|
||||
argoCDSettings, err := h.settingsMgr.GetSettings()
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
http.Error(w, "Failed to retrieve argoCD settings: "+fmt.Sprintf("%s", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
logoutRedirectURL := strings.TrimRight(strings.TrimLeft(argoCDSettings.URL, "/"), "/") + strings.TrimRight(strings.TrimLeft(h.rootPath, "/"), "/")
|
||||
|
||||
argocdCookie, err := r.Cookie(common.AuthCookieName)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
http.Error(w, "Failed to retrieve ArgoCD auth token: "+fmt.Sprintf("%s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
tokenString = argocdCookie.Value
|
||||
|
||||
argocdCookie.Value = ""
|
||||
argocdCookie.Path = fmt.Sprintf("/%s", strings.TrimRight(strings.TrimLeft(h.rootPath, "/"), "/"))
|
||||
w.Header().Set("Set-Cookie", argocdCookie.String())
|
||||
|
||||
claims, err := h.verifyToken(tokenString)
|
||||
if err != nil {
|
||||
http.Redirect(w, r, logoutRedirectURL, http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
mapClaims, err := jwtutil.MapClaims(claims)
|
||||
if err != nil {
|
||||
http.Redirect(w, r, logoutRedirectURL, http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
issuer := jwtutil.StringField(mapClaims, "iss")
|
||||
|
||||
if argoCDSettings.OIDCConfig() == nil || argoCDSettings.OIDCConfig().LogoutURL == "" || issuer == session.SessionManagerClaimsIssuer {
|
||||
http.Redirect(w, r, logoutRedirectURL, http.StatusSeeOther)
|
||||
} else {
|
||||
oidcConfig = argoCDSettings.OIDCConfig()
|
||||
logoutURL := constructLogoutURL(oidcConfig.LogoutURL, tokenString, logoutRedirectURL)
|
||||
http.Redirect(w, r, logoutURL, http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
290
server/logout/logout_test.go
Normal file
290
server/logout/logout_test.go
Normal file
@@ -0,0 +1,290 @@
|
||||
package logout
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
|
||||
"github.com/argoproj/argo-cd/test"
|
||||
"github.com/argoproj/argo-cd/util/session"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
var (
|
||||
validJWTPattern = regexp.MustCompile(`[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+`)
|
||||
baseURL = "http://localhost:4000"
|
||||
baseLogoutURL = "http://localhost:4000/logout"
|
||||
baseLogoutURLwithToken = "http://localhost:4000/logout?id_token_hint={{token}}"
|
||||
baseLogoutURLwithRedirectURL = "http://localhost:4000/logout?post_logout_redirect_uri={{logoutRedirectURL}}"
|
||||
baseLogoutURLwithTokenAndRedirectURL = "http://localhost:4000/logout?id_token_hint={{token}}&post_logout_redirect_uri={{logoutRedirectURL}}"
|
||||
invalidToken = "sample-token"
|
||||
oidcToken = "eyJraWQiOiJYQi1MM3ZFdHhYWXJLcmRSQnVEV0NwdnZsSnk3SEJVb2d5N253M1U1Z1ZZIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiIwMHVqNnM1NDVyNU5peVNLcjVkNSIsIm5hbWUiOiJqZCByIiwiZW1haWwiOiJqYWlkZWVwMTdydWx6QGdtYWlsLmNvbSIsInZlciI6MSwiaXNzIjoiaHR0cHM6Ly9kZXYtNTY5NTA5OC5va3RhLmNvbSIsImF1ZCI6IjBvYWowM2FmSEtqN3laWXJwNWQ1IiwiaWF0IjoxNjA1NTcyMzU5LCJleHAiOjE2MDU1NzU5NTksImp0aSI6IklELl9ORDJxVG5iREFtc3hIZUt2U2ZHeVBqTXRicXFEQXdkdlRQTDZCTnpfR3ciLCJhbXIiOlsicHdkIl0sImlkcCI6IjAwb2lnaGZmdkpRTDYzWjhoNWQ1IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiamFpZGVlcDE3cnVsekBnbWFpbC5jb20iLCJhdXRoX3RpbWUiOjE2MDU1NzIzNTcsImF0X2hhc2giOiJqZVEwRml2ak9nNGI2TUpXRDIxOWxnIn0.GHkqwXgW-lrAhJdypW7SVjW0YdNLFQiRL8iwgT6DHJxP9Nb0OtkH2NKcBYAA5N6bTPLRQUHgYwWcgm5zSXmvqa7ciIgPF3tiQI8UmJA9VFRRDR-x9ExX15nskCbXfiQ67MriLslUrQUyzSCfUrSjXKwnDxbKGQncrtmRsh5asfCzJFb9excn311W9HKbT3KA0Ot7eOMnVS6V7SGfXxnKs6szcXIEMa_FhB4zDAVLr-dnxvSG_uuWcHrAkLTUVhHbdQQXF7hXIEfyr5lkMJN-drjdz-bn40GaYulEmUvO1bjcL9toCVQ3Ismypyr0b8phj4w3uRsLDZQxTxK7jAXlyQ"
|
||||
nonOidcToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2MDU1NzQyMTIsImlzcyI6ImFyZ29jZCIsIm5iZiI6MTYwNTU3NDIxMiwic3ViIjoiYWRtaW4ifQ.zDJ4piwWnwsHON-oPusHMXWINlnrRDTQykYogT7afeE"
|
||||
expectedNonOIDCLogoutURL = "http://localhost:4000"
|
||||
expectedOIDCLogoutURL = "https://dev-5695098.okta.com/oauth2/v1/logout?id_token_hint=" + oidcToken + "&post_logout_redirect_uri=" + baseURL
|
||||
)
|
||||
|
||||
func TestConstructLogoutURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
logoutURL string
|
||||
token string
|
||||
logoutRedirectURL string
|
||||
expectedLogoutURL string
|
||||
}{
|
||||
{
|
||||
name: "Case: No additional parameters passed to logout URL",
|
||||
logoutURL: baseLogoutURL,
|
||||
token: oidcToken,
|
||||
logoutRedirectURL: baseURL,
|
||||
expectedLogoutURL: baseLogoutURL,
|
||||
},
|
||||
{
|
||||
name: "Case: ID token passed to logout URL",
|
||||
logoutURL: baseLogoutURLwithToken,
|
||||
token: oidcToken,
|
||||
logoutRedirectURL: baseURL,
|
||||
expectedLogoutURL: "http://localhost:4000/logout?id_token_hint=" + oidcToken,
|
||||
},
|
||||
{
|
||||
name: "Case: Redirect required",
|
||||
logoutURL: baseLogoutURLwithRedirectURL,
|
||||
token: oidcToken,
|
||||
logoutRedirectURL: baseURL,
|
||||
expectedLogoutURL: "http://localhost:4000/logout?post_logout_redirect_uri=" + baseURL,
|
||||
},
|
||||
{
|
||||
name: "Case: ID token and redirect URL passed to logout URL",
|
||||
logoutURL: baseLogoutURLwithTokenAndRedirectURL,
|
||||
token: oidcToken,
|
||||
logoutRedirectURL: baseURL,
|
||||
expectedLogoutURL: "http://localhost:4000/logout?id_token_hint=" + oidcToken + "&post_logout_redirect_uri=" + baseURL,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
constructedLogoutURL := constructLogoutURL(tt.logoutURL, tt.token, tt.logoutRedirectURL)
|
||||
assert.Equal(t, constructedLogoutURL, tt.expectedLogoutURL)
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestHandlerConstructLogoutURL(t *testing.T) {
|
||||
kubeClientWithOIDCConfig := fake.NewSimpleClientset(
|
||||
&corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDConfigMapName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: map[string]string{
|
||||
"oidc.config": "name: Okta \n" +
|
||||
"issuer: https://dev-5695098.okta.com \n" +
|
||||
"requestedScopes: [\"openid\", \"profile\", \"email\", \"groups\"] \n" +
|
||||
"requestedIDTokenClaims: {\"groups\": {\"essential\": true}} \n" +
|
||||
"logoutURL: https://dev-5695098.okta.com/oauth2/v1/logout?id_token_hint={{token}}&post_logout_redirect_uri={{logoutRedirectURL}}",
|
||||
"url": "http://localhost:4000",
|
||||
},
|
||||
},
|
||||
&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDSecretName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"admin.password": nil,
|
||||
"server.secretkey": nil,
|
||||
},
|
||||
},
|
||||
)
|
||||
kubeClientWithOIDCConfigButNoLogoutURL := fake.NewSimpleClientset(
|
||||
&corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDConfigMapName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: map[string]string{
|
||||
"oidc.config": "name: Okta \n" +
|
||||
"issuer: https://dev-5695098.okta.com \n" +
|
||||
"requestedScopes: [\"openid\", \"profile\", \"email\", \"groups\"] \n" +
|
||||
"requestedIDTokenClaims: {\"groups\": {\"essential\": true}} \n",
|
||||
"url": "http://localhost:4000",
|
||||
},
|
||||
},
|
||||
&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDSecretName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"admin.password": nil,
|
||||
"server.secretkey": nil,
|
||||
},
|
||||
},
|
||||
)
|
||||
kubeClientWithoutOIDCConfig := fake.NewSimpleClientset(
|
||||
&corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDConfigMapName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: map[string]string{
|
||||
"url": "http://localhost:4000",
|
||||
},
|
||||
},
|
||||
&corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDSecretName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"admin.password": nil,
|
||||
"server.secretkey": nil,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
settingsManagerWithOIDCConfig := settings.NewSettingsManager(context.Background(), kubeClientWithOIDCConfig, "default")
|
||||
settingsManagerWithoutOIDCConfig := settings.NewSettingsManager(context.Background(), kubeClientWithoutOIDCConfig, "default")
|
||||
settingsManagerWithOIDCConfigButNoLogoutURL := settings.NewSettingsManager(context.Background(), kubeClientWithOIDCConfigButNoLogoutURL, "default")
|
||||
|
||||
sessionManager := session.NewSessionManager(settingsManagerWithOIDCConfig, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
|
||||
|
||||
oidcHandler := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithOIDCConfig, sessionManager, "", "default")
|
||||
oidcHandler.verifyToken = func(tokenString string) (jwt.Claims, error) {
|
||||
if !validJWTPattern.MatchString(tokenString) {
|
||||
return nil, errors.New("invalid jwt")
|
||||
}
|
||||
return &jwt.StandardClaims{Issuer: "okta"}, nil
|
||||
}
|
||||
nonoidcHandler := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithoutOIDCConfig, sessionManager, "", "default")
|
||||
nonoidcHandler.verifyToken = func(tokenString string) (jwt.Claims, error) {
|
||||
if !validJWTPattern.MatchString(tokenString) {
|
||||
return nil, errors.New("invalid jwt")
|
||||
}
|
||||
return &jwt.StandardClaims{Issuer: session.SessionManagerClaimsIssuer}, nil
|
||||
}
|
||||
oidcHandlerWithoutLogoutURL := NewHandler(appclientset.NewSimpleClientset(), settingsManagerWithOIDCConfigButNoLogoutURL, sessionManager, "", "default")
|
||||
oidcHandlerWithoutLogoutURL.verifyToken = func(tokenString string) (jwt.Claims, error) {
|
||||
if !validJWTPattern.MatchString(tokenString) {
|
||||
return nil, errors.New("invalid jwt")
|
||||
}
|
||||
return &jwt.StandardClaims{Issuer: "okta"}, nil
|
||||
}
|
||||
|
||||
oidcTokenHeader := make(map[string][]string)
|
||||
oidcTokenHeader["Cookie"] = []string{"argocd.token=" + oidcToken}
|
||||
nonOidcTokenHeader := make(map[string][]string)
|
||||
nonOidcTokenHeader["Cookie"] = []string{"argocd.token=" + nonOidcToken}
|
||||
invalidHeader := make(map[string][]string)
|
||||
invalidHeader["Cookie"] = []string{"argocd.token=" + invalidToken}
|
||||
|
||||
oidcRequest, err := http.NewRequest("GET", "http://localhost:4000/api/logout", nil)
|
||||
assert.NoError(t, err)
|
||||
oidcRequest.Header = oidcTokenHeader
|
||||
nonoidcRequest, err := http.NewRequest("GET", "http://localhost:4000/api/logout", nil)
|
||||
assert.NoError(t, err)
|
||||
nonoidcRequest.Header = nonOidcTokenHeader
|
||||
assert.NoError(t, err)
|
||||
requestWithInvalidToken, err := http.NewRequest("GET", "http://localhost:4000/api/logout", nil)
|
||||
assert.NoError(t, err)
|
||||
requestWithInvalidToken.Header = invalidHeader
|
||||
invalidRequest, err := http.NewRequest("GET", "http://localhost:4000/api/logout", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
kubeClient *fake.Clientset
|
||||
handler http.Handler
|
||||
request *http.Request
|
||||
responseRecorder *httptest.ResponseRecorder
|
||||
expectedLogoutURL string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Case: OIDC logout request with valid token",
|
||||
handler: oidcHandler,
|
||||
request: oidcRequest,
|
||||
responseRecorder: httptest.NewRecorder(),
|
||||
expectedLogoutURL: expectedOIDCLogoutURL,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Case: non-OIDC logout request with valid token",
|
||||
handler: nonoidcHandler,
|
||||
request: nonoidcRequest,
|
||||
responseRecorder: httptest.NewRecorder(),
|
||||
expectedLogoutURL: expectedNonOIDCLogoutURL,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Case: Logout request with invalid token",
|
||||
handler: nonoidcHandler,
|
||||
request: requestWithInvalidToken,
|
||||
responseRecorder: httptest.NewRecorder(),
|
||||
expectedLogoutURL: expectedNonOIDCLogoutURL,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Case: Logout request with missing token",
|
||||
handler: oidcHandler,
|
||||
request: invalidRequest,
|
||||
responseRecorder: httptest.NewRecorder(),
|
||||
expectedLogoutURL: expectedNonOIDCLogoutURL,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Case:OIDC Logout request with missing logout URL configuration in config map",
|
||||
handler: oidcHandlerWithoutLogoutURL,
|
||||
request: oidcRequest,
|
||||
responseRecorder: httptest.NewRecorder(),
|
||||
expectedLogoutURL: expectedNonOIDCLogoutURL,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.handler.ServeHTTP(tt.responseRecorder, tt.request)
|
||||
if status := tt.responseRecorder.Code; status != http.StatusSeeOther {
|
||||
if !tt.wantErr {
|
||||
t.Errorf(tt.responseRecorder.Body.String())
|
||||
t.Errorf("handler returned wrong status code: " + fmt.Sprintf("%d", tt.responseRecorder.Code))
|
||||
}
|
||||
} else {
|
||||
if tt.wantErr {
|
||||
t.Errorf("expected error but did not get one")
|
||||
} else {
|
||||
assert.Equal(t, tt.expectedLogoutURL, tt.responseRecorder.Result().Header["Location"][0])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/argoproj/pkg/sync"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -108,16 +108,21 @@ func (s *Server) CreateToken(ctx context.Context, q *project.ProjectTokenCreateR
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
parser := &jwt.Parser{
|
||||
SkipClaimsValidation: true,
|
||||
ValidationHelper: jwt.NewValidationHelper(jwt.WithoutClaimsValidation(), jwt.WithoutAudienceValidation()),
|
||||
}
|
||||
claims := jwt.StandardClaims{}
|
||||
_, _, err = parser.ParseUnverified(jwtToken, &claims)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
issuedAt := claims.IssuedAt
|
||||
expiresAt := claims.ExpiresAt
|
||||
id = claims.Id
|
||||
var issuedAt, expiresAt int64
|
||||
if claims.IssuedAt != nil {
|
||||
issuedAt = claims.IssuedAt.Unix()
|
||||
}
|
||||
if claims.ExpiresAt != nil {
|
||||
expiresAt = claims.ExpiresAt.Unix()
|
||||
}
|
||||
id = claims.ID
|
||||
|
||||
items := append(prj.Status.JWTTokensByRole[q.Role].Items, v1alpha1.JWTToken{IssuedAt: issuedAt, ExpiresAt: expiresAt, ID: id})
|
||||
if _, found := prj.Status.JWTTokensByRole[q.Role]; found {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/pkg/sync"
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -17,14 +18,13 @@ import (
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
k8scache "k8s.io/client-go/tools/cache"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient/project"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
apps "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
|
||||
informer "github.com/argoproj/argo-cd/pkg/client/informers/externalversions"
|
||||
"github.com/argoproj/argo-cd/server/rbacpolicy"
|
||||
"github.com/argoproj/argo-cd/test"
|
||||
"github.com/argoproj/argo-cd/util/assets"
|
||||
jwtutil "github.com/argoproj/argo-cd/util/jwt"
|
||||
"github.com/argoproj/argo-cd/util/rbac"
|
||||
@@ -82,7 +82,7 @@ func TestProjectServer(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("TestNormalizeProj", func(t *testing.T) {
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
|
||||
projectWithRole := existingProj.DeepCopy()
|
||||
roleName := "roleName"
|
||||
role1 := v1alpha1.ProjectRole{Name: roleName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}}
|
||||
@@ -319,7 +319,7 @@ func TestProjectServer(t *testing.T) {
|
||||
id := "testId"
|
||||
|
||||
t.Run("TestCreateTokenDenied", func(t *testing.T) {
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
|
||||
projectWithRole := existingProj.DeepCopy()
|
||||
projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}}
|
||||
projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
|
||||
@@ -328,7 +328,7 @@ func TestProjectServer(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("TestCreateTokenSuccessfullyUsingGroup", func(t *testing.T) {
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
|
||||
projectWithRole := existingProj.DeepCopy()
|
||||
projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName, Groups: []string{"my-group"}}}
|
||||
projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
|
||||
@@ -339,11 +339,13 @@ func TestProjectServer(t *testing.T) {
|
||||
_ = enforcer.SetBuiltinPolicy(`p, role:admin, projects, update, *, allow`)
|
||||
|
||||
t.Run("TestCreateTokenSuccessfully", func(t *testing.T) {
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
projectWithRole := existingProj.DeepCopy()
|
||||
projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}}
|
||||
projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
|
||||
tokenResponse, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1})
|
||||
clientset := apps.NewSimpleClientset(projectWithRole)
|
||||
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", session.NewInMemoryUserStateStorage())
|
||||
projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
|
||||
tokenResponse, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 100})
|
||||
assert.NoError(t, err)
|
||||
claims, err := sessionMgr.Parse(tokenResponse.Token)
|
||||
assert.NoError(t, err)
|
||||
@@ -357,10 +359,12 @@ func TestProjectServer(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("TestCreateTokenWithIDSuccessfully", func(t *testing.T) {
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
projectWithRole := existingProj.DeepCopy()
|
||||
projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}}
|
||||
projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
|
||||
clientset := apps.NewSimpleClientset(projectWithRole)
|
||||
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", session.NewInMemoryUserStateStorage())
|
||||
projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
|
||||
tokenResponse, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1, Id: id})
|
||||
assert.NoError(t, err)
|
||||
claims, err := sessionMgr.Parse(tokenResponse.Token)
|
||||
@@ -375,10 +379,12 @@ func TestProjectServer(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("TestCreateTokenWithSameIdDeny", func(t *testing.T) {
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
projectWithRole := existingProj.DeepCopy()
|
||||
projectWithRole.Spec.Roles = []v1alpha1.ProjectRole{{Name: tokenName}}
|
||||
projectServer := NewServer("default", fake.NewSimpleClientset(), apps.NewSimpleClientset(projectWithRole), enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
|
||||
clientset := apps.NewSimpleClientset(projectWithRole)
|
||||
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjListerFromInterface(clientset.ArgoprojV1alpha1().AppProjects("default")), "", session.NewInMemoryUserStateStorage())
|
||||
projectServer := NewServer("default", fake.NewSimpleClientset(), clientset, enforcer, sync.NewKeyLock(), sessionMgr, policyEnf, projInformer, settingsMgr)
|
||||
tokenResponse, err := projectServer.CreateToken(context.Background(), &project.ProjectTokenCreateRequest{Project: projectWithRole.Name, Role: tokenName, ExpiresIn: 1, Id: id})
|
||||
|
||||
assert.NoError(t, err)
|
||||
@@ -400,7 +406,7 @@ func TestProjectServer(t *testing.T) {
|
||||
_ = enforcer.SetBuiltinPolicy(`p, *, *, *, *, deny`)
|
||||
|
||||
t.Run("TestDeleteTokenDenied", func(t *testing.T) {
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
|
||||
projWithToken := existingProj.DeepCopy()
|
||||
issuedAt := int64(1)
|
||||
secondIssuedAt := issuedAt + 1
|
||||
@@ -413,7 +419,7 @@ func TestProjectServer(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("TestDeleteTokenSuccessfullyWithGroup", func(t *testing.T) {
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
|
||||
projWithToken := existingProj.DeepCopy()
|
||||
issuedAt := int64(1)
|
||||
secondIssuedAt := issuedAt + 1
|
||||
@@ -429,7 +435,7 @@ func TestProjectServer(t *testing.T) {
|
||||
p, role:admin, projects, update, *, allow`)
|
||||
|
||||
t.Run("TestDeleteTokenSuccessfully", func(t *testing.T) {
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
|
||||
projWithToken := existingProj.DeepCopy()
|
||||
issuedAt := int64(1)
|
||||
secondIssuedAt := issuedAt + 1
|
||||
@@ -450,7 +456,7 @@ p, role:admin, projects, update, *, allow`)
|
||||
p, role:admin, projects, update, *, allow`)
|
||||
|
||||
t.Run("TestDeleteTokenByIdSuccessfully", func(t *testing.T) {
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
|
||||
projWithToken := existingProj.DeepCopy()
|
||||
issuedAt := int64(1)
|
||||
secondIssuedAt := issuedAt + 1
|
||||
@@ -473,7 +479,7 @@ p, role:admin, projects, update, *, allow`)
|
||||
enforcer = newEnforcer(kubeclientset)
|
||||
|
||||
t.Run("TestCreateTwoTokensInRoleSuccess", func(t *testing.T) {
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
|
||||
projWithToken := existingProj.DeepCopy()
|
||||
tokenName := "testToken"
|
||||
token := v1alpha1.ProjectRole{Name: tokenName, JWTTokens: []v1alpha1.JWTToken{{IssuedAt: 1}}}
|
||||
@@ -638,7 +644,7 @@ p, role:admin, projects, update, *, allow`)
|
||||
})
|
||||
|
||||
t.Run("TestSyncWindowsActive", func(t *testing.T) {
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
|
||||
projectWithSyncWindows := existingProj.DeepCopy()
|
||||
projectWithSyncWindows.Spec.SyncWindows = v1alpha1.SyncWindows{}
|
||||
win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"}
|
||||
@@ -651,7 +657,7 @@ p, role:admin, projects, update, *, allow`)
|
||||
})
|
||||
|
||||
t.Run("TestGetSyncWindowsStateCannotGetProjectDetails", func(t *testing.T) {
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
|
||||
projectWithSyncWindows := existingProj.DeepCopy()
|
||||
projectWithSyncWindows.Spec.SyncWindows = v1alpha1.SyncWindows{}
|
||||
win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"}
|
||||
@@ -670,7 +676,7 @@ p, role:admin, projects, update, *, allow`)
|
||||
// nolint:staticcheck
|
||||
ctx := context.WithValue(context.Background(), "claims", &jwt.MapClaims{"groups": []string{"my-group"}})
|
||||
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, "", session.NewInMemoryUserStateStorage())
|
||||
sessionMgr := session.NewSessionManager(settingsMgr, test.NewFakeProjLister(), "", session.NewInMemoryUserStateStorage())
|
||||
projectWithSyncWindows := existingProj.DeepCopy()
|
||||
win := &v1alpha1.SyncWindow{Kind: "allow", Schedule: "* * * * *", Duration: "1h"}
|
||||
projectWithSyncWindows.Spec.SyncWindows = append(projectWithSyncWindows.Spec.SyncWindows, win)
|
||||
|
||||
@@ -3,7 +3,7 @@ package rbacpolicy
|
||||
import (
|
||||
"strings"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
jwt "github.com/dgrijalva/jwt-go/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
@@ -82,7 +82,16 @@ func (p *RBACPolicyEnforcer) GetScopes() []string {
|
||||
}
|
||||
|
||||
func IsProjectSubject(subject string) bool {
|
||||
return strings.HasPrefix(subject, "proj:")
|
||||
_, _, ok := GetProjectRoleFromSubject(subject)
|
||||
return ok
|
||||
}
|
||||
|
||||
func GetProjectRoleFromSubject(subject string) (string, string, bool) {
|
||||
parts := strings.Split(subject, ":")
|
||||
if len(parts) == 3 && parts[0] == "proj" {
|
||||
return parts[1], parts[2], true
|
||||
}
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
// EnforceClaims is an RBAC claims enforcer specific to the Argo CD API server
|
||||
@@ -92,14 +101,14 @@ func (p *RBACPolicyEnforcer) EnforceClaims(claims jwt.Claims, rvals ...interface
|
||||
return false
|
||||
}
|
||||
|
||||
subject := jwtutil.GetField(mapClaims, "sub")
|
||||
subject := jwtutil.StringField(mapClaims, "sub")
|
||||
// Check if the request is for an application resource. We have special enforcement which takes
|
||||
// into consideration the project's token and group bindings
|
||||
var runtimePolicy string
|
||||
proj := p.getProjectFromRequest(rvals...)
|
||||
if proj != nil {
|
||||
if IsProjectSubject(subject) {
|
||||
return p.enforceProjectToken(subject, mapClaims, proj, rvals...)
|
||||
return p.enforceProjectToken(subject, proj, rvals...)
|
||||
}
|
||||
runtimePolicy = proj.ProjectPoliciesString()
|
||||
}
|
||||
@@ -158,31 +167,17 @@ func (p *RBACPolicyEnforcer) getProjectFromRequest(rvals ...interface{}) *v1alph
|
||||
}
|
||||
|
||||
// enforceProjectToken will check to see the valid token has not yet been revoked in the project
|
||||
func (p *RBACPolicyEnforcer) enforceProjectToken(subject string, claims jwt.MapClaims, proj *v1alpha1.AppProject, rvals ...interface{}) bool {
|
||||
func (p *RBACPolicyEnforcer) enforceProjectToken(subject string, proj *v1alpha1.AppProject, rvals ...interface{}) bool {
|
||||
subjectSplit := strings.Split(subject, ":")
|
||||
if len(subjectSplit) != 3 {
|
||||
return false
|
||||
}
|
||||
projName, roleName := subjectSplit[1], subjectSplit[2]
|
||||
projName, _ := subjectSplit[1], subjectSplit[2]
|
||||
if projName != proj.Name {
|
||||
// this should never happen (we generated a project token for a different project)
|
||||
return false
|
||||
}
|
||||
|
||||
var iat int64 = -1
|
||||
jti, err := jwtutil.GetID(claims)
|
||||
if err != nil || jti == "" {
|
||||
iat, err = jwtutil.GetIssuedAt(claims)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
_, _, err = proj.GetJWTToken(roleName, iat, jti)
|
||||
if err != nil {
|
||||
// if we get here the token is still valid, but has been revoked (no longer exists in the project)
|
||||
return false
|
||||
}
|
||||
vals := append([]interface{}{subject}, rvals[1:]...)
|
||||
return p.enf.EnforceRuntimePolicy(proj.ProjectPoliciesString(), vals...)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
jwt "github.com/dgrijalva/jwt-go/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
@@ -67,12 +67,6 @@ func TestEnforceAllPolicies(t *testing.T) {
|
||||
|
||||
claims = jwt.MapClaims{"sub": "cathy"}
|
||||
assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
|
||||
claims = jwt.MapClaims{"sub": "proj:my-proj:my-role"}
|
||||
assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
|
||||
claims = jwt.MapClaims{"sub": "proj:my-proj:other-role", "iat": 1234}
|
||||
assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
|
||||
claims = jwt.MapClaims{"groups": []string{"my-org:other-group"}}
|
||||
assert.False(t, enf.Enforce(claims, "applications", "create", "my-proj/my-app"))
|
||||
|
||||
// AWS cognito returns its groups in cognito:groups
|
||||
rbacEnf.SetScopes([]string{"cognito:groups"})
|
||||
|
||||
@@ -65,6 +65,9 @@ func (s *Server) ListRepositoryCredentials(ctx context.Context, q *repocredspkg.
|
||||
|
||||
// CreateRepositoryCredentials creates a new credential set in the configuration
|
||||
func (s *Server) CreateRepositoryCredentials(ctx context.Context, q *repocredspkg.RepoCredsCreateRequest) (*appsv1.RepoCreds, error) {
|
||||
if q.Creds == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "missing payload in request")
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionCreate, q.Creds.URL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -96,6 +99,9 @@ func (s *Server) CreateRepositoryCredentials(ctx context.Context, q *repocredspk
|
||||
|
||||
// UpdateRepositoryCredentials updates a repository credential set
|
||||
func (s *Server) UpdateRepositoryCredentials(ctx context.Context, q *repocredspkg.RepoCredsUpdateRequest) (*appsv1.RepoCreds, error) {
|
||||
if q.Creds == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "missing payload in request")
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionUpdate, q.Creds.URL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -205,6 +205,9 @@ func (s *Server) ListApps(ctx context.Context, q *repositorypkg.RepoAppsQuery) (
|
||||
}
|
||||
|
||||
func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDetailsQuery) (*apiclient.RepoAppDetailsResponse, error) {
|
||||
if q.Source == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "missing payload in request")
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, q.Source.RepoURL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -262,6 +265,9 @@ func (s *Server) Create(ctx context.Context, q *repositorypkg.RepoCreateRequest)
|
||||
|
||||
// CreateRepository creates a repository configuration
|
||||
func (s *Server) CreateRepository(ctx context.Context, q *repositorypkg.RepoCreateRequest) (*appsv1.Repository, error) {
|
||||
if q.Repo == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "missing payload in request")
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionCreate, q.Repo.Repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -320,6 +326,9 @@ func (s *Server) Update(ctx context.Context, q *repositorypkg.RepoUpdateRequest)
|
||||
|
||||
// UpdateRepository updates a repository configuration
|
||||
func (s *Server) UpdateRepository(ctx context.Context, q *repositorypkg.RepoUpdateRequest) (*appsv1.Repository, error) {
|
||||
if q.Repo == nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "missing payload in request")
|
||||
}
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionUpdate, q.Repo.Repo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
|
||||
"github.com/argoproj/pkg/jwt/zjwt"
|
||||
"github.com/argoproj/pkg/sync"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/gorilla/handlers"
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
@@ -73,6 +73,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/server/certificate"
|
||||
"github.com/argoproj/argo-cd/server/cluster"
|
||||
"github.com/argoproj/argo-cd/server/gpgkey"
|
||||
"github.com/argoproj/argo-cd/server/logout"
|
||||
"github.com/argoproj/argo-cd/server/metrics"
|
||||
"github.com/argoproj/argo-cd/server/project"
|
||||
"github.com/argoproj/argo-cd/server/rbacpolicy"
|
||||
@@ -104,7 +105,6 @@ import (
|
||||
|
||||
const maxConcurrentLoginRequestsCountEnv = "ARGOCD_MAX_CONCURRENT_LOGIN_REQUESTS_COUNT"
|
||||
const replicasCountEnv = "ARGOCD_API_SERVER_REPLICAS"
|
||||
const enableGRPCTimeHistogramEnv = "ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM"
|
||||
|
||||
// ErrNoSession indicates no auth token was supplied as part of a request
|
||||
var ErrNoSession = status.Errorf(codes.Unauthenticated, "no session information")
|
||||
@@ -138,7 +138,7 @@ func init() {
|
||||
if replicasCount > 0 {
|
||||
maxConcurrentLoginRequestsCount = maxConcurrentLoginRequestsCount / replicasCount
|
||||
}
|
||||
enableGRPCTimeHistogram = os.Getenv(enableGRPCTimeHistogramEnv) != "false"
|
||||
enableGRPCTimeHistogram = os.Getenv(common.EnvEnableGRPCTimeHistogramEnv) == "true"
|
||||
}
|
||||
|
||||
// ArgoCDServer is the API server for Argo CD
|
||||
@@ -209,8 +209,6 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer {
|
||||
err = initializeDefaultProject(opts)
|
||||
errors.CheckError(err)
|
||||
|
||||
sessionMgr := util_session.NewSessionManager(settingsMgr, opts.DexServerAddr, opts.Cache)
|
||||
|
||||
factory := appinformer.NewFilteredSharedInformerFactory(opts.AppClientset, 0, opts.Namespace, func(options *metav1.ListOptions) {})
|
||||
projInformer := factory.Argoproj().V1alpha1().AppProjects().Informer()
|
||||
projLister := factory.Argoproj().V1alpha1().AppProjects().Lister().AppProjects(opts.Namespace)
|
||||
@@ -218,6 +216,7 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer {
|
||||
appInformer := factory.Argoproj().V1alpha1().Applications().Informer()
|
||||
appLister := factory.Argoproj().V1alpha1().Applications().Lister().Applications(opts.Namespace)
|
||||
|
||||
sessionMgr := util_session.NewSessionManager(settingsMgr, projLister, opts.DexServerAddr, opts.Cache)
|
||||
enf := rbac.NewEnforcer(opts.KubeClientset, opts.Namespace, common.ArgoCDRBACConfigMapName, nil)
|
||||
enf.EnableEnforce(!opts.DisableAuth)
|
||||
err = enf.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
|
||||
@@ -241,6 +240,22 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer {
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// catches corrupted informer state; see https://github.com/argoproj/argo-cd/issues/4960 for more information
|
||||
notObjectErrMsg = "object does not implement the Object interfaces"
|
||||
)
|
||||
|
||||
func (a *ArgoCDServer) healthCheck(r *http.Request) error {
|
||||
if val, ok := r.URL.Query()["full"]; ok && len(val) > 0 && val[0] == "true" {
|
||||
argoDB := db.NewDB(a.Namespace, a.settingsMgr, a.KubeClientset)
|
||||
_, err := argoDB.ListClusters(r.Context())
|
||||
if err != nil && strings.Contains(err.Error(), notObjectErrMsg) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run runs the API Server
|
||||
// We use k8s.io/code-generator/cmd/go-to-protobuf to generate the .proto files from the API types.
|
||||
// k8s.io/ go-to-protobuf uses protoc-gen-gogo, which comes from gogo/protobuf (a fork of
|
||||
@@ -546,7 +561,16 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server {
|
||||
accountService := account.NewServer(a.sessionMgr, a.settingsMgr, a.enf)
|
||||
certificateService := certificate.NewServer(a.RepoClientset, db, a.enf)
|
||||
gpgkeyService := gpgkey.NewServer(a.RepoClientset, db, a.enf)
|
||||
versionpkg.RegisterVersionServiceServer(grpcS, &version.Server{})
|
||||
versionpkg.RegisterVersionServiceServer(grpcS, version.NewServer(a, func() (bool, error) {
|
||||
if a.DisableAuth {
|
||||
return true, nil
|
||||
}
|
||||
sett, err := a.settingsMgr.GetSettings()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return sett.AnonymousUserEnabled, err
|
||||
}))
|
||||
clusterpkg.RegisterClusterServiceServer(grpcS, clusterService)
|
||||
applicationpkg.RegisterApplicationServiceServer(grpcS, applicationService)
|
||||
repositorypkg.RegisterRepositoryServiceServer(grpcS, repoService)
|
||||
@@ -596,9 +620,7 @@ func withRootPath(handler http.Handler, a *ArgoCDServer) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/"+root+"/", http.StripPrefix("/"+root, handler))
|
||||
|
||||
healthz.ServeHealthCheck(mux, func() error {
|
||||
return nil
|
||||
})
|
||||
healthz.ServeHealthCheck(mux, a.healthCheck)
|
||||
|
||||
return mux
|
||||
}
|
||||
@@ -624,7 +646,8 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
Handler: &handlerSwitcher{
|
||||
handler: mux,
|
||||
urlToHandler: map[string]http.Handler{
|
||||
"/api/badge": badge.NewHandler(a.AppClientset, a.settingsMgr, a.Namespace),
|
||||
"/api/badge": badge.NewHandler(a.AppClientset, a.settingsMgr, a.Namespace),
|
||||
common.LogoutEndpoint: logout.NewHandler(a.AppClientset, a.settingsMgr, a.sessionMgr, a.ArgoCDServerOpts.RootPath, a.Namespace),
|
||||
},
|
||||
contentTypeToHandler: map[string]http.Handler{
|
||||
"application/grpc-web+proto": grpcWebHandler,
|
||||
@@ -680,9 +703,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
|
||||
// Swagger UI
|
||||
swagger.ServeSwaggerUI(mux, assets.SwaggerJSON, "/swagger-ui", a.RootPath)
|
||||
healthz.ServeHealthCheck(mux, func() error {
|
||||
return nil
|
||||
})
|
||||
healthz.ServeHealthCheck(mux, a.healthCheck)
|
||||
|
||||
// Dex reverse proxy and client app and OAuth2 login/callback
|
||||
a.registerDexHandlers(mux)
|
||||
@@ -820,6 +841,7 @@ func (server *ArgoCDServer) newStaticAssetsHandler(dir string, baseHRef string)
|
||||
if server.XFrameOptions != "" {
|
||||
w.Header().Set("X-Frame-Options", server.XFrameOptions)
|
||||
}
|
||||
w.Header().Set("X-XSS-Protection", "1")
|
||||
|
||||
// serve index.html for non file requests to support HTML5 History API
|
||||
if acceptHTML && !fileRequest && (r.Method == "GET" || r.Method == "HEAD") {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc/metadata"
|
||||
@@ -362,13 +362,6 @@ func TestRevokedToken(t *testing.T) {
|
||||
claims := jwt.MapClaims{"sub": defaultSub, "iat": defaultIssuedAt}
|
||||
assert.True(t, s.enf.Enforce(claims, "projects", "get", existingProj.ObjectMeta.Name))
|
||||
assert.True(t, s.enf.Enforce(claims, "applications", "get", defaultTestObject))
|
||||
// Now revoke the token by deleting the token
|
||||
existingProj.Spec.Roles[0].JWTTokens = nil
|
||||
existingProj.Status.JWTTokensByRole = nil
|
||||
_, _ = s.AppClientset.ArgoprojV1alpha1().AppProjects(test.FakeArgoCDNamespace).Update(context.Background(), &existingProj, metav1.UpdateOptions{})
|
||||
time.Sleep(200 * time.Millisecond) // this lets the informer get synced
|
||||
assert.False(t, s.enf.Enforce(claims, "projects", "get", existingProj.ObjectMeta.Name))
|
||||
assert.False(t, s.enf.Enforce(claims, "applications", "get", defaultTestObject))
|
||||
}
|
||||
|
||||
func TestCertsAreNotGeneratedInInsecureMode(t *testing.T) {
|
||||
|
||||
@@ -15,18 +15,26 @@ import (
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient/version"
|
||||
"github.com/argoproj/argo-cd/server/settings"
|
||||
"github.com/argoproj/argo-cd/util/helm"
|
||||
ksutil "github.com/argoproj/argo-cd/util/ksonnet"
|
||||
"github.com/argoproj/argo-cd/util/kustomize"
|
||||
"github.com/argoproj/argo-cd/util/log"
|
||||
sessionmgr "github.com/argoproj/argo-cd/util/session"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
type server struct {
|
||||
ksonnetVersion string
|
||||
kustomizeVersion string
|
||||
helmVersion string
|
||||
kubectlVersion string
|
||||
jsonnetVersion string
|
||||
authenticator settings.Authenticator
|
||||
disableAuth func() (bool, error)
|
||||
}
|
||||
|
||||
func NewServer(authenticator settings.Authenticator, disableAuth func() (bool, error)) *server {
|
||||
return &server{authenticator: authenticator, disableAuth: disableAuth}
|
||||
}
|
||||
|
||||
func getVersion() (string, error) {
|
||||
@@ -51,8 +59,17 @@ func getVersion() (string, error) {
|
||||
}
|
||||
|
||||
// Version returns the version of the API server
|
||||
func (s *Server) Version(context.Context, *empty.Empty) (*version.VersionMessage, error) {
|
||||
func (s *server) Version(ctx context.Context, _ *empty.Empty) (*version.VersionMessage, error) {
|
||||
vers := common.GetVersion()
|
||||
disableAuth, err := s.disableAuth()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !sessionmgr.LoggedIn(ctx) && !disableAuth {
|
||||
return &version.VersionMessage{Version: vers.Version}, nil
|
||||
}
|
||||
|
||||
if s.ksonnetVersion == "" {
|
||||
ksonnetVersion, err := ksutil.Version()
|
||||
if err == nil {
|
||||
@@ -104,6 +121,10 @@ func (s *Server) Version(context.Context, *empty.Empty) (*version.VersionMessage
|
||||
}
|
||||
|
||||
// AuthFuncOverride allows the version to be returned without auth
|
||||
func (s *Server) AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error) {
|
||||
func (s *server) AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error) {
|
||||
if s.authenticator != nil {
|
||||
// this authenticates the user, but ignores any error, so that we have claims populated
|
||||
ctx, _ = s.authenticator.Authenticate(ctx)
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
controller: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true go run ./cmd/argocd-application-controller/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
|
||||
api-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true go run ./cmd/argocd-server/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --staticassets ui/dist/app"
|
||||
dex: sh -c "test $ARGOCD_IN_CI = true && exit 0; go run github.com/argoproj/argo-cd/cmd/argocd-util 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.25.0 serve /dex.yaml"
|
||||
dex: sh -c "test $ARGOCD_IN_CI = true && exit 0; go run github.com/argoproj/argo-cd/cmd/argocd-util gendexcfg -o `pwd`/dist/dex.yaml && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:v2.27.0 serve /dex.yaml"
|
||||
redis: sh -c "/usr/local/bin/redis-server --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}"
|
||||
repo-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} 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 "test $ARGOCD_IN_CI = true && exit 0; cd ui && ARGOCD_E2E_YARN_HOST=0.0.0.0 ${ARGOCD_E2E_YARN_CMD:-yarn} start"
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/argoproj/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
networkingv1beta "k8s.io/api/networking/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -425,6 +426,15 @@ func TestAppWithSecrets(t *testing.T) {
|
||||
diffOutput := FailOnErr(RunCli("app", "diff", app.Name)).(string)
|
||||
assert.Empty(t, diffOutput)
|
||||
|
||||
// make sure resource update error does not print secret details
|
||||
_, err = RunCli("app", "patch-resource", "test-app-with-secrets", "--resource-name", "test-secret",
|
||||
"--kind", "Secret", "--patch", `{"op": "add", "path": "/data", "value": "hello"}'`,
|
||||
"--patch-type", "application/json-patch+json")
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), fmt.Sprintf("failed to patch Secret %s/test-secret", DeploymentNamespace()))
|
||||
assert.NotContains(t, err.Error(), "username")
|
||||
assert.NotContains(t, err.Error(), "password")
|
||||
|
||||
// patch secret and make sure app is out of sync and diff detects the change
|
||||
FailOnErr(KubeClientset.CoreV1().Secrets(DeploymentNamespace()).Patch(context.Background(),
|
||||
"test-secret", types.JSONPatchType, []byte(`[
|
||||
@@ -1370,10 +1380,11 @@ func TestNamespaceAutoCreation(t *testing.T) {
|
||||
|
||||
func TestFailedSyncWithRetry(t *testing.T) {
|
||||
Given(t).
|
||||
Path(guestbookPath).
|
||||
Path("hook").
|
||||
When().
|
||||
// app should be attempted to auto-synced once and marked with error after failed attempt detected
|
||||
PatchFile("guestbook-ui-deployment.yaml", `[{"op": "replace", "path": "/spec/revisionHistoryLimit", "value": "badValue"}]`).
|
||||
PatchFile("hook.yaml", `[{"op": "replace", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/hook": "PreSync"}}]`).
|
||||
// make hook fail
|
||||
PatchFile("hook.yaml", `[{"op": "replace", "path": "/spec/containers/0/command", "value": ["false"]}]`).
|
||||
Create().
|
||||
IgnoreErrors().
|
||||
Sync("--retry-limit=1", "--retry-backoff-duration=1s").
|
||||
@@ -1513,3 +1524,30 @@ definitions:
|
||||
assert.Equal(t, "update-status", text)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAppWaitOperationInProgress(t *testing.T) {
|
||||
Given(t).
|
||||
And(func() {
|
||||
SetResourceOverrides(map[string]ResourceOverride{
|
||||
"batch/Job": {
|
||||
HealthLua: `return { status = 'Running' }`,
|
||||
},
|
||||
"apps/Deployment": {
|
||||
HealthLua: `return { status = 'Suspended' }`,
|
||||
},
|
||||
})
|
||||
}).
|
||||
Async(true).
|
||||
Path("hook-and-deployment").
|
||||
When().
|
||||
Create().
|
||||
Sync().
|
||||
Then().
|
||||
// stuck in running state
|
||||
Expect(OperationPhaseIs(OperationRunning)).
|
||||
When().
|
||||
And(func() {
|
||||
_, err := RunCli("app", "wait", Name(), "--suspended")
|
||||
errors.CheckError(err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
. "github.com/argoproj/argo-cd/test/e2e/fixture"
|
||||
. "github.com/argoproj/argo-cd/test/e2e/fixture/app"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
@@ -16,23 +17,30 @@ func TestFixingDegradedApp(t *testing.T) {
|
||||
When().
|
||||
IgnoreErrors().
|
||||
Create().
|
||||
PatchFile("pod-1.yaml", `[{"op": "replace", "path": "/spec/containers/0/image", "value": "rubbish"}]`).
|
||||
And(func() {
|
||||
SetResourceOverrides(map[string]ResourceOverride{
|
||||
"ConfigMap": {
|
||||
HealthLua: `return { status = obj.metadata.annotations and obj.metadata.annotations['health'] or 'Degraded' }`,
|
||||
},
|
||||
})
|
||||
}).
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationFailed)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
|
||||
Expect(HealthIs(health.HealthStatusMissing)).
|
||||
Expect(ResourceResultNumbering(1)).
|
||||
Expect(ResourceSyncStatusIs("Pod", "pod-1", SyncStatusCodeSynced)).
|
||||
Expect(ResourceHealthIs("Pod", "pod-1", health.HealthStatusDegraded)).
|
||||
Expect(ResourceSyncStatusIs("Pod", "pod-2", SyncStatusCodeOutOfSync)).
|
||||
Expect(ResourceHealthIs("Pod", "pod-2", health.HealthStatusMissing)).
|
||||
Expect(ResourceSyncStatusIs("ConfigMap", "cm-1", SyncStatusCodeSynced)).
|
||||
Expect(ResourceHealthIs("ConfigMap", "cm-1", health.HealthStatusDegraded)).
|
||||
Expect(ResourceSyncStatusIs("ConfigMap", "cm-2", SyncStatusCodeOutOfSync)).
|
||||
Expect(ResourceHealthIs("ConfigMap", "cm-2", health.HealthStatusMissing)).
|
||||
When().
|
||||
PatchFile("pod-1.yaml", `[{"op": "replace", "path": "/spec/containers/0/image", "value": "nginx:1.17.4-alpine"}]`).
|
||||
PatchFile("cm-1.yaml", `[{"op": "replace", "path": "/metadata/annotations/health", "value": "Healthy"}]`).
|
||||
PatchFile("cm-2.yaml", `[{"op": "replace", "path": "/metadata/annotations/health", "value": "Healthy"}]`).
|
||||
// need to force a refresh here
|
||||
Refresh(RefreshTypeNormal).
|
||||
Then().
|
||||
Expect(ResourceSyncStatusIs("Pod", "pod-1", SyncStatusCodeOutOfSync)).
|
||||
Expect(ResourceSyncStatusIs("ConfigMap", "cm-1", SyncStatusCodeOutOfSync)).
|
||||
When().
|
||||
Sync().
|
||||
Then().
|
||||
@@ -40,10 +48,10 @@ func TestFixingDegradedApp(t *testing.T) {
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
Expect(ResourceResultNumbering(2)).
|
||||
Expect(ResourceSyncStatusIs("Pod", "pod-1", SyncStatusCodeSynced)).
|
||||
Expect(ResourceHealthIs("Pod", "pod-1", health.HealthStatusHealthy)).
|
||||
Expect(ResourceSyncStatusIs("Pod", "pod-2", SyncStatusCodeSynced)).
|
||||
Expect(ResourceHealthIs("Pod", "pod-2", health.HealthStatusHealthy))
|
||||
Expect(ResourceSyncStatusIs("ConfigMap", "cm-1", SyncStatusCodeSynced)).
|
||||
Expect(ResourceHealthIs("ConfigMap", "cm-1", health.HealthStatusHealthy)).
|
||||
Expect(ResourceSyncStatusIs("ConfigMap", "cm-2", SyncStatusCodeSynced)).
|
||||
Expect(ResourceHealthIs("ConfigMap", "cm-2", health.HealthStatusHealthy))
|
||||
}
|
||||
|
||||
func TestOneProgressingDeploymentIsSucceededAndSynced(t *testing.T) {
|
||||
|
||||
6
test/e2e/testdata/sync-waves/cm-1.yaml
vendored
Normal file
6
test/e2e/testdata/sync-waves/cm-1.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm-1
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "1"
|
||||
6
test/e2e/testdata/sync-waves/cm-2.yaml
vendored
Normal file
6
test/e2e/testdata/sync-waves/cm-2.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm-2
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "2"
|
||||
11
test/e2e/testdata/sync-waves/pod-1.yaml
vendored
11
test/e2e/testdata/sync-waves/pod-1.yaml
vendored
@@ -1,11 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-1
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "1"
|
||||
spec:
|
||||
containers:
|
||||
- name: main
|
||||
image: nginx:1.17.4-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
11
test/e2e/testdata/sync-waves/pod-2.yaml
vendored
11
test/e2e/testdata/sync-waves/pod-2.yaml
vendored
@@ -1,11 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-2
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "2"
|
||||
spec:
|
||||
containers:
|
||||
- name: main
|
||||
image: nginx:1.17.4-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
@@ -1,14 +1,19 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/testing"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
apps "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
|
||||
appclient "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/typed/application/v1alpha1"
|
||||
appinformer "github.com/argoproj/argo-cd/pkg/client/informers/externalversions"
|
||||
applister "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
|
||||
)
|
||||
@@ -114,6 +119,30 @@ func NewFakeSecret(policy ...string) *apiv1.Secret {
|
||||
return &secret
|
||||
}
|
||||
|
||||
type interfaceLister struct {
|
||||
appProjects appclient.AppProjectInterface
|
||||
}
|
||||
|
||||
func (l interfaceLister) List(selector labels.Selector) ([]*v1alpha1.AppProject, error) {
|
||||
res, err := l.appProjects.List(context.Background(), metav1.ListOptions{LabelSelector: selector.String()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]*v1alpha1.AppProject, len(res.Items))
|
||||
for i := range res.Items {
|
||||
items[i] = &res.Items[i]
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (l interfaceLister) Get(name string) (*v1alpha1.AppProject, error) {
|
||||
return l.appProjects.Get(context.Background(), name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
func NewFakeProjListerFromInterface(appProjects appclient.AppProjectInterface) applister.AppProjectNamespaceLister {
|
||||
return &interfaceLister{appProjects: appProjects}
|
||||
}
|
||||
|
||||
func NewFakeProjLister(objects ...runtime.Object) applister.AppProjectNamespaceLister {
|
||||
fakeAppClientset := apps.NewSimpleClientset(objects...)
|
||||
factory := appinformer.NewFilteredSharedInformerFactory(fakeAppClientset, 0, "", func(options *metav1.ListOptions) {})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {AutocompleteField, DropDownMenu, FormField, FormSelect, HelpIcon, PopupApi} from 'argo-ui';
|
||||
import * as React from 'react';
|
||||
import {FormApi, Text} from 'react-form';
|
||||
import {Cluster, DataLoader, EditablePanel, EditablePanelItem, Expandable, MapInputField, Repo, Revision, RevisionHelpIcon} from '../../../shared/components';
|
||||
import {Cluster, DataLoader, EditablePanel, EditablePanelItem, Expandable, MapInputField, NumberField, Repo, Revision, RevisionHelpIcon} from '../../../shared/components';
|
||||
import {BadgePanel, Spinner} from '../../../shared/components';
|
||||
import {Consumer} from '../../../shared/context';
|
||||
import * as models from '../../../shared/models';
|
||||
@@ -202,7 +202,7 @@ export const ApplicationSummary = (props: {app: models.Application; updateApp: (
|
||||
view: app.spec.revisionHistoryLimit,
|
||||
edit: (formApi: FormApi) => (
|
||||
<div style={{position: 'relative'}}>
|
||||
<FormField formApi={formApi} field='spec.revisionHistoryLimit' component={Text} />
|
||||
<FormField formApi={formApi} field='spec.revisionHistoryLimit' componentProps={{style: {paddingRight: '1em'}, placeholder: '10'}} component={NumberField} />
|
||||
<div style={{position: 'absolute', right: '0', top: '0'}}>
|
||||
<HelpIcon
|
||||
title='This limits this number of items kept in the apps revision history.
|
||||
|
||||
@@ -23,3 +23,4 @@ export * from './revision';
|
||||
export * from './timestamp';
|
||||
export * from './spinner';
|
||||
export * from './badge-panel/badge-panel';
|
||||
export * from './number-field';
|
||||
|
||||
11
ui/src/app/shared/components/number-field.tsx
Normal file
11
ui/src/app/shared/components/number-field.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactForm from 'react-form';
|
||||
|
||||
export const NumberField = ReactForm.FormField((props: {fieldApi: ReactForm.FieldApi; className: string}) => {
|
||||
const {
|
||||
fieldApi: {getValue, setValue},
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
return <input {...rest} className={props.className} type='number' value={getValue()} onChange={el => setValue(parseInt(el.target.value, 10))} />;
|
||||
});
|
||||
@@ -4,7 +4,7 @@ import * as React from 'react';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import {AppContext} from '../context';
|
||||
import {services} from '../services';
|
||||
|
||||
import requests from '../services/requests';
|
||||
const mostRecentLoggedIn = new BehaviorSubject<boolean>(false);
|
||||
|
||||
function isLoggedIn(): Observable<boolean> {
|
||||
@@ -51,9 +51,10 @@ export class Page extends React.Component<{title: string; toolbar?: Toolbar | Ob
|
||||
|
||||
private async goToLogin(logout = false) {
|
||||
if (logout) {
|
||||
await services.users.logout();
|
||||
window.location.href = requests.toAbsURL('/auth/logout');
|
||||
} else {
|
||||
this.appContext.history.push('/login');
|
||||
}
|
||||
this.appContext.history.push('/login');
|
||||
}
|
||||
|
||||
private get appContext(): AppContext {
|
||||
|
||||
@@ -22,7 +22,7 @@ export class ClustersService {
|
||||
public update(cluster: models.Cluster, ...paths: string[]): Promise<models.Cluster> {
|
||||
return requests
|
||||
.put(`/clusters/${encodeURIComponent(cluster.server)}`)
|
||||
.query({updatedPaths: paths})
|
||||
.query({updatedFields: paths})
|
||||
.send(cluster)
|
||||
.then(res => res.body as models.Cluster);
|
||||
}
|
||||
|
||||
@@ -131,12 +131,8 @@ func TestRepoWithKnownType(repo *argoappv1.Repository, isHelm bool, isHelmOci bo
|
||||
} else {
|
||||
repo.Type = "git"
|
||||
}
|
||||
repo.EnableOCI = repo.EnableOCI || isHelmOci
|
||||
|
||||
if isHelmOci {
|
||||
repo.EnableOCI = true
|
||||
} else {
|
||||
repo.EnableOCI = false
|
||||
}
|
||||
return TestRepo(repo)
|
||||
}
|
||||
|
||||
|
||||
7
util/cache/appstate/cache.go
vendored
7
util/cache/appstate/cache.go
vendored
@@ -3,6 +3,7 @@ package appstate
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
@@ -61,6 +62,9 @@ func (c *Cache) GetAppManagedResources(appName string, res *[]*appv1.ResourceDif
|
||||
}
|
||||
|
||||
func (c *Cache) SetAppManagedResources(appName string, managedResources []*appv1.ResourceDiff) error {
|
||||
sort.Slice(managedResources, func(i, j int) bool {
|
||||
return managedResources[i].FullName() < managedResources[j].FullName()
|
||||
})
|
||||
return c.SetItem(appManagedResourcesKey(appName), managedResources, c.appStateCacheExpiration, managedResources == nil)
|
||||
}
|
||||
|
||||
@@ -82,6 +86,9 @@ func (c *Cache) OnAppResourcesTreeChanged(ctx context.Context, appName string, c
|
||||
}
|
||||
|
||||
func (c *Cache) SetAppResourcesTree(appName string, resourcesTree *appv1.ApplicationTree) error {
|
||||
if resourcesTree != nil {
|
||||
resourcesTree.Normalize()
|
||||
}
|
||||
err := c.SetItem(appResourcesTreeKey(appName), resourcesTree, c.appStateCacheExpiration, resourcesTree == nil)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
14
util/cache/cache.go
vendored
14
util/cache/cache.go
vendored
@@ -78,14 +78,22 @@ type Cache struct {
|
||||
client CacheClient
|
||||
}
|
||||
|
||||
func (c *Cache) GetClient() CacheClient {
|
||||
return c.client
|
||||
}
|
||||
|
||||
func (c *Cache) SetClient(client CacheClient) {
|
||||
c.client = client
|
||||
}
|
||||
|
||||
func (c *Cache) SetItem(key string, item interface{}, expiration time.Duration, delete bool) error {
|
||||
if item == nil {
|
||||
return fmt.Errorf("cannot set item to nil for key %s", key)
|
||||
}
|
||||
key = fmt.Sprintf("%s|%s", key, common.CacheVersion)
|
||||
if delete {
|
||||
return c.client.Delete(key)
|
||||
} else {
|
||||
if item == nil {
|
||||
return fmt.Errorf("cannot set item to nil for key %s", key)
|
||||
}
|
||||
return c.client.Set(&Item{Object: item, Key: key, Expiration: expiration})
|
||||
}
|
||||
}
|
||||
|
||||
20
util/cache/inmemory.go
vendored
20
util/cache/inmemory.go
vendored
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
gocache "github.com/patrickmn/go-cache"
|
||||
@@ -29,6 +30,25 @@ func (i *InMemoryCache) Set(item *Item) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasSame returns true if key with the same value already present in cache
|
||||
func (i *InMemoryCache) HasSame(key string, obj interface{}) (bool, error) {
|
||||
var buf bytes.Buffer
|
||||
err := gob.NewEncoder(&buf).Encode(obj)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
bufIf, found := i.memCache.Get(key)
|
||||
if !found {
|
||||
return false, nil
|
||||
}
|
||||
existingBuf, ok := bufIf.(bytes.Buffer)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("InMemoryCache has unexpected entry: %v", existingBuf))
|
||||
}
|
||||
return bytes.Equal(buf.Bytes(), existingBuf.Bytes()), nil
|
||||
}
|
||||
|
||||
func (i *InMemoryCache) Get(key string, obj interface{}) error {
|
||||
bufIf, found := i.memCache.Get(key)
|
||||
if !found {
|
||||
|
||||
19
util/cache/redis.go
vendored
19
util/cache/redis.go
vendored
@@ -2,6 +2,7 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
ioutil "github.com/argoproj/argo-cd/util/io"
|
||||
@@ -30,20 +31,28 @@ func (r *redisCache) Set(item *Item) error {
|
||||
expiration = r.expiration
|
||||
}
|
||||
|
||||
val, err := json.Marshal(item.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.cache.Set(&rediscache.Item{
|
||||
Key: item.Key,
|
||||
Value: item.Object,
|
||||
Value: val,
|
||||
TTL: expiration,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *redisCache) Get(key string, obj interface{}) error {
|
||||
err := r.cache.Get(context.TODO(), key, obj)
|
||||
|
||||
var data []byte
|
||||
err := r.cache.Get(context.TODO(), key, &data)
|
||||
if err == rediscache.ErrCacheMiss {
|
||||
return ErrCacheMiss
|
||||
err = ErrCacheMiss
|
||||
}
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(data, obj)
|
||||
}
|
||||
|
||||
func (r *redisCache) Delete(key string) error {
|
||||
|
||||
68
util/cache/twolevelclient.go
vendored
Normal file
68
util/cache/twolevelclient.go
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// NewTwoLevelClient creates cache client that proxies requests to given external cache and tries to minimize
|
||||
// number of requests to external client by storing cache entries in local in-memory cache.
|
||||
func NewTwoLevelClient(client CacheClient, inMemoryExpiration time.Duration) *twoLevelClient {
|
||||
return &twoLevelClient{inMemoryCache: NewInMemoryCache(inMemoryExpiration), externalCache: client}
|
||||
}
|
||||
|
||||
type twoLevelClient struct {
|
||||
inMemoryCache *InMemoryCache
|
||||
externalCache CacheClient
|
||||
}
|
||||
|
||||
// Set stores the given value in both in-memory and external cache.
|
||||
// Skip storing the value in external cache if the same value already exists in memory to avoid requesting external cache.
|
||||
func (c *twoLevelClient) Set(item *Item) error {
|
||||
has, err := c.inMemoryCache.HasSame(item.Key, item.Object)
|
||||
if has {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
log.Warnf("Failed to check key '%s' in in-memory cache: %v", item.Key, err)
|
||||
}
|
||||
err = c.inMemoryCache.Set(item)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to save key '%s' in in-memory cache: %v", item.Key, err)
|
||||
}
|
||||
return c.externalCache.Set(item)
|
||||
}
|
||||
|
||||
// Get returns cache value from in-memory cache if it present. Otherwise loads it from external cache and persists
|
||||
// in memory to avoid future requests to external cache.
|
||||
func (c *twoLevelClient) Get(key string, obj interface{}) error {
|
||||
err := c.inMemoryCache.Get(key, obj)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = c.externalCache.Get(key, obj)
|
||||
if err == nil {
|
||||
_ = c.inMemoryCache.Set(&Item{Key: key, Object: obj})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete deletes cache for given key in both in-memory and external cache.
|
||||
func (c *twoLevelClient) Delete(key string) error {
|
||||
err := c.inMemoryCache.Delete(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.externalCache.Delete(key)
|
||||
}
|
||||
|
||||
func (c *twoLevelClient) OnUpdated(ctx context.Context, key string, callback func() error) error {
|
||||
return c.externalCache.OnUpdated(ctx, key, callback)
|
||||
}
|
||||
|
||||
func (c *twoLevelClient) NotifyUpdated(key string) error {
|
||||
return c.externalCache.NotifyUpdated(key)
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
jwt "github.com/dgrijalva/jwt-go/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
@@ -296,14 +296,14 @@ type ServiceAccountClaims struct {
|
||||
}
|
||||
|
||||
// Valid satisfies the jwt.Claims interface to enable JWT parsing
|
||||
func (sac *ServiceAccountClaims) Valid() error {
|
||||
func (sac *ServiceAccountClaims) Valid(helper *jwt.ValidationHelper) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseServiceAccountToken parses a Kubernetes service account token
|
||||
func ParseServiceAccountToken(token string) (*ServiceAccountClaims, error) {
|
||||
parser := &jwt.Parser{
|
||||
SkipClaimsValidation: true,
|
||||
ValidationHelper: jwt.NewValidationHelper(jwt.WithoutClaimsValidation(), jwt.WithoutAudienceValidation()),
|
||||
}
|
||||
var claims ServiceAccountClaims
|
||||
_, _, err := parser.ParseUnverified(token, &claims)
|
||||
|
||||
@@ -53,6 +53,7 @@ func GenerateDexConfigYAML(settings *settings.ArgoCDSettings) ([]byte, error) {
|
||||
"public": true,
|
||||
"redirectURIs": []string{
|
||||
"http://localhost",
|
||||
"http://localhost:8085/auth/callback",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -34,13 +34,15 @@ func Run(cmd *exec.Cmd) (string, error) {
|
||||
}
|
||||
|
||||
func RunWithRedactor(cmd *exec.Cmd, redactor func(text string) string) (string, error) {
|
||||
opts := argoexec.CmdOpts{Timeout: timeout}
|
||||
span := tracing.NewLoggingTracer(log.NewLogrusLogger(logrus.New())).StartSpan(fmt.Sprintf("exec %v", cmd.Args[0]))
|
||||
span.SetBaggageItem("dir", fmt.Sprintf("%v", cmd.Dir))
|
||||
span.SetBaggageItem("args", fmt.Sprintf("%v", cmd.Args))
|
||||
defer span.Finish()
|
||||
opts := argoexec.CmdOpts{Timeout: timeout}
|
||||
if redactor != nil {
|
||||
span.SetBaggageItem("args", redactor(fmt.Sprintf("%v", cmd.Args)))
|
||||
opts.Redactor = redactor
|
||||
} else {
|
||||
span.SetBaggageItem("args", fmt.Sprintf("%v", cmd.Args))
|
||||
}
|
||||
defer span.Finish()
|
||||
return argoexec.RunCommandExt(cmd, opts)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package exec
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -27,3 +28,14 @@ func TestRun(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, out)
|
||||
}
|
||||
|
||||
func TestHideUsernamePassword(t *testing.T) {
|
||||
_, err := RunWithRedactor(exec.Command("helm registry login https://charts.bitnami.com/bitnami", "--username", "foo", "--password", "bar"), nil)
|
||||
assert.NotEmpty(t, err)
|
||||
|
||||
var redactor = func(text string) string {
|
||||
return regexp.MustCompile("(--username|--password) [^ ]*").ReplaceAllString(text, "$1 ******")
|
||||
}
|
||||
_, err = RunWithRedactor(exec.Command("helm registry login https://charts.bitnami.com/bitnami", "--username", "foo", "--password", "bar"), redactor)
|
||||
assert.NotEmpty(t, err)
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
|
||||
// ServeHealthCheck serves the health check endpoint.
|
||||
// ServeHealthCheck relies on the provided function to return an error if unhealthy and nil otherwise.
|
||||
func ServeHealthCheck(mux *http.ServeMux, f func() error) {
|
||||
func ServeHealthCheck(mux *http.ServeMux, f func(r *http.Request) error) {
|
||||
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := f(); err != nil {
|
||||
if err := f(r); err != nil {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
log.Errorln(w, err)
|
||||
} else {
|
||||
|
||||
@@ -21,7 +21,7 @@ func TestHealthCheck(t *testing.T) {
|
||||
c <- listener.Addr().String()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
ServeHealthCheck(mux, func() error {
|
||||
ServeHealthCheck(mux, func(r *http.Request) error {
|
||||
if sentinel {
|
||||
return fmt.Errorf("This is a dummy error")
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ func (c *nativeHelmChart) ensureHelmChartRepoPath() error {
|
||||
}
|
||||
|
||||
func (c *nativeHelmChart) CleanChartCache(chart string, version *semver.Version) error {
|
||||
return os.RemoveAll(c.getChartPath(chart, version))
|
||||
return os.RemoveAll(c.getCachedChartPath(chart, version))
|
||||
}
|
||||
|
||||
func (c *nativeHelmChart) ExtractChart(chart string, version *semver.Version) (string, io.Closer, error) {
|
||||
@@ -98,77 +98,99 @@ func (c *nativeHelmChart) ExtractChart(chart string, version *semver.Version) (s
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
chartPath := c.getChartPath(chart, version)
|
||||
|
||||
c.repoLock.Lock(chartPath)
|
||||
defer c.repoLock.Unlock(chartPath)
|
||||
// always use Helm V3 since we don't have chart content to determine correct Helm version
|
||||
helmCmd, err := NewCmdWithVersion(c.repoPath, HelmV3, c.enableOci)
|
||||
|
||||
exists, err := fileExist(chartPath)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
defer helmCmd.Close()
|
||||
|
||||
_, err = helmCmd.Init()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
// throw away temp directory that stores extracted chart and should be deleted as soon as no longer needed by returned closer
|
||||
tempDir, err := ioutil.TempDir("", "helm")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
cachedChartPath := c.getCachedChartPath(chart, version)
|
||||
|
||||
c.repoLock.Lock(cachedChartPath)
|
||||
defer c.repoLock.Unlock(cachedChartPath)
|
||||
|
||||
// check if chart tar is already downloaded
|
||||
exists, err := fileExist(cachedChartPath)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
// always use Helm V3 since we don't have chart content to determine correct Helm version
|
||||
helmCmd, err := NewCmdWithVersion(c.repoPath, HelmV3, c.enableOci)
|
||||
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
defer helmCmd.Close()
|
||||
|
||||
_, err = helmCmd.Init()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if c.enableOci {
|
||||
_, err = helmCmd.Login(c.repoURL, c.creds)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
defer func() {
|
||||
_, _ = helmCmd.Logout(c.repoURL, c.creds)
|
||||
}()
|
||||
}
|
||||
|
||||
// (1) because `helm fetch` downloads an arbitrary file name, we download to an empty temp directory
|
||||
// create empty temp directory to extract chart from the registry
|
||||
tempDest, err := ioutil.TempDir("", "helm")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tempDest) }()
|
||||
_, err = helmCmd.Fetch(c.repoURL, chart, version.String(), tempDest, c.creds)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if c.enableOci {
|
||||
_, err = helmCmd.Export(c.repoURL, chart, version.String(), tempDest)
|
||||
if c.creds.Password != "" && c.creds.Username != "" {
|
||||
_, err = helmCmd.Login(c.repoURL, c.creds)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = helmCmd.Logout(c.repoURL, c.creds)
|
||||
}()
|
||||
}
|
||||
|
||||
// 'helm chart pull' ensures that chart is downloaded into local repository cache
|
||||
_, err = helmCmd.ChartPull(c.repoURL, chart, version.String())
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
// 'helm chart export' copies cached chart into temp directory
|
||||
_, err = helmCmd.ChartExport(c.repoURL, chart, version.String(), tempDest)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
// use downloaded chart content to produce tar file in expected cache location
|
||||
cmd := exec.Command("tar", "-zcvf", cachedChartPath, normalizeChartName(chart))
|
||||
cmd.Dir = tempDest
|
||||
_, err = executil.Run(cmd)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
} else {
|
||||
_, err = helmCmd.Fetch(c.repoURL, chart, version.String(), tempDest, c.creds)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
// 'helm fetch' file downloads chart into the tgz file and we move that to where we want it
|
||||
infos, err := ioutil.ReadDir(tempDest)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if len(infos) != 1 {
|
||||
return "", nil, fmt.Errorf("expected 1 file, found %v", len(infos))
|
||||
}
|
||||
err = os.Rename(filepath.Join(tempDest, infos[0].Name()), cachedChartPath)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// (2) then we assume that the only file downloaded into the directory is the tgz file
|
||||
// and we move that to where we want it
|
||||
infos, err := ioutil.ReadDir(tempDest)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if len(infos) != 1 {
|
||||
return "", nil, fmt.Errorf("expected 1 file, found %v", len(infos))
|
||||
}
|
||||
err = os.Rename(filepath.Join(tempDest, infos[0].Name()), chartPath)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
// untar helm chart into throw away temp directory which should be deleted as soon as no longer needed
|
||||
tempDir, err := ioutil.TempDir("", "helm")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
cmd := exec.Command("tar", "-zxvf", chartPath)
|
||||
|
||||
cmd := exec.Command("tar", "-zxvf", cachedChartPath)
|
||||
cmd.Dir = tempDir
|
||||
_, err = executil.Run(cmd)
|
||||
if err != nil {
|
||||
@@ -214,16 +236,19 @@ func (c *nativeHelmChart) TestHelmOCI() (bool, error) {
|
||||
}
|
||||
defer helmCmd.Close()
|
||||
|
||||
_, err = helmCmd.Login(c.repoURL, c.creds)
|
||||
if err != nil {
|
||||
return false, err
|
||||
// Looks like there is no good way to test access to OCI repo if credentials are not provided
|
||||
// just assume it is accessible
|
||||
if c.creds.Username != "" && c.creds.Password != "" {
|
||||
_, err = helmCmd.Login(c.repoURL, c.creds)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer func() {
|
||||
_, _ = helmCmd.Logout(c.repoURL, c.creds)
|
||||
}()
|
||||
|
||||
log.WithFields(log.Fields{"seconds": time.Since(start).Seconds()}).Info("took to test helm oci repository")
|
||||
}
|
||||
defer func() {
|
||||
_, _ = helmCmd.Logout(c.repoURL, c.creds)
|
||||
}()
|
||||
|
||||
log.WithFields(log.Fields{"seconds": time.Since(start).Seconds()}).Info("took to test helm oci repository")
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -293,6 +318,7 @@ func newTLSConfig(creds Creds) (*tls.Config, error) {
|
||||
|
||||
// Normalize a chart name for file system use, that is, if chart name is foo/bar/baz, returns the last component as chart name.
|
||||
func normalizeChartName(chart string) string {
|
||||
strings.Join(strings.Split(chart, "/"), "_")
|
||||
_, nc := path.Split(chart)
|
||||
// We do not want to return the empty string or something else related to filesystem access
|
||||
// Instead, return original string
|
||||
@@ -302,17 +328,11 @@ func normalizeChartName(chart string) string {
|
||||
return nc
|
||||
}
|
||||
|
||||
func (c *nativeHelmChart) getChartPath(chart string, version *semver.Version) string {
|
||||
if repoNamespace, chartName, isHelmOci := IsHelmOci(chart); isHelmOci {
|
||||
return path.Join(c.repoPath, fmt.Sprintf("%s-%s-%v.tgz", repoNamespace, normalizeChartName(chartName), version))
|
||||
}
|
||||
return path.Join(c.repoPath, fmt.Sprintf("%s-%v.tgz", normalizeChartName(chart), version))
|
||||
func (c *nativeHelmChart) getCachedChartPath(chart string, version *semver.Version) string {
|
||||
return path.Join(c.repoPath, fmt.Sprintf("%s-%v.tgz", strings.ReplaceAll(chart, "/", "_"), version))
|
||||
}
|
||||
|
||||
func IsHelmOci(chart string) (string, string, bool) {
|
||||
chartArray := strings.Split(chart, "/")
|
||||
if len(chartArray) == 2 {
|
||||
return chartArray[0], chartArray[1], true
|
||||
}
|
||||
return "", "", false
|
||||
// Only OCI registries support storing charts under sub-directories.
|
||||
func IsHelmOciChart(chart string) bool {
|
||||
return strings.Contains(chart, "/")
|
||||
}
|
||||
|
||||
@@ -211,26 +211,18 @@ func writeToTmp(data []byte) (string, io.Closer, error) {
|
||||
}
|
||||
|
||||
func (c *Cmd) Fetch(repo, chartName, version, destination string, creds Creds) (string, error) {
|
||||
args := []string{}
|
||||
|
||||
if _, _, isHelmOci := IsHelmOci(chartName); isHelmOci {
|
||||
args = append(args, "chart", "pull")
|
||||
repoUrl := fmt.Sprintf(repo + "/" + chartName + ":" + version)
|
||||
args = append(args, repoUrl)
|
||||
} else {
|
||||
args = append(args, c.pullCommand, "--destination", destination)
|
||||
if version != "" {
|
||||
args = append(args, "--version", version)
|
||||
}
|
||||
if creds.Username != "" {
|
||||
args = append(args, "--username", creds.Username)
|
||||
}
|
||||
if creds.Password != "" {
|
||||
args = append(args, "--password", creds.Password)
|
||||
}
|
||||
|
||||
args = append(args, "--repo", repo, chartName)
|
||||
args := []string{c.pullCommand, "--destination", destination}
|
||||
if version != "" {
|
||||
args = append(args, "--version", version)
|
||||
}
|
||||
if creds.Username != "" {
|
||||
args = append(args, "--username", creds.Username)
|
||||
}
|
||||
if creds.Password != "" {
|
||||
args = append(args, "--password", creds.Password)
|
||||
}
|
||||
|
||||
args = append(args, "--repo", repo, chartName)
|
||||
|
||||
if creds.CAPath != "" {
|
||||
args = append(args, "--ca-file", creds.CAPath)
|
||||
@@ -255,29 +247,16 @@ func (c *Cmd) Fetch(repo, chartName, version, destination string, creds Creds) (
|
||||
return c.run(args...)
|
||||
}
|
||||
|
||||
func (c *Cmd) Export(repo, chartName, version, destination string) (string, error) {
|
||||
output := ""
|
||||
var err error
|
||||
func (c *Cmd) ChartPull(repo string, chart string, version string) (string, error) {
|
||||
return c.run("chart", "pull", fmt.Sprintf("%s/%s:%s", repo, chart, version))
|
||||
}
|
||||
|
||||
func (c *Cmd) ChartExport(repo string, chartName string, version string, destination string) (string, error) {
|
||||
args := []string{"chart", "export"}
|
||||
repoUrl := fmt.Sprintf(repo + "/" + chartName + ":" + version)
|
||||
args = append(args, repoUrl, "--destination", destination)
|
||||
chartURL := fmt.Sprintf(repo + "/" + chartName + ":" + version)
|
||||
args = append(args, chartURL, "--destination", destination)
|
||||
|
||||
output, err = c.run(args...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// tar helm chart
|
||||
repoNamespace, repoName, _ := IsHelmOci(chartName)
|
||||
cmd := exec.Command("tar", "-zcvf", repoNamespace+"-"+repoName+"-"+version+".tgz", repoName)
|
||||
cmd.Dir = destination
|
||||
_, err = executil.Run(cmd)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(destination + "/" + repoName) }()
|
||||
|
||||
return output, nil
|
||||
return c.run(args...)
|
||||
}
|
||||
|
||||
func (c *Cmd) dependencyBuild() (string, error) {
|
||||
|
||||
@@ -2,9 +2,11 @@ package jwt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
jwtgo "github.com/dgrijalva/jwt-go"
|
||||
jwtgo "github.com/dgrijalva/jwt-go/v4"
|
||||
)
|
||||
|
||||
// MapClaims converts a jwt.Claims to a MapClaims
|
||||
@@ -21,8 +23,8 @@ func MapClaims(claims jwtgo.Claims) (jwtgo.MapClaims, error) {
|
||||
return mapClaims, nil
|
||||
}
|
||||
|
||||
// GetField extracts a field from the claims as a string
|
||||
func GetField(claims jwtgo.MapClaims, fieldName string) string {
|
||||
// StringField extracts a field from the claims as a string
|
||||
func StringField(claims jwtgo.MapClaims, fieldName string) string {
|
||||
if fieldIf, ok := claims[fieldName]; ok {
|
||||
if field, ok := fieldIf.(string); ok {
|
||||
return field
|
||||
@@ -31,6 +33,16 @@ func GetField(claims jwtgo.MapClaims, fieldName string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Float64Field extracts a field from the claims as a float64
|
||||
func Float64Field(claims jwtgo.MapClaims, fieldName string) float64 {
|
||||
if fieldIf, ok := claims[fieldName]; ok {
|
||||
if field, ok := fieldIf.(float64); ok {
|
||||
return field
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetScopeValues extracts the values of specified scopes from the claims
|
||||
func GetScopeValues(claims jwtgo.MapClaims, scopes []string) []string {
|
||||
groups := make([]string, 0)
|
||||
@@ -67,9 +79,13 @@ func GetID(m jwtgo.MapClaims) (string, error) {
|
||||
return "", fmt.Errorf("jti '%v' is not a string", m["jti"])
|
||||
}
|
||||
|
||||
// GetIssuedAt returns the issued at as an int64
|
||||
func GetIssuedAt(m jwtgo.MapClaims) (int64, error) {
|
||||
switch iat := m["iat"].(type) {
|
||||
// IssuedAt returns the issued at as an int64
|
||||
func IssuedAt(m jwtgo.MapClaims) (int64, error) {
|
||||
iatField, ok := m["iat"]
|
||||
if !ok {
|
||||
return 0, errors.New("token does not have iat claim")
|
||||
}
|
||||
switch iat := iatField.(type) {
|
||||
case float64:
|
||||
return int64(iat), nil
|
||||
case json.Number:
|
||||
@@ -81,6 +97,12 @@ func GetIssuedAt(m jwtgo.MapClaims) (int64, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// IssuedAtTime returns the issued at as a time.Time
|
||||
func IssuedAtTime(m jwtgo.MapClaims) (time.Time, error) {
|
||||
iat, err := IssuedAt(m)
|
||||
return time.Unix(iat, 0), err
|
||||
}
|
||||
|
||||
func Claims(in interface{}) jwtgo.Claims {
|
||||
claims, ok := in.(jwtgo.Claims)
|
||||
if ok {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
jwt "github.com/dgrijalva/jwt-go/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -36,3 +38,25 @@ func TestGetGroups(t *testing.T) {
|
||||
assert.Empty(t, GetGroups(jwt.MapClaims{}, []string{"groups"}))
|
||||
assert.Equal(t, []string{"foo"}, GetGroups(jwt.MapClaims{"groups": []string{"foo"}}, []string{"groups"}))
|
||||
}
|
||||
|
||||
func TestIssuedAtTime_Int64(t *testing.T) {
|
||||
// Tuesday, 1 December 2020 14:00:00
|
||||
claims := jwt.MapClaims{"iat": int64(1606831200)}
|
||||
issuedAt, err := IssuedAtTime(claims)
|
||||
assert.Nil(t, err)
|
||||
str := fmt.Sprint(issuedAt.UTC().Format("Mon Jan _2 15:04:05 2006"))
|
||||
assert.Equal(t, "Tue Dec 1 14:00:00 2020", str)
|
||||
}
|
||||
|
||||
func TestIssuedAtTime_Error_NoInt(t *testing.T) {
|
||||
claims := jwt.MapClaims{"iat": 1606831200}
|
||||
_, err := IssuedAtTime(claims)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestIssuedAtTime_Error_Missing(t *testing.T) {
|
||||
claims := jwt.MapClaims{}
|
||||
iat, err := IssuedAtTime(claims)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, time.Unix(0, 0), iat)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"os/user"
|
||||
"path"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
|
||||
configUtil "github.com/argoproj/argo-cd/util/config"
|
||||
)
|
||||
@@ -64,7 +64,7 @@ type User struct {
|
||||
// Claims returns the standard claims from the JWT claims
|
||||
func (u *User) Claims() (*jwt.StandardClaims, error) {
|
||||
parser := &jwt.Parser{
|
||||
SkipClaimsValidation: true,
|
||||
ValidationHelper: jwt.NewValidationHelper(jwt.WithoutClaimsValidation(), jwt.WithoutAudienceValidation()),
|
||||
}
|
||||
claims := jwt.StandardClaims{}
|
||||
_, _, err := parser.ParseUnverified(u.AuthToken, &claims)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user