feat(server): make deep copies of objects returned by informers (#22173) (#22179)

Signed-off-by: rumstead <37445536+rumstead@users.noreply.github.com>
This commit is contained in:
rumstead
2025-03-10 10:29:07 -04:00
committed by GitHub
parent 922d080ae5
commit e3bd56972d
6 changed files with 689 additions and 17 deletions

View File

@@ -76,4 +76,7 @@ packages:
SessionServiceClient:
github.com/argoproj/argo-cd/v3/pkg/apiclient/cluster:
interfaces:
ClusterServiceServer:
ClusterServiceServer:
github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned/typed/application/v1alpha1:
interfaces:
AppProjectInterface:

View File

@@ -0,0 +1,258 @@
// Code generated by mockery v2.52.4. DO NOT EDIT.
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
types "k8s.io/apimachinery/pkg/types"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
watch "k8s.io/apimachinery/pkg/watch"
)
// AppProjectInterface is an autogenerated mock type for the AppProjectInterface type
type AppProjectInterface struct {
mock.Mock
}
// Create provides a mock function with given fields: ctx, appProject, opts
func (_m *AppProjectInterface) Create(ctx context.Context, appProject *v1alpha1.AppProject, opts v1.CreateOptions) (*v1alpha1.AppProject, error) {
ret := _m.Called(ctx, appProject, opts)
if len(ret) == 0 {
panic("no return value specified for Create")
}
var r0 *v1alpha1.AppProject
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.AppProject, v1.CreateOptions) (*v1alpha1.AppProject, error)); ok {
return rf(ctx, appProject, opts)
}
if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.AppProject, v1.CreateOptions) *v1alpha1.AppProject); ok {
r0 = rf(ctx, appProject, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*v1alpha1.AppProject)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.AppProject, v1.CreateOptions) error); ok {
r1 = rf(ctx, appProject, opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Delete provides a mock function with given fields: ctx, name, opts
func (_m *AppProjectInterface) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
ret := _m.Called(ctx, name, opts)
if len(ret) == 0 {
panic("no return value specified for Delete")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, v1.DeleteOptions) error); ok {
r0 = rf(ctx, name, opts)
} else {
r0 = ret.Error(0)
}
return r0
}
// DeleteCollection provides a mock function with given fields: ctx, opts, listOpts
func (_m *AppProjectInterface) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
ret := _m.Called(ctx, opts, listOpts)
if len(ret) == 0 {
panic("no return value specified for DeleteCollection")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, v1.DeleteOptions, v1.ListOptions) error); ok {
r0 = rf(ctx, opts, listOpts)
} else {
r0 = ret.Error(0)
}
return r0
}
// Get provides a mock function with given fields: ctx, name, opts
func (_m *AppProjectInterface) Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.AppProject, error) {
ret := _m.Called(ctx, name, opts)
if len(ret) == 0 {
panic("no return value specified for Get")
}
var r0 *v1alpha1.AppProject
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, v1.GetOptions) (*v1alpha1.AppProject, error)); ok {
return rf(ctx, name, opts)
}
if rf, ok := ret.Get(0).(func(context.Context, string, v1.GetOptions) *v1alpha1.AppProject); ok {
r0 = rf(ctx, name, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*v1alpha1.AppProject)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string, v1.GetOptions) error); ok {
r1 = rf(ctx, name, opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// List provides a mock function with given fields: ctx, opts
func (_m *AppProjectInterface) List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.AppProjectList, error) {
ret := _m.Called(ctx, opts)
if len(ret) == 0 {
panic("no return value specified for List")
}
var r0 *v1alpha1.AppProjectList
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, v1.ListOptions) (*v1alpha1.AppProjectList, error)); ok {
return rf(ctx, opts)
}
if rf, ok := ret.Get(0).(func(context.Context, v1.ListOptions) *v1alpha1.AppProjectList); ok {
r0 = rf(ctx, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*v1alpha1.AppProjectList)
}
}
if rf, ok := ret.Get(1).(func(context.Context, v1.ListOptions) error); ok {
r1 = rf(ctx, opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Patch provides a mock function with given fields: ctx, name, pt, data, opts, subresources
func (_m *AppProjectInterface) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (*v1alpha1.AppProject, error) {
_va := make([]interface{}, len(subresources))
for _i := range subresources {
_va[_i] = subresources[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, name, pt, data, opts)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
if len(ret) == 0 {
panic("no return value specified for Patch")
}
var r0 *v1alpha1.AppProject
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, v1.PatchOptions, ...string) (*v1alpha1.AppProject, error)); ok {
return rf(ctx, name, pt, data, opts, subresources...)
}
if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, v1.PatchOptions, ...string) *v1alpha1.AppProject); ok {
r0 = rf(ctx, name, pt, data, opts, subresources...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*v1alpha1.AppProject)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string, types.PatchType, []byte, v1.PatchOptions, ...string) error); ok {
r1 = rf(ctx, name, pt, data, opts, subresources...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Update provides a mock function with given fields: ctx, appProject, opts
func (_m *AppProjectInterface) Update(ctx context.Context, appProject *v1alpha1.AppProject, opts v1.UpdateOptions) (*v1alpha1.AppProject, error) {
ret := _m.Called(ctx, appProject, opts)
if len(ret) == 0 {
panic("no return value specified for Update")
}
var r0 *v1alpha1.AppProject
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.AppProject, v1.UpdateOptions) (*v1alpha1.AppProject, error)); ok {
return rf(ctx, appProject, opts)
}
if rf, ok := ret.Get(0).(func(context.Context, *v1alpha1.AppProject, v1.UpdateOptions) *v1alpha1.AppProject); ok {
r0 = rf(ctx, appProject, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*v1alpha1.AppProject)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *v1alpha1.AppProject, v1.UpdateOptions) error); ok {
r1 = rf(ctx, appProject, opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Watch provides a mock function with given fields: ctx, opts
func (_m *AppProjectInterface) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
ret := _m.Called(ctx, opts)
if len(ret) == 0 {
panic("no return value specified for Watch")
}
var r0 watch.Interface
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, v1.ListOptions) (watch.Interface, error)); ok {
return rf(ctx, opts)
}
if rf, ok := ret.Get(0).(func(context.Context, v1.ListOptions) watch.Interface); ok {
r0 = rf(ctx, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(watch.Interface)
}
}
if rf, ok := ret.Get(1).(func(context.Context, v1.ListOptions) error); ok {
r1 = rf(ctx, opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewAppProjectInterface creates a new instance of AppProjectInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewAppProjectInterface(t interface {
mock.TestingT
Cleanup(func())
}) *AppProjectInterface {
mock := &AppProjectInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -48,7 +48,6 @@ import (
"github.com/argoproj/argo-cd/v3/reposerver/apiclient"
servercache "github.com/argoproj/argo-cd/v3/server/cache"
"github.com/argoproj/argo-cd/v3/server/deeplinks"
"github.com/argoproj/argo-cd/v3/util"
"github.com/argoproj/argo-cd/v3/util/argo"
"github.com/argoproj/argo-cd/v3/util/collections"
"github.com/argoproj/argo-cd/v3/util/db"
@@ -127,8 +126,8 @@ func NewServer(
}
s := &Server{
ns: namespace,
appclientset: appclientset,
appLister: appLister,
appclientset: &deepCopyAppClientset{appclientset},
appLister: &deepCopyApplicationLister{appLister},
appInformer: appInformer,
appBroadcaster: appBroadcaster,
kubeclientset: kubeclientset,
@@ -261,9 +260,7 @@ func (s *Server) getApplicationEnforceRBACClient(ctx context.Context, action, pr
if err != nil {
return nil, err
}
// Objects returned by the lister must be treated as read-only.
// To allow us to modify the app later, make a copy
return app.DeepCopy(), nil
return app, nil
})
}
@@ -380,9 +377,6 @@ func (s *Server) Create(ctx context.Context, q *application.ApplicationCreateReq
if err != nil {
return nil, status.Errorf(codes.Internal, "unable to check existing application details (%s): %v", appNs, err)
}
// Objects returned from listers have to be treated as read-only
// Take a deep copy so we can edit it below
existing = existing.DeepCopy()
if _, err := argo.GetDestinationCluster(ctx, existing.Spec.Destination, s.db); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "application destination spec for %s is invalid: %s", existing.Name, err.Error())
@@ -491,7 +485,7 @@ func (s *Server) GetManifests(ctx context.Context, q *application.ApplicationMan
}
sources := make([]v1alpha1.ApplicationSource, 0)
appSpec := a.Spec.DeepCopy()
appSpec := a.Spec
if a.Spec.HasMultipleSources() {
numOfSources := int64(len(a.Spec.GetSources()))
for i, pos := range q.SourcePositions {
@@ -888,7 +882,7 @@ func (s *Server) ListResourceEvents(ctx context.Context, q *application.Applicat
if err != nil {
return nil, fmt.Errorf("error listing resource events: %w", err)
}
return list, nil
return list.DeepCopy(), nil
}
// validateAndUpdateApp validates and updates the application. currentProject is the name of the project the app
@@ -1226,7 +1220,6 @@ func (s *Server) Watch(q *application.ApplicationQuery, ws application.Applicati
if err != nil {
return fmt.Errorf("error listing apps with selector: %w", err)
}
apps = util.SliceCopy(apps)
sort.Slice(apps, func(i, j int) bool {
return apps[i].QualifiedName() < apps[j].QualifiedName()
})
@@ -2330,7 +2323,7 @@ func (s *Server) TerminateOperation(ctx context.Context, termOpReq *application.
}
log.Warnf("failed to set operation for app %q due to update conflict. retrying again...", *termOpReq.Name)
time.Sleep(100 * time.Millisecond)
a, err = s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
_, err = s.appclientset.ArgoprojV1alpha1().Applications(appNs).Get(ctx, appName, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("error getting application by name: %w", err)
}

View File

@@ -5,7 +5,9 @@ import (
stderrors "errors"
"fmt"
"io"
"slices"
"strconv"
"strings"
"sync/atomic"
"testing"
"time"
@@ -1547,7 +1549,7 @@ func TestDeleteApp(t *testing.T) {
require.NoError(t, err)
assert.NotNil(t, app)
fakeAppCs := appServer.appclientset.(*apps.Clientset)
fakeAppCs := appServer.appclientset.(*deepCopyAppClientset).GetUnderlyingClientSet().(*apps.Clientset)
// this removes the default */* reactor so we can set our own patch/delete reactor
fakeAppCs.ReactionChain = nil
patched := false
@@ -2069,7 +2071,7 @@ func TestGetCachedAppState(t *testing.T) {
},
}
appServer := newTestAppServer(t, testApp, testProj)
fakeClientSet := appServer.appclientset.(*apps.Clientset)
fakeClientSet := appServer.appclientset.(*deepCopyAppClientset).GetUnderlyingClientSet().(*apps.Clientset)
fakeClientSet.AddReactor("get", "applications", func(_ kubetesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &v1alpha1.Application{Spec: v1alpha1.ApplicationSpec{Source: &v1alpha1.ApplicationSource{}}}, nil
})
@@ -2330,7 +2332,6 @@ func refreshAnnotationRemover(t *testing.T, ctx context.Context, patched *int32,
aName, appNs := argo.ParseFromQualifiedName(appName, appServer.ns)
a, err := appServer.appLister.Applications(appNs).Get(aName)
require.NoError(t, err)
a = a.DeepCopy()
if a.GetAnnotations() != nil && a.GetAnnotations()[v1alpha1.AnnotationKeyRefresh] != "" {
a.SetAnnotations(map[string]string{})
a.SetResourceVersion("999")
@@ -3350,3 +3351,106 @@ func Test_RevisionMetadata(t *testing.T) {
})
}
}
func Test_DeepCopyInformers(t *testing.T) {
t.Parallel()
namespace := "test-namespace"
var ro []runtime.Object
appOne := newTestApp(func(app *v1alpha1.Application) {
app.Name = "appOne"
app.ObjectMeta.Namespace = namespace
app.Spec = v1alpha1.ApplicationSpec{}
})
appTwo := newTestApp(func(app *v1alpha1.Application) {
app.Name = "appTwo"
app.ObjectMeta.Namespace = namespace
app.Spec = v1alpha1.ApplicationSpec{}
})
appThree := newTestApp(func(app *v1alpha1.Application) {
app.Name = "appThree"
app.ObjectMeta.Namespace = namespace
app.Spec = v1alpha1.ApplicationSpec{}
})
ro = append(ro, appOne, appTwo, appThree)
appls := []v1alpha1.Application{*appOne, *appTwo, *appThree}
appSetOne := &v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{Name: "appSetOne", Namespace: namespace},
Spec: v1alpha1.ApplicationSetSpec{},
}
appSetTwo := &v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{Name: "appSetTwo", Namespace: namespace},
Spec: v1alpha1.ApplicationSetSpec{},
}
appSetThree := &v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{Name: "appSetThree", Namespace: namespace},
Spec: v1alpha1.ApplicationSetSpec{},
}
ro = append(ro, appSetOne, appSetTwo, appSetThree)
appSets := []v1alpha1.ApplicationSet{*appSetOne, *appSetTwo, *appSetThree}
appProjects := createAppProject("projOne", "projTwo", "projThree")
for i := range appProjects {
ro = append(ro, &appProjects[i])
}
s := newTestAppServer(t, ro...)
appList, err := s.appclientset.ArgoprojV1alpha1().Applications(namespace).List(context.Background(), metav1.ListOptions{})
require.NoError(t, err)
assert.ElementsMatch(t, appls, appList.Items)
sAppList := appList.Items
slices.SortFunc(sAppList, func(a, b v1alpha1.Application) int {
return strings.Compare(a.Name, b.Name)
})
slices.SortFunc(appls, func(a, b v1alpha1.Application) int {
return strings.Compare(a.Name, b.Name)
})
// ensure there is a deep copy
for i := range appls {
assert.NotSame(t, &appls[i], &sAppList[i])
assert.NotSame(t, &appls[i].Spec, &sAppList[i].Spec)
a, err := s.appclientset.ArgoprojV1alpha1().Applications(namespace).Get(context.Background(), sAppList[i].Name, metav1.GetOptions{})
require.NoError(t, err)
assert.NotSame(t, a, &sAppList[i])
}
appSetList, err := s.appclientset.ArgoprojV1alpha1().ApplicationSets(namespace).List(context.Background(), metav1.ListOptions{})
require.NoError(t, err)
assert.ElementsMatch(t, appSets, appSetList.Items)
sAppSetList := appSetList.Items
slices.SortFunc(sAppSetList, func(a, b v1alpha1.ApplicationSet) int {
return strings.Compare(a.Name, b.Name)
})
slices.SortFunc(appSets, func(a, b v1alpha1.ApplicationSet) int {
return strings.Compare(a.Name, b.Name)
})
for i := range appSets {
assert.NotSame(t, &appSets[i], &sAppSetList[i])
assert.NotSame(t, &appSets[i].Spec, &sAppSetList[i].Spec)
a, err := s.appclientset.ArgoprojV1alpha1().ApplicationSets(namespace).Get(context.Background(),
sAppSetList[i].Name, metav1.GetOptions{})
require.NoError(t, err)
assert.NotSame(t, a, &sAppSetList[i])
}
projList, err := s.appclientset.ArgoprojV1alpha1().AppProjects("deep-copy-ns").List(context.Background(), metav1.ListOptions{})
require.NoError(t, err)
assert.ElementsMatch(t, appProjects, projList.Items)
spList := projList.Items
slices.SortFunc(spList, func(a, b v1alpha1.AppProject) int {
return strings.Compare(a.Name, b.Name)
})
slices.SortFunc(appProjects, func(a, b v1alpha1.AppProject) int {
return strings.Compare(a.Name, b.Name)
})
for i := range appProjects {
assert.NotSame(t, &appProjects[i], &spList[i])
assert.NotSame(t, &appProjects[i].Spec, &spList[i].Spec)
p, err := s.appclientset.ArgoprojV1alpha1().AppProjects("deep-copy-ns").Get(context.Background(),
spList[i].Name, metav1.GetOptions{})
require.NoError(t, err)
assert.NotSame(t, p, &spList[i])
}
}

View File

@@ -0,0 +1,164 @@
package application
import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned"
argoprojv1alpha1 "github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned/typed/application/v1alpha1"
applisters "github.com/argoproj/argo-cd/v3/pkg/client/listers/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/util"
"k8s.io/apimachinery/pkg/labels"
)
// deepCopyApplicationLister wraps an ApplicationLister and returns deep copies of the applications.
type deepCopyApplicationLister struct {
applisters.ApplicationLister
}
// List lists all Applications in the indexer and returns deep copies.
func (d *deepCopyApplicationLister) List(selector labels.Selector) ([]*v1alpha1.Application, error) {
apps, err := d.ApplicationLister.List(selector)
if err != nil {
return nil, err
}
deepCopiedApps := util.SliceCopy(apps)
return deepCopiedApps, nil
}
// Applications return an object that can list and get Applications and returns deep copies.
func (d *deepCopyApplicationLister) Applications(namespace string) applisters.ApplicationNamespaceLister {
return &deepCopyApplicationNamespaceLister{
ApplicationNamespaceLister: d.ApplicationLister.Applications(namespace),
}
}
// deepCopyApplicationNamespaceLister wraps an ApplicationNamespaceLister and returns deep copies of the applications.
type deepCopyApplicationNamespaceLister struct {
applisters.ApplicationNamespaceLister
}
// List lists all Applications in the indexer for a given namespace and returns deep copies.
func (d *deepCopyApplicationNamespaceLister) List(selector labels.Selector) ([]*v1alpha1.Application, error) {
apps, err := d.ApplicationNamespaceLister.List(selector)
if err != nil {
return nil, err
}
deepCopiedApps := util.SliceCopy(apps)
return deepCopiedApps, nil
}
// Get retrieves the Application from the indexer for a given namespace and name and returns a deep copy.
func (d *deepCopyApplicationNamespaceLister) Get(name string) (*v1alpha1.Application, error) {
app, err := d.ApplicationNamespaceLister.Get(name)
if err != nil {
return nil, err
}
return app.DeepCopy(), nil
}
type deepCopyAppClientset struct {
appclientset.Interface
}
func (d *deepCopyAppClientset) ArgoprojV1alpha1() argoprojv1alpha1.ArgoprojV1alpha1Interface {
return &deepCopyArgoprojV1alpha1Client{
ArgoprojV1alpha1Interface: d.Interface.ArgoprojV1alpha1(),
}
}
// GetUnderlyingClientSet returns the underlying clientset.Interface.
// Unit tests should only call this
func (d *deepCopyAppClientset) GetUnderlyingClientSet() appclientset.Interface {
return d.Interface
}
type deepCopyArgoprojV1alpha1Client struct {
argoprojv1alpha1.ArgoprojV1alpha1Interface
}
func (d *deepCopyArgoprojV1alpha1Client) RESTClient() rest.Interface {
return d.ArgoprojV1alpha1Interface.RESTClient()
}
func (d *deepCopyArgoprojV1alpha1Client) AppProjects(namespace string) argoprojv1alpha1.AppProjectInterface {
return &deepCopyAppProjectClient{
AppProjectInterface: d.ArgoprojV1alpha1Interface.AppProjects(namespace),
}
}
func (d *deepCopyArgoprojV1alpha1Client) ApplicationSets(namespace string) argoprojv1alpha1.ApplicationSetInterface {
return &deepCopyApplicationSetClient{
ApplicationSetInterface: d.ArgoprojV1alpha1Interface.ApplicationSets(namespace),
}
}
func (d *deepCopyArgoprojV1alpha1Client) Applications(namespace string) argoprojv1alpha1.ApplicationInterface {
return &deepCopyApplicationClient{
ApplicationInterface: d.ArgoprojV1alpha1Interface.Applications(namespace),
}
}
type deepCopyApplicationClient struct {
argoprojv1alpha1.ApplicationInterface
}
func (d *deepCopyApplicationClient) Get(ctx context.Context, name string, options metav1.GetOptions) (*v1alpha1.Application, error) {
app, err := d.ApplicationInterface.Get(ctx, name, options)
if err != nil {
return nil, err
}
return app.DeepCopy(), nil
}
func (d *deepCopyApplicationClient) List(ctx context.Context, opts metav1.ListOptions) (*v1alpha1.ApplicationList, error) {
appList, err := d.ApplicationInterface.List(ctx, opts)
if err != nil {
return nil, err
}
return appList.DeepCopy(), nil
}
type deepCopyAppProjectClient struct {
argoprojv1alpha1.AppProjectInterface
}
func (d *deepCopyAppProjectClient) Get(ctx context.Context, name string, options metav1.GetOptions) (*v1alpha1.AppProject, error) {
appProject, err := d.AppProjectInterface.Get(ctx, name, options)
if err != nil {
return nil, err
}
return appProject.DeepCopy(), nil
}
func (d *deepCopyAppProjectClient) List(ctx context.Context, opts metav1.ListOptions) (*v1alpha1.AppProjectList, error) {
appProjectList, err := d.AppProjectInterface.List(ctx, opts)
if err != nil {
return nil, err
}
return appProjectList.DeepCopy(), nil
}
type deepCopyApplicationSetClient struct {
argoprojv1alpha1.ApplicationSetInterface
}
func (d *deepCopyApplicationSetClient) Get(ctx context.Context, name string, options metav1.GetOptions) (*v1alpha1.ApplicationSet, error) {
appSet, err := d.ApplicationSetInterface.Get(ctx, name, options)
if err != nil {
return nil, err
}
return appSet.DeepCopy(), nil
}
func (d *deepCopyApplicationSetClient) List(ctx context.Context, opts metav1.ListOptions) (*v1alpha1.ApplicationSetList, error) {
appSetList, err := d.ApplicationSetInterface.List(ctx, opts)
if err != nil {
return nil, err
}
return appSetList.DeepCopy(), nil
}

View File

@@ -0,0 +1,150 @@
package application
import (
"context"
"errors"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned/fake"
clientset "github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned/typed/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned/typed/application/v1alpha1/mocks"
)
func Test_deepCopyAppProjectClient_Get(t *testing.T) {
type fields struct {
AppProjectInterface clientset.AppProjectInterface
}
type args struct {
name string
}
tests := []struct {
name string
fields fields
args args
want *v1alpha1.AppProject
wantErr assert.ErrorAssertionFunc
}{
{name: "Get an app project", fields: fields{AppProjectInterface: setupAppProjects("appproject")}, args: args{
name: "appproject",
}, want: &v1alpha1.AppProject{
ObjectMeta: metav1.ObjectMeta{Name: "appproject", Namespace: "deep-copy-ns"},
}, wantErr: assert.NoError},
{
name: "Error getting an app project",
fields: fields{
AppProjectInterface: func() clientset.AppProjectInterface {
appProject := mocks.AppProjectInterface{}
appProject.On("Get", context.Background(), "appproject2", metav1.GetOptions{}).Return(nil, errors.New("error"))
return &appProject
}(),
},
args: args{
name: "appproject2",
},
want: nil,
wantErr: assert.Error,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := &deepCopyAppProjectClient{
AppProjectInterface: tt.fields.AppProjectInterface,
}
got, err := d.Get(context.Background(), tt.args.name, metav1.GetOptions{})
if !tt.wantErr(t, err, fmt.Sprintf("Get(%v)", tt.args.name)) {
return
}
assert.Equalf(t, tt.want, got, "Get(%v)", tt.args.name)
if tt.want != nil {
assert.NotSamef(t, tt.want, got, "%v and %v are the same ptr", tt.want, got)
}
})
}
}
func Test_deepCopyAppProjectClient_List(t *testing.T) {
type fields struct {
AppProjectInterface clientset.AppProjectInterface
}
tests := []struct {
name string
fields fields
want []v1alpha1.AppProject
wantErr assert.ErrorAssertionFunc
}{
{
name: "List app projects", fields: fields{AppProjectInterface: setupAppProjects("proj1", "proj2")},
want: createAppProject("proj1", "proj2"), wantErr: assert.NoError,
},
{name: "Error listing app project", fields: fields{
AppProjectInterface: func() clientset.AppProjectInterface {
appProject := mocks.AppProjectInterface{}
appProject.On("List", context.Background(), metav1.ListOptions{}).Return(nil, errors.New("error"))
return &appProject
}(),
}, want: nil, wantErr: assert.Error},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := &deepCopyAppProjectClient{
AppProjectInterface: tt.fields.AppProjectInterface,
}
got, err := d.List(context.Background(), metav1.ListOptions{})
if !tt.wantErr(t, err, "List") {
return
}
if tt.want != nil {
assert.Equalf(t, tt.want, got.Items, "List")
for i := range tt.want {
assert.NotSamef(t, &tt.want[i], &got.Items[i], "%v and %v are the same ptr", tt.want, got)
}
}
})
}
}
func createAppProject(projects ...string) []v1alpha1.AppProject {
appProjects := make([]v1alpha1.AppProject, len(projects))
for i, p := range projects {
appProjects[i] = v1alpha1.AppProject{ObjectMeta: metav1.ObjectMeta{Name: p, Namespace: "deep-copy-ns"}}
}
return appProjects
}
func setupAppProjects(projects ...string) clientset.AppProjectInterface {
appProjects := createAppProject(projects...)
ro := make([]runtime.Object, len(appProjects))
for i := range appProjects {
ro[i] = &appProjects[i]
}
return fake.NewSimpleClientset(ro...).ArgoprojV1alpha1().AppProjects("deep-copy-ns")
}
func Test_deepCopyArgoprojV1alpha1Client_RESTClient(t *testing.T) {
fclientset := fake.NewSimpleClientset().ArgoprojV1alpha1()
type fields struct {
ArgoprojV1alpha1Interface clientset.ArgoprojV1alpha1Interface
}
tests := []struct {
name string
fields fields
want rest.Interface
}{
{name: "RestClientGetter", fields: fields{ArgoprojV1alpha1Interface: fclientset}, want: fclientset.RESTClient()},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := &deepCopyArgoprojV1alpha1Client{
ArgoprojV1alpha1Interface: tt.fields.ArgoprojV1alpha1Interface,
}
assert.Equalf(t, tt.want, d.RESTClient(), "RESTClient()")
})
}
}