mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-02 06:28:47 +01:00
Compare commits
14 Commits
v1.3.2
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89be1c9ce6 | ||
|
|
d16f29f0ac | ||
|
|
5c1f581584 | ||
|
|
3e150df0ed | ||
|
|
ce8d5a4533 | ||
|
|
c93ce9082e | ||
|
|
68e1a5fbeb | ||
|
|
6f38cf3133 | ||
|
|
2be56f232a | ||
|
|
bb5d2f4197 | ||
|
|
fff5355314 | ||
|
|
f44ce07664 | ||
|
|
116440690b | ||
|
|
7cef1313c7 |
94
Gopkg.lock
generated
94
Gopkg.lock
generated
@@ -135,6 +135,17 @@
|
||||
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
|
||||
version = "v3.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c05f1899f086e3b4613d94d9e6f7ba6f4b6587498a1aa6037c5c294b22f5a743"
|
||||
name = "github.com/docker/distribution"
|
||||
packages = [
|
||||
"digestset",
|
||||
"reference",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "2461543d988979529609e8cb6fca9ca190dc48da"
|
||||
version = "v2.7.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b021ef379356343bdc13ec101e546b756fcef4b1186d08163bef7d3bc8c1e07f"
|
||||
name = "github.com/docker/docker"
|
||||
@@ -651,6 +662,14 @@
|
||||
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
|
||||
version = "1.0.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5d9b668b0b4581a978f07e7d2e3314af18eb27b3fb5d19b70185b7c575723d11"
|
||||
name = "github.com/opencontainers/go-digest"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf"
|
||||
version = "v1.0.0-rc1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:4c0404dc03d974acd5fcd8b8d3ce687b13bd169db032b89275e8b9d77b98ce8c"
|
||||
name = "github.com/patrickmn/go-cache"
|
||||
@@ -1548,15 +1567,74 @@
|
||||
digest = "1:78aa6079e011ece0d28513c7fe1bd64284fa9eb5d671760803a839ffdf0e9e38"
|
||||
name = "k8s.io/kubernetes"
|
||||
packages = [
|
||||
"pkg/api/legacyscheme",
|
||||
"pkg/api/v1/pod",
|
||||
"pkg/apis/apps",
|
||||
"pkg/apis/apps/install",
|
||||
"pkg/apis/apps/v1",
|
||||
"pkg/apis/apps/v1beta1",
|
||||
"pkg/apis/apps/v1beta2",
|
||||
"pkg/apis/authentication",
|
||||
"pkg/apis/authentication/install",
|
||||
"pkg/apis/authentication/v1",
|
||||
"pkg/apis/authentication/v1beta1",
|
||||
"pkg/apis/authorization",
|
||||
"pkg/apis/authorization/install",
|
||||
"pkg/apis/authorization/v1",
|
||||
"pkg/apis/authorization/v1beta1",
|
||||
"pkg/apis/autoscaling",
|
||||
"pkg/apis/autoscaling/install",
|
||||
"pkg/apis/autoscaling/v1",
|
||||
"pkg/apis/autoscaling/v2beta1",
|
||||
"pkg/apis/autoscaling/v2beta2",
|
||||
"pkg/apis/batch",
|
||||
"pkg/apis/batch/install",
|
||||
"pkg/apis/batch/v1",
|
||||
"pkg/apis/batch/v1beta1",
|
||||
"pkg/apis/batch/v2alpha1",
|
||||
"pkg/apis/certificates",
|
||||
"pkg/apis/certificates/install",
|
||||
"pkg/apis/certificates/v1beta1",
|
||||
"pkg/apis/coordination",
|
||||
"pkg/apis/coordination/install",
|
||||
"pkg/apis/coordination/v1",
|
||||
"pkg/apis/coordination/v1beta1",
|
||||
"pkg/apis/core",
|
||||
"pkg/apis/core/install",
|
||||
"pkg/apis/core/v1",
|
||||
"pkg/apis/events",
|
||||
"pkg/apis/events/install",
|
||||
"pkg/apis/events/v1beta1",
|
||||
"pkg/apis/extensions",
|
||||
"pkg/apis/extensions/install",
|
||||
"pkg/apis/extensions/v1beta1",
|
||||
"pkg/apis/networking",
|
||||
"pkg/apis/policy",
|
||||
"pkg/apis/policy/install",
|
||||
"pkg/apis/policy/v1beta1",
|
||||
"pkg/apis/rbac",
|
||||
"pkg/apis/rbac/install",
|
||||
"pkg/apis/rbac/v1",
|
||||
"pkg/apis/rbac/v1alpha1",
|
||||
"pkg/apis/rbac/v1beta1",
|
||||
"pkg/apis/scheduling",
|
||||
"pkg/apis/scheduling/install",
|
||||
"pkg/apis/scheduling/v1",
|
||||
"pkg/apis/scheduling/v1alpha1",
|
||||
"pkg/apis/scheduling/v1beta1",
|
||||
"pkg/apis/settings",
|
||||
"pkg/apis/settings/install",
|
||||
"pkg/apis/settings/v1alpha1",
|
||||
"pkg/apis/storage",
|
||||
"pkg/apis/storage/install",
|
||||
"pkg/apis/storage/v1",
|
||||
"pkg/apis/storage/v1alpha1",
|
||||
"pkg/apis/storage/v1beta1",
|
||||
"pkg/kubectl/scheme",
|
||||
"pkg/kubectl/util/term",
|
||||
"pkg/util/interrupt",
|
||||
"pkg/util/node",
|
||||
"pkg/util/parsers",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "2d20b5759406ded89f8b25cf085ff4733b144ba5"
|
||||
@@ -1733,10 +1811,26 @@
|
||||
"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1",
|
||||
"k8s.io/kube-openapi/cmd/openapi-gen",
|
||||
"k8s.io/kube-openapi/pkg/common",
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme",
|
||||
"k8s.io/kubernetes/pkg/api/v1/pod",
|
||||
"k8s.io/kubernetes/pkg/apis/apps",
|
||||
"k8s.io/kubernetes/pkg/apis/apps/install",
|
||||
"k8s.io/kubernetes/pkg/apis/authentication/install",
|
||||
"k8s.io/kubernetes/pkg/apis/authorization/install",
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling/install",
|
||||
"k8s.io/kubernetes/pkg/apis/batch",
|
||||
"k8s.io/kubernetes/pkg/apis/batch/install",
|
||||
"k8s.io/kubernetes/pkg/apis/certificates/install",
|
||||
"k8s.io/kubernetes/pkg/apis/coordination/install",
|
||||
"k8s.io/kubernetes/pkg/apis/core",
|
||||
"k8s.io/kubernetes/pkg/apis/core/install",
|
||||
"k8s.io/kubernetes/pkg/apis/events/install",
|
||||
"k8s.io/kubernetes/pkg/apis/extensions/install",
|
||||
"k8s.io/kubernetes/pkg/apis/policy/install",
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/install",
|
||||
"k8s.io/kubernetes/pkg/apis/scheduling/install",
|
||||
"k8s.io/kubernetes/pkg/apis/settings/install",
|
||||
"k8s.io/kubernetes/pkg/apis/storage/install",
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme",
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/term",
|
||||
"k8s.io/kubernetes/pkg/util/node",
|
||||
|
||||
@@ -74,6 +74,7 @@ func NewCommand() *cobra.Command {
|
||||
command.AddCommand(NewImportCommand())
|
||||
command.AddCommand(NewExportCommand())
|
||||
command.AddCommand(NewClusterConfig())
|
||||
command.AddCommand(NewProjectsCommand())
|
||||
|
||||
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
return command
|
||||
@@ -220,6 +221,7 @@ func NewImportCommand() *cobra.Command {
|
||||
os.Exit(1)
|
||||
}
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
config.QPS = 100
|
||||
config.Burst = 50
|
||||
errors.CheckError(err)
|
||||
|
||||
192
cmd/argocd-util/projects.go
Normal file
192
cmd/argocd-util/projects.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
appclient "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/typed/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
func NewProjectsCommand() *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "projects",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
},
|
||||
}
|
||||
|
||||
command.AddCommand(NewUpdatePolicyRuleCommand())
|
||||
return command
|
||||
}
|
||||
|
||||
func globMatch(pattern string, val string) bool {
|
||||
if pattern == "*" {
|
||||
return true
|
||||
}
|
||||
if ok, err := filepath.Match(pattern, val); ok && err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getModification(modification string, resource string, scope string, permission string) (func(string, string) string, error) {
|
||||
switch modification {
|
||||
case "set":
|
||||
if scope == "" {
|
||||
return nil, fmt.Errorf("Flag --group cannot be empty if permission should be set in role")
|
||||
}
|
||||
if permission == "" {
|
||||
return nil, fmt.Errorf("Flag --permission cannot be empty if permission should be set in role")
|
||||
}
|
||||
return func(proj string, action string) string {
|
||||
return fmt.Sprintf("%s, %s, %s/%s, %s", resource, action, proj, scope, permission)
|
||||
}, nil
|
||||
case "remove":
|
||||
return func(proj string, action string) string {
|
||||
return ""
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("modification %s is not supported", modification)
|
||||
}
|
||||
|
||||
func saveProject(updated v1alpha1.AppProject, orig v1alpha1.AppProject, projectsIf appclient.AppProjectInterface, dryRun bool) error {
|
||||
fmt.Printf("===== %s ======\n", updated.Name)
|
||||
target, err := kube.ToUnstructured(&updated)
|
||||
errors.CheckError(err)
|
||||
live, err := kube.ToUnstructured(&orig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = diff.PrintDiff(updated.Name, target, live)
|
||||
if !dryRun {
|
||||
_, err = projectsIf.Update(&updated)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatPolicy(proj string, role string, permission string) string {
|
||||
return fmt.Sprintf("p, proj:%s:%s, %s", proj, role, permission)
|
||||
}
|
||||
|
||||
func split(input string, delimiter string) []string {
|
||||
parts := strings.Split(input, delimiter)
|
||||
for i := range parts {
|
||||
parts[i] = strings.TrimSpace(parts[i])
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
func NewUpdatePolicyRuleCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
resource string
|
||||
scope string
|
||||
rolePattern string
|
||||
permission string
|
||||
dryRun bool
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "update-role-policy PROJECT_GLOB MODIFICATION ACTION",
|
||||
Short: "Implement bulk project role update. Useful to back-fill existing project policies or remove obsolete actions.",
|
||||
Example: ` # Add policy that allows executing any action (action/*) to roles which name matches to *deployer* in all projects
|
||||
argocd-util projects update-role-policy '*' set 'action/*' --role '*deployer*' --resource applications --scope '*' --permission allow
|
||||
|
||||
# Remove policy that which manages running (action/*) from all roles which name matches *deployer* in all projects
|
||||
argocd-util projects update-role-policy '*' remove override --role '*deployer*'
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 3 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
projectGlob := args[0]
|
||||
modificationType := args[1]
|
||||
action := args[2]
|
||||
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
config.QPS = 100
|
||||
config.Burst = 50
|
||||
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
appclients := appclientset.NewForConfigOrDie(config)
|
||||
|
||||
modification, err := getModification(modificationType, resource, scope, permission)
|
||||
errors.CheckError(err)
|
||||
projIf := appclients.ArgoprojV1alpha1().AppProjects(namespace)
|
||||
|
||||
err = updateProjects(projIf, projectGlob, rolePattern, action, modification, dryRun)
|
||||
errors.CheckError(err)
|
||||
},
|
||||
}
|
||||
command.Flags().StringVar(&resource, "resource", "", "Resource e.g. 'applications'")
|
||||
command.Flags().StringVar(&scope, "scope", "", "Resource scope e.g. '*'")
|
||||
command.Flags().StringVar(&rolePattern, "role", "*", "Role name pattern e.g. '*deployer*'")
|
||||
command.Flags().StringVar(&permission, "permission", "", "Action permission")
|
||||
command.Flags().BoolVar(&dryRun, "dry-run", true, "Dry run")
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(command)
|
||||
return command
|
||||
}
|
||||
|
||||
func updateProjects(projIf appclient.AppProjectInterface, projectGlob string, rolePattern string, action string, modification func(string, string) string, dryRun bool) error {
|
||||
projects, err := projIf.List(v1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, proj := range projects.Items {
|
||||
if !globMatch(projectGlob, proj.Name) {
|
||||
continue
|
||||
}
|
||||
origProj := proj.DeepCopy()
|
||||
updated := false
|
||||
for i, role := range proj.Spec.Roles {
|
||||
if !globMatch(rolePattern, role.Name) {
|
||||
continue
|
||||
}
|
||||
actionPolicyIndex := -1
|
||||
for i := range role.Policies {
|
||||
parts := split(role.Policies[i], ",")
|
||||
if len(parts) != 6 || parts[3] != action {
|
||||
continue
|
||||
}
|
||||
actionPolicyIndex = i
|
||||
break
|
||||
}
|
||||
policyPermission := modification(proj.Name, action)
|
||||
if actionPolicyIndex == -1 && policyPermission != "" {
|
||||
updated = true
|
||||
role.Policies = append(role.Policies, formatPolicy(proj.Name, role.Name, policyPermission))
|
||||
} else if actionPolicyIndex > -1 && policyPermission == "" {
|
||||
updated = true
|
||||
role.Policies = append(role.Policies[:actionPolicyIndex], role.Policies[actionPolicyIndex+1:]...)
|
||||
} else if actionPolicyIndex > -1 && policyPermission != "" {
|
||||
updated = true
|
||||
role.Policies[actionPolicyIndex] = formatPolicy(proj.Name, role.Name, policyPermission)
|
||||
}
|
||||
proj.Spec.Roles[i] = role
|
||||
}
|
||||
if updated {
|
||||
err = saveProject(proj, *origProj, projIf, dryRun)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
78
cmd/argocd-util/projects_test.go
Normal file
78
cmd/argocd-util/projects_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
|
||||
)
|
||||
|
||||
const (
|
||||
namespace = "default"
|
||||
)
|
||||
|
||||
func newProj(name string, roleNames ...string) *v1alpha1.AppProject {
|
||||
var roles []v1alpha1.ProjectRole
|
||||
for i := range roleNames {
|
||||
roles = append(roles, v1alpha1.ProjectRole{Name: roleNames[i]})
|
||||
}
|
||||
return &v1alpha1.AppProject{ObjectMeta: v1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
}, Spec: v1alpha1.AppProjectSpec{
|
||||
Roles: roles,
|
||||
}}
|
||||
}
|
||||
|
||||
func TestUpdateProjects_FindMatchingProject(t *testing.T) {
|
||||
clientset := fake.NewSimpleClientset(newProj("foo", "test"), newProj("bar", "test"))
|
||||
|
||||
modification, err := getModification("set", "*", "*", "allow")
|
||||
assert.NoError(t, err)
|
||||
err = updateProjects(clientset.ArgoprojV1alpha1().AppProjects(namespace), "ba*", "*", "set", modification, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
fooProj, err := clientset.ArgoprojV1alpha1().AppProjects(namespace).Get("foo", v1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, fooProj.Spec.Roles[0].Policies, 0)
|
||||
|
||||
barProj, err := clientset.ArgoprojV1alpha1().AppProjects(namespace).Get("bar", v1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, barProj.Spec.Roles[0].Policies, []string{"p, proj:bar:test, *, set, bar/*, allow"})
|
||||
}
|
||||
|
||||
func TestUpdateProjects_FindMatchingRole(t *testing.T) {
|
||||
clientset := fake.NewSimpleClientset(newProj("proj", "foo", "bar"))
|
||||
|
||||
modification, err := getModification("set", "*", "*", "allow")
|
||||
assert.NoError(t, err)
|
||||
err = updateProjects(clientset.ArgoprojV1alpha1().AppProjects(namespace), "*", "fo*", "set", modification, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
proj, err := clientset.ArgoprojV1alpha1().AppProjects(namespace).Get("proj", v1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, proj.Spec.Roles[0].Policies, []string{"p, proj:proj:foo, *, set, proj/*, allow"})
|
||||
assert.Len(t, proj.Spec.Roles[1].Policies, 0)
|
||||
}
|
||||
|
||||
func TestGetModification_SetPolicy(t *testing.T) {
|
||||
modification, err := getModification("set", "*", "*", "allow")
|
||||
assert.NoError(t, err)
|
||||
policy := modification("proj", "myaction")
|
||||
assert.Equal(t, "*, myaction, proj/*, allow", policy)
|
||||
}
|
||||
|
||||
func TestGetModification_RemovePolicy(t *testing.T) {
|
||||
modification, err := getModification("remove", "*", "*", "allow")
|
||||
assert.NoError(t, err)
|
||||
policy := modification("proj", "myaction")
|
||||
assert.Equal(t, "", policy)
|
||||
}
|
||||
|
||||
func TestGetModification_NotSupported(t *testing.T) {
|
||||
_, err := getModification("bar", "*", "*", "allow")
|
||||
assert.Errorf(t, err, "modification bar is not supported")
|
||||
}
|
||||
@@ -784,7 +784,7 @@ func getLocalObjects(app *argoappv1.Application, local, appLabelKey, kubeVersion
|
||||
}
|
||||
|
||||
func getLocalObjectsString(app *argoappv1.Application, local, appLabelKey, kubeVersion string, kustomizeOptions *argoappv1.KustomizeOptions) []string {
|
||||
res, err := repository.GenerateManifests(local, &repoapiclient.ManifestRequest{
|
||||
res, err := repository.GenerateManifests(local, "/", &repoapiclient.ManifestRequest{
|
||||
ApplicationSource: &app.Spec.Source,
|
||||
AppLabelKey: appLabelKey,
|
||||
AppLabelValue: app.Name,
|
||||
|
||||
@@ -107,6 +107,7 @@ func newFakeController(data *fakeData) *ApplicationController {
|
||||
ctrl.stateCache = &mockStateCache
|
||||
mockStateCache.On("IsNamespaced", mock.Anything, mock.Anything).Return(true, nil)
|
||||
mockStateCache.On("GetManagedLiveObjs", mock.Anything, mock.Anything).Return(data.managedLiveObjs, nil)
|
||||
mockStateCache.On("GetServerVersion", mock.Anything).Return("v1.2.3", nil)
|
||||
response := make(map[kube.ResourceKey]argoappv1.ResourceNode)
|
||||
for k, v := range data.namespacedResources {
|
||||
response[k] = v.ResourceNode
|
||||
|
||||
10
controller/cache/cache.go
vendored
10
controller/cache/cache.go
vendored
@@ -27,6 +27,9 @@ type cacheSettings struct {
|
||||
}
|
||||
|
||||
type LiveStateCache interface {
|
||||
// Returns k8s server version
|
||||
GetServerVersion(serverURL string) (string, error)
|
||||
// Returns true of given group kind is a namespaced resource
|
||||
IsNamespaced(server string, gk schema.GroupKind) (bool, error)
|
||||
// Executes give callback against resource specified by the key and all its children
|
||||
IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string)) error
|
||||
@@ -187,6 +190,13 @@ func (c *liveStateCache) GetManagedLiveObjs(a *appv1.Application, targetObjs []*
|
||||
}
|
||||
return clusterInfo.getManagedLiveObjs(a, targetObjs, c.metricsServer)
|
||||
}
|
||||
func (c *liveStateCache) GetServerVersion(serverURL string) (string, error) {
|
||||
clusterInfo, err := c.getSyncedCluster(serverURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return clusterInfo.serverVersion, nil
|
||||
}
|
||||
|
||||
func isClusterHasApps(apps []interface{}, cluster *appv1.Cluster) bool {
|
||||
for _, obj := range apps {
|
||||
|
||||
27
controller/cache/cache_test.go
vendored
Normal file
27
controller/cache/cache_test.go
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetServerVersion(t *testing.T) {
|
||||
now := time.Now()
|
||||
cache := &liveStateCache{
|
||||
lock: &sync.Mutex{},
|
||||
clusters: map[string]*clusterInfo{
|
||||
"http://localhost": {
|
||||
syncTime: &now,
|
||||
syncLock: &sync.Mutex{},
|
||||
lock: &sync.Mutex{},
|
||||
serverVersion: "123",
|
||||
},
|
||||
}}
|
||||
|
||||
version, err := cache.GetServerVersion("http://localhost")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "123", version)
|
||||
}
|
||||
18
controller/cache/cluster.go
vendored
18
controller/cache/cluster.go
vendored
@@ -39,10 +39,11 @@ type apiMeta struct {
|
||||
}
|
||||
|
||||
type clusterInfo struct {
|
||||
syncLock *sync.Mutex
|
||||
syncTime *time.Time
|
||||
syncError error
|
||||
apisMeta map[schema.GroupKind]*apiMeta
|
||||
syncLock *sync.Mutex
|
||||
syncTime *time.Time
|
||||
syncError error
|
||||
apisMeta map[schema.GroupKind]*apiMeta
|
||||
serverVersion string
|
||||
|
||||
lock *sync.Mutex
|
||||
nodes map[kube.ResourceKey]*node
|
||||
@@ -309,8 +310,13 @@ func (c *clusterInfo) sync() (err error) {
|
||||
}
|
||||
c.apisMeta = make(map[schema.GroupKind]*apiMeta)
|
||||
c.nodes = make(map[kube.ResourceKey]*node)
|
||||
|
||||
apis, err := c.kubectl.GetAPIResources(c.cluster.RESTConfig(), c.cacheSettingsSrc().ResourcesFilter)
|
||||
config := c.cluster.RESTConfig()
|
||||
version, err := c.kubectl.GetServerVersion(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.serverVersion = version
|
||||
apis, err := c.kubectl.GetAPIResources(config, c.cacheSettingsSrc().ResourcesFilter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
24
controller/cache/mocks/LiveStateCache.go
vendored
24
controller/cache/mocks/LiveStateCache.go
vendored
@@ -5,9 +5,8 @@ package mocks
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
kube "github.com/argoproj/argo-cd/util/kube"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
@@ -67,6 +66,27 @@ func (_m *LiveStateCache) GetNamespaceTopLevelResources(server string, namespace
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetServerVersion provides a mock function with given fields: server
|
||||
func (_m *LiveStateCache) GetServerVersion(server string) (string, error) {
|
||||
ret := _m.Called(server)
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func(string) string); ok {
|
||||
r0 = rf(server)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(server)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Invalidate provides a mock function with given fields:
|
||||
func (_m *LiveStateCache) Invalidate() {
|
||||
_m.Called()
|
||||
|
||||
@@ -128,11 +128,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
cluster, err := m.db.GetCluster(context.Background(), app.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
cluster.ServerVersion, err = m.kubectl.GetServerVersion(cluster.RESTConfig())
|
||||
serverVersion, err := m.liveStateCache.GetServerVersion(app.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
@@ -149,7 +145,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
|
||||
KustomizeOptions: &appv1.KustomizeOptions{
|
||||
BuildOptions: buildOptions,
|
||||
},
|
||||
KubeVersion: cluster.ServerVersion,
|
||||
KubeVersion: serverVersion,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestCompareAppStateMissing(t *testing.T) {
|
||||
data := fakeData{
|
||||
apps: []runtime.Object{app},
|
||||
manifestResponse: &apiclient.ManifestResponse{
|
||||
Manifests: []string{string(test.PodManifest)},
|
||||
Manifests: []string{test.PodManifest},
|
||||
Namespace: test.FakeDestNamespace,
|
||||
Server: test.FakeClusterURL,
|
||||
Revision: "abc123",
|
||||
|
||||
@@ -12,7 +12,7 @@ bases:
|
||||
images:
|
||||
- name: argoproj/argocd
|
||||
newName: argoproj/argocd
|
||||
newTag: v1.3.2
|
||||
newTag: v1.3.6
|
||||
- name: argoproj/argocd-ui
|
||||
newName: argoproj/argocd-ui
|
||||
newTag: v1.3.2
|
||||
newTag: v1.3.6
|
||||
|
||||
@@ -18,7 +18,7 @@ bases:
|
||||
images:
|
||||
- name: argoproj/argocd
|
||||
newName: argoproj/argocd
|
||||
newTag: v1.3.2
|
||||
newTag: v1.3.6
|
||||
- name: argoproj/argocd-ui
|
||||
newName: argoproj/argocd-ui
|
||||
newTag: v1.3.2
|
||||
newTag: v1.3.6
|
||||
|
||||
@@ -2982,7 +2982,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -3036,7 +3036,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -3092,7 +3092,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 5
|
||||
@@ -3166,7 +3166,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -2897,7 +2897,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2951,7 +2951,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -3007,7 +3007,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 5
|
||||
@@ -3081,7 +3081,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -2746,7 +2746,7 @@ spec:
|
||||
- "20"
|
||||
- --operation-processors
|
||||
- "10"
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2800,7 +2800,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2864,7 +2864,7 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis:6379
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 5
|
||||
@@ -2915,7 +2915,7 @@ spec:
|
||||
- argocd-server
|
||||
- --staticassets
|
||||
- /shared/app
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -2661,7 +2661,7 @@ spec:
|
||||
- "20"
|
||||
- --operation-processors
|
||||
- "10"
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2715,7 +2715,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2779,7 +2779,7 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis:6379
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 5
|
||||
@@ -2830,7 +2830,7 @@ spec:
|
||||
- argocd-server
|
||||
- --staticassets
|
||||
- /shared/app
|
||||
image: argoproj/argocd:v1.3.2
|
||||
image: argoproj/argocd:v1.3.6
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/TomOnTime/utfutil"
|
||||
argoexec "github.com/argoproj/pkg/exec"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/google/go-jsonnet"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -24,6 +23,9 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
executil "github.com/argoproj/argo-cd/util/exec"
|
||||
"github.com/argoproj/argo-cd/util/security"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
@@ -32,13 +34,11 @@ import (
|
||||
"github.com/argoproj/argo-cd/util/app/discovery"
|
||||
argopath "github.com/argoproj/argo-cd/util/app/path"
|
||||
"github.com/argoproj/argo-cd/util/cache"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
"github.com/argoproj/argo-cd/util/helm"
|
||||
"github.com/argoproj/argo-cd/util/ksonnet"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/kustomize"
|
||||
"github.com/argoproj/argo-cd/util/security"
|
||||
"github.com/argoproj/argo-cd/util/text"
|
||||
)
|
||||
|
||||
@@ -117,7 +117,7 @@ func (s *Service) runRepoOperation(
|
||||
repo *v1alpha1.Repository,
|
||||
source *v1alpha1.ApplicationSource,
|
||||
getCached func(revision string) bool,
|
||||
operation func(appPath string, revision string) error,
|
||||
operation func(appPath, repoRoot, revision string) error,
|
||||
settings operationSettings) error {
|
||||
|
||||
var gitClient git.Client
|
||||
@@ -158,7 +158,7 @@ func (s *Service) runRepoOperation(
|
||||
return err
|
||||
}
|
||||
defer util.Close(closer)
|
||||
return operation(chartPath, revision)
|
||||
return operation(chartPath, chartPath, revision)
|
||||
} else {
|
||||
s.repoLock.Lock(gitClient.Root())
|
||||
defer s.repoLock.Unlock(gitClient.Root())
|
||||
@@ -174,7 +174,7 @@ func (s *Service) runRepoOperation(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return operation(appPath, revision)
|
||||
return operation(appPath, gitClient.Root(), revision)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,9 +194,9 @@ func (s *Service) GenerateManifest(ctx context.Context, q *apiclient.ManifestReq
|
||||
}
|
||||
return false
|
||||
}
|
||||
err := s.runRepoOperation(ctx, q.Revision, q.Repo, q.ApplicationSource, getCached, func(appPath string, revision string) error {
|
||||
err := s.runRepoOperation(ctx, q.Revision, q.Repo, q.ApplicationSource, getCached, func(appPath, repoRoot, revision string) error {
|
||||
var err error
|
||||
res, err = GenerateManifests(appPath, q)
|
||||
res, err = GenerateManifests(appPath, repoRoot, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -218,7 +218,7 @@ func getHelmRepos(repositories []*v1alpha1.Repository) []helm.HelmRepository {
|
||||
return repos
|
||||
}
|
||||
|
||||
func helmTemplate(appPath string, q *apiclient.ManifestRequest) ([]*unstructured.Unstructured, error) {
|
||||
func helmTemplate(appPath string, repoRoot string, q *apiclient.ManifestRequest) ([]*unstructured.Unstructured, error) {
|
||||
templateOpts := &helm.TemplateOpts{
|
||||
Name: q.AppLabelValue,
|
||||
Namespace: q.Namespace,
|
||||
@@ -236,22 +236,24 @@ func helmTemplate(appPath string, q *apiclient.ManifestRequest) ([]*unstructured
|
||||
for _, val := range appHelm.ValueFiles {
|
||||
// If val is not a URL, run it against the directory enforcer. If it is a URL, use it without checking
|
||||
if _, err := url.ParseRequestURI(val); err != nil {
|
||||
baseDirectoryPath, err := security.SubtractRelativeFromAbsolutePath(appPath, q.ApplicationSource.Path)
|
||||
|
||||
// Ensure that the repo root provided is absolute
|
||||
absRepoPath, err := filepath.Abs(repoRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
absBaseDir, err := filepath.Abs(baseDirectoryPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !filepath.IsAbs(val) {
|
||||
|
||||
// If the path to the file is relative, join it with the current working directory (appPath)
|
||||
path := val
|
||||
if !filepath.IsAbs(path) {
|
||||
absWorkDir, err := filepath.Abs(appPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val = filepath.Join(absWorkDir, val)
|
||||
path = filepath.Join(absWorkDir, path)
|
||||
}
|
||||
_, err = security.EnforceToCurrentRoot(absBaseDir, val)
|
||||
|
||||
_, err = security.EnforceToCurrentRoot(absRepoPath, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -312,7 +314,7 @@ func helmTemplate(appPath string, q *apiclient.ManifestRequest) ([]*unstructured
|
||||
}
|
||||
|
||||
// GenerateManifests generates manifests from a path
|
||||
func GenerateManifests(appPath string, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) {
|
||||
func GenerateManifests(appPath, repoRoot string, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) {
|
||||
var targetObjs []*unstructured.Unstructured
|
||||
var dest *v1alpha1.ApplicationDestination
|
||||
|
||||
@@ -325,7 +327,7 @@ func GenerateManifests(appPath string, q *apiclient.ManifestRequest) (*apiclient
|
||||
case v1alpha1.ApplicationSourceTypeKsonnet:
|
||||
targetObjs, dest, err = ksShow(q.AppLabelKey, appPath, q.ApplicationSource.Ksonnet)
|
||||
case v1alpha1.ApplicationSourceTypeHelm:
|
||||
targetObjs, err = helmTemplate(appPath, q)
|
||||
targetObjs, err = helmTemplate(appPath, repoRoot, q)
|
||||
case v1alpha1.ApplicationSourceTypeKustomize:
|
||||
k := kustomize.NewKustomizeApp(appPath, q.Repo.GetGitCreds(), repoURL)
|
||||
targetObjs, _, err = k.Build(q.ApplicationSource.Kustomize, q.KustomizeOptions)
|
||||
@@ -561,7 +563,7 @@ func runCommand(command v1alpha1.Command, path string, env []string) (string, er
|
||||
cmd := exec.Command(command.Command[0], append(command.Command[1:], command.Args...)...)
|
||||
cmd.Env = env
|
||||
cmd.Dir = path
|
||||
return argoexec.RunCommandExt(cmd, config.CmdOpts())
|
||||
return executil.Run(cmd)
|
||||
}
|
||||
|
||||
func findPlugin(plugins []*v1alpha1.ConfigManagementPlugin, name string) *v1alpha1.ConfigManagementPlugin {
|
||||
@@ -618,7 +620,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD
|
||||
return false
|
||||
}
|
||||
|
||||
err := s.runRepoOperation(ctx, q.Source.TargetRevision, q.Repo, q.Source, getCached, func(appPath string, revision string) error {
|
||||
err := s.runRepoOperation(ctx, q.Source.TargetRevision, q.Repo, q.Source, getCached, func(appPath, repoRoot, revision string) error {
|
||||
appSourceType, err := GetAppSourceType(q.Source, appPath)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -42,6 +42,7 @@ func newServiceWithMocks(root string) (*Service, *gitmocks.Client, *helmmocks.Cl
|
||||
gitClient.On("CommitSHA").Return(mock.Anything, nil)
|
||||
gitClient.On("Root").Return(root)
|
||||
|
||||
helmClient.On("CleanChartCache", mock.Anything, mock.Anything).Return(nil)
|
||||
helmClient.On("ExtractChart", mock.Anything, mock.Anything).Return(func(chart string, version string) string {
|
||||
return path.Join(root, chart)
|
||||
}, util.NewCloser(func() error {
|
||||
@@ -81,8 +82,8 @@ func TestGenerateYamlManifestInDir(t *testing.T) {
|
||||
assert.Equal(t, countOfManifests, len(res1.Manifests))
|
||||
|
||||
// this will test concatenated manifests to verify we split YAMLs correctly
|
||||
res2, err := GenerateManifests("./testdata/concatenated", &q)
|
||||
assert.Nil(t, err)
|
||||
res2, err := GenerateManifests("./testdata/concatenated", "/", &q)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(res2.Manifests))
|
||||
})
|
||||
}
|
||||
@@ -210,7 +211,6 @@ func TestGenerateHelmWithValues(t *testing.T) {
|
||||
// since the requested value is sill under the repo directory (`~/go/src/github.com/argoproj/argo-cd`), it is allowed
|
||||
func TestGenerateHelmWithValuesDirectoryTraversal(t *testing.T) {
|
||||
service := newService("../..")
|
||||
|
||||
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppLabelValue: "test",
|
||||
@@ -223,6 +223,57 @@ func TestGenerateHelmWithValuesDirectoryTraversal(t *testing.T) {
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test the case where the path is "."
|
||||
service = newService("./testdata/my-chart")
|
||||
_, err = service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppLabelValue: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: ".",
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// This is a Helm first-class app with a values file inside the repo directory
|
||||
// (`~/go/src/github.com/argoproj/argo-cd/reposerver/repository`), so it is allowed
|
||||
func TestHelmManifestFromChartRepoWithValueFile(t *testing.T) {
|
||||
service := newService(".")
|
||||
source := &argoappv1.ApplicationSource{
|
||||
Chart: "./testdata/my-chart",
|
||||
TargetRevision: "1.1.0",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"./my-chart-values.yaml"},
|
||||
},
|
||||
}
|
||||
request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true}
|
||||
response, err := service.GenerateManifest(context.Background(), request)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, response)
|
||||
assert.Equal(t, &apiclient.ManifestResponse{
|
||||
Manifests: []string{"{\"apiVersion\":\"v1\",\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"my-map\"}}"},
|
||||
Namespace: "",
|
||||
Server: "",
|
||||
Revision: "1.1.0",
|
||||
SourceType: "Helm",
|
||||
}, response)
|
||||
}
|
||||
|
||||
// This is a Helm first-class app with a values file outside the repo directory
|
||||
// (`~/go/src/github.com/argoproj/argo-cd/reposerver/repository`), so it is not allowed
|
||||
func TestHelmManifestFromChartRepoWithValueFileOutsideRepo(t *testing.T) {
|
||||
service := newService(".")
|
||||
source := &argoappv1.ApplicationSource{
|
||||
Chart: "./testdata/my-chart",
|
||||
TargetRevision: "1.0.0",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"../my-chart-2/my-chart-2-values.yaml"},
|
||||
},
|
||||
}
|
||||
request := &apiclient.ManifestRequest{Repo: &argoappv1.Repository{}, ApplicationSource: source, NoCache: true}
|
||||
_, err := service.GenerateManifest(context.Background(), request)
|
||||
assert.Error(t, err, "should be on or under current directory")
|
||||
}
|
||||
|
||||
func TestGenerateHelmWithURL(t *testing.T) {
|
||||
@@ -246,7 +297,6 @@ func TestGenerateHelmWithURL(t *testing.T) {
|
||||
// (`~/go/src/github.com/argoproj/argo-cd`), so it is blocked
|
||||
func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) {
|
||||
service := newService("../..")
|
||||
|
||||
_, err := service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppLabelValue: "test",
|
||||
@@ -259,6 +309,20 @@ func TestGenerateHelmWithValuesDirectoryTraversalOutsideRepo(t *testing.T) {
|
||||
},
|
||||
})
|
||||
assert.Error(t, err, "should be on or under current directory")
|
||||
|
||||
service = newService("./testdata/my-chart")
|
||||
_, err = service.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{},
|
||||
AppLabelValue: "test",
|
||||
ApplicationSource: &argoappv1.ApplicationSource{
|
||||
Path: ".",
|
||||
Helm: &argoappv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{"../my-chart-2/values.yaml"},
|
||||
Values: `cluster: {slaveCount: 2}`,
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.Error(t, err, "should be on or under current directory")
|
||||
}
|
||||
|
||||
func TestGenerateNullList(t *testing.T) {
|
||||
@@ -342,7 +406,7 @@ func TestGenerateFromUTF16(t *testing.T) {
|
||||
q := apiclient.ManifestRequest{
|
||||
ApplicationSource: &argoappv1.ApplicationSource{},
|
||||
}
|
||||
res1, err := GenerateManifests("./testdata/utf-16", &q)
|
||||
res1, err := GenerateManifests("./testdata/utf-16", "/", &q)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(res1.Manifests))
|
||||
}
|
||||
@@ -359,6 +423,8 @@ func TestListApps(t *testing.T) {
|
||||
"invalid-kustomize": "Kustomize",
|
||||
"kustomization_yaml": "Kustomize",
|
||||
"kustomization_yml": "Kustomize",
|
||||
"my-chart": "Helm",
|
||||
"my-chart-2": "Helm",
|
||||
}
|
||||
assert.Equal(t, expectedApps, res.Apps)
|
||||
}
|
||||
|
||||
2
reposerver/repository/testdata/my-chart-2/Chart.yaml
vendored
Normal file
2
reposerver/repository/testdata/my-chart-2/Chart.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
name: my-chart
|
||||
version: 1.1.0
|
||||
1
reposerver/repository/testdata/my-chart-2/my-chart-2-values.yaml
vendored
Normal file
1
reposerver/repository/testdata/my-chart-2/my-chart-2-values.yaml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
app: go
|
||||
4
reposerver/repository/testdata/my-chart-2/templates/my-map.yaml
vendored
Normal file
4
reposerver/repository/testdata/my-chart-2/templates/my-map.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-map
|
||||
2
reposerver/repository/testdata/my-chart/Chart.yaml
vendored
Normal file
2
reposerver/repository/testdata/my-chart/Chart.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
name: my-chart
|
||||
version: 1.1.0
|
||||
0
reposerver/repository/testdata/my-chart/my-chart-values.yaml
vendored
Normal file
0
reposerver/repository/testdata/my-chart/my-chart-values.yaml
vendored
Normal file
4
reposerver/repository/testdata/my-chart/templates/my-map.yaml
vendored
Normal file
4
reposerver/repository/testdata/my-chart/templates/my-map.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-map
|
||||
@@ -184,6 +184,20 @@ func TestKubeVersion(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestHelmValuesHiddenDirectory(t *testing.T) {
|
||||
Given(t).
|
||||
Path(".hidden-helm").
|
||||
When().
|
||||
AddFile("foo.yaml", "").
|
||||
Create().
|
||||
AppSet("--values", "foo.yaml").
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(HealthIs(HealthStatusHealthy)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced))
|
||||
}
|
||||
|
||||
func TestHelmWithDependencies(t *testing.T) {
|
||||
testHelmWithDependencies(t, false)
|
||||
}
|
||||
|
||||
2
test/e2e/testdata/.hidden-helm/Chart.yaml
vendored
Normal file
2
test/e2e/testdata/.hidden-helm/Chart.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
version: 1.0.0
|
||||
name: helm
|
||||
6
test/e2e/testdata/.hidden-helm/templates/config-map.yaml
vendored
Normal file
6
test/e2e/testdata/.hidden-helm/templates/config-map.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-map
|
||||
data:
|
||||
foo: bar
|
||||
0
test/e2e/testdata/.hidden-helm/values.yaml
vendored
Normal file
0
test/e2e/testdata/.hidden-helm/values.yaml
vendored
Normal file
@@ -1,9 +1,6 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -23,7 +20,7 @@ const (
|
||||
FakeClusterURL = "https://fake-cluster:443"
|
||||
)
|
||||
|
||||
var PodManifest = []byte(`
|
||||
var PodManifest = `
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
@@ -44,19 +41,14 @@ var PodManifest = []byte(`
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
`
|
||||
|
||||
func NewPod() *unstructured.Unstructured {
|
||||
var un unstructured.Unstructured
|
||||
err := json.Unmarshal(PodManifest, &un)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &un
|
||||
return Unstructured(PodManifest)
|
||||
}
|
||||
|
||||
func NewCRD() *unstructured.Unstructured {
|
||||
var un unstructured.Unstructured
|
||||
err := yaml.Unmarshal([]byte(`apiVersion: apiextensions.k8s.io/v1beta1
|
||||
return Unstructured(`apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: testcrds.argoproj.io
|
||||
@@ -66,11 +58,7 @@ spec:
|
||||
scope: Namespaced
|
||||
names:
|
||||
plural: testcrds
|
||||
kind: TestCrd`), &un)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &un
|
||||
kind: TestCrd`)
|
||||
}
|
||||
|
||||
// DEPRECATED
|
||||
@@ -97,7 +85,7 @@ func Annotate(obj *unstructured.Unstructured, key, val string) *unstructured.Uns
|
||||
return obj
|
||||
}
|
||||
|
||||
var ServiceManifest = []byte(`
|
||||
var ServiceManifest = `
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
@@ -118,18 +106,13 @@ var ServiceManifest = []byte(`
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
`
|
||||
|
||||
func NewService() *unstructured.Unstructured {
|
||||
var un unstructured.Unstructured
|
||||
err := json.Unmarshal(ServiceManifest, &un)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &un
|
||||
return Unstructured(ServiceManifest)
|
||||
}
|
||||
|
||||
var DeploymentManifest = []byte(`
|
||||
var DeploymentManifest = `
|
||||
{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
@@ -168,15 +151,10 @@ var DeploymentManifest = []byte(`
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
`
|
||||
|
||||
func NewDeployment() *unstructured.Unstructured {
|
||||
var un unstructured.Unstructured
|
||||
err := json.Unmarshal(DeploymentManifest, &un)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &un
|
||||
return Unstructured(DeploymentManifest)
|
||||
}
|
||||
|
||||
func DemoDeployment() *appsv1.Deployment {
|
||||
|
||||
32
test/unstructured.go
Normal file
32
test/unstructured.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
func UnstructuredFromFile(path string) *unstructured.Unstructured {
|
||||
file, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return Unstructured(string(file))
|
||||
}
|
||||
|
||||
func Unstructured(text string) *unstructured.Unstructured {
|
||||
un := &unstructured.Unstructured{}
|
||||
var err error
|
||||
if strings.HasPrefix(text, "{") {
|
||||
err = json.Unmarshal([]byte(text), &un)
|
||||
} else {
|
||||
err = yaml.Unmarshal([]byte(text), &un)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return un
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/pkg/exec"
|
||||
)
|
||||
|
||||
var timeout time.Duration
|
||||
|
||||
func init() {
|
||||
initTimeout()
|
||||
}
|
||||
|
||||
func initTimeout() {
|
||||
var err error
|
||||
timeout, err = time.ParseDuration(os.Getenv("ARGOCD_EXEC_TIMEOUT"))
|
||||
if err != nil {
|
||||
timeout = 90 * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
func CmdOpts() exec.CmdOpts {
|
||||
return exec.CmdOpts{Timeout: timeout}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/pkg/exec"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_timeout(t *testing.T) {
|
||||
defer func() { _ = os.Unsetenv("ARGOCD_EXEC_TIMEOUT") }()
|
||||
tests := []struct {
|
||||
name string
|
||||
text string
|
||||
want time.Duration
|
||||
}{
|
||||
{"Default", "", 90 * time.Second},
|
||||
{"OneSecond", "1s", 1 * time.Second},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_ = os.Setenv("ARGOCD_EXEC_TIMEOUT", tt.text)
|
||||
initTimeout()
|
||||
assert.Equal(t, tt.want, timeout)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCmdOpts(t *testing.T) {
|
||||
initTimeout()
|
||||
assert.Equal(t, exec.CmdOpts{Timeout: 90 * time.Second}, CmdOpts())
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
@@ -57,7 +58,7 @@ func (db *db) ListHelmRepositories(ctx context.Context) ([]*v1alpha1.Repository,
|
||||
}
|
||||
result[i] = repo
|
||||
}
|
||||
repos, err := db.ListRepositories(ctx)
|
||||
repos, err := db.listRepositories(ctx, pointer.StringPtr("helm"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -5,9 +5,8 @@ package mocks
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
db "github.com/argoproj/argo-cd/util/db"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
v1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
@@ -40,6 +39,29 @@ func (_m *ArgoDB) CreateCluster(ctx context.Context, c *v1alpha1.Cluster) (*v1al
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CreateRepoCertificate provides a mock function with given fields: ctx, certificate, upsert
|
||||
func (_m *ArgoDB) CreateRepoCertificate(ctx context.Context, certificate *v1alpha1.RepositoryCertificateList, upsert bool) (*v1alpha1.RepositoryCertificateList, error) {
|
||||
ret := _m.Called(ctx, certificate, upsert)
|
||||
|
||||
var r0 *v1alpha1.RepositoryCertificateList
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.RepositoryCertificateList, bool) *v1alpha1.RepositoryCertificateList); ok {
|
||||
r0 = rf(ctx, certificate, upsert)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v1alpha1.RepositoryCertificateList)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.RepositoryCertificateList, bool) error); ok {
|
||||
r1 = rf(ctx, certificate, upsert)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CreateRepository provides a mock function with given fields: ctx, r
|
||||
func (_m *ArgoDB) CreateRepository(ctx context.Context, r *v1alpha1.Repository) (*v1alpha1.Repository, error) {
|
||||
ret := _m.Called(ctx, r)
|
||||
@@ -77,13 +99,13 @@ func (_m *ArgoDB) DeleteCluster(ctx context.Context, name string) error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteRepository provides a mock function with given fields: ctx, name
|
||||
func (_m *ArgoDB) DeleteRepository(ctx context.Context, name string) error {
|
||||
ret := _m.Called(ctx, name)
|
||||
// DeleteRepository provides a mock function with given fields: ctx, url
|
||||
func (_m *ArgoDB) DeleteRepository(ctx context.Context, url string) error {
|
||||
ret := _m.Called(ctx, url)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
|
||||
r0 = rf(ctx, name)
|
||||
r0 = rf(ctx, url)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
@@ -114,13 +136,13 @@ func (_m *ArgoDB) GetCluster(ctx context.Context, name string) (*v1alpha1.Cluste
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetRepository provides a mock function with given fields: ctx, name
|
||||
func (_m *ArgoDB) GetRepository(ctx context.Context, name string) (*v1alpha1.Repository, error) {
|
||||
ret := _m.Called(ctx, name)
|
||||
// GetRepository provides a mock function with given fields: ctx, url
|
||||
func (_m *ArgoDB) GetRepository(ctx context.Context, url string) (*v1alpha1.Repository, error) {
|
||||
ret := _m.Called(ctx, url)
|
||||
|
||||
var r0 *v1alpha1.Repository
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) *v1alpha1.Repository); ok {
|
||||
r0 = rf(ctx, name)
|
||||
r0 = rf(ctx, url)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v1alpha1.Repository)
|
||||
@@ -129,7 +151,7 @@ func (_m *ArgoDB) GetRepository(ctx context.Context, name string) (*v1alpha1.Rep
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, name)
|
||||
r1 = rf(ctx, url)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -160,6 +182,52 @@ func (_m *ArgoDB) ListClusters(ctx context.Context) (*v1alpha1.ClusterList, erro
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ListHelmRepositories provides a mock function with given fields: ctx
|
||||
func (_m *ArgoDB) ListHelmRepositories(ctx context.Context) ([]*v1alpha1.Repository, error) {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
var r0 []*v1alpha1.Repository
|
||||
if rf, ok := ret.Get(0).(func(context.Context) []*v1alpha1.Repository); ok {
|
||||
r0 = rf(ctx)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*v1alpha1.Repository)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
|
||||
r1 = rf(ctx)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ListRepoCertificates provides a mock function with given fields: ctx, selector
|
||||
func (_m *ArgoDB) ListRepoCertificates(ctx context.Context, selector *db.CertificateListSelector) (*v1alpha1.RepositoryCertificateList, error) {
|
||||
ret := _m.Called(ctx, selector)
|
||||
|
||||
var r0 *v1alpha1.RepositoryCertificateList
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *db.CertificateListSelector) *v1alpha1.RepositoryCertificateList); ok {
|
||||
r0 = rf(ctx, selector)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v1alpha1.RepositoryCertificateList)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *db.CertificateListSelector) error); ok {
|
||||
r1 = rf(ctx, selector)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ListRepositories provides a mock function with given fields: ctx
|
||||
func (_m *ArgoDB) ListRepositories(ctx context.Context) ([]*v1alpha1.Repository, error) {
|
||||
ret := _m.Called(ctx)
|
||||
@@ -183,6 +251,29 @@ func (_m *ArgoDB) ListRepositories(ctx context.Context) ([]*v1alpha1.Repository,
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RemoveRepoCertificates provides a mock function with given fields: ctx, selector
|
||||
func (_m *ArgoDB) RemoveRepoCertificates(ctx context.Context, selector *db.CertificateListSelector) (*v1alpha1.RepositoryCertificateList, error) {
|
||||
ret := _m.Called(ctx, selector)
|
||||
|
||||
var r0 *v1alpha1.RepositoryCertificateList
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *db.CertificateListSelector) *v1alpha1.RepositoryCertificateList); ok {
|
||||
r0 = rf(ctx, selector)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v1alpha1.RepositoryCertificateList)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *db.CertificateListSelector) error); ok {
|
||||
r1 = rf(ctx, selector)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateCluster provides a mock function with given fields: ctx, c
|
||||
func (_m *ArgoDB) UpdateCluster(ctx context.Context, c *v1alpha1.Cluster) (*v1alpha1.Cluster, error) {
|
||||
ret := _m.Called(ctx, c)
|
||||
|
||||
@@ -108,6 +108,10 @@ func (db *db) GetRepository(ctx context.Context, repoURL string) (*appsv1.Reposi
|
||||
}
|
||||
|
||||
func (db *db) ListRepositories(ctx context.Context) ([]*appsv1.Repository, error) {
|
||||
return db.listRepositories(ctx, nil)
|
||||
}
|
||||
|
||||
func (db *db) listRepositories(ctx context.Context, repoType *string) ([]*appsv1.Repository, error) {
|
||||
inRepos, err := db.settingsMgr.GetRepositories()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -115,12 +119,13 @@ func (db *db) ListRepositories(ctx context.Context) ([]*appsv1.Repository, error
|
||||
|
||||
var repos []*appsv1.Repository
|
||||
for _, inRepo := range inRepos {
|
||||
r, err := db.GetRepository(ctx, inRepo.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if repoType == nil || *repoType == inRepo.Type {
|
||||
r, err := db.GetRepository(ctx, inRepo.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repos = append(repos, r)
|
||||
}
|
||||
repos = append(repos, r)
|
||||
|
||||
}
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
42
util/exec/exec.go
Normal file
42
util/exec/exec.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
argoexec "github.com/argoproj/pkg/exec"
|
||||
|
||||
tracing "github.com/argoproj/argo-cd/util/tracing"
|
||||
)
|
||||
|
||||
var timeout time.Duration
|
||||
|
||||
func init() {
|
||||
initTimeout()
|
||||
}
|
||||
|
||||
func initTimeout() {
|
||||
var err error
|
||||
timeout, err = time.ParseDuration(os.Getenv("ARGOCD_EXEC_TIMEOUT"))
|
||||
if err != nil {
|
||||
timeout = 90 * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
func Run(cmd *exec.Cmd) (string, error) {
|
||||
return RunWithRedactor(cmd, nil)
|
||||
}
|
||||
|
||||
func RunWithRedactor(cmd *exec.Cmd, redactor func(text string) string) (string, error) {
|
||||
span := tracing.StartSpan(fmt.Sprintf("exec %v", cmd.Args[0]))
|
||||
span.SetBaggageItem("dir", fmt.Sprintf("%v", cmd.Dir))
|
||||
span.SetBaggageItem("args", fmt.Sprintf("%v", cmd.Args))
|
||||
defer span.Finish()
|
||||
opts := argoexec.CmdOpts{Timeout: timeout}
|
||||
if redactor != nil {
|
||||
opts.Redactor = redactor
|
||||
}
|
||||
return argoexec.RunCommandExt(cmd, opts)
|
||||
}
|
||||
29
util/exec/exec_test.go
Normal file
29
util/exec/exec_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_timeout(t *testing.T) {
|
||||
defer func() { _ = os.Unsetenv("ARGOCD_EXEC_TIMEOUT") }()
|
||||
t.Run("Default", func(t *testing.T) {
|
||||
initTimeout()
|
||||
assert.Equal(t, 90*time.Second, timeout)
|
||||
})
|
||||
t.Run("Default", func(t *testing.T) {
|
||||
_ = os.Setenv("ARGOCD_EXEC_TIMEOUT", "1s")
|
||||
initTimeout()
|
||||
assert.Equal(t, 1*time.Second, timeout)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
out, err := Run(exec.Command("ls"))
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, out)
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
argoexec "github.com/argoproj/pkg/exec"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/knownhosts"
|
||||
@@ -27,7 +26,7 @@ import (
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
certutil "github.com/argoproj/argo-cd/util/cert"
|
||||
argoconfig "github.com/argoproj/argo-cd/util/config"
|
||||
executil "github.com/argoproj/argo-cd/util/exec"
|
||||
)
|
||||
|
||||
type RevisionMetadata struct {
|
||||
@@ -221,7 +220,7 @@ func (m *nativeGitClient) Init() error {
|
||||
return err
|
||||
}
|
||||
log.Infof("Initializing %s to %s", m.repoURL, m.root)
|
||||
_, err = argoexec.RunCommand("rm", argoconfig.CmdOpts(), "-rf", m.root)
|
||||
_, err = executil.Run(exec.Command("rm", "-rf", m.root))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to clean repo at %s: %v", m.root, err)
|
||||
}
|
||||
@@ -475,5 +474,5 @@ func (m *nativeGitClient) runCmdOutput(cmd *exec.Cmd) (string, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return argoexec.RunCommandExt(cmd, argoconfig.CmdOpts())
|
||||
return executil.Run(cmd)
|
||||
}
|
||||
|
||||
@@ -15,13 +15,12 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
argoexec "github.com/argoproj/pkg/exec"
|
||||
"github.com/patrickmn/go-cache"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
executil "github.com/argoproj/argo-cd/util/exec"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -160,7 +159,7 @@ func (c *nativeHelmChart) ExtractChart(chart string, version string) (string, ut
|
||||
}
|
||||
cmd := exec.Command("tar", "-zxvf", chartPath)
|
||||
cmd.Dir = tempDir
|
||||
_, err = argoexec.RunCommandExt(cmd, config.CmdOpts())
|
||||
_, err = executil.Run(cmd)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
return "", nil, err
|
||||
|
||||
@@ -8,10 +8,8 @@ import (
|
||||
"os/exec"
|
||||
"regexp"
|
||||
|
||||
argoexec "github.com/argoproj/pkg/exec"
|
||||
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
executil "github.com/argoproj/argo-cd/util/exec"
|
||||
)
|
||||
|
||||
// A thin wrapper around the "helm" command, adding logging and error translation.
|
||||
@@ -37,10 +35,7 @@ func (c Cmd) run(args ...string) (string, error) {
|
||||
cmd.Dir = c.WorkDir
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("HELM_HOME=%s", c.helmHome))
|
||||
return argoexec.RunCommandExt(cmd, argoexec.CmdOpts{
|
||||
Timeout: config.CmdOpts().Timeout,
|
||||
Redactor: redactor,
|
||||
})
|
||||
return executil.RunWithRedactor(cmd, redactor)
|
||||
}
|
||||
|
||||
func (c *Cmd) Init() (string, error) {
|
||||
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
argoexec "github.com/argoproj/pkg/exec"
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
executil "github.com/argoproj/argo-cd/util/exec"
|
||||
)
|
||||
|
||||
type HelmRepository struct {
|
||||
@@ -87,10 +87,7 @@ func (h *helm) Dispose() {
|
||||
|
||||
func Version() (string, error) {
|
||||
cmd := exec.Command("helm", "version", "--client")
|
||||
out, err := argoexec.RunCommandExt(cmd, argoexec.CmdOpts{
|
||||
Timeout: config.CmdOpts().Timeout,
|
||||
Redactor: redactor,
|
||||
})
|
||||
out, err := executil.RunWithRedactor(cmd, redactor)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not get helm version: %s", err)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,11 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
argoexec "github.com/argoproj/pkg/exec"
|
||||
"github.com/ghodss/yaml"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
executil "github.com/argoproj/argo-cd/util/exec"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
)
|
||||
|
||||
@@ -103,7 +102,7 @@ func (k *ksonnetApp) ksCmd(args ...string) (string, error) {
|
||||
cmd := exec.Command("ks", args...)
|
||||
cmd.Dir = k.Root()
|
||||
|
||||
return argoexec.RunCommandExt(cmd, config.CmdOpts())
|
||||
return executil.Run(cmd)
|
||||
}
|
||||
|
||||
func (k *ksonnetApp) Root() string {
|
||||
|
||||
25
util/kube/convert.go
Normal file
25
util/kube/convert.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package kube
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
)
|
||||
|
||||
func convertToVersionWithScheme(obj *unstructured.Unstructured, group string, version string) (*unstructured.Unstructured, error) {
|
||||
s := legacyscheme.Scheme
|
||||
object, err := s.ConvertToVersion(obj, runtime.InternalGroupVersioner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unmarshalledObj, err := s.ConvertToVersion(object, schema.GroupVersion{Group: group, Version: version})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unstrBody, err := runtime.DefaultUnstructuredConverter.ToUnstructured(unmarshalledObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &unstructured.Unstructured{Object: unstrBody}, nil
|
||||
}
|
||||
93
util/kube/convert_test.go
Normal file
93
util/kube/convert_test.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package kube
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/argoproj/argo-cd/test"
|
||||
)
|
||||
|
||||
type testcase struct {
|
||||
name string
|
||||
file string
|
||||
outputVersion string
|
||||
fields []checkField
|
||||
}
|
||||
|
||||
type checkField struct {
|
||||
expected string
|
||||
}
|
||||
|
||||
func Test_convertToVersionWithScheme(t *testing.T) {
|
||||
for _, tt := range []testcase{
|
||||
{
|
||||
name: "apps deployment to extensions deployment",
|
||||
file: "appsdeployment.yaml",
|
||||
outputVersion: "extensions/v1beta1",
|
||||
fields: []checkField{
|
||||
{
|
||||
expected: "apiVersion: extensions/v1beta1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "extensions deployment to apps deployment",
|
||||
file: "extensionsdeployment.yaml",
|
||||
outputVersion: "apps/v1beta2",
|
||||
fields: []checkField{
|
||||
{
|
||||
expected: "apiVersion: apps/v1beta2",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "v1 HPA to v2beta1 HPA",
|
||||
file: "v1HPA.yaml",
|
||||
outputVersion: "autoscaling/v2beta1",
|
||||
fields: []checkField{
|
||||
{
|
||||
expected: "apiVersion: autoscaling/v2beta1",
|
||||
},
|
||||
{
|
||||
expected: "name: cpu",
|
||||
},
|
||||
{
|
||||
expected: "targetAverageUtilization: 50",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "v2beta1 HPA to v1 HPA",
|
||||
file: "v2beta1HPA.yaml",
|
||||
outputVersion: "autoscaling/v1",
|
||||
fields: []checkField{
|
||||
{
|
||||
expected: "apiVersion: autoscaling/v1",
|
||||
},
|
||||
{
|
||||
expected: "targetCPUUtilizationPercentage: 50",
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
obj := test.UnstructuredFromFile("testdata/" + tt.file)
|
||||
target, err := schema.ParseGroupVersion(tt.outputVersion)
|
||||
assert.NoError(t, err)
|
||||
out, err := convertToVersionWithScheme(obj, target.Group, target.Version)
|
||||
if assert.NoError(t, err) {
|
||||
assert.NotNil(t, out)
|
||||
assert.Equal(t, target.Group, out.GroupVersionKind().Group)
|
||||
assert.Equal(t, target.Version, out.GroupVersionKind().Version)
|
||||
bytes, err := yaml.Marshal(out)
|
||||
assert.NoError(t, err)
|
||||
for _, field := range tt.fields {
|
||||
assert.Contains(t, string(bytes), field.expected)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,9 @@ import (
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
executil "github.com/argoproj/argo-cd/util/exec"
|
||||
"github.com/argoproj/argo-cd/util/tracing"
|
||||
)
|
||||
|
||||
type Kubectl interface {
|
||||
@@ -109,6 +110,8 @@ func isSupportedVerb(apiResource *metav1.APIResource, verb string) bool {
|
||||
}
|
||||
|
||||
func (k *KubectlCmd) GetAPIResources(config *rest.Config, resourceFilter ResourceFilter) ([]APIResourceInfo, error) {
|
||||
span := tracing.StartSpan("GetAPIResources")
|
||||
defer span.Finish()
|
||||
apiResIfs, err := filterAPIResources(config, resourceFilter, func(apiResource *metav1.APIResource) bool {
|
||||
return isSupportedVerb(apiResource, listVerb) && isSupportedVerb(apiResource, watchVerb)
|
||||
}, "")
|
||||
@@ -120,6 +123,10 @@ func (k *KubectlCmd) GetAPIResources(config *rest.Config, resourceFilter Resourc
|
||||
|
||||
// GetResource returns resource
|
||||
func (k *KubectlCmd) GetResource(config *rest.Config, gvk schema.GroupVersionKind, name string, namespace string) (*unstructured.Unstructured, error) {
|
||||
span := tracing.StartSpan("GetResource")
|
||||
span.SetBaggageItem("kind", gvk.Kind)
|
||||
span.SetBaggageItem("name", name)
|
||||
defer span.Finish()
|
||||
dynamicIf, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -139,6 +146,10 @@ func (k *KubectlCmd) GetResource(config *rest.Config, gvk schema.GroupVersionKin
|
||||
|
||||
// PatchResource patches resource
|
||||
func (k *KubectlCmd) PatchResource(config *rest.Config, gvk schema.GroupVersionKind, name string, namespace string, patchType types.PatchType, patchBytes []byte) (*unstructured.Unstructured, error) {
|
||||
span := tracing.StartSpan("PatchResource")
|
||||
span.SetBaggageItem("kind", gvk.Kind)
|
||||
span.SetBaggageItem("name", name)
|
||||
defer span.Finish()
|
||||
dynamicIf, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -158,6 +169,10 @@ func (k *KubectlCmd) PatchResource(config *rest.Config, gvk schema.GroupVersionK
|
||||
|
||||
// DeleteResource deletes resource
|
||||
func (k *KubectlCmd) DeleteResource(config *rest.Config, gvk schema.GroupVersionKind, name string, namespace string, forceDelete bool) error {
|
||||
span := tracing.StartSpan("DeleteResource")
|
||||
span.SetBaggageItem("kind", gvk.Kind)
|
||||
span.SetBaggageItem("name", name)
|
||||
defer span.Finish()
|
||||
dynamicIf, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -185,6 +200,10 @@ func (k *KubectlCmd) DeleteResource(config *rest.Config, gvk schema.GroupVersion
|
||||
|
||||
// ApplyResource performs an apply of a unstructured resource
|
||||
func (k *KubectlCmd) ApplyResource(config *rest.Config, obj *unstructured.Unstructured, namespace string, dryRun, force, validate bool) (string, error) {
|
||||
span := tracing.StartSpan("ApplyResource")
|
||||
span.SetBaggageItem("kind", obj.GetKind())
|
||||
span.SetBaggageItem("name", obj.GetName())
|
||||
defer span.Finish()
|
||||
log.Infof("Applying resource %s/%s in cluster: %s, namespace: %s", obj.GetKind(), obj.GetName(), config.Host, namespace)
|
||||
f, err := ioutil.TempFile(util.TempDir, "")
|
||||
if err != nil {
|
||||
@@ -303,7 +322,7 @@ func (k *KubectlCmd) runKubectl(kubeconfigPath string, namespace string, args []
|
||||
log.Debug(string(redactedBytes))
|
||||
}
|
||||
cmd.Stdin = bytes.NewReader(manifestBytes)
|
||||
out, err := argoexec.RunCommandExt(cmd, config.CmdOpts())
|
||||
out, err := executil.Run(cmd)
|
||||
if err != nil {
|
||||
return "", convertKubectlError(err)
|
||||
}
|
||||
@@ -311,8 +330,10 @@ func (k *KubectlCmd) runKubectl(kubeconfigPath string, namespace string, args []
|
||||
}
|
||||
|
||||
func Version() (string, error) {
|
||||
span := tracing.StartSpan("Version")
|
||||
defer span.Finish()
|
||||
cmd := exec.Command("kubectl", "version", "--client")
|
||||
out, err := argoexec.RunCommandExt(cmd, config.CmdOpts())
|
||||
out, err := executil.Run(cmd)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not get kubectl version: %s", err)
|
||||
}
|
||||
@@ -330,52 +351,20 @@ func Version() (string, error) {
|
||||
|
||||
// ConvertToVersion converts an unstructured object into the specified group/version
|
||||
func (k *KubectlCmd) ConvertToVersion(obj *unstructured.Unstructured, group string, version string) (*unstructured.Unstructured, error) {
|
||||
gvk := obj.GroupVersionKind()
|
||||
if gvk.Group == group && gvk.Version == version {
|
||||
span := tracing.StartSpan("ConvertToVersion")
|
||||
from := obj.GroupVersionKind().GroupVersion()
|
||||
span.SetBaggageItem("from", from.String())
|
||||
span.SetBaggageItem("to", schema.GroupVersion{Group: group, Version: version}.String())
|
||||
defer span.Finish()
|
||||
if from.Group == group && from.Version == version {
|
||||
return obj.DeepCopy(), nil
|
||||
}
|
||||
|
||||
manifestBytes, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := ioutil.TempFile(util.TempDir, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to generate temp file for kubectl: %v", err)
|
||||
}
|
||||
_ = f.Close()
|
||||
if err := ioutil.WriteFile(f.Name(), manifestBytes, 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer util.DeleteFile(f.Name())
|
||||
|
||||
closer, err := k.processKubectlRun([]string{"convert"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer util.Close(closer)
|
||||
|
||||
outputVersion := fmt.Sprintf("%s/%s", group, version)
|
||||
cmd := exec.Command("kubectl", "convert", "--output-version", outputVersion, "-o", "json", "--local=true", "-f", f.Name())
|
||||
cmd.Stdin = bytes.NewReader(manifestBytes)
|
||||
out, err := argoexec.RunCommandExt(cmd, config.CmdOpts())
|
||||
if err != nil {
|
||||
return nil, convertKubectlError(err)
|
||||
}
|
||||
// NOTE: when kubectl convert runs against stdin (i.e. kubectl convert -f -), the output is
|
||||
// a unstructured list instead of an unstructured object
|
||||
var convertedObj unstructured.Unstructured
|
||||
err = json.Unmarshal([]byte(out), &convertedObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if convertedObj.GetNamespace() == "" {
|
||||
convertedObj.SetNamespace(obj.GetNamespace())
|
||||
}
|
||||
return &convertedObj, nil
|
||||
return convertToVersionWithScheme(obj, group, version)
|
||||
}
|
||||
|
||||
func (k *KubectlCmd) GetServerVersion(config *rest.Config) (string, error) {
|
||||
span := tracing.StartSpan("GetServerVersion")
|
||||
defer span.Finish()
|
||||
client, err := discovery.NewDiscoveryClientForConfig(config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -1,50 +1,56 @@
|
||||
package kube
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/test"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
func TestConvertToVersion(t *testing.T) {
|
||||
callbackExecuted := false
|
||||
closerExecuted := false
|
||||
kubectl := KubectlCmd{}
|
||||
kubectl.SetOnKubectlRun(func(command string) (util.Closer, error) {
|
||||
callbackExecuted = true
|
||||
return util.NewCloser(func() error {
|
||||
closerExecuted = true
|
||||
return nil
|
||||
}), nil
|
||||
t.Run("AppsDeployment", func(t *testing.T) {
|
||||
newObj, err := kubectl.ConvertToVersion(test.UnstructuredFromFile("testdata/appsdeployment.yaml"), "extensions", "v1beta1")
|
||||
if assert.NoError(t, err) {
|
||||
gvk := newObj.GroupVersionKind()
|
||||
assert.Equal(t, "extensions", gvk.Group)
|
||||
assert.Equal(t, "v1beta1", gvk.Version)
|
||||
}
|
||||
})
|
||||
t.Run("CustomResource", func(t *testing.T) {
|
||||
_, err := kubectl.ConvertToVersion(test.UnstructuredFromFile("testdata/cr.yaml"), "argoproj.io", "v1")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("ExtensionsDeployment", func(t *testing.T) {
|
||||
obj := test.UnstructuredFromFile("testdata/nginx.yaml")
|
||||
|
||||
yamlBytes, err := ioutil.ReadFile("testdata/nginx.yaml")
|
||||
assert.Nil(t, err)
|
||||
var obj unstructured.Unstructured
|
||||
err = yaml.Unmarshal(yamlBytes, &obj)
|
||||
assert.Nil(t, err)
|
||||
// convert an extensions/v1beta1 object into itself
|
||||
newObj, err := kubectl.ConvertToVersion(obj, "extensions", "v1beta1")
|
||||
if assert.NoError(t, err) {
|
||||
gvk := newObj.GroupVersionKind()
|
||||
assert.Equal(t, "extensions", gvk.Group)
|
||||
assert.Equal(t, "v1beta1", gvk.Version)
|
||||
}
|
||||
|
||||
// convert an extensions/v1beta1 object into an apps/v1
|
||||
newObj, err := kubectl.ConvertToVersion(&obj, "apps", "v1")
|
||||
assert.Nil(t, err)
|
||||
gvk := newObj.GroupVersionKind()
|
||||
assert.Equal(t, "apps", gvk.Group)
|
||||
assert.Equal(t, "v1", gvk.Version)
|
||||
assert.True(t, callbackExecuted)
|
||||
assert.True(t, closerExecuted)
|
||||
// convert an extensions/v1beta1 object into an apps/v1
|
||||
newObj, err = kubectl.ConvertToVersion(obj, "apps", "v1")
|
||||
if assert.NoError(t, err) {
|
||||
gvk := newObj.GroupVersionKind()
|
||||
assert.Equal(t, "apps", gvk.Group)
|
||||
assert.Equal(t, "v1", gvk.Version)
|
||||
}
|
||||
|
||||
// converting it again should not have any affect
|
||||
newObj, err = kubectl.ConvertToVersion(&obj, "apps", "v1")
|
||||
assert.Nil(t, err)
|
||||
gvk = newObj.GroupVersionKind()
|
||||
assert.Equal(t, "apps", gvk.Group)
|
||||
assert.Equal(t, "v1", gvk.Version)
|
||||
// converting it again should not have any affect
|
||||
newObj, err = kubectl.ConvertToVersion(obj, "apps", "v1")
|
||||
if assert.NoError(t, err) {
|
||||
gvk := newObj.GroupVersionKind()
|
||||
assert.Equal(t, "apps", gvk.Group)
|
||||
assert.Equal(t, "v1", gvk.Version)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunKubectl(t *testing.T) {
|
||||
|
||||
19
util/kube/import_known_versions.go
Normal file
19
util/kube/import_known_versions.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package kube
|
||||
|
||||
import (
|
||||
_ "k8s.io/kubernetes/pkg/apis/apps/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/authentication/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/authorization/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/batch/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/certificates/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/coordination/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/events/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/policy/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/rbac/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/scheduling/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/settings/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/storage/install"
|
||||
)
|
||||
@@ -21,6 +21,7 @@ type MockKubectlCmd struct {
|
||||
Commands map[string]KubectlOutput
|
||||
Events chan watch.Event
|
||||
LastValidate bool
|
||||
Version string
|
||||
}
|
||||
|
||||
func (k *MockKubectlCmd) GetAPIResources(config *rest.Config, resourceFilter kube.ResourceFilter) ([]kube.APIResourceInfo, error) {
|
||||
@@ -58,7 +59,7 @@ func (k *MockKubectlCmd) ConvertToVersion(obj *unstructured.Unstructured, group,
|
||||
}
|
||||
|
||||
func (k *MockKubectlCmd) GetServerVersion(config *rest.Config) (string, error) {
|
||||
return "", nil
|
||||
return k.Version, nil
|
||||
}
|
||||
|
||||
func (k *MockKubectlCmd) SetOnKubectlRun(onKubectlRun func(command string) (util.Closer, error)) {
|
||||
|
||||
13
util/kube/testdata/appsdeployment.yaml
vendored
Normal file
13
util/kube/testdata/appsdeployment.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
apiVersion: apps/v1beta2
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
13
util/kube/testdata/cr.yaml
vendored
Normal file
13
util/kube/testdata/cr.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: custom-resource
|
||||
namespace: default
|
||||
spec:
|
||||
destination:
|
||||
namespace: default
|
||||
server: https://kubernetes.default.svc
|
||||
project: default
|
||||
source:
|
||||
path: guestbook
|
||||
repoURL: https://github.com/argoproj/argocd-example-apps.git
|
||||
13
util/kube/testdata/extensionsdeployment.yaml
vendored
Normal file
13
util/kube/testdata/extensionsdeployment.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
12
util/kube/testdata/v1HPA.yaml
vendored
Normal file
12
util/kube/testdata/v1HPA.yaml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
apiVersion: autoscaling/v1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: php-apache
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: php-apache
|
||||
minReplicas: 1
|
||||
maxReplicas: 10
|
||||
targetCPUUtilizationPercentage: 50
|
||||
16
util/kube/testdata/v2beta1HPA.yaml
vendored
Normal file
16
util/kube/testdata/v2beta1HPA.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
apiVersion: autoscaling/v2beta1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: php-apache
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: php-apache
|
||||
minReplicas: 1
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
targetAverageUtilization: 50
|
||||
@@ -9,13 +9,12 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
argoexec "github.com/argoproj/pkg/exec"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
executil "github.com/argoproj/argo-cd/util/exec"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
|
||||
@@ -53,9 +52,9 @@ func (k *kustomize) Build(opts *v1alpha1.ApplicationSourceKustomize, kustomizeOp
|
||||
|
||||
if opts != nil {
|
||||
if opts.NamePrefix != "" {
|
||||
cmd := exec.Command("kustomize", "edit", "set", "nameprefix", opts.NamePrefix)
|
||||
cmd := exec.Command("kustomize", "edit", "set", "nameprefix", "--", opts.NamePrefix)
|
||||
cmd.Dir = k.path
|
||||
_, err := argoexec.RunCommandExt(cmd, config.CmdOpts())
|
||||
_, err := executil.Run(cmd)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -69,7 +68,7 @@ func (k *kustomize) Build(opts *v1alpha1.ApplicationSourceKustomize, kustomizeOp
|
||||
}
|
||||
cmd := exec.Command("kustomize", args...)
|
||||
cmd.Dir = k.path
|
||||
_, err := argoexec.RunCommandExt(cmd, config.CmdOpts())
|
||||
_, err := executil.Run(cmd)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -88,7 +87,7 @@ func (k *kustomize) Build(opts *v1alpha1.ApplicationSourceKustomize, kustomizeOp
|
||||
args = append(args, arg)
|
||||
cmd := exec.Command("kustomize", args...)
|
||||
cmd.Dir = k.path
|
||||
_, err := argoexec.RunCommandExt(cmd, config.CmdOpts())
|
||||
_, err := executil.Run(cmd)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -132,7 +131,7 @@ func (k *kustomize) Build(opts *v1alpha1.ApplicationSourceKustomize, kustomizeOp
|
||||
}
|
||||
|
||||
cmd.Env = append(cmd.Env, environ...)
|
||||
out, err := argoexec.RunCommandExt(cmd, config.CmdOpts())
|
||||
out, err := executil.Run(cmd)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -173,7 +172,7 @@ func IsKustomization(path string) bool {
|
||||
|
||||
func Version() (string, error) {
|
||||
cmd := exec.Command("kustomize", "version")
|
||||
out, err := argoexec.RunCommandExt(cmd, config.CmdOpts())
|
||||
out, err := executil.Run(cmd)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not get kustomize version: %s", err)
|
||||
}
|
||||
|
||||
@@ -17,36 +17,19 @@ func EnforceToCurrentRoot(currentRoot, requestedPath string) (string, error) {
|
||||
return requestedDir + string(filepath.Separator) + requestedFile, nil
|
||||
}
|
||||
|
||||
func SubtractRelativeFromAbsolutePath(abs, rel string) (string, error) {
|
||||
if len(rel) == 0 {
|
||||
return abs, nil
|
||||
}
|
||||
if rel[0] == '.' {
|
||||
return SubtractRelativeFromAbsolutePath(abs, rel[1:])
|
||||
}
|
||||
if rel[0] != '/' {
|
||||
return SubtractRelativeFromAbsolutePath(abs, "/"+rel)
|
||||
}
|
||||
if rel[len(rel)-1] == '/' {
|
||||
return SubtractRelativeFromAbsolutePath(abs, rel[:len(rel)-1])
|
||||
}
|
||||
rel = filepath.Clean(rel)
|
||||
lastIndex := strings.LastIndex(abs, rel)
|
||||
if lastIndex < 0 {
|
||||
// This should be unreachable, because by this point the App Path will have already been validated by Path in
|
||||
// util/app/path/path.go
|
||||
return "", fmt.Errorf("app path is not under repo path (unreachable and most likely a bug)")
|
||||
}
|
||||
return abs[:lastIndex], nil
|
||||
}
|
||||
|
||||
func isRequestedDirUnderCurrentRoot(currentRoot, requestedDir string) bool {
|
||||
func isRequestedDirUnderCurrentRoot(currentRoot, requestedPath string) bool {
|
||||
if currentRoot == string(filepath.Separator) {
|
||||
return true
|
||||
} else if currentRoot == requestedDir {
|
||||
} else if currentRoot == requestedPath {
|
||||
return true
|
||||
}
|
||||
return strings.HasPrefix(requestedDir, currentRoot+string(filepath.Separator))
|
||||
if requestedPath[len(requestedPath)-1] != '/' {
|
||||
requestedPath = requestedPath + "/"
|
||||
}
|
||||
if currentRoot[len(currentRoot)-1] != '/' {
|
||||
currentRoot = currentRoot + "/"
|
||||
}
|
||||
return strings.HasPrefix(requestedPath, currentRoot)
|
||||
}
|
||||
|
||||
func parsePath(path string) (string, string) {
|
||||
|
||||
@@ -24,32 +24,3 @@ func TestEnforceToCurrentRoot(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "/home/argo/helmapp/values.yaml", cleanDir)
|
||||
}
|
||||
|
||||
func TestSubtractRelativeFromAbsolutePath(t *testing.T) {
|
||||
for _, test := range []string{"env", "/env", "env/", "/env/", "./env"} {
|
||||
subtracted, err := SubtractRelativeFromAbsolutePath("/argocd-example-apps/helm-guestbook/env/guestbook/env", test)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "/argocd-example-apps/helm-guestbook/env/guestbook", subtracted)
|
||||
}
|
||||
for _, test := range []string{"guestbook/env", "/guestbook/env", "guestbook/env/", "/guestbook/env/", "./guestbook/env"} {
|
||||
subtracted, err := SubtractRelativeFromAbsolutePath("/argocd-example-apps/helm-guestbook/env/guestbook/env", test)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "/argocd-example-apps/helm-guestbook/env", subtracted)
|
||||
}
|
||||
for _, test := range []string{"", ".", "/", "./"} {
|
||||
subtracted, err := SubtractRelativeFromAbsolutePath("/argocd-example-apps/helm-guestbook/env/guestbook/env", test)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "/argocd-example-apps/helm-guestbook/env/guestbook/env", subtracted)
|
||||
}
|
||||
// "Dirty" strings
|
||||
for _, test := range []string{"guestbook/foo/../env", "/guestbook//env", "../guestbook/env/", "/../guestbook/env/", "./guestbook/env///"} {
|
||||
subtracted, err := SubtractRelativeFromAbsolutePath("/argocd-example-apps/helm-guestbook/env/guestbook/env", test)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "/argocd-example-apps/helm-guestbook/env", subtracted)
|
||||
}
|
||||
// Invalid strings
|
||||
for _, test := range []string{"/not/in/path", "../not/in/path", "not/in/path"} {
|
||||
_, err := SubtractRelativeFromAbsolutePath("/argocd-example-apps/helm-guestbook/env/guestbook/env", test)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +201,8 @@ type SettingsManager struct {
|
||||
// mutex protects concurrency sensitive parts of settings manager: access to subscribers list and initialization flag
|
||||
mutex *sync.Mutex
|
||||
initContextCancel func()
|
||||
reposCache []RepoCredentials
|
||||
repoCredsCache []RepoCredentials
|
||||
}
|
||||
|
||||
type incompleteSettingsError struct {
|
||||
@@ -219,6 +221,38 @@ func (mgr *SettingsManager) GetSecretsLister() (v1listers.SecretLister, error) {
|
||||
return mgr.secrets, nil
|
||||
}
|
||||
|
||||
func (mgr *SettingsManager) updateConfigMap(callback func(*apiv1.ConfigMap) error) error {
|
||||
argoCDCM, err := mgr.getConfigMap()
|
||||
createCM := false
|
||||
if err != nil {
|
||||
if !apierr.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
argoCDCM = &apiv1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDConfigMapName,
|
||||
},
|
||||
}
|
||||
createCM = true
|
||||
}
|
||||
if argoCDCM.Data == nil {
|
||||
argoCDCM.Data = make(map[string]string)
|
||||
}
|
||||
err = callback(argoCDCM)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if createCM {
|
||||
_, err = mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Create(argoCDCM)
|
||||
} else {
|
||||
_, err = mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Update(argoCDCM)
|
||||
}
|
||||
|
||||
mgr.invalidateCache()
|
||||
return err
|
||||
}
|
||||
|
||||
func (mgr *SettingsManager) getConfigMap() (*apiv1.ConfigMap, error) {
|
||||
err := mgr.ensureSynced(false)
|
||||
if err != nil {
|
||||
@@ -349,54 +383,78 @@ func (mgr *SettingsManager) GetHelmRepositories() ([]HelmRepoCredentials, error)
|
||||
}
|
||||
|
||||
func (mgr *SettingsManager) GetRepositories() ([]RepoCredentials, error) {
|
||||
argoCDCM, err := mgr.getConfigMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repositories := make([]RepoCredentials, 0)
|
||||
repositoriesStr := argoCDCM.Data[repositoriesKey]
|
||||
if repositoriesStr != "" {
|
||||
err := yaml.Unmarshal([]byte(repositoriesStr), &repositories)
|
||||
if mgr.reposCache == nil {
|
||||
argoCDCM, err := mgr.getConfigMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mgr.mutex.Lock()
|
||||
defer mgr.mutex.Unlock()
|
||||
repositories := make([]RepoCredentials, 0)
|
||||
repositoriesStr := argoCDCM.Data[repositoriesKey]
|
||||
if repositoriesStr != "" {
|
||||
err := yaml.Unmarshal([]byte(repositoriesStr), &repositories)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
mgr.reposCache = repositories
|
||||
}
|
||||
return repositories, nil
|
||||
|
||||
return mgr.reposCache, nil
|
||||
}
|
||||
|
||||
func (mgr *SettingsManager) SaveRepositories(repos []RepoCredentials) error {
|
||||
argoCDCM, err := mgr.getConfigMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(repos) > 0 {
|
||||
yamlStr, err := yaml.Marshal(repos)
|
||||
if err != nil {
|
||||
return err
|
||||
return mgr.updateConfigMap(func(argoCDCM *apiv1.ConfigMap) error {
|
||||
if len(repos) > 0 {
|
||||
yamlStr, err := yaml.Marshal(repos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
argoCDCM.Data[repositoriesKey] = string(yamlStr)
|
||||
} else {
|
||||
delete(argoCDCM.Data, repositoriesKey)
|
||||
}
|
||||
argoCDCM.Data[repositoriesKey] = string(yamlStr)
|
||||
} else {
|
||||
delete(argoCDCM.Data, repositoriesKey)
|
||||
}
|
||||
_, err = mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Update(argoCDCM)
|
||||
return err
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (mgr *SettingsManager) SaveRepositoryCredentials(creds []RepoCredentials) error {
|
||||
return mgr.updateConfigMap(func(argoCDCM *apiv1.ConfigMap) error {
|
||||
if len(creds) > 0 {
|
||||
yamlStr, err := yaml.Marshal(creds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
argoCDCM.Data[repositoryCredentialsKey] = string(yamlStr)
|
||||
} else {
|
||||
delete(argoCDCM.Data, repositoryCredentialsKey)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (mgr *SettingsManager) GetRepositoryCredentials() ([]RepoCredentials, error) {
|
||||
argoCDCM, err := mgr.getConfigMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repositoryCredentials := make([]RepoCredentials, 0)
|
||||
repositoryCredentialsStr := argoCDCM.Data[repositoryCredentialsKey]
|
||||
if repositoryCredentialsStr != "" {
|
||||
err := yaml.Unmarshal([]byte(repositoryCredentialsStr), &repositoryCredentials)
|
||||
if mgr.repoCredsCache == nil {
|
||||
argoCDCM, err := mgr.getConfigMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mgr.mutex.Lock()
|
||||
defer mgr.mutex.Unlock()
|
||||
creds := make([]RepoCredentials, 0)
|
||||
credsStr := argoCDCM.Data[repositoryCredentialsKey]
|
||||
if credsStr != "" {
|
||||
err := yaml.Unmarshal([]byte(credsStr), &creds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
mgr.repoCredsCache = creds
|
||||
}
|
||||
return repositoryCredentials, nil
|
||||
return mgr.repoCredsCache, nil
|
||||
}
|
||||
|
||||
func (mgr *SettingsManager) GetGoogleAnalytics() (*GoogleAnalytics, error) {
|
||||
@@ -451,16 +509,31 @@ func (mgr *SettingsManager) GetSettings() (*ArgoCDSettings, error) {
|
||||
return &settings, nil
|
||||
}
|
||||
|
||||
// Clears cached settings on configmap/secret change
|
||||
func (mgr *SettingsManager) invalidateCache() {
|
||||
mgr.mutex.Lock()
|
||||
defer mgr.mutex.Unlock()
|
||||
|
||||
mgr.reposCache = nil
|
||||
mgr.repoCredsCache = nil
|
||||
}
|
||||
|
||||
func (mgr *SettingsManager) initialize(ctx context.Context) error {
|
||||
tweakConfigMap := func(options *metav1.ListOptions) {
|
||||
//cmFieldSelector := fields.ParseSelectorOrDie(fmt.Sprintf("metadata.name=%s", common.ArgoCDConfigMapName))
|
||||
cmLabelSelector := fields.ParseSelectorOrDie("app.kubernetes.io/part-of=argocd")
|
||||
options.LabelSelector = cmLabelSelector.String()
|
||||
}
|
||||
|
||||
eventHandler := cache.ResourceEventHandlerFuncs{
|
||||
UpdateFunc: func(oldObj, newObj interface{}) {
|
||||
mgr.invalidateCache()
|
||||
},
|
||||
}
|
||||
indexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}
|
||||
cmInformer := v1.NewFilteredConfigMapInformer(mgr.clientset, mgr.namespace, 3*time.Minute, indexers, tweakConfigMap)
|
||||
secretsInformer := v1.NewSecretInformer(mgr.clientset, mgr.namespace, 3*time.Minute, indexers)
|
||||
cmInformer.AddEventHandler(eventHandler)
|
||||
secretsInformer.AddEventHandler(eventHandler)
|
||||
|
||||
log.Info("Starting configmap/secret informers")
|
||||
go func() {
|
||||
@@ -596,49 +669,25 @@ func updateSettingsFromSecret(settings *ArgoCDSettings, argoCDSecret *apiv1.Secr
|
||||
|
||||
// SaveSettings serializes ArgoCDSettings and upserts it into K8s secret/configmap
|
||||
func (mgr *SettingsManager) SaveSettings(settings *ArgoCDSettings) error {
|
||||
err := mgr.ensureSynced(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Upsert the config data
|
||||
argoCDCM, err := mgr.configmaps.ConfigMaps(mgr.namespace).Get(common.ArgoCDConfigMapName)
|
||||
createCM := false
|
||||
if err != nil {
|
||||
if !apierr.IsNotFound(err) {
|
||||
return err
|
||||
err := mgr.updateConfigMap(func(argoCDCM *apiv1.ConfigMap) error {
|
||||
if settings.URL != "" {
|
||||
argoCDCM.Data[settingURLKey] = settings.URL
|
||||
} else {
|
||||
delete(argoCDCM.Data, settingURLKey)
|
||||
}
|
||||
argoCDCM = &apiv1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: common.ArgoCDConfigMapName,
|
||||
},
|
||||
if settings.DexConfig != "" {
|
||||
argoCDCM.Data[settingDexConfigKey] = settings.DexConfig
|
||||
} else {
|
||||
delete(argoCDCM.Data, settings.DexConfig)
|
||||
}
|
||||
createCM = true
|
||||
}
|
||||
if argoCDCM.Data == nil {
|
||||
argoCDCM.Data = make(map[string]string)
|
||||
}
|
||||
if settings.URL != "" {
|
||||
argoCDCM.Data[settingURLKey] = settings.URL
|
||||
} else {
|
||||
delete(argoCDCM.Data, settingURLKey)
|
||||
}
|
||||
if settings.DexConfig != "" {
|
||||
argoCDCM.Data[settingDexConfigKey] = settings.DexConfig
|
||||
} else {
|
||||
delete(argoCDCM.Data, settings.DexConfig)
|
||||
}
|
||||
if settings.OIDCConfigRAW != "" {
|
||||
argoCDCM.Data[settingsOIDCConfigKey] = settings.OIDCConfigRAW
|
||||
} else {
|
||||
delete(argoCDCM.Data, settingsOIDCConfigKey)
|
||||
}
|
||||
if settings.OIDCConfigRAW != "" {
|
||||
argoCDCM.Data[settingsOIDCConfigKey] = settings.OIDCConfigRAW
|
||||
} else {
|
||||
delete(argoCDCM.Data, settingsOIDCConfigKey)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if createCM {
|
||||
_, err = mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Create(argoCDCM)
|
||||
} else {
|
||||
_, err = mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Update(argoCDCM)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -45,6 +45,34 @@ func TestSaveRepositories(t *testing.T) {
|
||||
cm, err := kubeClient.CoreV1().ConfigMaps("default").Get(common.ArgoCDConfigMapName, metav1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, cm.Data["repositories"], "- url: http://foo\n")
|
||||
|
||||
repos, err := settingsManager.GetRepositories()
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, repos, []RepoCredentials{{URL: "http://foo"}})
|
||||
}
|
||||
|
||||
func TestSaveRepositoresNoConfigMap(t *testing.T) {
|
||||
kubeClient := fake.NewSimpleClientset()
|
||||
settingsManager := NewSettingsManager(context.Background(), kubeClient, "default")
|
||||
|
||||
err := settingsManager.SaveRepositories([]RepoCredentials{{URL: "http://foo"}})
|
||||
assert.NoError(t, err)
|
||||
cm, err := kubeClient.CoreV1().ConfigMaps("default").Get(common.ArgoCDConfigMapName, metav1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, cm.Data["repositories"], "- url: http://foo\n")
|
||||
}
|
||||
|
||||
func TestSaveRepositoryCredentials(t *testing.T) {
|
||||
kubeClient, settingsManager := fixtures(nil)
|
||||
err := settingsManager.SaveRepositoryCredentials([]RepoCredentials{{URL: "http://foo"}})
|
||||
assert.NoError(t, err)
|
||||
cm, err := kubeClient.CoreV1().ConfigMaps("default").Get(common.ArgoCDConfigMapName, metav1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, cm.Data["repository.credentials"], "- url: http://foo\n")
|
||||
|
||||
creds, err := settingsManager.GetRepositoryCredentials()
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, creds, []RepoCredentials{{URL: "http://foo"}})
|
||||
}
|
||||
|
||||
func TestGetRepositoryCredentials(t *testing.T) {
|
||||
|
||||
44
util/tracing/span.go
Normal file
44
util/tracing/span.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
/*
|
||||
Poor Mans OpenTracing.
|
||||
|
||||
Standardizes logging of operation duration.
|
||||
*/
|
||||
|
||||
var enabled = false
|
||||
var logger = log.New()
|
||||
|
||||
func init() {
|
||||
enabled = os.Getenv("ARGOCD_TRACING_ENABLED") == "1"
|
||||
}
|
||||
|
||||
type Span struct {
|
||||
operationName string
|
||||
baggage map[string]interface{}
|
||||
start time.Time
|
||||
}
|
||||
|
||||
func (s Span) Finish() {
|
||||
if enabled {
|
||||
logger.WithFields(s.baggage).
|
||||
WithField("operation_name", s.operationName).
|
||||
WithField("time_ms", time.Since(s.start).Seconds()*1e3).
|
||||
Info()
|
||||
}
|
||||
}
|
||||
|
||||
func (s Span) SetBaggageItem(key string, value interface{}) {
|
||||
s.baggage[key] = value
|
||||
}
|
||||
|
||||
func StartSpan(operationName string) Span {
|
||||
return Span{operationName, make(map[string]interface{}), time.Now()}
|
||||
}
|
||||
42
util/tracing/span_test.go
Normal file
42
util/tracing/span_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStartSpan(t *testing.T) {
|
||||
|
||||
testLogger, hook := test.NewNullLogger()
|
||||
defer hook.Reset()
|
||||
logger = testLogger
|
||||
defer func() { logger = log.New() }()
|
||||
|
||||
t.Run("Disabled", func(t *testing.T) {
|
||||
span := StartSpan("my-operation")
|
||||
span.SetBaggageItem("my-key", "my-value")
|
||||
span.Finish()
|
||||
|
||||
assert.Empty(t, hook.Entries)
|
||||
|
||||
})
|
||||
hook.Reset()
|
||||
t.Run("Enabled", func(t *testing.T) {
|
||||
enabled = true
|
||||
defer func() { enabled = false }()
|
||||
span := StartSpan("my-operation")
|
||||
span.SetBaggageItem("my-key", "my-value")
|
||||
span.Finish()
|
||||
|
||||
e := hook.LastEntry()
|
||||
if assert.NotNil(t, e) {
|
||||
assert.Empty(t, e.Message)
|
||||
assert.Equal(t, "my-operation", e.Data["operation_name"])
|
||||
assert.Equal(t, "my-value", e.Data["my-key"])
|
||||
assert.Contains(t, e.Data, "time_ms")
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user