mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-27 21:18:49 +01:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81630e6d50 | ||
|
|
e31fdb6105 | ||
|
|
2e194b1087 | ||
|
|
cec5799d97 | ||
|
|
7ab2ba361c | ||
|
|
1d5e15c600 | ||
|
|
f4f52212ba | ||
|
|
507c669259 | ||
|
|
993459aebc | ||
|
|
3293aa0bb7 | ||
|
|
aafb6ce0a3 | ||
|
|
d3d490632d | ||
|
|
a48bca03c7 | ||
|
|
6930ceb414 | ||
|
|
01b7a73922 | ||
|
|
ff2d9b918d | ||
|
|
cea91ce935 | ||
|
|
51b73096f5 | ||
|
|
2c166dac97 | ||
|
|
16c015f0cb | ||
|
|
7ca60cb957 | ||
|
|
d0c80ee0bb | ||
|
|
9f1a33865a | ||
|
|
f09c84f30a | ||
|
|
f7f7493e9f | ||
|
|
c1ddb53d29 | ||
|
|
e04288482d | ||
|
|
1f3e1ec803 | ||
|
|
c3423e8df3 | ||
|
|
5ef48c1123 | ||
|
|
e3ae286e2d | ||
|
|
7abf6713b4 | ||
|
|
0232073ccf | ||
|
|
860d8fd7e1 | ||
|
|
147ff80543 | ||
|
|
3800a1e49d | ||
|
|
fe80bdcfdc | ||
|
|
94c09dff59 | ||
|
|
3d9e4d439e | ||
|
|
4a955e25a0 | ||
|
|
4f99f251bf | ||
|
|
fd418cec0d | ||
|
|
dc291a629f | ||
|
|
e23a1c6026 | ||
|
|
aee88c6452 | ||
|
|
3f111cc640 | ||
|
|
596038fc0f |
1
USERS.md
1
USERS.md
@@ -37,6 +37,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Chargetrip](https://chargetrip.com)
|
||||
1. [Chime](https://www.chime.com)
|
||||
1. [Cisco ET&I](https://eti.cisco.com/)
|
||||
1. [Cobalt](https://www.cobalt.io/)
|
||||
1. [Codefresh](https://www.codefresh.io/)
|
||||
1. [Codility](https://www.codility.com/)
|
||||
1. [Commonbond](https://commonbond.co/)
|
||||
|
||||
@@ -162,8 +162,10 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
|
||||
}
|
||||
params["path"] = path.Dir(filePath)
|
||||
params["path.basename"] = path.Base(params["path"])
|
||||
params["path.filename"] = path.Base(filePath)
|
||||
params["path.basenameNormalized"] = sanitizeName(path.Base(params["path"]))
|
||||
for k, v := range strings.Split(strings.TrimSuffix(params["path"], params["path.basename"]), "/") {
|
||||
params["path.filenameNormalized"] = sanitizeName(path.Base(params["path.filename"]))
|
||||
for k, v := range strings.Split(params["path"], "/") {
|
||||
if len(v) > 0 {
|
||||
params["path["+strconv.Itoa(k)+"]"] = v
|
||||
}
|
||||
@@ -213,7 +215,7 @@ func (g *GitGenerator) generateParamsFromApps(requestedApps []string, _ *argopro
|
||||
params["path"] = a
|
||||
params["path.basename"] = path.Base(a)
|
||||
params["path.basenameNormalized"] = sanitizeName(path.Base(a))
|
||||
for k, v := range strings.Split(strings.TrimSuffix(params["path"], params["path.basename"]), "/") {
|
||||
for k, v := range strings.Split(params["path"], "/") {
|
||||
if len(v) > 0 {
|
||||
params["path["+strconv.Itoa(k)+"]"] = v
|
||||
}
|
||||
|
||||
@@ -47,6 +47,28 @@ func (a argoCDServiceMock) GetDirectories(ctx context.Context, repoURL string, r
|
||||
return args.Get(0).([]string), args.Error(1)
|
||||
}
|
||||
|
||||
func Test_generateParamsFromGitFile(t *testing.T) {
|
||||
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
|
||||
foo:
|
||||
bar: baz
|
||||
`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, []map[string]string{
|
||||
{
|
||||
"foo.bar": "baz",
|
||||
"path": "path/dir",
|
||||
"path.basename": "dir",
|
||||
"path.filename": "file_name.yaml",
|
||||
"path.basenameNormalized": "dir",
|
||||
"path.filenameNormalized": "file-name.yaml",
|
||||
"path[0]": "path",
|
||||
"path[1]": "dir",
|
||||
},
|
||||
}, params)
|
||||
}
|
||||
|
||||
func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
|
||||
cases := []struct {
|
||||
@@ -68,9 +90,9 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
},
|
||||
repoError: nil,
|
||||
expected: []map[string]string{
|
||||
{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1"},
|
||||
{"path": "app2", "path.basename": "app2", "path.basenameNormalized": "app2"},
|
||||
{"path": "app_3", "path.basename": "app_3", "path.basenameNormalized": "app-3"},
|
||||
{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "path[0]": "app1"},
|
||||
{"path": "app2", "path.basename": "app2", "path.basenameNormalized": "app2", "path[0]": "app2"},
|
||||
{"path": "app_3", "path.basename": "app_3", "path.basenameNormalized": "app-3", "path[0]": "app_3"},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
@@ -85,8 +107,8 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
},
|
||||
repoError: nil,
|
||||
expected: []map[string]string{
|
||||
{"path": "p1/app2", "path.basename": "app2", "path[0]": "p1", "path.basenameNormalized": "app2"},
|
||||
{"path": "p1/p2/app3", "path.basename": "app3", "path[0]": "p1", "path[1]": "p2", "path.basenameNormalized": "app3"},
|
||||
{"path": "p1/app2", "path.basename": "app2", "path[0]": "p1", "path[1]": "app2", "path.basenameNormalized": "app2"},
|
||||
{"path": "p1/p2/app3", "path.basename": "app3", "path[0]": "p1", "path[1]": "p2", "path[2]": "app3", "path.basenameNormalized": "app3"},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
@@ -102,9 +124,9 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
},
|
||||
repoError: nil,
|
||||
expected: []map[string]string{
|
||||
{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1"},
|
||||
{"path": "app2", "path.basename": "app2", "path.basenameNormalized": "app2"},
|
||||
{"path": "p2/app3", "path.basename": "app3", "path[0]": "p2", "path.basenameNormalized": "app3"},
|
||||
{"path": "app1", "path.basename": "app1", "path[0]": "app1", "path.basenameNormalized": "app1"},
|
||||
{"path": "app2", "path.basename": "app2", "path[0]": "app2", "path.basenameNormalized": "app2"},
|
||||
{"path": "p2/app3", "path.basename": "app3", "path[0]": "p2", "path[1]": "app3", "path.basenameNormalized": "app3"},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
@@ -120,9 +142,9 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
},
|
||||
repoError: nil,
|
||||
expected: []map[string]string{
|
||||
{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1"},
|
||||
{"path": "app2", "path.basename": "app2", "path.basenameNormalized": "app2"},
|
||||
{"path": "p2/app3", "path.basename": "app3", "path[0]": "p2", "path.basenameNormalized": "app3"},
|
||||
{"path": "app1", "path.basename": "app1", "path[0]": "app1", "path.basenameNormalized": "app1"},
|
||||
{"path": "app2", "path.basename": "app2", "path[0]": "app2", "path.basenameNormalized": "app2"},
|
||||
{"path": "p2/app3", "path.basename": "app3", "path[0]": "p2", "path[1]": "app3", "path.basenameNormalized": "app3"},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
@@ -238,7 +260,10 @@ func TestGitGenerateParamsFromFiles(t *testing.T) {
|
||||
"path": "cluster-config/production",
|
||||
"path.basename": "production",
|
||||
"path[0]": "cluster-config",
|
||||
"path[1]": "production",
|
||||
"path.basenameNormalized": "production",
|
||||
"path.filename": "config.json",
|
||||
"path.filenameNormalized": "config.json",
|
||||
},
|
||||
{
|
||||
"cluster.owner": "foo.bar@example.com",
|
||||
@@ -247,7 +272,10 @@ func TestGitGenerateParamsFromFiles(t *testing.T) {
|
||||
"path": "cluster-config/staging",
|
||||
"path.basename": "staging",
|
||||
"path[0]": "cluster-config",
|
||||
"path[1]": "staging",
|
||||
"path.basenameNormalized": "staging",
|
||||
"path.filename": "config.json",
|
||||
"path.filenameNormalized": "config.json",
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
@@ -305,7 +333,10 @@ func TestGitGenerateParamsFromFiles(t *testing.T) {
|
||||
"path": "cluster-config/production",
|
||||
"path.basename": "production",
|
||||
"path[0]": "cluster-config",
|
||||
"path[1]": "production",
|
||||
"path.basenameNormalized": "production",
|
||||
"path.filename": "config.json",
|
||||
"path.filenameNormalized": "config.json",
|
||||
},
|
||||
{
|
||||
"cluster.owner": "john.doe@example.com",
|
||||
@@ -314,7 +345,10 @@ func TestGitGenerateParamsFromFiles(t *testing.T) {
|
||||
"path": "cluster-config/production",
|
||||
"path.basename": "production",
|
||||
"path[0]": "cluster-config",
|
||||
"path[1]": "production",
|
||||
"path.basenameNormalized": "production",
|
||||
"path.filename": "config.json",
|
||||
"path.filenameNormalized": "config.json",
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
@@ -353,7 +387,10 @@ cluster:
|
||||
"path": "cluster-config/production",
|
||||
"path.basename": "production",
|
||||
"path[0]": "cluster-config",
|
||||
"path[1]": "production",
|
||||
"path.basenameNormalized": "production",
|
||||
"path.filename": "config.yaml",
|
||||
"path.filenameNormalized": "config.yaml",
|
||||
},
|
||||
{
|
||||
"cluster.owner": "foo.bar@example.com",
|
||||
@@ -362,7 +399,10 @@ cluster:
|
||||
"path": "cluster-config/staging",
|
||||
"path.basename": "staging",
|
||||
"path[0]": "cluster-config",
|
||||
"path[1]": "staging",
|
||||
"path.basenameNormalized": "staging",
|
||||
"path.filename": "config.yaml",
|
||||
"path.filenameNormalized": "config.yaml",
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
@@ -393,7 +433,10 @@ cluster:
|
||||
"path": "cluster-config/production",
|
||||
"path.basename": "production",
|
||||
"path[0]": "cluster-config",
|
||||
"path[1]": "production",
|
||||
"path.basenameNormalized": "production",
|
||||
"path.filename": "config.yaml",
|
||||
"path.filenameNormalized": "config.yaml",
|
||||
},
|
||||
{
|
||||
"cluster.owner": "john.doe@example.com",
|
||||
@@ -402,7 +445,10 @@ cluster:
|
||||
"path": "cluster-config/production",
|
||||
"path.basename": "production",
|
||||
"path[0]": "cluster-config",
|
||||
"path[1]": "production",
|
||||
"path.basenameNormalized": "production",
|
||||
"path.filename": "config.yaml",
|
||||
"path.filenameNormalized": "config.yaml",
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
|
||||
@@ -81,6 +81,7 @@ func NewCommand() *cobra.Command {
|
||||
redisClient *redis.Client
|
||||
disableTLS bool
|
||||
maxCombinedDirectoryManifestsSize string
|
||||
cmpTarExcludedGlobs []string
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -113,6 +114,7 @@ func NewCommand() *cobra.Command {
|
||||
PauseGenerationOnFailureForRequests: getPauseGenerationOnFailureForRequests(),
|
||||
SubmoduleEnabled: getSubmoduleEnabled(),
|
||||
MaxCombinedDirectoryManifestsSize: maxCombinedDirectoryManifestsQuantity,
|
||||
CMPTarExcludedGlobs: cmpTarExcludedGlobs,
|
||||
}, askPassServer)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -189,6 +191,7 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringVar(&otlpAddress, "otlp-address", env.StringFromEnv("ARGOCD_REPO_SERVER_OTLP_ADDRESS", ""), "OpenTelemetry collector address to send traces to")
|
||||
command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_TLS", false), "Disable TLS on the gRPC endpoint")
|
||||
command.Flags().StringVar(&maxCombinedDirectoryManifestsSize, "max-combined-directory-manifests-size", env.StringFromEnv("ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE", "10M"), "Max combined size of manifest files in a directory-type Application")
|
||||
command.Flags().StringArrayVar(&cmpTarExcludedGlobs, "plugin-tar-exclude", env.StringsFromEnv("ARGOCD_REPO_SERVER_PLUGIN_TAR_EXCLUSIONS", []string{}, ";"), "Globs to filter when sending tarballs to plugins.")
|
||||
|
||||
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
|
||||
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
|
||||
@@ -153,6 +153,7 @@ func NewCommand() *cobra.Command {
|
||||
stats.StartStatsTicker(10 * time.Minute)
|
||||
stats.RegisterHeapDumper("memprofile")
|
||||
argocd := server.NewServer(context.Background(), argoCDOpts)
|
||||
argocd.Init(context.Background())
|
||||
lns, err := argocd.Listen()
|
||||
errors.CheckError(err)
|
||||
for {
|
||||
|
||||
@@ -215,6 +215,7 @@ func StartLocalServer(clientOpts *apiclient.ClientOptions, ctxStr string, port *
|
||||
ListenHost: *address,
|
||||
RepoClientset: &forwardRepoClientset{namespace: namespace, context: ctxStr},
|
||||
})
|
||||
srv.Init(ctx)
|
||||
|
||||
lns, err := srv.Listen()
|
||||
if err != nil {
|
||||
|
||||
@@ -68,7 +68,8 @@ argocd login cd.argoproj.io --core`,
|
||||
server = "kubernetes"
|
||||
} else {
|
||||
server = args[0]
|
||||
tlsTestResult, err := grpc_util.TestTLS(server)
|
||||
dialTime := 30 * time.Second
|
||||
tlsTestResult, err := grpc_util.TestTLS(server, dialTime)
|
||||
errors.CheckError(err)
|
||||
if !tlsTestResult.TLS {
|
||||
if !globalClientOpts.PlainText {
|
||||
|
||||
@@ -2,12 +2,13 @@ package cmpserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
|
||||
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"
|
||||
@@ -24,6 +25,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/v2/server/version"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
grpc_util "github.com/argoproj/argo-cd/v2/util/grpc"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
)
|
||||
|
||||
// ArgoCDCMPServer is the config management plugin server implementation
|
||||
@@ -61,6 +63,11 @@ func NewServer(initConstants plugin.CMPServerInitConstants) (*ArgoCDCMPServer, e
|
||||
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(streamInterceptors...)),
|
||||
grpc.MaxRecvMsgSize(apiclient.MaxGRPCMessageSize),
|
||||
grpc.MaxSendMsgSize(apiclient.MaxGRPCMessageSize),
|
||||
grpc.KeepaliveEnforcementPolicy(
|
||||
keepalive.EnforcementPolicy{
|
||||
MinTime: common.GRPCKeepAliveEnforcementMinimum,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
return &ArgoCDCMPServer{
|
||||
|
||||
@@ -286,3 +286,10 @@ const (
|
||||
// AnnotationApplicationRefresh is an annotation that is added when an ApplicationSet is requested to be refreshed by a webhook. The ApplicationSet controller will remove this annotation at the end of reconcilation.
|
||||
AnnotationApplicationSetRefresh = "argocd.argoproj.io/application-set-refresh"
|
||||
)
|
||||
|
||||
// gRPC settings
|
||||
const (
|
||||
GRPCKeepAliveEnforcementMinimum = 10 * time.Second
|
||||
// Keep alive is 2x enforcement minimum to ensure network jitter does not introduce ENHANCE_YOUR_CALM errors
|
||||
GRPCKeepAliveTime = 2 * GRPCKeepAliveEnforcementMinimum
|
||||
)
|
||||
|
||||
12
controller/cache/cache.go
vendored
12
controller/cache/cache.go
vendored
@@ -176,6 +176,7 @@ func NewLiveStateCache(
|
||||
type cacheSettings struct {
|
||||
clusterSettings clustercache.Settings
|
||||
appInstanceLabelKey string
|
||||
trackingMethod appv1.TrackingMethod
|
||||
}
|
||||
|
||||
type liveStateCache struct {
|
||||
@@ -210,7 +211,7 @@ func (c *liveStateCache) loadCacheSettings() (*cacheSettings, error) {
|
||||
ResourceHealthOverride: lua.ResourceHealthOverrides(resourceOverrides),
|
||||
ResourcesFilter: resourcesFilter,
|
||||
}
|
||||
return &cacheSettings{clusterSettings, appInstanceLabelKey}, nil
|
||||
return &cacheSettings{clusterSettings, appInstanceLabelKey, argo.GetTrackingMethod(c.settingsMgr)}, nil
|
||||
}
|
||||
|
||||
func asResourceNode(r *clustercache.Resource) appv1.ResourceNode {
|
||||
@@ -354,7 +355,8 @@ func isTransientNetworkErr(err error) bool {
|
||||
}
|
||||
if strings.Contains(errorString, "net/http: TLS handshake timeout") ||
|
||||
strings.Contains(errorString, "i/o timeout") ||
|
||||
strings.Contains(errorString, "connection timed out") {
|
||||
strings.Contains(errorString, "connection timed out") ||
|
||||
strings.Contains(errorString, "connection reset by peer") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -387,7 +389,6 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
|
||||
return nil, fmt.Errorf("controller is configured to ignore cluster %s", cluster.Server)
|
||||
}
|
||||
|
||||
trackingMethod := argo.GetTrackingMethod(c.settingsMgr)
|
||||
clusterCacheOpts := []clustercache.UpdateSettingsFunc{
|
||||
clustercache.SetListSemaphore(semaphore.NewWeighted(clusterCacheListSemaphoreSize)),
|
||||
clustercache.SetListPageSize(clusterCacheListPageSize),
|
||||
@@ -400,9 +401,12 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
|
||||
clustercache.SetPopulateResourceInfoHandler(func(un *unstructured.Unstructured, isRoot bool) (interface{}, bool) {
|
||||
res := &ResourceInfo{}
|
||||
populateNodeInfo(un, res)
|
||||
c.lock.RLock()
|
||||
cacheSettings := c.cacheSettings
|
||||
c.lock.RUnlock()
|
||||
res.Health, _ = health.GetResourceHealth(un, cacheSettings.clusterSettings.ResourceHealthOverride)
|
||||
|
||||
appName := c.resourceTracking.GetAppName(un, cacheSettings.appInstanceLabelKey, trackingMethod)
|
||||
appName := c.resourceTracking.GetAppName(un, cacheSettings.appInstanceLabelKey, cacheSettings.trackingMethod)
|
||||
if isRoot && appName != "" {
|
||||
res.AppName = appName
|
||||
}
|
||||
|
||||
4
controller/cache/cache_test.go
vendored
4
controller/cache/cache_test.go
vendored
@@ -111,6 +111,7 @@ func TestIsRetryableError(t *testing.T) {
|
||||
tlsHandshakeTimeoutErr net.Error = netError("net/http: TLS handshake timeout")
|
||||
ioTimeoutErr net.Error = netError("i/o timeout")
|
||||
connectionTimedout net.Error = netError("connection timed out")
|
||||
connectionReset net.Error = netError("connection reset by peer")
|
||||
)
|
||||
t.Run("Nil", func(t *testing.T) {
|
||||
assert.False(t, isRetryableError(nil))
|
||||
@@ -148,4 +149,7 @@ func TestIsRetryableError(t *testing.T) {
|
||||
t.Run("ConnectionTimeout", func(t *testing.T) {
|
||||
assert.True(t, isRetryableError(connectionTimedout))
|
||||
})
|
||||
t.Run("ConnectionReset", func(t *testing.T) {
|
||||
assert.True(t, isRetryableError(connectionReset))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -494,6 +494,8 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
}
|
||||
gvk := obj.GroupVersionKind()
|
||||
|
||||
isSelfReferencedObj := m.isSelfReferencedObj(liveObj, appLabelKey, trackingMethod)
|
||||
|
||||
resState := v1alpha1.ResourceStatus{
|
||||
Namespace: obj.GetNamespace(),
|
||||
Name: obj.GetName(),
|
||||
@@ -501,7 +503,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
Version: gvk.Version,
|
||||
Group: gvk.Group,
|
||||
Hook: hookutil.IsHook(obj),
|
||||
RequiresPruning: targetObj == nil && liveObj != nil,
|
||||
RequiresPruning: targetObj == nil && liveObj != nil && isSelfReferencedObj,
|
||||
}
|
||||
|
||||
var diffResult diff.DiffResult
|
||||
@@ -510,8 +512,11 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
} else {
|
||||
diffResult = diff.DiffResult{Modified: false, NormalizedLive: []byte("{}"), PredictedLive: []byte("{}")}
|
||||
}
|
||||
if resState.Hook || ignore.Ignore(obj) || (targetObj != nil && hookutil.Skip(targetObj)) {
|
||||
// For resource hooks or skipped resources, don't store sync status, and do not affect overall sync status
|
||||
if resState.Hook || ignore.Ignore(obj) || (targetObj != nil && hookutil.Skip(targetObj)) || !isSelfReferencedObj {
|
||||
// For resource hooks, skipped resources or objects that may have
|
||||
// been created by another controller with annotations copied from
|
||||
// the source object, don't store sync status, and do not affect
|
||||
// overall sync status
|
||||
} else if diffResult.Modified || targetObj == nil || liveObj == nil {
|
||||
// Set resource state to OutOfSync since one of the following is true:
|
||||
// * target and live resource are different
|
||||
@@ -667,3 +672,33 @@ func NewAppStateManager(
|
||||
resourceTracking: resourceTracking,
|
||||
}
|
||||
}
|
||||
|
||||
// isSelfReferencedObj returns whether the given obj is managed by the application
|
||||
// according to the values in the tracking annotation. It returns true when all
|
||||
// of the properties in the annotation (name, namespace, group and kind) match
|
||||
// the properties of the inspected object, or if the tracking method used does
|
||||
// not provide the required properties for matching.
|
||||
func (m *appStateManager) isSelfReferencedObj(obj *unstructured.Unstructured, appLabelKey string, trackingMethod v1alpha1.TrackingMethod) bool {
|
||||
if obj == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// If tracking method doesn't contain required metadata for this check,
|
||||
// we are not able to determine and just assume the object to be managed.
|
||||
if trackingMethod == argo.TrackingMethodLabel {
|
||||
return true
|
||||
}
|
||||
|
||||
// In order for us to assume obj to be managed by this application, the
|
||||
// values from the annotation have to match the properties from the live
|
||||
// object.
|
||||
appInstance := m.resourceTracking.GetAppInstance(obj, appLabelKey, trackingMethod)
|
||||
if appInstance != nil {
|
||||
return obj.GetNamespace() == appInstance.Namespace &&
|
||||
obj.GetName() == appInstance.Name &&
|
||||
obj.GetObjectKind().GroupVersionKind().Group == appInstance.Group &&
|
||||
obj.GetObjectKind().GroupVersionKind().Kind == appInstance.Kind
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
. "github.com/argoproj/gitops-engine/pkg/utils/testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
corev1 "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/runtime"
|
||||
@@ -21,6 +22,7 @@ import (
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/test"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
)
|
||||
|
||||
// TestCompareAppStateEmpty tests comparison when both git and live have no objects
|
||||
@@ -770,3 +772,128 @@ func TestComparisonResult_GetSyncStatus(t *testing.T) {
|
||||
|
||||
assert.Equal(t, status, res.GetSyncStatus())
|
||||
}
|
||||
|
||||
func TestIsLiveResourceManaged(t *testing.T) {
|
||||
managedObj := kube.MustToUnstructured(&corev1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "configmap1",
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{
|
||||
common.AnnotationKeyAppInstance: "guestbook:/ConfigMap:default/configmap1",
|
||||
},
|
||||
},
|
||||
})
|
||||
managedObjWithLabel := kube.MustToUnstructured(&corev1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "configmap1",
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
common.LabelKeyAppInstance: "guestbook",
|
||||
},
|
||||
},
|
||||
})
|
||||
unmanagedObjWrongName := kube.MustToUnstructured(&corev1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "configmap2",
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{
|
||||
common.AnnotationKeyAppInstance: "guestbook:/ConfigMap:default/configmap1",
|
||||
},
|
||||
},
|
||||
})
|
||||
unmanagedObjWrongKind := kube.MustToUnstructured(&corev1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "configmap2",
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{
|
||||
common.AnnotationKeyAppInstance: "guestbook:/Service:default/configmap2",
|
||||
},
|
||||
},
|
||||
})
|
||||
unmanagedObjWrongGroup := kube.MustToUnstructured(&corev1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "configmap2",
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{
|
||||
common.AnnotationKeyAppInstance: "guestbook:apps/ConfigMap:default/configmap2",
|
||||
},
|
||||
},
|
||||
})
|
||||
unmanagedObjWrongNamespace := kube.MustToUnstructured(&corev1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "configmap2",
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{
|
||||
common.AnnotationKeyAppInstance: "guestbook:/ConfigMap:fakens/configmap2",
|
||||
},
|
||||
},
|
||||
})
|
||||
ctrl := newFakeController(&fakeData{
|
||||
apps: []runtime.Object{app, &defaultProj},
|
||||
manifestResponse: &apiclient.ManifestResponse{
|
||||
Manifests: []string{},
|
||||
Namespace: test.FakeDestNamespace,
|
||||
Server: test.FakeClusterURL,
|
||||
Revision: "abc123",
|
||||
},
|
||||
managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
|
||||
kube.GetResourceKey(managedObj): managedObj,
|
||||
kube.GetResourceKey(unmanagedObjWrongName): unmanagedObjWrongName,
|
||||
kube.GetResourceKey(unmanagedObjWrongKind): unmanagedObjWrongKind,
|
||||
kube.GetResourceKey(unmanagedObjWrongGroup): unmanagedObjWrongGroup,
|
||||
kube.GetResourceKey(unmanagedObjWrongNamespace): unmanagedObjWrongNamespace,
|
||||
},
|
||||
})
|
||||
|
||||
manager := ctrl.appStateManager.(*appStateManager)
|
||||
|
||||
// Managed resource w/ annotations
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObj, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObj, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
|
||||
// Managed resource w/ label
|
||||
assert.True(t, manager.isSelfReferencedObj(managedObjWithLabel, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
|
||||
// Wrong resource name
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongName, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongName, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
|
||||
// Wrong resource group
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongGroup, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongGroup, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
|
||||
// Wrong resource kind
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongKind, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongKind, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
|
||||
// Wrong resource namespace
|
||||
assert.True(t, manager.isSelfReferencedObj(unmanagedObjWrongNamespace, common.AnnotationKeyAppInstance, argo.TrackingMethodLabel))
|
||||
assert.False(t, manager.isSelfReferencedObj(unmanagedObjWrongNamespace, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotationAndLabel))
|
||||
|
||||
// Nil resource
|
||||
assert.True(t, manager.isSelfReferencedObj(nil, common.AnnotationKeyAppInstance, argo.TrackingMethodAnnotation))
|
||||
}
|
||||
|
||||
BIN
docs/assets/terminal.png
Normal file
BIN
docs/assets/terminal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 145 KiB |
@@ -4,10 +4,10 @@ You can download the latest Argo CD version from [the latest release page of thi
|
||||
|
||||
## Linux and WSL
|
||||
|
||||
### ArchLinux User Repository ([AUR](https://aur.archlinux.org/packages/))
|
||||
### ArchLinux
|
||||
|
||||
```bash
|
||||
yay -Sy argocd-bin
|
||||
pacman -S argocd
|
||||
```
|
||||
|
||||
### Homebrew
|
||||
|
||||
@@ -24,8 +24,7 @@ You will need at least the following things in your toolchain in order to develo
|
||||
|
||||
* A Kubernetes cluster. You won't need a fully blown multi-master, multi-node cluster, but you will need something like K3S, Minikube or microk8s. You will also need a working Kubernetes client (`kubectl`) configuration in your development environment. The configuration must reside in `~/.kube/config` and the API server URL must point to the IP address of your local machine (or VM), and **not** to `localhost` or `127.0.0.1` if you are using the virtualized development toolchain (see below)
|
||||
|
||||
* You will also need a working Docker runtime environment, to be able to build and run images.
|
||||
The Docker version must be fairly recent, and support multi-stage builds. You should not work as root. Make your local user a member of the `docker` group to be able to control the Docker service on your machine.
|
||||
* You will also need a working Docker runtime environment, to be able to build and run images. The Docker version must be 17.05.0 or higher, to support multi-stage builds.
|
||||
|
||||
* Obviously, you will need a `git` client for pulling source code and pushing back your changes.
|
||||
|
||||
|
||||
@@ -174,7 +174,9 @@ argocd ... --grpc-web
|
||||
|
||||
## Why Am I Getting `x509: certificate signed by unknown authority` When Using The CLI?
|
||||
|
||||
Your not running your server with correct certs.
|
||||
The certificate created by default by Argo CD is not automatically recognised by the Argo CD CLI, in order
|
||||
to create a secure system you must follow the instructions to [install a certificate](/operator-manual/tls/)
|
||||
and configure your client OS to trust that certificate.
|
||||
|
||||
If you're not running in a production system (e.g. you're testing Argo CD out), try the `--insecure` flag:
|
||||
|
||||
|
||||
@@ -29,6 +29,13 @@ kubectl create namespace argocd
|
||||
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/core-install.yaml
|
||||
```
|
||||
|
||||
This default installation will have a self-signed certificate and cannot be accessed without a bit of extra work.
|
||||
Do one of:
|
||||
|
||||
* Follow the [instructions to configure a certificate](./operator-manual/tls) (and ensure that the client OS trusts it).
|
||||
* Configure the client OS to trust the self signed certificate.
|
||||
* Use the --insecure flag on all Argo CD CLI operations in this guide.
|
||||
|
||||
Use `argocd login --core` to [configure](./user-guide/commands/argocd_login.md) CLI access and skip steps 3-5.
|
||||
|
||||
## 2. Download Argo CD CLI
|
||||
|
||||
@@ -92,6 +92,12 @@ spec:
|
||||
- code: false
|
||||
name: foo
|
||||
value: bar
|
||||
# Exclude contains a glob pattern to match paths against that should be explicitly excluded from being used during
|
||||
# manifest generation. This takes precedence over the `include` field.
|
||||
exclude: string
|
||||
# Include contains a glob pattern to match paths against that should be explicitly included during manifest
|
||||
# generation. If this field is set, only matching manifests will be included.
|
||||
include: string
|
||||
|
||||
# plugin specific config
|
||||
plugin:
|
||||
|
||||
@@ -65,13 +65,15 @@ The generator parameters are:
|
||||
- `{{path.basename}}`: For any directory path within the Git repository that matches the `path` wildcard, the right-most path name is extracted (e.g. `/directory/directory2` would produce `directory2`).
|
||||
- `{{path.basenameNormalized}}`: This field is the same as `path.basename` with unsupported characters replaced with `-` (e.g. a `path` of `/directory/directory_2`, and `path.basename` of `directory_2` would produce `directory-2` here).
|
||||
|
||||
Whenever a new Helm chart/Kustomize YAML/Application/plain subfolder is added to the Git repository, the ApplicationSet controller will detect this change and automatically deploy the resulting manifests within new `Application` resources.
|
||||
**Note**: The right-most path name always becomes `{{path.basename}}`. For example, for `- path: /one/two/three/four`, `{{path.basename}}` is `four`.
|
||||
|
||||
Whenever a new Helm chart/Kustomize YAML/Application/plain subdirectory is added to the Git repository, the ApplicationSet controller will detect this change and automatically deploy the resulting manifests within new `Application` resources.
|
||||
|
||||
As with other generators, clusters *must* already be defined within Argo CD, in order to generate Applications for them.
|
||||
|
||||
### Exclude directories
|
||||
|
||||
The Git directory generator will automatically exclude folders that begin with `.` (such as `.git`).
|
||||
The Git directory generator will automatically exclude directories that begin with `.` (such as `.git`).
|
||||
|
||||
The Git directory generator also supports an `exclude` option in order to exclude directories in the repository from being scanned by the ApplicationSet controller:
|
||||
|
||||
@@ -105,7 +107,7 @@ spec:
|
||||
```
|
||||
(*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/examples/applicationset/git-generator-directory/excludes).*)
|
||||
|
||||
This example excludes the `exclude-helm-guestbook` directory from the list of directories scanned for this `ApplictionSet` resource.
|
||||
This example excludes the `exclude-helm-guestbook` directory from the list of directories scanned for this `ApplicationSet` resource.
|
||||
|
||||
!!! note "Exclude rules have higher priority than include rules"
|
||||
|
||||
@@ -205,7 +207,7 @@ Suppose you have a Git repository with the following directory structure:
|
||||
└── git-generator-files.yaml
|
||||
```
|
||||
|
||||
The folders are:
|
||||
The directories are:
|
||||
|
||||
- `guestbook` contains the Kubernetes resources for a simple guestbook application
|
||||
- `cluster-config` contains JSON/YAML files describing the individual engineering clusters: one for `dev` and one for `prod`.
|
||||
@@ -269,10 +271,16 @@ As with other generators, clusters *must* already be defined within Argo CD, in
|
||||
|
||||
In addition to the flattened key/value pairs from the configuration file, the following generator parameters are provided:
|
||||
|
||||
- `{{path}}`: The path to the folder containing matching configuration file within the Git repository. Example: `/clusters/clusterA`, if the config file was `/clusters/clusterA/config.json`
|
||||
- `{{path}}`: The path to the directory containing matching configuration file within the Git repository. Example: `/clusters/clusterA`, if the config file was `/clusters/clusterA/config.json`
|
||||
- `{{path[n]}}`: The path to the matching configuration file within the Git repository, split into array elements (`n` - array index). Example: `path[0]: clusters`, `path[1]: clusterA`
|
||||
- `{{path.basename}}`: Basename of the path to the folder containing the configuration file (e.g. `clusterA`, with the above example.)
|
||||
- `{{path.basename}}`: Basename of the path to the directory containing the configuration file (e.g. `clusterA`, with the above example.)
|
||||
- `{{path.basenameNormalized}}`: This field is the same as `path.basename` with unsupported characters replaced with `-` (e.g. a `path` of `/directory/directory_2`, and `path.basename` of `directory_2` would produce `directory-2` here).
|
||||
- `{{path.filename}}`: The matched filename. e.g., `config.json` in the above example.
|
||||
- `{{path.filenameNormalized}}`: The matched filename with unsupported characters replaced with `-`.
|
||||
|
||||
**Note**: The right-most *directory* name always becomes `{{path.basename}}`. For example, from `- path: /one/two/three/four/config.json`, `{{path.basename}}` will be `four`.
|
||||
The filename can always be accessed using `{{path.filename}}`.
|
||||
|
||||
|
||||
## Webhook Configuration
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ data:
|
||||
# The URLs to download additional ArgoCD binaries (besides the Linux amd64 binary included by default)
|
||||
# for different OS architectures. If provided, additional download buttons will be displayed on the help page.
|
||||
help.download.linux-arm64: "path-or-url-to-download"
|
||||
help.download.linux-ppc64le: "path-or-url-to-download"
|
||||
help.download.linux-s390x: "path-or-url-to-download"
|
||||
help.download.darwin-amd64: "path-or-url-to-download"
|
||||
help.download.darwin-arm64: "path-or-url-to-download"
|
||||
help.download.windows-amd64: "path-or-url-to-download"
|
||||
@@ -231,6 +233,14 @@ data:
|
||||
# If omitted, Argo CD injects the app name into the label: 'app.kubernetes.io/instance'
|
||||
application.instanceLabelKey: mycompany.com/appname
|
||||
|
||||
# You can change the resource tracking method Argo CD uses by changing the
|
||||
# setting application.resourceTrackingMethod to the desired method.
|
||||
# The following methods are available:
|
||||
# - label : Uses the application.instanceLabelKey label for tracking
|
||||
# - annotation : Uses an annotation with additional metadata for tracking instead of the label
|
||||
# - annotation+label : Also uses an annotation for tracking, but additionally labels the resource with the application name
|
||||
application.resourceTrackingMethod: annotation
|
||||
|
||||
# disables admin user. Admin is enabled by default
|
||||
admin.enabled: "false"
|
||||
# add an additional local user with apiKey and login capabilities
|
||||
@@ -288,3 +298,12 @@ data:
|
||||
|
||||
# exec.enabled indicates whether the UI exec feature is enabled. It is disabled by default.
|
||||
exec.enabled: "false"
|
||||
|
||||
# exec.shells restricts which shells are allowed for `exec`, and in which order they are attempted
|
||||
exec.shells: "bash,sh,powershell,cmd"
|
||||
|
||||
# oidc.tls.insecure.skip.verify determines whether certificate verification is skipped when verifying tokens with the
|
||||
# configured OIDC provider (either external or the bundled Dex instance). Setting this to "true" will cause JWT
|
||||
# token verification to pass despite the OIDC provider having an invalid certificate. Only set to "true" if you
|
||||
# understand the risks.
|
||||
oidc.tls.insecure.skip.verify: "false"
|
||||
|
||||
@@ -111,3 +111,5 @@ data:
|
||||
# for 300x memory expansion and N Applications running at the same time.
|
||||
# (example 10M max * 300 expansion * 10 Apps = 30G max theoretical memory usage).
|
||||
reposerver.max.combined.directory.manifests.size: '10M'
|
||||
# Paths to be excluded from the tarball streamed to plugins. Separate with ;
|
||||
reposerver.plugin.tar.exclusions: ""
|
||||
|
||||
@@ -81,3 +81,20 @@ resources:
|
||||
|
||||
The Argo CD can be installed using [Helm](https://helm.sh/). The Helm chart is currently community maintained and available at
|
||||
[argo-helm/charts/argo-cd](https://github.com/argoproj/argo-helm/tree/master/charts/argo-cd).
|
||||
|
||||
## Supported versions
|
||||
|
||||
Similar to the Kubernetes project, the supported versions of Argo CD at any given point in time are the latest patch releases for the N
|
||||
and N - 1 minor versions.
|
||||
These Argo CD versions are supported on the same versions of Kubernetes that are supported by Kubernetes itself (normally the last 3 released versions).
|
||||
|
||||
Essentially the Argo CD project follows the same support scheme as Kubernetes but for N, N-1 while Kubernetes supports N, N-1, N-2 versions.
|
||||
|
||||
For example if the latest minor version of ArgoCD are 2.4.3 and 2.3.5 while supported Kubernetes versions are 1.24, 1.23 and 1.22 then the following combinations are supported:
|
||||
|
||||
* Argo CD 2.4.3 on Kubernetes 1.24
|
||||
* Argo CD 2.4.3 on Kubernetes 1.23
|
||||
* Argo CD 2.4.3 on Kubernetes 1.22
|
||||
* Argo CD 2.3.5 on Kubernetes 1.24
|
||||
* Argo CD 2.3.5 on Kubernetes 1.23
|
||||
* Argo CD 2.3.5 on Kubernetes 1.22
|
||||
@@ -59,22 +59,7 @@ also use glob patterns in the action path: `action/*` (or regex patterns if you
|
||||
`exec` is a special resource. When enabled with the `create` action, this privilege allows a user to `exec` into Pods via
|
||||
the Argo CD UI. The functionality is similar to `kubectl exec`.
|
||||
|
||||
`exec` is a powerful privilege. It allows the user to run arbitrary code on any Pod managed by an Application for which
|
||||
they have `create` privileges. If the Pod mounts a ServiceAccount token (which is the default behavior of Kubernetes),
|
||||
then the user effectively has the same privileges as that ServiceAccount.
|
||||
|
||||
The exec feature is disabled entirely by default. To enable it, set the `exec.enabled` key to "true" on the argocd-cm
|
||||
ConfigMap. You will also need to add the following to the argocd-api-server Role (if you're using Argo CD in namespaced
|
||||
mode) or ClusterRole (if you're using Argo CD in cluster mode).
|
||||
|
||||
```yaml
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods/exec
|
||||
verbs:
|
||||
- create
|
||||
```
|
||||
See [Web-based Terminal](web_based_terminal.md) for more info.
|
||||
|
||||
## Tying It All Together
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ argocd-repo-server [flags]
|
||||
--metrics-port int Start metrics server on given port (default 8084)
|
||||
--otlp-address string OpenTelemetry collector address to send traces to
|
||||
--parallelismlimit int Limit on number of concurrent manifests generate requests. Any value less the 1 means no limit.
|
||||
--plugin-tar-exclude stringArray Globs to filter when sending tarballs to plugins.
|
||||
--port int Listen on given port for incoming connections (default 8081)
|
||||
--redis string Redis server hostname and port (e.g. argocd-redis:6379).
|
||||
--redis-ca-certificate string Path to Redis server CA certificate (e.g. /etc/certs/redis/ca.crt). If not specified, system trusted CAs will be used for server certificate validation.
|
||||
|
||||
@@ -197,6 +197,7 @@ NOTES:
|
||||
* There is no need to set `redirectURI` in the `connectors.config` as shown in the dex documentation.
|
||||
Argo CD will automatically use the correct `redirectURI` for any OAuth2 connectors, to match the
|
||||
correct external callback URL (e.g. `https://argocd.example.com/api/dex/callback`)
|
||||
* When using a custom secret (e.g., `some_K8S_secret` above,) it *must* have the label `app.kubernetes.io/part-of: argocd`.
|
||||
|
||||
## OIDC Configuration with DEX
|
||||
|
||||
@@ -495,3 +496,20 @@ data:
|
||||
clientSecret: $another-secret:oidc.auth0.clientSecret # Mind the ':'
|
||||
...
|
||||
```
|
||||
|
||||
### Skipping certificate verification on OIDC provider connections
|
||||
|
||||
By default, all connections made by the API server to OIDC providers (either external providers or the bundled Dex
|
||||
instance) must pass certificate validation. These connections occur when getting the OIDC provider's well-known
|
||||
configuration, when getting the OIDC provider's keys, and when exchanging an authorization code or verifying an ID
|
||||
token as part of an OIDC login flow.
|
||||
|
||||
Disabling certificate verification might make sense if:
|
||||
* You are using the bundled Dex instance **and** your Argo CD instance has TLS configured with a self-signed certificate
|
||||
**and** you understand and accept the risks of skipping OIDC provider cert verification.
|
||||
* You are using an external OIDC provider **and** that provider uses an invalid certificate **and** you cannot solve
|
||||
the problem by setting `oidcConfig.rootCA` **and** you understand and accept the risks of skipping OIDC provider cert
|
||||
verification.
|
||||
|
||||
If either of those two applies, then you can disable OIDC provider certificate verification by setting
|
||||
`oidc.tls.insecure.skip.verify` to `"true"` in the `argocd-cm` ConfigMap.
|
||||
|
||||
45
docs/operator-manual/web_based_terminal.md
Normal file
45
docs/operator-manual/web_based_terminal.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Web-based Terminal
|
||||
|
||||

|
||||
|
||||
Since v2.4, Argo CD has a web-based terminal that allows you to get a shell inside a running pod just like you would with
|
||||
`kubectl exec`. It's basically SSH from your browser, full ANSI color support and all! However, for security this feature
|
||||
is disabled by default.
|
||||
|
||||
This is a powerful privilege. It allows the user to run arbitrary code on any Pod managed by an Application for which
|
||||
they have the `exec/create` privilege. If the Pod mounts a ServiceAccount token (which is the default behavior of
|
||||
Kubernetes), then the user effectively has the same privileges as that ServiceAccount.
|
||||
|
||||
## Enabling the terminal
|
||||
|
||||
1. Set the `exec.enabled` key to `"true"` on the `argocd-cm` ConfigMap.
|
||||
|
||||
2. Patch the `argocd-server` Role (if using namespaced Argo) or ClusterRole (if using clustered Argo) to allow `argocd-server`
|
||||
to exec into pods
|
||||
```yaml
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods/exec
|
||||
verbs:
|
||||
- create
|
||||
```
|
||||
|
||||
3. Add RBAC rules to allow your users to `create` the `exec` resource, i.e.
|
||||
```
|
||||
p, role:myrole, exec, create, */*, allow
|
||||
```
|
||||
|
||||
See [RBAC Configuration](rbac.md#exec-resource) for more info.
|
||||
|
||||
## Changing allowed shells
|
||||
|
||||
By default, Argo CD attempts to execute shells in this order:
|
||||
|
||||
1. bash
|
||||
2. sh
|
||||
3. powershell
|
||||
4. cmd
|
||||
|
||||
If none of the shells are found, the terminal session will fail. To add to or change the allowed shells, change the
|
||||
`exec.shells` key in the `argocd-cm` ConfigMap, separating them with commas.
|
||||
@@ -2,4 +2,5 @@ mkdocs==1.2.3
|
||||
mkdocs-material==7.1.7
|
||||
markdown_include==0.6.0
|
||||
pygments==2.7.4
|
||||
jinja2===3.0.3
|
||||
jinja2==3.0.3
|
||||
markdown==3.3.7
|
||||
@@ -234,3 +234,18 @@ If you don't need to set any environment variables, you can set an empty plugin
|
||||
Each CMP command will also independently timeout on the `ARGOCD_EXEC_TIMEOUT` set for the CMP sidecar. The default
|
||||
is 90s. So if you increase the repo server timeout greater than 90s, be sure to set `ARGOCD_EXEC_TIMEOUT` on the
|
||||
sidecar.
|
||||
|
||||
## Plugin tar stream exclusions
|
||||
|
||||
In order to increase the speed of manifest generation, certain files and folders can be excluded from being sent to your
|
||||
plugin. We recommend excluding your `.git` folder if it isn't necessary. Use Go's
|
||||
[filepatch.Match](https://pkg.go.dev/path/filepath#Match) syntax.
|
||||
|
||||
You can set it one of three ways:
|
||||
|
||||
1. The `--plugin-tar-exclude` argument on the repo server.
|
||||
2. The `reposerver.plugin.tar.exclusions` key if you are using `argocd-cmd-params-cm`
|
||||
3. Directly setting `ARGOCD_REPO_SERVER_PLUGIN_TAR_EXCLUSIONS` environment variable on the repo server.
|
||||
|
||||
For option 1, the flag can be repeated multiple times. For option 2 and 3, you can specify multiple globs by separating
|
||||
them with semicolons.
|
||||
@@ -85,3 +85,22 @@ argocd app set <appName> --kustomize-version v3.5.4
|
||||
## Build Environment
|
||||
|
||||
Kustomize does not support parameters and therefore cannot support the standard [build environment](build-environment.md).
|
||||
|
||||
## Kustomizing Helm charts
|
||||
|
||||
It's possible to [render Helm charts with Kustomize](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/chart.md).
|
||||
Doing so requires that you pass the `--enable-helm` flag to the `kustomize build` command.
|
||||
This flag is not part of the Kustomize options within Argo CD.
|
||||
If you would like to render Helm charts through Kustomize in an Argo CD application, you have two options:
|
||||
You can either create a [custom plugin](https://argo-cd.readthedocs.io/en/stable/user-guide/config-management-plugins/), or modify the `argocd-cm` ConfigMap to include the `--enable-helm` flag globally for all Kustomize applications:
|
||||
|
||||
```
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-cm
|
||||
namespace: argocd
|
||||
data:
|
||||
kustomize.buildOptions: --enable-helm
|
||||
```
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ kind: Kustomization
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.4.3
|
||||
newTag: v2.4.7
|
||||
resources:
|
||||
- ./application-controller
|
||||
- ./dex
|
||||
|
||||
@@ -107,6 +107,12 @@ spec:
|
||||
name: argocd-cmd-params-cm
|
||||
key: reposerver.max.combined.directory.manifests.size
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_PLUGIN_TAR_EXCLUSIONS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: argocd-cmd-params-cm
|
||||
key: reposerver.plugin.tar.exclusions
|
||||
optional: true
|
||||
- name: HELM_CACHE_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_CONFIG_HOME
|
||||
|
||||
@@ -9385,7 +9385,7 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -9603,13 +9603,19 @@ spec:
|
||||
key: reposerver.max.combined.directory.manifests.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_PLUGIN_TAR_EXCLUSIONS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.plugin.tar.exclusions
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: HELM_CACHE_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_CONFIG_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -9658,7 +9664,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -9845,7 +9851,7 @@ spec:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -12,4 +12,4 @@ resources:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.4.3
|
||||
newTag: v2.4.7
|
||||
|
||||
@@ -11,7 +11,7 @@ patchesStrategicMerge:
|
||||
images:
|
||||
- name: quay.io/argoproj/argocd
|
||||
newName: quay.io/argoproj/argocd
|
||||
newTag: v2.4.3
|
||||
newTag: v2.4.7
|
||||
resources:
|
||||
- ../../base/application-controller
|
||||
- ../../base/applicationset-controller
|
||||
|
||||
@@ -10320,7 +10320,7 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -10417,7 +10417,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -10457,7 +10457,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -10702,13 +10702,19 @@ spec:
|
||||
key: reposerver.max.combined.directory.manifests.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_PLUGIN_TAR_EXCLUSIONS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.plugin.tar.exclusions
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: HELM_CACHE_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_CONFIG_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -10757,7 +10763,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -11004,7 +11010,7 @@ spec:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -11212,7 +11218,7 @@ spec:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -1244,7 +1244,7 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -1341,7 +1341,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -1381,7 +1381,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -1626,13 +1626,19 @@ spec:
|
||||
key: reposerver.max.combined.directory.manifests.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_PLUGIN_TAR_EXCLUSIONS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.plugin.tar.exclusions
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: HELM_CACHE_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_CONFIG_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1681,7 +1687,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -1928,7 +1934,7 @@ spec:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2136,7 +2142,7 @@ spec:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -9692,7 +9692,7 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -9789,7 +9789,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -9829,7 +9829,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -10042,13 +10042,19 @@ spec:
|
||||
key: reposerver.max.combined.directory.manifests.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_PLUGIN_TAR_EXCLUSIONS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.plugin.tar.exclusions
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: HELM_CACHE_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_CONFIG_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -10097,7 +10103,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -10340,7 +10346,7 @@ spec:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -10542,7 +10548,7 @@ spec:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -616,7 +616,7 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
name: argocd-applicationset-controller
|
||||
ports:
|
||||
@@ -713,7 +713,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /shared/argocd-dex
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
securityContext:
|
||||
@@ -753,7 +753,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- argocd-notifications
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
@@ -966,13 +966,19 @@ spec:
|
||||
key: reposerver.max.combined.directory.manifests.size
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: ARGOCD_REPO_SERVER_PLUGIN_TAR_EXCLUSIONS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: reposerver.plugin.tar.exclusions
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
- name: HELM_CACHE_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_CONFIG_HOME
|
||||
value: /helm-working-dir
|
||||
- name: HELM_DATA_HOME
|
||||
value: /helm-working-dir
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
@@ -1021,7 +1027,7 @@ spec:
|
||||
- -n
|
||||
- /usr/local/bin/argocd
|
||||
- /var/run/argocd/argocd-cmp-server
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
name: copyutil
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -1264,7 +1270,7 @@ spec:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -1466,7 +1472,7 @@ spec:
|
||||
key: otlp.address
|
||||
name: argocd-cmd-params-cm
|
||||
optional: true
|
||||
image: quay.io/argoproj/argocd:v2.4.3
|
||||
image: quay.io/argoproj/argocd:v2.4.7
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -44,6 +44,7 @@ nav:
|
||||
- operator-manual/custom_tools.md
|
||||
- operator-manual/custom-styles.md
|
||||
- operator-manual/metrics.md
|
||||
- operator-manual/web_based_terminal.md
|
||||
- Notification:
|
||||
- Overview: operator-manual/notifications/index.md
|
||||
- operator-manual/notifications/triggers.md
|
||||
|
||||
@@ -1542,6 +1542,19 @@ func isValidAction(action string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: same as validActions, refacotor to use rbacpolicy.ResourceApplications etc.
|
||||
var validResources = map[string]bool{
|
||||
"applications": true,
|
||||
"repositories": true,
|
||||
"clusters": true,
|
||||
"exec": true,
|
||||
"logs": true,
|
||||
}
|
||||
|
||||
func isValidResource(resource string) bool {
|
||||
return validResources[resource]
|
||||
}
|
||||
|
||||
func validatePolicy(proj string, role string, policy string) error {
|
||||
policyComponents := strings.Split(policy, ",")
|
||||
if len(policyComponents) != 6 || strings.Trim(policyComponents[0], " ") != "p" {
|
||||
@@ -1555,7 +1568,7 @@ func validatePolicy(proj string, role string, policy string) error {
|
||||
}
|
||||
// resource
|
||||
resource := strings.Trim(policyComponents[2], " ")
|
||||
if resource != "applications" && resource != "repositories" && resource != "clusters" {
|
||||
if !isValidResource(resource) {
|
||||
return status.Errorf(codes.InvalidArgument, "invalid policy rule '%s': project resource must be: 'applications', 'repositories' or 'clusters', not '%s'", policy, resource)
|
||||
}
|
||||
// action
|
||||
|
||||
@@ -2546,10 +2546,19 @@ func Test_validatePolicy_projIsNotRegex(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_validatePolicy_ValidResource(t *testing.T) {
|
||||
err := validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, repositories, *, some-project/*, allow")
|
||||
err := validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, applications, *, some-project/*, allow")
|
||||
assert.NoError(t, err)
|
||||
err = validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, repositories, *, some-project/*, allow")
|
||||
assert.NoError(t, err)
|
||||
err = validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, clusters, *, some-project/*, allow")
|
||||
assert.NoError(t, err)
|
||||
err = validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, exec, *, some-project/*, allow")
|
||||
assert.NoError(t, err)
|
||||
err = validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, logs, *, some-project/*, allow")
|
||||
assert.NoError(t, err)
|
||||
err = validatePolicy("some-project", "org-admin", "p, proj:some-project:org-admin, unknown, *, some-project/*, allow")
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestEnvsubst(t *testing.T) {
|
||||
|
||||
@@ -103,6 +103,7 @@ type RepoServerInitConstants struct {
|
||||
PauseGenerationOnFailureForRequests int
|
||||
SubmoduleEnabled bool
|
||||
MaxCombinedDirectoryManifestsSize resource.Quantity
|
||||
CMPTarExcludedGlobs []string
|
||||
}
|
||||
|
||||
// NewService returns a new instance of the Manifest service
|
||||
@@ -213,7 +214,7 @@ func (s *Service) ListApps(ctx context.Context, q *apiclient.ListAppsRequest) (*
|
||||
}
|
||||
|
||||
defer io.Close(closer)
|
||||
apps, err := discovery.Discover(ctx, gitClient.Root(), q.EnabledSourceTypes)
|
||||
apps, err := discovery.Discover(ctx, gitClient.Root(), q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -465,7 +466,7 @@ func (s *Service) runManifestGenAsync(ctx context.Context, repoRoot, commitSHA,
|
||||
var manifestGenResult *apiclient.ManifestResponse
|
||||
opContext, err := opContextSrc()
|
||||
if err == nil {
|
||||
manifestGenResult, err = GenerateManifests(ctx, opContext.appPath, repoRoot, commitSHA, q, false, s.gitCredsStore, s.initConstants.MaxCombinedDirectoryManifestsSize, WithCMPTarDoneChannel(ch.tarDoneCh))
|
||||
manifestGenResult, err = GenerateManifests(ctx, opContext.appPath, repoRoot, commitSHA, q, false, s.gitCredsStore, s.initConstants.MaxCombinedDirectoryManifestsSize, WithCMPTarDoneChannel(ch.tarDoneCh), WithCMPTarExcludedGlobs(s.initConstants.CMPTarExcludedGlobs))
|
||||
}
|
||||
if err != nil {
|
||||
// If manifest generation error caching is enabled
|
||||
@@ -858,7 +859,8 @@ func getRepoCredential(repoCredentials []*v1alpha1.RepoCreds, repoURL string) *v
|
||||
|
||||
type GenerateManifestOpt func(*generateManifestOpt)
|
||||
type generateManifestOpt struct {
|
||||
cmpTarDoneCh chan<- bool
|
||||
cmpTarDoneCh chan<- bool
|
||||
cmpTarExcludedGlobs []string
|
||||
}
|
||||
|
||||
func newGenerateManifestOpt(opts ...GenerateManifestOpt) *generateManifestOpt {
|
||||
@@ -878,6 +880,14 @@ func WithCMPTarDoneChannel(ch chan<- bool) GenerateManifestOpt {
|
||||
}
|
||||
}
|
||||
|
||||
// WithCMPTarExcludedGlobs defines globs for files to filter out when streaming the tarball
|
||||
// to a CMP sidecar.
|
||||
func WithCMPTarExcludedGlobs(excludedGlobs []string) GenerateManifestOpt {
|
||||
return func(o *generateManifestOpt) {
|
||||
o.cmpTarExcludedGlobs = excludedGlobs
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateManifests generates manifests from a path. Overrides are applied as a side effect on the given ApplicationSource.
|
||||
func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string, q *apiclient.ManifestRequest, isLocal bool, gitCredsStore git.CredsStore, maxCombinedManifestQuantity resource.Quantity, opts ...GenerateManifestOpt) (*apiclient.ManifestResponse, error) {
|
||||
opt := newGenerateManifestOpt(opts...)
|
||||
@@ -886,7 +896,7 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string,
|
||||
|
||||
resourceTracking := argo.NewResourceTracking()
|
||||
|
||||
appSourceType, err := GetAppSourceType(ctx, q.ApplicationSource, appPath, q.AppName, q.EnabledSourceTypes)
|
||||
appSourceType, err := GetAppSourceType(ctx, q.ApplicationSource, appPath, q.AppName, q.EnabledSourceTypes, opt.cmpTarExcludedGlobs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -910,7 +920,7 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string,
|
||||
if q.ApplicationSource.Plugin != nil && q.ApplicationSource.Plugin.Name != "" {
|
||||
targetObjs, err = runConfigManagementPlugin(appPath, repoRoot, env, q, q.Repo.GetGitCreds(gitCredsStore))
|
||||
} else {
|
||||
targetObjs, err = runConfigManagementPluginSidecars(ctx, appPath, repoRoot, env, q, q.Repo.GetGitCreds(gitCredsStore), opt.cmpTarDoneCh)
|
||||
targetObjs, err = runConfigManagementPluginSidecars(ctx, appPath, repoRoot, env, q, q.Repo.GetGitCreds(gitCredsStore), opt.cmpTarDoneCh, opt.cmpTarExcludedGlobs)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("plugin sidecar failed. %s", err.Error())
|
||||
}
|
||||
@@ -1044,7 +1054,7 @@ func mergeSourceParameters(source *v1alpha1.ApplicationSource, path, appName str
|
||||
}
|
||||
|
||||
// GetAppSourceType returns explicit application source type or examines a directory and determines its application source type
|
||||
func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, path, appName string, enableGenerateManifests map[string]bool) (v1alpha1.ApplicationSourceType, error) {
|
||||
func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, path, appName string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (v1alpha1.ApplicationSourceType, error) {
|
||||
err := mergeSourceParameters(source, path, appName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error while parsing source parameters: %v", err)
|
||||
@@ -1061,7 +1071,7 @@ func GetAppSourceType(ctx context.Context, source *v1alpha1.ApplicationSource, p
|
||||
}
|
||||
return *appSourceType, nil
|
||||
}
|
||||
appType, err := discovery.AppType(ctx, path, enableGenerateManifests)
|
||||
appType, err := discovery.AppType(ctx, path, enableGenerateManifests, tarExcludedGlobs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -1465,7 +1475,7 @@ func getPluginEnvs(envVars *v1alpha1.Env, q *apiclient.ManifestRequest, creds gi
|
||||
return env, nil
|
||||
}
|
||||
|
||||
func runConfigManagementPluginSidecars(ctx context.Context, appPath, repoPath string, envVars *v1alpha1.Env, q *apiclient.ManifestRequest, creds git.Creds, tarDoneCh chan<- bool) ([]*unstructured.Unstructured, error) {
|
||||
func runConfigManagementPluginSidecars(ctx context.Context, appPath, repoPath string, envVars *v1alpha1.Env, q *apiclient.ManifestRequest, creds git.Creds, tarDoneCh chan<- bool, tarExcludedGlobs []string) ([]*unstructured.Unstructured, error) {
|
||||
// compute variables.
|
||||
env, err := getPluginEnvs(envVars, q, creds, true)
|
||||
if err != nil {
|
||||
@@ -1473,14 +1483,14 @@ func runConfigManagementPluginSidecars(ctx context.Context, appPath, repoPath st
|
||||
}
|
||||
|
||||
// detect config management plugin server (sidecar)
|
||||
conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, env)
|
||||
conn, cmpClient, err := discovery.DetectConfigManagementPlugin(ctx, appPath, env, tarExcludedGlobs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer io.Close(conn)
|
||||
|
||||
// generate manifests using commands provided in plugin config file in detected cmp-server sidecar
|
||||
cmpManifests, err := generateManifestsCMP(ctx, appPath, repoPath, env, cmpClient, tarDoneCh)
|
||||
cmpManifests, err := generateManifestsCMP(ctx, appPath, repoPath, env, cmpClient, tarDoneCh, tarExcludedGlobs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating manifests in cmp: %s", err)
|
||||
}
|
||||
@@ -1498,7 +1508,7 @@ func runConfigManagementPluginSidecars(ctx context.Context, appPath, repoPath st
|
||||
// generateManifestsCMP will send the appPath files to the cmp-server over a gRPC stream.
|
||||
// The cmp-server will generate the manifests. Returns a response object with the generated
|
||||
// manifests.
|
||||
func generateManifestsCMP(ctx context.Context, appPath, repoPath string, env []string, cmpClient pluginclient.ConfigManagementPluginServiceClient, tarDoneCh chan<- bool) (*pluginclient.ManifestResponse, error) {
|
||||
func generateManifestsCMP(ctx context.Context, appPath, repoPath string, env []string, cmpClient pluginclient.ConfigManagementPluginServiceClient, tarDoneCh chan<- bool, tarExcludedGlobs []string) (*pluginclient.ManifestResponse, error) {
|
||||
generateManifestStream, err := cmpClient.GenerateManifest(ctx, grpc_retry.Disable())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting generateManifestStream: %s", err)
|
||||
@@ -1506,7 +1516,7 @@ func generateManifestsCMP(ctx context.Context, appPath, repoPath string, env []s
|
||||
opts := []cmp.SenderOption{
|
||||
cmp.WithTarDoneChan(tarDoneCh),
|
||||
}
|
||||
err = cmp.SendRepoStream(generateManifestStream.Context(), appPath, repoPath, generateManifestStream, env, opts...)
|
||||
err = cmp.SendRepoStream(generateManifestStream.Context(), appPath, repoPath, generateManifestStream, env, tarExcludedGlobs, opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error sending file to cmp-server: %s", err)
|
||||
}
|
||||
@@ -1524,7 +1534,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD
|
||||
return err
|
||||
}
|
||||
|
||||
appSourceType, err := GetAppSourceType(ctx, q.Source, opContext.appPath, q.AppName, q.EnabledSourceTypes)
|
||||
appSourceType, err := GetAppSourceType(ctx, q.Source, opContext.appPath, q.AppName, q.EnabledSourceTypes, s.initConstants.CMPTarExcludedGlobs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1639,7 +1649,7 @@ func loadFileIntoIfExists(path pathutil.ResolvedFilePath, destination *string) e
|
||||
info, err := os.Stat(stringPath)
|
||||
|
||||
if err == nil && !info.IsDir() {
|
||||
bytes, err := ioutil.ReadFile(stringPath);
|
||||
bytes, err := ioutil.ReadFile(stringPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -133,6 +133,31 @@ func newServiceWithCommitSHA(root, revision string) *Service {
|
||||
return service
|
||||
}
|
||||
|
||||
// createSymlink creates a symlink with name linkName to file destName in
|
||||
// workingDir
|
||||
func createSymlink(t *testing.T, workingDir, destName, linkName string) error {
|
||||
oldWorkingDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if workingDir != "" {
|
||||
err = os.Chdir(workingDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := os.Chdir(oldWorkingDir); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
err = os.Symlink(destName, linkName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestGenerateYamlManifestInDir(t *testing.T) {
|
||||
service := newService("../..")
|
||||
|
||||
@@ -1049,15 +1074,15 @@ func TestGenerateNullList(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIdentifyAppSourceTypeByAppDirWithKustomizations(t *testing.T) {
|
||||
sourceType, err := GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yaml", "testapp", map[string]bool{})
|
||||
sourceType, err := GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yaml", "testapp", map[string]bool{}, []string{})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType)
|
||||
|
||||
sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yml", "testapp", map[string]bool{})
|
||||
sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/kustomization_yml", "testapp", map[string]bool{}, []string{})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType)
|
||||
|
||||
sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/Kustomization", "testapp", map[string]bool{})
|
||||
sourceType, err = GetAppSourceType(context.Background(), &argoappv1.ApplicationSource{}, "./testdata/Kustomization", "testapp", map[string]bool{}, []string{})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, argoappv1.ApplicationSourceTypeKustomize, sourceType)
|
||||
}
|
||||
@@ -1582,7 +1607,7 @@ func TestGenerateManifestsWithAppParameterFile(t *testing.T) {
|
||||
source := &argoappv1.ApplicationSource{
|
||||
Path: path,
|
||||
}
|
||||
sourceCopy := source.DeepCopy() // make a copy in case GenerateManifest mutates it.
|
||||
sourceCopy := source.DeepCopy() // make a copy in case GenerateManifest mutates it.
|
||||
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
ApplicationSource: sourceCopy,
|
||||
@@ -2008,7 +2033,12 @@ func Test_getPotentiallyValidManifests(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("circular link should throw an error", func(t *testing.T) {
|
||||
require.DirExists(t, "./testdata/circular-link")
|
||||
const testDir = "./testdata/circular-link"
|
||||
require.DirExists(t, testDir)
|
||||
require.NoError(t, createSymlink(t, testDir, "a.json", "b.json"))
|
||||
defer os.Remove(path.Join(testDir, "a.json"))
|
||||
require.NoError(t, createSymlink(t, testDir, "b.json", "a.json"))
|
||||
defer os.Remove(path.Join(testDir, "b.json"))
|
||||
manifests, err := getPotentiallyValidManifests(logCtx, "./testdata/circular-link", "./testdata/circular-link", false, "", "", resource.MustParse("0"))
|
||||
assert.Empty(t, manifests)
|
||||
assert.Error(t, err)
|
||||
@@ -2103,7 +2133,12 @@ func Test_findManifests(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("circular link should throw an error", func(t *testing.T) {
|
||||
require.DirExists(t, "./testdata/circular-link")
|
||||
const testDir = "./testdata/circular-link"
|
||||
require.DirExists(t, testDir)
|
||||
require.NoError(t, createSymlink(t, testDir, "a.json", "b.json"))
|
||||
defer os.Remove(path.Join(testDir, "a.json"))
|
||||
require.NoError(t, createSymlink(t, testDir, "b.json", "a.json"))
|
||||
defer os.Remove(path.Join(testDir, "b.json"))
|
||||
manifests, err := findManifests(logCtx, "./testdata/circular-link", "./testdata/circular-link", nil, noRecurse, nil, resource.MustParse("0"))
|
||||
assert.Empty(t, manifests)
|
||||
assert.Error(t, err)
|
||||
|
||||
0
reposerver/repository/testdata/circular-link/.keep
vendored
Normal file
0
reposerver/repository/testdata/circular-link/.keep
vendored
Normal file
@@ -1 +0,0 @@
|
||||
b.json
|
||||
@@ -1 +0,0 @@
|
||||
a.json
|
||||
@@ -3,10 +3,11 @@ package reposerver
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
|
||||
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"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/health"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/reflection"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
@@ -86,6 +88,11 @@ func NewServer(metricsServer *metrics.MetricsServer, cache *reposervercache.Cach
|
||||
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(streamInterceptors...)),
|
||||
grpc.MaxRecvMsgSize(apiclient.MaxGRPCMessageSize),
|
||||
grpc.MaxSendMsgSize(apiclient.MaxGRPCMessageSize),
|
||||
grpc.KeepaliveEnforcementPolicy(
|
||||
keepalive.EnforcementPolicy{
|
||||
MinTime: common.GRPCKeepAliveEnforcementMinimum,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
// We do allow for non-TLS servers to be created, in case of mTLS will be
|
||||
|
||||
@@ -33,17 +33,19 @@ type terminalHandler struct {
|
||||
enf *rbac.Enforcer
|
||||
cache *servercache.Cache
|
||||
appResourceTreeFn func(ctx context.Context, app *appv1.Application) (*appv1.ApplicationTree, error)
|
||||
allowedShells []string
|
||||
}
|
||||
|
||||
// NewHandler returns a new terminal handler.
|
||||
func NewHandler(appLister applisters.ApplicationNamespaceLister, db db.ArgoDB, enf *rbac.Enforcer, cache *servercache.Cache,
|
||||
appResourceTree AppResourceTreeFn) *terminalHandler {
|
||||
appResourceTree AppResourceTreeFn, allowedShells []string) *terminalHandler {
|
||||
return &terminalHandler{
|
||||
appLister: appLister,
|
||||
db: db,
|
||||
enf: enf,
|
||||
cache: cache,
|
||||
appResourceTreeFn: appResourceTree,
|
||||
allowedShells: allowedShells,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +125,7 @@ func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Namespace name is not valid", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
shell := q.Get("shell") // No need to validate. Will only buse used if it's in the allow-list.
|
||||
shell := q.Get("shell") // No need to validate. Will only be used if it's in the allow-list.
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
@@ -216,14 +218,12 @@ func (s *terminalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
defer session.Done()
|
||||
|
||||
validShells := []string{"bash", "sh", "powershell", "cmd"}
|
||||
if isValidShell(validShells, shell) {
|
||||
if isValidShell(s.allowedShells, shell) {
|
||||
cmd := []string{shell}
|
||||
err = startProcess(kubeClientset, config, namespace, podName, container, cmd, session)
|
||||
} else {
|
||||
// No shell given or it was not valid: try some shells until one succeeds or all fail
|
||||
// FIXME: if the first shell fails then the first keyboard event is lost
|
||||
for _, testShell := range validShells {
|
||||
// No shell given or the given shell was not allowed: try the configured shells until one succeeds or all fail.
|
||||
for _, testShell := range s.allowedShells {
|
||||
cmd := []string{testShell}
|
||||
if err = startProcess(kubeClientset, config, namespace, podName, container, cmd, session); err == nil {
|
||||
break
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
@@ -22,10 +23,12 @@ var upgrader = func() websocket.Upgrader {
|
||||
|
||||
// terminalSession implements PtyHandler
|
||||
type terminalSession struct {
|
||||
wsConn *websocket.Conn
|
||||
sizeChan chan remotecommand.TerminalSize
|
||||
doneChan chan struct{}
|
||||
tty bool
|
||||
wsConn *websocket.Conn
|
||||
sizeChan chan remotecommand.TerminalSize
|
||||
doneChan chan struct{}
|
||||
tty bool
|
||||
readLock sync.Mutex
|
||||
writeLock sync.Mutex
|
||||
}
|
||||
|
||||
// newTerminalSession create terminalSession
|
||||
@@ -60,7 +63,9 @@ func (t *terminalSession) Next() *remotecommand.TerminalSize {
|
||||
|
||||
// Read called in a loop from remotecommand as long as the process is running
|
||||
func (t *terminalSession) Read(p []byte) (int, error) {
|
||||
t.readLock.Lock()
|
||||
_, message, err := t.wsConn.ReadMessage()
|
||||
t.readLock.Unlock()
|
||||
if err != nil {
|
||||
log.Errorf("read message err: %v", err)
|
||||
return copy(p, EndOfTransmission), err
|
||||
@@ -91,7 +96,10 @@ func (t *terminalSession) Write(p []byte) (int, error) {
|
||||
log.Errorf("write parse message err: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
if err := t.wsConn.WriteMessage(websocket.TextMessage, msg); err != nil {
|
||||
t.writeLock.Lock()
|
||||
err = t.wsConn.WriteMessage(websocket.TextMessage, msg)
|
||||
t.writeLock.Unlock()
|
||||
if err != nil {
|
||||
log.Errorf("write message err: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
// nolint:staticcheck
|
||||
golang_proto "github.com/golang/protobuf/proto"
|
||||
|
||||
netCtx "context"
|
||||
|
||||
"github.com/argoproj/pkg/sync"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
@@ -35,11 +37,11 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/soheilhy/cmux"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
netCtx "golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/reflection"
|
||||
"google.golang.org/grpc/status"
|
||||
@@ -362,6 +364,12 @@ func (a *ArgoCDServer) Listen() (*Listeners, error) {
|
||||
return &Listeners{Main: mainLn, Metrics: metricsLn, GatewayConn: conn}, nil
|
||||
}
|
||||
|
||||
// Init starts informers used by the API server
|
||||
func (a *ArgoCDServer) Init(ctx context.Context) {
|
||||
go a.projInformer.Run(ctx.Done())
|
||||
go a.appInformer.Run(ctx.Done())
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -429,9 +437,6 @@ func (a *ArgoCDServer) Run(ctx context.Context, listeners *Listeners) {
|
||||
log.Infof("argocd %s serving on port %d (url: %s, tls: %v, namespace: %s, sso: %v)",
|
||||
common.GetVersion(), a.ListenPort, a.settings.URL, a.useTLS(), a.Namespace, a.settings.IsSSOConfigured())
|
||||
|
||||
go a.projInformer.Run(ctx.Done())
|
||||
go a.appInformer.Run(ctx.Done())
|
||||
|
||||
go func() { a.checkServeErr("grpcS", grpcS.Serve(grpcL)) }()
|
||||
go func() { a.checkServeErr("httpS", httpS.Serve(httpL)) }()
|
||||
if a.useTLS() {
|
||||
@@ -604,6 +609,11 @@ func (a *ArgoCDServer) newGRPCServer() (*grpc.Server, application.AppResourceTre
|
||||
grpc.MaxRecvMsgSize(apiclient.MaxGRPCMessageSize),
|
||||
grpc.MaxSendMsgSize(apiclient.MaxGRPCMessageSize),
|
||||
grpc.ConnectionTimeout(300 * time.Second),
|
||||
grpc.KeepaliveEnforcementPolicy(
|
||||
keepalive.EnforcementPolicy{
|
||||
MinTime: common.GRPCKeepAliveEnforcementMinimum,
|
||||
},
|
||||
),
|
||||
}
|
||||
sensitiveMethods := map[string]bool{
|
||||
"/cluster.ClusterService/Create": true,
|
||||
@@ -798,7 +808,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
|
||||
}
|
||||
mux.Handle("/api/", handler)
|
||||
|
||||
terminalHandler := application.NewHandler(a.appLister, a.db, a.enf, a.Cache, appResourceTreeFn)
|
||||
terminalHandler := application.NewHandler(a.appLister, a.db, a.enf, a.Cache, appResourceTreeFn, a.settings.ExecShells)
|
||||
mux.HandleFunc("/terminal", func(writer http.ResponseWriter, request *http.Request) {
|
||||
argocdSettings, err := a.settingsMgr.GetSettings()
|
||||
if err != nil {
|
||||
|
||||
@@ -36,6 +36,7 @@ func TestUserAgent(t *testing.T) {
|
||||
defer cancelInformer()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
s.Init(ctx)
|
||||
go s.Run(ctx, lns)
|
||||
defer func() { time.Sleep(3 * time.Second) }()
|
||||
|
||||
@@ -101,6 +102,7 @@ func Test_StaticHeaders(t *testing.T) {
|
||||
defer cancelInformer()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
s.Init(ctx)
|
||||
go s.Run(ctx, lns)
|
||||
defer func() { time.Sleep(3 * time.Second) }()
|
||||
|
||||
@@ -129,6 +131,7 @@ func Test_StaticHeaders(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
s.Init(ctx)
|
||||
go s.Run(ctx, lns)
|
||||
defer func() { time.Sleep(3 * time.Second) }()
|
||||
|
||||
@@ -157,6 +160,7 @@ func Test_StaticHeaders(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
s.Init(ctx)
|
||||
go s.Run(ctx, lns)
|
||||
defer func() { time.Sleep(3 * time.Second) }()
|
||||
|
||||
|
||||
@@ -66,7 +66,8 @@ func fakeServer() (*ArgoCDServer, func()) {
|
||||
),
|
||||
RedisClient: redis,
|
||||
}
|
||||
return NewServer(context.Background(), argoCDOpts), closer
|
||||
srv := NewServer(context.Background(), argoCDOpts)
|
||||
return srv, closer
|
||||
}
|
||||
|
||||
func TestEnforceProjectToken(t *testing.T) {
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
projectFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/project"
|
||||
repoFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/repos"
|
||||
"github.com/argoproj/argo-cd/v2/test/e2e/testdata"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
. "github.com/argoproj/argo-cd/v2/util/argo"
|
||||
. "github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
@@ -947,7 +948,7 @@ func TestLocalManifestSync(t *testing.T) {
|
||||
And(func(app *Application) {
|
||||
res, _ := RunCli("app", "manifests", app.Name)
|
||||
assert.Contains(t, res, "containerPort: 80")
|
||||
assert.Contains(t, res, "image: gcr.io/heptio-images/ks-guestbook-demo:0.2")
|
||||
assert.Contains(t, res, "image: quay.io/argoprojlabs/argocd-e2e-container:0.2")
|
||||
}).
|
||||
Given().
|
||||
LocalPath(guestbookPathLocal).
|
||||
@@ -958,7 +959,7 @@ func TestLocalManifestSync(t *testing.T) {
|
||||
And(func(app *Application) {
|
||||
res, _ := RunCli("app", "manifests", app.Name)
|
||||
assert.Contains(t, res, "containerPort: 81")
|
||||
assert.Contains(t, res, "image: gcr.io/heptio-images/ks-guestbook-demo:0.3")
|
||||
assert.Contains(t, res, "image: quay.io/argoprojlabs/argocd-e2e-container:0.3")
|
||||
}).
|
||||
Given().
|
||||
LocalPath("").
|
||||
@@ -969,7 +970,7 @@ func TestLocalManifestSync(t *testing.T) {
|
||||
And(func(app *Application) {
|
||||
res, _ := RunCli("app", "manifests", app.Name)
|
||||
assert.Contains(t, res, "containerPort: 80")
|
||||
assert.Contains(t, res, "image: gcr.io/heptio-images/ks-guestbook-demo:0.2")
|
||||
assert.Contains(t, res, "image: quay.io/argoprojlabs/argocd-e2e-container:0.2")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1190,7 +1191,8 @@ func TestPermissionWithScopedRepo(t *testing.T) {
|
||||
Name(projName).
|
||||
Destination("*,*").
|
||||
When().
|
||||
Create()
|
||||
Create().
|
||||
AddSource("*")
|
||||
|
||||
repoFixture.Given(t, true).
|
||||
When().
|
||||
@@ -2063,3 +2065,241 @@ func TestDisableManifestGeneration(t *testing.T) {
|
||||
assert.Equal(t, app.Status.SourceType, ApplicationSourceTypeDirectory)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSwitchTrackingMethod(t *testing.T) {
|
||||
ctx := Given(t)
|
||||
|
||||
ctx.
|
||||
SetTrackingMethod(string(argo.TrackingMethodAnnotation)).
|
||||
Path("deployment").
|
||||
When().
|
||||
CreateApp().
|
||||
Sync().
|
||||
Refresh(RefreshTypeNormal).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
And(func() {
|
||||
// Add resource with tracking annotation. This should put the
|
||||
// application OutOfSync.
|
||||
FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "other-configmap",
|
||||
Annotations: map[string]string{
|
||||
common.AnnotationKeyAppInstance: fmt.Sprintf("%s:/ConfigMap:%s/other-configmap", Name(), DeploymentNamespace()),
|
||||
},
|
||||
},
|
||||
}, metav1.CreateOptions{}))
|
||||
}).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
And(func() {
|
||||
// Delete resource to bring application back in sync
|
||||
FailOnErr(nil, KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Delete(context.Background(), "other-configmap", metav1.DeleteOptions{}))
|
||||
}).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
SetTrackingMethod(string(argo.TrackingMethodLabel)).
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
And(func() {
|
||||
// Add a resource with a tracking annotation. This should not
|
||||
// affect the application, because we now use the tracking method
|
||||
// "label".
|
||||
FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "other-configmap",
|
||||
Annotations: map[string]string{
|
||||
common.AnnotationKeyAppInstance: fmt.Sprintf("%s:/ConfigMap:%s/other-configmap", Name(), DeploymentNamespace()),
|
||||
},
|
||||
},
|
||||
}, metav1.CreateOptions{}))
|
||||
}).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
And(func() {
|
||||
// Add a resource with the tracking label. The app should become
|
||||
// OutOfSync.
|
||||
FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "extra-configmap",
|
||||
Labels: map[string]string{
|
||||
common.LabelKeyAppInstance: Name(),
|
||||
},
|
||||
},
|
||||
}, metav1.CreateOptions{}))
|
||||
}).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
And(func() {
|
||||
// Delete resource to bring application back in sync
|
||||
FailOnErr(nil, KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Delete(context.Background(), "extra-configmap", metav1.DeleteOptions{}))
|
||||
}).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy))
|
||||
}
|
||||
|
||||
func TestSwitchTrackingLabel(t *testing.T) {
|
||||
ctx := Given(t)
|
||||
|
||||
ctx.
|
||||
Path("deployment").
|
||||
When().
|
||||
CreateApp().
|
||||
Sync().
|
||||
Refresh(RefreshTypeNormal).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
And(func() {
|
||||
// Add extra resource that carries the default tracking label
|
||||
// We expect the app to go out of sync.
|
||||
FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "other-configmap",
|
||||
Labels: map[string]string{
|
||||
common.LabelKeyAppInstance: Name(),
|
||||
},
|
||||
},
|
||||
}, metav1.CreateOptions{}))
|
||||
}).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
And(func() {
|
||||
// Delete resource to bring application back in sync
|
||||
FailOnErr(nil, KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Delete(context.Background(), "other-configmap", metav1.DeleteOptions{}))
|
||||
}).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
// Change tracking label
|
||||
SetTrackingLabel("argocd.tracking").
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
And(func() {
|
||||
// Create resource with the new tracking label, the application
|
||||
// is expected to go out of sync
|
||||
FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "other-configmap",
|
||||
Labels: map[string]string{
|
||||
"argocd.tracking": Name(),
|
||||
},
|
||||
},
|
||||
}, metav1.CreateOptions{}))
|
||||
}).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
And(func() {
|
||||
// Delete resource to bring application back in sync
|
||||
FailOnErr(nil, KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Delete(context.Background(), "other-configmap", metav1.DeleteOptions{}))
|
||||
}).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
And(func() {
|
||||
// Add extra resource that carries the default tracking label
|
||||
// We expect the app to stay in sync, because the configured
|
||||
// label is different.
|
||||
FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "other-configmap",
|
||||
Labels: map[string]string{
|
||||
common.LabelKeyAppInstance: Name(),
|
||||
},
|
||||
},
|
||||
}, metav1.CreateOptions{}))
|
||||
}).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy))
|
||||
}
|
||||
|
||||
func TestAnnotationTrackingExtraResources(t *testing.T) {
|
||||
ctx := Given(t)
|
||||
|
||||
SetTrackingMethod(string(argo.TrackingMethodAnnotation))
|
||||
ctx.
|
||||
Path("deployment").
|
||||
When().
|
||||
CreateApp().
|
||||
Sync().
|
||||
Refresh(RefreshTypeNormal).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
And(func() {
|
||||
// Add a resource with an annotation that is not referencing the
|
||||
// resource.
|
||||
FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "extra-configmap",
|
||||
Annotations: map[string]string{
|
||||
common.AnnotationKeyAppInstance: fmt.Sprintf("%s:apps/Deployment:%s/guestbook-cm", Name(), DeploymentNamespace()),
|
||||
},
|
||||
},
|
||||
}, metav1.CreateOptions{}))
|
||||
}).
|
||||
Refresh(RefreshTypeNormal).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy)).
|
||||
When().
|
||||
And(func() {
|
||||
// Add a resource with an annotation that is self-referencing the
|
||||
// resource.
|
||||
FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "other-configmap",
|
||||
Annotations: map[string]string{
|
||||
common.AnnotationKeyAppInstance: fmt.Sprintf("%s:/ConfigMap:%s/other-configmap", Name(), DeploymentNamespace()),
|
||||
},
|
||||
},
|
||||
}, metav1.CreateOptions{}))
|
||||
}).
|
||||
Refresh(RefreshTypeNormal).
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
|
||||
Expect(HealthIs(health.HealthStatusHealthy))
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: main
|
||||
image: alpine:3.10.2
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- "true"
|
||||
@@ -43,7 +43,7 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: main
|
||||
image: alpine:3.10.2
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- "true"
|
||||
|
||||
@@ -341,3 +341,13 @@ func (a *Actions) verifyAction() {
|
||||
a.Then().Expect(Success(""))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Actions) SetTrackingMethod(trackingMethod string) *Actions {
|
||||
fixture.SetTrackingMethod(trackingMethod)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Actions) SetTrackingLabel(trackingLabel string) *Actions {
|
||||
fixture.SetTrackingLabel(trackingLabel)
|
||||
return a
|
||||
}
|
||||
|
||||
@@ -308,3 +308,8 @@ func (c *Context) HelmSkipCrds() *Context {
|
||||
c.helmSkipCrds = true
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Context) SetTrackingMethod(trackingMethod string) *Context {
|
||||
fixture.SetTrackingMethod(trackingMethod)
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -14,11 +14,12 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
argocommon "github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/applicationset/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils"
|
||||
"github.com/argoproj/argo-cd/v2/util/clusterauth"
|
||||
)
|
||||
|
||||
// this implements the "when" part of given/when/then
|
||||
@@ -61,42 +62,6 @@ func (a *Actions) Then() *Consequences {
|
||||
return &Consequences{a.context, a}
|
||||
}
|
||||
|
||||
// GetServiceAccountBearerToken will attempt to get the provided service account until it
|
||||
// exists, iterate the secrets associated with it looking for one of type
|
||||
// kubernetes.io/service-account-token, and return it's token if found.
|
||||
// (function based on 'GetServiceAccountBearerToken' from Argo CD's 'clusterauth.go')
|
||||
func GetServiceAccountBearerToken(clientset kubernetes.Interface, ns string, sa string) (string, error) {
|
||||
var serviceAccount *corev1.ServiceAccount
|
||||
var secret *corev1.Secret
|
||||
var err error
|
||||
err = wait.Poll(500*time.Millisecond, 30*time.Second, func() (bool, error) {
|
||||
serviceAccount, err = clientset.CoreV1().ServiceAccounts(ns).Get(context.Background(), sa, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// Scan all secrets looking for one of the correct type:
|
||||
for _, oRef := range serviceAccount.Secrets {
|
||||
var getErr error
|
||||
secret, err = clientset.CoreV1().Secrets(ns).Get(context.Background(), oRef.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to retrieve secret %q: %v", oRef.Name, getErr)
|
||||
}
|
||||
if secret.Type == corev1.SecretTypeServiceAccountToken {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to wait for service account secret: %v", err)
|
||||
}
|
||||
token, ok := secret.Data["token"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("secret %q for service account %q did not have a token", secret.Name, serviceAccount)
|
||||
}
|
||||
return string(token), nil
|
||||
}
|
||||
|
||||
// CreateClusterSecret creates a faux cluster secret, with the given cluster server and cluster name (this cluster
|
||||
// will not actually be used by the Argo CD controller, but that's not needed for our E2E tests)
|
||||
func (a *Actions) CreateClusterSecret(secretName string, clusterName string, clusterServer string) *Actions {
|
||||
@@ -135,7 +100,7 @@ func (a *Actions) CreateClusterSecret(secretName string, clusterName string, clu
|
||||
|
||||
if err == nil {
|
||||
var bearerToken string
|
||||
bearerToken, err = GetServiceAccountBearerToken(fixtureClient.KubeClientset, utils.ArgoCDNamespace, serviceAccountName)
|
||||
bearerToken, err = clusterauth.GetServiceAccountBearerToken(fixtureClient.KubeClientset, utils.ArgoCDNamespace, serviceAccountName, common.BearerTokenTimeout)
|
||||
|
||||
// bearerToken
|
||||
secret := &corev1.Secret{
|
||||
|
||||
@@ -160,7 +160,8 @@ func init() {
|
||||
adminUsername = GetEnvWithDefault(EnvAdminUsername, defaultAdminUsername)
|
||||
AdminPassword = GetEnvWithDefault(EnvAdminPassword, defaultAdminPassword)
|
||||
|
||||
tlsTestResult, err := grpcutil.TestTLS(apiServerAddress)
|
||||
dialTime := 30 * time.Second
|
||||
tlsTestResult, err := grpcutil.TestTLS(apiServerAddress, dialTime)
|
||||
CheckError(err)
|
||||
|
||||
ArgoCDClientset, err = apiclient.NewClient(&apiclient.ClientOptions{Insecure: true, ServerAddr: apiServerAddress, PlainText: !tlsTestResult.TLS})
|
||||
@@ -357,6 +358,13 @@ func SetTrackingMethod(trackingMethod string) {
|
||||
})
|
||||
}
|
||||
|
||||
func SetTrackingLabel(trackingLabel string) {
|
||||
updateSettingConfigMap(func(cm *corev1.ConfigMap) error {
|
||||
cm.Data["application.instanceLabelKey"] = trackingLabel
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func SetResourceOverridesSplitKeys(overrides map[string]v1alpha1.ResourceOverride) {
|
||||
updateSettingConfigMap(func(cm *corev1.ConfigMap) error {
|
||||
for k, v := range overrides {
|
||||
|
||||
@@ -15,6 +15,7 @@ type Context struct {
|
||||
timeout int
|
||||
name string
|
||||
destination string
|
||||
repos []string
|
||||
}
|
||||
|
||||
func Given(t *testing.T) *Context {
|
||||
@@ -43,6 +44,11 @@ func (c *Context) Destination(destination string) *Context {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Context) SourceRepositories(repos []string) *Context {
|
||||
c.repos = repos
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Context) And(block func()) *Context {
|
||||
block()
|
||||
return c
|
||||
|
||||
@@ -171,7 +171,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- "true"
|
||||
image: "alpine:latest"
|
||||
image: "quay.io/argoprojlabs/argocd-e2e-container:0.1"
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: main
|
||||
restartPolicy: Never
|
||||
@@ -202,7 +202,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- "true"
|
||||
image: "alpine:latest"
|
||||
image: "quay.io/argoprojlabs/argocd-e2e-container:0.1"
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: main
|
||||
restartPolicy: Never
|
||||
@@ -218,7 +218,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- "false"
|
||||
image: "alpine:latest"
|
||||
image: "quay.io/argoprojlabs/argocd-e2e-container:0.1"
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: main
|
||||
restartPolicy: Never
|
||||
|
||||
2
test/e2e/multiarch-container/Dockerfile
Normal file
2
test/e2e/multiarch-container/Dockerfile
Normal file
@@ -0,0 +1,2 @@
|
||||
FROM docker.io/library/busybox
|
||||
CMD exec sh -c "trap : TERM INT; echo 'Hi' && tail -f /dev/null"
|
||||
11
test/e2e/multiarch-container/build.sh
Executable file
11
test/e2e/multiarch-container/build.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
VERSIONS="0.1 0.2 0.3"
|
||||
PLATFORMS="linux/amd64,linux/arm64,linux/s390x,linux/ppc64le"
|
||||
for version in $VERSIONS; do
|
||||
docker buildx build \
|
||||
-t "quay.io/argoprojlabs/argocd-e2e-container:${version}" \
|
||||
--platform "${PLATFORMS}" \
|
||||
--push \
|
||||
.
|
||||
done
|
||||
2
test/e2e/testdata/cluster-role/pod.yaml
vendored
2
test/e2e/testdata/cluster-role/pod.yaml
vendored
@@ -5,7 +5,7 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: main
|
||||
image: alpine:3.10.2
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- "true"
|
||||
|
||||
@@ -16,7 +16,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.17.4-alpine
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
ports:
|
||||
- containerPort: "80"
|
||||
imagePullPolicy: IfNotPresent
|
||||
2
test/e2e/testdata/deployment/deployment.yaml
vendored
2
test/e2e/testdata/deployment/deployment.yaml
vendored
@@ -16,6 +16,6 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.17.4-alpine
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
@@ -16,7 +16,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: extensions-deployment
|
||||
image: "gcr.io/heptio-images/ks-guestbook-demo:0.2"
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
|
||||
@@ -5,7 +5,7 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: main
|
||||
image: alpine:3.10.2
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- "true"
|
||||
|
||||
@@ -14,13 +14,8 @@ spec:
|
||||
app: guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- image: busybox
|
||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: guestbook-ui
|
||||
command:
|
||||
- sh
|
||||
args:
|
||||
- -c
|
||||
- "echo \"Hi\" && tail -f /dev/null"
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
app: guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: guestbook-ui
|
||||
ports:
|
||||
|
||||
@@ -14,7 +14,7 @@ spec:
|
||||
app: guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: guestbook-ui
|
||||
ports:
|
||||
|
||||
@@ -14,7 +14,7 @@ spec:
|
||||
app: guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: guestbook-ui
|
||||
ports:
|
||||
|
||||
@@ -14,7 +14,7 @@ spec:
|
||||
app: guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.3
|
||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.3
|
||||
name: guestbook-ui
|
||||
ports:
|
||||
- containerPort: 81
|
||||
|
||||
@@ -14,7 +14,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: main
|
||||
image: nginx:1.17.4-alpine
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
readinessProbe:
|
||||
failureThreshold: 3
|
||||
|
||||
@@ -10,7 +10,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- "true"
|
||||
image: "alpine:3.10.2"
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: main
|
||||
restartPolicy: Never
|
||||
2
test/e2e/testdata/hook/hook.yaml
vendored
2
test/e2e/testdata/hook/hook.yaml
vendored
@@ -8,7 +8,7 @@ spec:
|
||||
containers:
|
||||
- command:
|
||||
- "true"
|
||||
image: "alpine:latest"
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: main
|
||||
restartPolicy: Never
|
||||
2
test/e2e/testdata/hook/pod.yaml
vendored
2
test/e2e/testdata/hook/pod.yaml
vendored
@@ -5,7 +5,7 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: main
|
||||
image: alpine:latest
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- "true"
|
||||
|
||||
2
test/e2e/testdata/kustomize/pod.yaml
vendored
2
test/e2e/testdata/kustomize/pod.yaml
vendored
@@ -5,7 +5,7 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: main
|
||||
image: alpine:3.10.2
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- "true"
|
||||
|
||||
@@ -22,7 +22,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.17.4-alpine
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 80
|
||||
@@ -39,7 +39,7 @@ spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: main
|
||||
image: alpine:3.10.2
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: [sh, -c, "sleep 10"]
|
||||
|
||||
@@ -56,6 +56,6 @@ spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: main
|
||||
image: alpine:3.10.2
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: [sh, -c, "sleep 10"]
|
||||
|
||||
@@ -17,7 +17,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: helm-guestbook
|
||||
image: "gcr.io/heptio-images/ks-guestbook-demo:0.2"
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
@@ -30,4 +30,4 @@ spec:
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
port: http
|
||||
|
||||
@@ -16,7 +16,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: helm-guestbook
|
||||
image: "gcr.io/heptio-images/ks-guestbook-demo:0.2"
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
|
||||
@@ -14,7 +14,7 @@ spec:
|
||||
app: guestbook-ui
|
||||
spec:
|
||||
containers:
|
||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
||||
name: guestbook-ui
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
@@ -16,6 +16,6 @@ spec:
|
||||
containers:
|
||||
- name: main
|
||||
command: ["sleep", "999"]
|
||||
image: alpine:3.10.2
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
|
||||
|
||||
2
test/e2e/testdata/two-nice-pods/pod-1.yaml
vendored
2
test/e2e/testdata/two-nice-pods/pod-1.yaml
vendored
@@ -5,7 +5,7 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: main
|
||||
image: alpine:3.10.2
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- "true"
|
||||
|
||||
2
test/e2e/testdata/two-nice-pods/pod-2.yaml
vendored
2
test/e2e/testdata/two-nice-pods/pod-2.yaml
vendored
@@ -5,7 +5,7 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: main
|
||||
image: alpine:3.10.2
|
||||
image: quay.io/argoprojlabs/argocd-e2e-container:0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- "true"
|
||||
|
||||
@@ -13,6 +13,7 @@ import {RevisionFormField} from '../revision-form-field/revision-form-field';
|
||||
import {ComparisonStatusIcon, HealthStatusIcon, syncStatusMessage, urlPattern} from '../utils';
|
||||
import {ApplicationRetryOptions} from '../application-retry-options/application-retry-options';
|
||||
import {ApplicationRetryView} from '../application-retry-view/application-retry-view';
|
||||
import {Link} from 'react-router-dom';
|
||||
|
||||
require('./application-summary.scss');
|
||||
|
||||
@@ -31,7 +32,7 @@ export const ApplicationSummary = (props: {app: models.Application; updateApp: (
|
||||
const attributes = [
|
||||
{
|
||||
title: 'PROJECT',
|
||||
view: <a href={'/settings/projects/' + app.spec.project}>{app.spec.project}</a>,
|
||||
view: <Link to={'/settings/projects/' + app.spec.project}>{app.spec.project}</Link>,
|
||||
edit: (formApi: FormApi) => (
|
||||
<DataLoader load={() => services.projects.list('items.metadata.name').then(projs => projs.map(item => item.metadata.name))}>
|
||||
{projects => <FormField formApi={formApi} field='spec.project' component={FormSelect} componentProps={{options: projects}} />}
|
||||
|
||||
@@ -43,7 +43,7 @@ export const Help = () => {
|
||||
|
||||
{Object.keys(binaryUrls || {}).map(binaryName => {
|
||||
const url = binaryUrls[binaryName];
|
||||
const match = binaryName.match(/.*(darwin|windows|linux)-(amd64|arm64)/);
|
||||
const match = binaryName.match(/.*(darwin|windows|linux)-(amd64|arm64|ppc64le|s390x)/);
|
||||
const [platform, arch] = match ? match.slice(1) : ['', ''];
|
||||
return (
|
||||
<>
|
||||
|
||||
16
ui/yarn.lock
16
ui/yarn.lock
@@ -6792,10 +6792,10 @@ parse-json@^5.2.0:
|
||||
json-parse-even-better-errors "^2.3.0"
|
||||
lines-and-columns "^1.1.6"
|
||||
|
||||
parse-path@^4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf"
|
||||
integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==
|
||||
parse-path@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.4.tgz#4bf424e6b743fb080831f03b536af9fc43f0ffea"
|
||||
integrity sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==
|
||||
dependencies:
|
||||
is-ssh "^1.3.0"
|
||||
protocols "^1.4.0"
|
||||
@@ -6803,13 +6803,13 @@ parse-path@^4.0.0:
|
||||
query-string "^6.13.8"
|
||||
|
||||
parse-url@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.0.tgz#f5dd262a7de9ec00914939220410b66cff09107d"
|
||||
integrity sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.2.tgz#4a30b057bfc452af64512dfb1a7755c103db3ea1"
|
||||
integrity sha512-uCSjOvD3T+6B/sPWhR+QowAZcU/o4bjPrVBQBGFxcDF6J6FraCGIaDBsdoQawiaaAVdHvtqBe3w3vKlfBKySOQ==
|
||||
dependencies:
|
||||
is-ssh "^1.3.0"
|
||||
normalize-url "^6.1.0"
|
||||
parse-path "^4.0.0"
|
||||
parse-path "^4.0.4"
|
||||
protocols "^1.4.0"
|
||||
|
||||
parse5@6.0.1:
|
||||
|
||||
@@ -29,11 +29,11 @@ func IsManifestGenerationEnabled(sourceType v1alpha1.ApplicationSourceType, enab
|
||||
return enabled
|
||||
}
|
||||
|
||||
func Discover(ctx context.Context, repoPath string, enableGenerateManifests map[string]bool) (map[string]string, error) {
|
||||
func Discover(ctx context.Context, repoPath string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (map[string]string, error) {
|
||||
apps := make(map[string]string)
|
||||
|
||||
// Check if it is CMP
|
||||
conn, _, err := DetectConfigManagementPlugin(ctx, repoPath, []string{})
|
||||
conn, _, err := DetectConfigManagementPlugin(ctx, repoPath, []string{}, tarExcludedGlobs)
|
||||
if err == nil {
|
||||
// Found CMP
|
||||
io.Close(conn)
|
||||
@@ -65,8 +65,8 @@ func Discover(ctx context.Context, repoPath string, enableGenerateManifests map[
|
||||
return apps, err
|
||||
}
|
||||
|
||||
func AppType(ctx context.Context, path string, enableGenerateManifests map[string]bool) (string, error) {
|
||||
apps, err := Discover(ctx, path, enableGenerateManifests)
|
||||
func AppType(ctx context.Context, path string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (string, error) {
|
||||
apps, err := Discover(ctx, path, enableGenerateManifests, tarExcludedGlobs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -82,7 +82,7 @@ func AppType(ctx context.Context, path string, enableGenerateManifests map[strin
|
||||
// 3. check isSupported(path)?
|
||||
// 4.a if no then close connection
|
||||
// 4.b if yes then return conn for detected plugin
|
||||
func DetectConfigManagementPlugin(ctx context.Context, repoPath string, env []string) (io.Closer, pluginclient.ConfigManagementPluginServiceClient, error) {
|
||||
func DetectConfigManagementPlugin(ctx context.Context, repoPath string, env []string, tarExcludedGlobs []string) (io.Closer, pluginclient.ConfigManagementPluginServiceClient, error) {
|
||||
var conn io.Closer
|
||||
var cmpClient pluginclient.ConfigManagementPluginServiceClient
|
||||
|
||||
@@ -106,7 +106,7 @@ func DetectConfigManagementPlugin(ctx context.Context, repoPath string, env []st
|
||||
continue
|
||||
}
|
||||
|
||||
isSupported, err := matchRepositoryCMP(ctx, repoPath, cmpClient, env)
|
||||
isSupported, err := matchRepositoryCMP(ctx, repoPath, cmpClient, env, tarExcludedGlobs)
|
||||
if err != nil {
|
||||
log.Errorf("repository %s is not the match because %v", repoPath, err)
|
||||
continue
|
||||
@@ -131,13 +131,13 @@ func DetectConfigManagementPlugin(ctx context.Context, repoPath string, env []st
|
||||
// matchRepositoryCMP will send the repoPath to the cmp-server. The cmp-server will
|
||||
// inspect the files and return true if the repo is supported for manifest generation.
|
||||
// Will return false otherwise.
|
||||
func matchRepositoryCMP(ctx context.Context, repoPath string, client pluginclient.ConfigManagementPluginServiceClient, env []string) (bool, error) {
|
||||
func matchRepositoryCMP(ctx context.Context, repoPath string, client pluginclient.ConfigManagementPluginServiceClient, env []string, tarExcludedGlobs []string) (bool, error) {
|
||||
matchRepoStream, err := client.MatchRepository(ctx, grpc_retry.Disable())
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error getting stream client: %s", err)
|
||||
}
|
||||
|
||||
err = cmp.SendRepoStream(ctx, repoPath, repoPath, matchRepoStream, env)
|
||||
err = cmp.SendRepoStream(ctx, repoPath, repoPath, matchRepoStream, env, tarExcludedGlobs)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error sending stream: %s", err)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestDiscover(t *testing.T) {
|
||||
apps, err := Discover(context.Background(), "./testdata", map[string]bool{})
|
||||
apps, err := Discover(context.Background(), "./testdata", map[string]bool{}, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, map[string]string{
|
||||
"foo": "Kustomize",
|
||||
@@ -19,15 +19,15 @@ func TestDiscover(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAppType(t *testing.T) {
|
||||
appType, err := AppType(context.Background(), "./testdata/foo", map[string]bool{})
|
||||
appType, err := AppType(context.Background(), "./testdata/foo", map[string]bool{}, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Kustomize", appType)
|
||||
|
||||
appType, err = AppType(context.Background(), "./testdata/baz", map[string]bool{})
|
||||
appType, err = AppType(context.Background(), "./testdata/baz", map[string]bool{}, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Helm", appType)
|
||||
|
||||
appType, err = AppType(context.Background(), "./testdata", map[string]bool{})
|
||||
appType, err = AppType(context.Background(), "./testdata", map[string]bool{}, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Directory", appType)
|
||||
}
|
||||
@@ -37,15 +37,15 @@ func TestAppType_Disabled(t *testing.T) {
|
||||
string(v1alpha1.ApplicationSourceTypeKustomize): false,
|
||||
string(v1alpha1.ApplicationSourceTypeHelm): false,
|
||||
}
|
||||
appType, err := AppType(context.Background(), "./testdata/foo", enableManifestGeneration)
|
||||
appType, err := AppType(context.Background(), "./testdata/foo", enableManifestGeneration, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Directory", appType)
|
||||
|
||||
appType, err = AppType(context.Background(), "./testdata/baz", enableManifestGeneration)
|
||||
appType, err = AppType(context.Background(), "./testdata/baz", enableManifestGeneration, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Directory", appType)
|
||||
|
||||
appType, err = AppType(context.Background(), "./testdata", enableManifestGeneration)
|
||||
appType, err = AppType(context.Background(), "./testdata", enableManifestGeneration, []string{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Directory", appType)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ var LabelMaxLength = 63
|
||||
// ResourceTracking defines methods which allow setup and retrieve tracking information to resource
|
||||
type ResourceTracking interface {
|
||||
GetAppName(un *unstructured.Unstructured, key string, trackingMethod v1alpha1.TrackingMethod) string
|
||||
GetAppInstance(un *unstructured.Unstructured, key string, trackingMethod v1alpha1.TrackingMethod) *AppInstanceValue
|
||||
SetAppInstance(un *unstructured.Unstructured, key, val, namespace string, trackingMethod v1alpha1.TrackingMethod) error
|
||||
BuildAppInstanceValue(value AppInstanceValue) string
|
||||
ParseAppInstanceValue(value string) (*AppInstanceValue, error)
|
||||
@@ -54,7 +55,7 @@ func NewResourceTracking() ResourceTracking {
|
||||
// GetTrackingMethod retrieve tracking method from settings
|
||||
func GetTrackingMethod(settingsMgr *settings.SettingsManager) v1alpha1.TrackingMethod {
|
||||
tm, err := settingsMgr.GetTrackingMethod()
|
||||
if err != nil {
|
||||
if err != nil || tm == "" {
|
||||
return TrackingMethodLabel
|
||||
}
|
||||
return v1alpha1.TrackingMethod(tm)
|
||||
@@ -64,15 +65,23 @@ func IsOldTrackingMethod(trackingMethod string) bool {
|
||||
return trackingMethod == "" || trackingMethod == string(TrackingMethodLabel)
|
||||
}
|
||||
|
||||
func (rt *resourceTracking) getAppInstanceValue(un *unstructured.Unstructured, key string, trackingMethod v1alpha1.TrackingMethod) *AppInstanceValue {
|
||||
appInstanceAnnotation := argokube.GetAppInstanceAnnotation(un, common.AnnotationKeyAppInstance)
|
||||
value, err := rt.ParseAppInstanceValue(appInstanceAnnotation)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// GetAppName retrieve application name base on tracking method
|
||||
func (rt *resourceTracking) GetAppName(un *unstructured.Unstructured, key string, trackingMethod v1alpha1.TrackingMethod) string {
|
||||
retrieveAppInstanceValue := func() string {
|
||||
appInstanceAnnotation := argokube.GetAppInstanceAnnotation(un, common.AnnotationKeyAppInstance)
|
||||
value, err := rt.ParseAppInstanceValue(appInstanceAnnotation)
|
||||
if err != nil {
|
||||
return ""
|
||||
value := rt.getAppInstanceValue(un, key, trackingMethod)
|
||||
if value != nil {
|
||||
return value.ApplicationName
|
||||
}
|
||||
return value.ApplicationName
|
||||
return ""
|
||||
}
|
||||
switch trackingMethod {
|
||||
case TrackingMethodLabel:
|
||||
@@ -86,6 +95,18 @@ func (rt *resourceTracking) GetAppName(un *unstructured.Unstructured, key string
|
||||
}
|
||||
}
|
||||
|
||||
// GetAppInstance returns the representation of the app instance annotation.
|
||||
// If the tracking method does not support metadata, or the annotation could
|
||||
// not be parsed, it returns nil.
|
||||
func (rt *resourceTracking) GetAppInstance(un *unstructured.Unstructured, key string, trackingMethod v1alpha1.TrackingMethod) *AppInstanceValue {
|
||||
switch trackingMethod {
|
||||
case TrackingMethodAnnotation, TrackingMethodAnnotationAndLabel:
|
||||
return rt.getAppInstanceValue(un, key, trackingMethod)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// SetAppInstance set label/annotation base on tracking method
|
||||
func (rt *resourceTracking) SetAppInstance(un *unstructured.Unstructured, key, val, namespace string, trackingMethod v1alpha1.TrackingMethod) error {
|
||||
setAppInstanceAnnotation := func() error {
|
||||
|
||||
@@ -84,11 +84,11 @@ func WithTarDoneChan(ch chan<- bool) SenderOption {
|
||||
|
||||
// SendRepoStream will compress the files under the given repoPath and send
|
||||
// them using the plugin stream sender.
|
||||
func SendRepoStream(ctx context.Context, appPath, repoPath string, sender StreamSender, env []string, opts ...SenderOption) error {
|
||||
func SendRepoStream(ctx context.Context, appPath, repoPath string, sender StreamSender, env []string, excludedGlobs []string, opts ...SenderOption) error {
|
||||
opt := newSenderOption(opts...)
|
||||
|
||||
// compress all files in appPath in tgz
|
||||
tgz, checksum, err := compressFiles(repoPath)
|
||||
tgz, checksum, err := compressFiles(repoPath, excludedGlobs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error compressing repo files: %w", err)
|
||||
}
|
||||
@@ -162,11 +162,10 @@ func closeAndDelete(f *os.File) {
|
||||
}
|
||||
|
||||
// compressFiles will create a tgz file with all contents of appPath
|
||||
// directory excluding the .git folder. Returns the file alongside
|
||||
// its sha256 hash to be used as checksum. It is the responsibility
|
||||
// of the caller to close the file.
|
||||
func compressFiles(appPath string) (*os.File, string, error) {
|
||||
excluded := []string{".git"}
|
||||
// directory excluding globs in the excluded array. Returns the file
|
||||
// alongside its sha256 hash to be used as checksum. It is the
|
||||
// responsibility of the caller to close the file.
|
||||
func compressFiles(appPath string, excluded []string) (*os.File, string, error) {
|
||||
appName := filepath.Base(appPath)
|
||||
tempDir, err := files.CreateTempDir(os.TempDir())
|
||||
if err != nil {
|
||||
|
||||
@@ -60,7 +60,7 @@ func TestReceiveApplicationStream(t *testing.T) {
|
||||
close(streamMock.messages)
|
||||
os.RemoveAll(workdir)
|
||||
}()
|
||||
go streamMock.sendFile(context.Background(), t, appDir, streamMock, []string{"env1", "env2"})
|
||||
go streamMock.sendFile(context.Background(), t, appDir, streamMock, []string{"env1", "env2"}, []string{"DUMMY.md", "dum*"})
|
||||
|
||||
// when
|
||||
env, err := cmp.ReceiveRepoStream(context.Background(), streamMock, workdir)
|
||||
@@ -77,16 +77,18 @@ func TestReceiveApplicationStream(t *testing.T) {
|
||||
}
|
||||
assert.Contains(t, names, "README.md")
|
||||
assert.Contains(t, names, "applicationset")
|
||||
assert.NotContains(t, names, "DUMMY.md")
|
||||
assert.NotContains(t, names, "dummy")
|
||||
assert.NotNil(t, env)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *streamMock) sendFile(ctx context.Context, t *testing.T, basedir string, sender cmp.StreamSender, env []string) {
|
||||
func (m *streamMock) sendFile(ctx context.Context, t *testing.T, basedir string, sender cmp.StreamSender, env []string, excludedGlobs []string) {
|
||||
t.Helper()
|
||||
defer func() {
|
||||
m.done <- true
|
||||
}()
|
||||
err := cmp.SendRepoStream(ctx, basedir, basedir, sender, env)
|
||||
err := cmp.SendRepoStream(ctx, basedir, basedir, sender, env, excludedGlobs)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
|
||||
1
util/cmp/testdata/app/DUMMY.md
vendored
Normal file
1
util/cmp/testdata/app/DUMMY.md
vendored
Normal file
@@ -0,0 +1 @@
|
||||
This file is used to test the tar stream exclusions.
|
||||
1
util/cmp/testdata/app/dummy/DUMMY2.md
vendored
Normal file
1
util/cmp/testdata/app/dummy/DUMMY2.md
vendored
Normal file
@@ -0,0 +1 @@
|
||||
This is another file that should get excluded because the entire directory is excluded.
|
||||
7
util/env/env.go
vendored
7
util/env/env.go
vendored
@@ -126,6 +126,13 @@ func StringFromEnv(env string, defaultValue string) string {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func StringsFromEnv(env string, defaultValue []string, separator string) []string {
|
||||
if str := os.Getenv(env); str != "" {
|
||||
return strings.Split(str, separator)
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// ParseBoolFromEnv retrieves a boolean value from given environment envVar.
|
||||
// Returns default value if envVar is not set.
|
||||
//
|
||||
|
||||
@@ -58,6 +58,7 @@ type Client interface {
|
||||
Root() string
|
||||
Init() error
|
||||
Fetch(revision string) error
|
||||
Submodule() error
|
||||
Checkout(revision string, submoduleEnabled bool) error
|
||||
LsRefs() (*Refs, error)
|
||||
LsRemote(revision string) (string, error)
|
||||
@@ -326,6 +327,16 @@ func (m *nativeGitClient) IsLFSEnabled() bool {
|
||||
return m.enableLfs
|
||||
}
|
||||
|
||||
func (m *nativeGitClient) fetch(revision string) error {
|
||||
var err error
|
||||
if revision != "" {
|
||||
err = m.runCredentialedCmd("git", "fetch", "origin", revision, "--tags", "--force")
|
||||
} else {
|
||||
err = m.runCredentialedCmd("git", "fetch", "origin", "--tags", "--force")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Fetch fetches latest updates from origin
|
||||
func (m *nativeGitClient) Fetch(revision string) error {
|
||||
if m.OnFetch != nil {
|
||||
@@ -334,11 +345,19 @@ func (m *nativeGitClient) Fetch(revision string) error {
|
||||
}
|
||||
|
||||
var err error
|
||||
if revision != "" {
|
||||
err = m.runCredentialedCmd("git", "fetch", "origin", revision, "--tags", "--force")
|
||||
} else {
|
||||
err = m.runCredentialedCmd("git", "fetch", "origin", "--tags", "--force")
|
||||
|
||||
err = m.fetch(revision)
|
||||
if err != nil {
|
||||
errMsg := strings.ReplaceAll(err.Error(), "\n", "")
|
||||
if strings.Contains(errMsg, "try running 'git remote prune origin'") {
|
||||
// Prune any deleted refs, then try fetching again
|
||||
if err := m.runCredentialedCmd("git", "remote", "prune", "origin"); err != nil {
|
||||
return err
|
||||
}
|
||||
err = m.fetch(revision)
|
||||
}
|
||||
}
|
||||
|
||||
// When we have LFS support enabled, check for large files and fetch them too.
|
||||
if err == nil && m.IsLFSEnabled() {
|
||||
largeFiles, err := m.LsLargeFiles()
|
||||
@@ -349,6 +368,7 @@ func (m *nativeGitClient) Fetch(revision string) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -373,6 +393,17 @@ func (m *nativeGitClient) LsLargeFiles() ([]string, error) {
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
// Submodule embed other repositories into this repository
|
||||
func (m *nativeGitClient) Submodule() error {
|
||||
if err := m.runCredentialedCmd("git", "submodule", "sync", "--recursive"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.runCredentialedCmd("git", "submodule", "update", "--init", "--recursive"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checkout checkout specified revision
|
||||
func (m *nativeGitClient) Checkout(revision string, submoduleEnabled bool) error {
|
||||
if revision == "" || revision == "HEAD" {
|
||||
@@ -396,7 +427,7 @@ func (m *nativeGitClient) Checkout(revision string, submoduleEnabled bool) error
|
||||
}
|
||||
if _, err := os.Stat(m.root + "/.gitmodules"); !os.IsNotExist(err) {
|
||||
if submoduleEnabled {
|
||||
if err := m.runCredentialedCmd("git", "submodule", "update", "--init", "--recursive"); err != nil {
|
||||
if err := m.Submodule(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
209
util/git/client_test.go
Normal file
209
util/git/client_test.go
Normal file
@@ -0,0 +1,209 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_nativeGitClient_Fetch(t *testing.T) {
|
||||
tempDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd := exec.Command("git", "init")
|
||||
cmd.Dir = tempDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd = exec.Command("git", "commit", "-m", "Initial commit", "--allow-empty")
|
||||
cmd.Dir = tempDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
client, err := NewClient(fmt.Sprintf("file://%s", tempDir), NopCreds{}, true, false, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Init()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Fetch("")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_nativeGitClient_Fetch_Prune(t *testing.T) {
|
||||
tempDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd := exec.Command("git", "init")
|
||||
cmd.Dir = tempDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd = exec.Command("git", "commit", "-m", "Initial commit", "--allow-empty")
|
||||
cmd.Dir = tempDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
client, err := NewClient(fmt.Sprintf("file://%s", tempDir), NopCreds{}, true, false, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Init()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create branch
|
||||
cmd = exec.Command("git", "branch", "test/foo")
|
||||
cmd.Dir = tempDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Fetch("")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Delete branch
|
||||
cmd = exec.Command("git", "branch", "-d", "test/foo")
|
||||
cmd.Dir = tempDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create branch that conflicts with previous branch name
|
||||
cmd = exec.Command("git", "branch", "test/foo/bar")
|
||||
cmd.Dir = tempDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Fetch("")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_nativeGitClient_Submodule(t *testing.T) {
|
||||
tempDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
foo := filepath.Join(tempDir, "foo")
|
||||
err = os.Mkdir(foo, 0755)
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd := exec.Command("git", "init")
|
||||
cmd.Dir = foo
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
bar := filepath.Join(tempDir, "bar")
|
||||
err = os.Mkdir(bar, 0755)
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd = exec.Command("git", "init")
|
||||
cmd.Dir = bar
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd = exec.Command("git", "commit", "-m", "Initial commit", "--allow-empty")
|
||||
cmd.Dir = bar
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Embed repository bar into repository foo
|
||||
cmd = exec.Command("git", "submodule", "add", bar)
|
||||
cmd.Dir = foo
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd = exec.Command("git", "commit", "-m", "Initial commit")
|
||||
cmd.Dir = foo
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
tempDir, err = os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Clone foo
|
||||
cmd = exec.Command("git", "clone", foo)
|
||||
cmd.Dir = tempDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
client, err := NewClient(fmt.Sprintf("file://%s", foo), NopCreds{}, true, false, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Init()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = client.Fetch("")
|
||||
assert.NoError(t, err)
|
||||
|
||||
commitSHA, err := client.LsRemote("HEAD")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Call Checkout() with submoduleEnabled=false.
|
||||
err = client.Checkout(commitSHA, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Check if submodule url does not exist in .git/config
|
||||
cmd = exec.Command("git", "config", "submodule.bar.url")
|
||||
cmd.Dir = client.Root()
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
assert.Error(t, err)
|
||||
|
||||
// Call Submodule() via Checkout() with submoduleEnabled=true.
|
||||
err = client.Checkout(commitSHA, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Check if the .gitmodule URL is reflected in .git/config
|
||||
cmd = exec.Command("git", "config", "submodule.bar.url")
|
||||
cmd.Dir = client.Root()
|
||||
result, err := cmd.Output()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, bar+"\n", string(result))
|
||||
|
||||
// Change URL of submodule bar
|
||||
cmd = exec.Command("git", "config", "--file=.gitmodules", "submodule.bar.url", bar+"baz")
|
||||
cmd.Dir = client.Root()
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Call Submodule()
|
||||
err = client.Submodule()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Check if the URL change in .gitmodule is reflected in .git/config
|
||||
cmd = exec.Command("git", "config", "submodule.bar.url")
|
||||
cmd.Dir = client.Root()
|
||||
result, err = cmd.Output()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, bar+"baz\n", string(result))
|
||||
}
|
||||
@@ -3,9 +3,8 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
git "github.com/argoproj/argo-cd/v2/util/git"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// Client is an autogenerated mock type for the Client type
|
||||
@@ -203,6 +202,20 @@ func (_m *Client) Root() string {
|
||||
return r0
|
||||
}
|
||||
|
||||
// Submodule provides a mock function with given fields:
|
||||
func (_m *Client) Submodule() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// VerifyCommitSignature provides a mock function with given fields: _a0
|
||||
func (_m *Client) VerifyCommitSignature(_a0 string) (string, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user