Skip to content

Commit 7d592a8

Browse files
committed
Swap upstream core informers factory with out wrapper
This does not actually change how the informers work. This also adds a partial metadata client to root context Signed-off-by: irbekrm <irbekrm@gmail.com>
1 parent 1612d75 commit 7d592a8

File tree

52 files changed

+200
-203
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+200
-203
lines changed

internal/controller/certificates/policies/gatherer.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@ import (
213213

214214
apierrors "k8s.io/apimachinery/pkg/api/errors"
215215
"k8s.io/apimachinery/pkg/labels"
216-
corelisters "k8s.io/client-go/listers/core/v1"
217216

217+
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
218218
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
219219
cmlisters "github.com/cert-manager/cert-manager/pkg/client/listers/certmanager/v1"
220220
"github.com/cert-manager/cert-manager/pkg/controller/certificates"
@@ -226,7 +226,7 @@ import (
226226
// its current readiness/state by applying policy functions to it.
227227
type Gatherer struct {
228228
CertificateRequestLister cmlisters.CertificateRequestLister
229-
SecretLister corelisters.SecretLister
229+
SecretLister internalinformers.SecretLister
230230
}
231231

232232
// DataForCertificate returns the secret as well as the "current" and "next"

internal/controller/certificates/policies/gatherer_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ func TestDataForCertificate(t *testing.T) {
168168
// type by registering a fake handler.
169169
noop := cache.ResourceEventHandlerFuncs{AddFunc: func(obj interface{}) {}}
170170
test.builder.SharedInformerFactory.Certmanager().V1().CertificateRequests().Informer().AddEventHandler(noop)
171-
test.builder.KubeSharedInformerFactory.Core().V1().Secrets().Informer().AddEventHandler(noop)
171+
test.builder.KubeSharedInformerFactory.Secrets().Informer().AddEventHandler(noop)
172172

173173
// Even though we are only relying on listers in this unit test
174174
// and do not use the informer event handlers, we still need to
@@ -212,7 +212,7 @@ func TestDataForCertificate(t *testing.T) {
212212

213213
g := &Gatherer{
214214
CertificateRequestLister: test.builder.SharedInformerFactory.Certmanager().V1().CertificateRequests().Lister(),
215-
SecretLister: test.builder.KubeSharedInformerFactory.Core().V1().Secrets().Lister(),
215+
SecretLister: test.builder.KubeSharedInformerFactory.Secrets().Lister(),
216216
}
217217

218218
ctx := logf.NewContext(context.Background(), logf.WithResource(log, test.givenCert))

internal/vault/fake/vault.go

+6-7
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,13 @@ package fake
2020
import (
2121
"time"
2222

23-
corelisters "k8s.io/client-go/listers/core/v1"
24-
25-
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
23+
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
24+
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
2625
)
2726

2827
// Vault is a mock implementation of the Vault interface
2928
type Vault struct {
30-
NewFn func(string, corelisters.SecretLister, v1.GenericIssuer) (*Vault, error)
29+
NewFn func(string, internalinformers.SecretLister, cmapi.GenericIssuer) (*Vault, error)
3130
SignFn func([]byte, time.Duration) ([]byte, []byte, error)
3231
IsVaultInitializedAndUnsealedFn func() error
3332
}
@@ -43,7 +42,7 @@ func New() *Vault {
4342
},
4443
}
4544

46-
v.NewFn = func(string, corelisters.SecretLister, v1.GenericIssuer) (*Vault, error) {
45+
v.NewFn = func(string, internalinformers.SecretLister, cmapi.GenericIssuer) (*Vault, error) {
4746
return v, nil
4847
}
4948

@@ -64,13 +63,13 @@ func (v *Vault) WithSign(certPEM, caPEM []byte, err error) *Vault {
6463
}
6564

6665
// WithNew sets the fake Vault's New function.
67-
func (v *Vault) WithNew(f func(string, corelisters.SecretLister, v1.GenericIssuer) (*Vault, error)) *Vault {
66+
func (v *Vault) WithNew(f func(string, internalinformers.SecretLister, cmapi.GenericIssuer) (*Vault, error)) *Vault {
6867
v.NewFn = f
6968
return v
7069
}
7170

7271
// New call NewFn and returns a pointer to the fake Vault.
73-
func (v *Vault) New(ns string, sl corelisters.SecretLister, iss v1.GenericIssuer) (*Vault, error) {
72+
func (v *Vault) New(ns string, sl internalinformers.SecretLister, iss cmapi.GenericIssuer) (*Vault, error) {
7473
_, err := v.NewFn(ns, sl, iss)
7574
if err != nil {
7675
return nil, err

internal/vault/vault.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ import (
3131
"github.com/hashicorp/vault/sdk/helper/certutil"
3232
authv1 "k8s.io/api/authentication/v1"
3333
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34-
corelisters "k8s.io/client-go/listers/core/v1"
3534
"k8s.io/utils/pointer"
3635

36+
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
3737
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
3838
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
3939
"github.com/cert-manager/cert-manager/pkg/util/pki"
@@ -43,7 +43,7 @@ var _ Interface = &Vault{}
4343

4444
// ClientBuilder is a function type that returns a new Interface.
4545
// Can be used in tests to create a mock signer of Vault certificate requests.
46-
type ClientBuilder func(namespace string, _ func(ns string) CreateToken, _ corelisters.SecretLister, _ v1.GenericIssuer) (Interface, error)
46+
type ClientBuilder func(namespace string, _ func(ns string) CreateToken, _ internalinformers.SecretLister, _ v1.GenericIssuer) (Interface, error)
4747

4848
// Interface implements various high level functionality related to connecting
4949
// with a Vault server, verifying its status and signing certificate request for
@@ -67,7 +67,7 @@ type CreateToken func(ctx context.Context, saName string, req *authv1.TokenReque
6767
// Vault client.
6868
type Vault struct {
6969
createToken CreateToken // Uses the same namespace as below.
70-
secretsLister corelisters.SecretLister
70+
secretsLister internalinformers.SecretLister
7171
issuer v1.GenericIssuer
7272
namespace string
7373

@@ -93,7 +93,7 @@ type Vault struct {
9393
// secrets lister.
9494
// Returned errors may be network failures and should be considered for
9595
// retrying.
96-
func New(namespace string, createTokenFn func(ns string) CreateToken, secretsLister corelisters.SecretLister, issuer v1.GenericIssuer) (Interface, error) {
96+
func New(namespace string, createTokenFn func(ns string) CreateToken, secretsLister internalinformers.SecretLister, issuer v1.GenericIssuer) (Interface, error) {
9797
v := &Vault{
9898
createToken: createTokenFn(namespace),
9999
secretsLister: secretsLister,

pkg/controller/acmechallenges/controller.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ import (
2323
"github.com/go-logr/logr"
2424
corev1 "k8s.io/api/core/v1"
2525
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
26-
corelisters "k8s.io/client-go/listers/core/v1"
2726
"k8s.io/client-go/tools/cache"
2827
"k8s.io/client-go/tools/record"
2928
"k8s.io/client-go/util/workqueue"
3029

30+
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
3131
"github.com/cert-manager/cert-manager/pkg/acme/accounts"
3232
cmacmelisters "github.com/cert-manager/cert-manager/pkg/client/listers/acme/v1"
3333
cmlisters "github.com/cert-manager/cert-manager/pkg/client/listers/certmanager/v1"
@@ -50,7 +50,7 @@ type controller struct {
5050
challengeLister cmacmelisters.ChallengeLister
5151
issuerLister cmlisters.IssuerLister
5252
clusterIssuerLister cmlisters.ClusterIssuerLister
53-
secretLister corelisters.SecretLister
53+
secretLister internalinformers.SecretLister
5454

5555
// ACME challenge solvers are instantiated once at the time of controller
5656
// construction.
@@ -91,12 +91,12 @@ func (c *controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin
9191
// obtain references to all the informers used by this controller
9292
challengeInformer := ctx.SharedInformerFactory.Acme().V1().Challenges()
9393
issuerInformer := ctx.SharedInformerFactory.Certmanager().V1().Issuers()
94-
secretInformer := ctx.KubeSharedInformerFactory.Core().V1().Secrets()
94+
secretInformer := ctx.KubeSharedInformerFactory.Secrets()
9595
// we register these informers here so the HTTP01 solver has a synced
9696
// cache when managing pod/service/ingress resources
97-
podInformer := ctx.KubeSharedInformerFactory.Core().V1().Pods()
98-
serviceInformer := ctx.KubeSharedInformerFactory.Core().V1().Services()
99-
ingressInformer := ctx.KubeSharedInformerFactory.Networking().V1().Ingresses()
97+
podInformer := ctx.KubeSharedInformerFactory.Pods()
98+
serviceInformer := ctx.KubeSharedInformerFactory.Services()
99+
ingressInformer := ctx.KubeSharedInformerFactory.Ingresses()
100100

101101
// build a list of InformerSynced functions that will be returned by the Register method.
102102
// the controller will only begin processing items once all of these informers have synced.

pkg/controller/acmeorders/controller.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,14 @@ import (
2222

2323
"github.com/go-logr/logr"
2424
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
25-
"k8s.io/client-go/informers"
26-
corelisters "k8s.io/client-go/listers/core/v1"
2725
"k8s.io/client-go/tools/cache"
2826
"k8s.io/client-go/tools/record"
2927
"k8s.io/client-go/util/workqueue"
3028
"k8s.io/utils/clock"
3129

30+
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
3231
"github.com/cert-manager/cert-manager/pkg/acme/accounts"
3332
cmclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned"
34-
cminformers "github.com/cert-manager/cert-manager/pkg/client/informers/externalversions"
3533
cmacmelisters "github.com/cert-manager/cert-manager/pkg/client/listers/acme/v1"
3634
cmlisters "github.com/cert-manager/cert-manager/pkg/client/listers/certmanager/v1"
3735
controllerpkg "github.com/cert-manager/cert-manager/pkg/controller"
@@ -54,7 +52,7 @@ type controller struct {
5452
challengeLister cmacmelisters.ChallengeLister
5553
issuerLister cmlisters.IssuerLister
5654
clusterIssuerLister cmlisters.ClusterIssuerLister
57-
secretLister corelisters.SecretLister
55+
secretLister internalinformers.SecretLister
5856

5957
// used for testing
6058
clock clock.Clock

pkg/controller/certificate-shim/ingresses/controller.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ type controller struct {
4545
func (c *controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitingInterface, []cache.InformerSynced, error) {
4646
cmShared := ctx.SharedInformerFactory
4747

48-
ingressInformer := ctx.KubeSharedInformerFactory.Networking().V1().Ingresses()
48+
ingressInformer := ctx.KubeSharedInformerFactory.Ingresses()
4949
c.ingressLister = ingressInformer.Lister()
5050

5151
log := logf.FromContext(ctx.RootContext, ControllerName)

pkg/controller/certificaterequests/ca/ca.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import (
2323
"fmt"
2424

2525
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
26-
corelisters "k8s.io/client-go/listers/core/v1"
2726

27+
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
2828
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
2929
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
3030
controllerpkg "github.com/cert-manager/cert-manager/pkg/controller"
@@ -46,7 +46,7 @@ type signingFn func([]*x509.Certificate, crypto.Signer, *x509.Certificate) (pki.
4646

4747
type CA struct {
4848
issuerOptions controllerpkg.IssuerOptions
49-
secretsLister corelisters.SecretLister
49+
secretsLister internalinformers.SecretLister
5050

5151
reporter *crutil.Reporter
5252

@@ -67,7 +67,7 @@ func init() {
6767
func NewCA(ctx *controllerpkg.Context) certificaterequests.Issuer {
6868
return &CA{
6969
issuerOptions: ctx.IssuerOptions,
70-
secretsLister: ctx.KubeSharedInformerFactory.Core().V1().Secrets().Lister(),
70+
secretsLister: ctx.KubeSharedInformerFactory.Secrets().Lister(),
7171
reporter: crutil.NewReporter(ctx.Clock, ctx.Recorder),
7272
templateGenerator: pki.GenerateTemplateFromCertificateRequest,
7373
signingFn: pki.SignCSRTemplate,

pkg/controller/certificaterequests/controller.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ import (
2222

2323
"github.com/go-logr/logr"
2424
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
25-
corelisters "k8s.io/client-go/listers/core/v1"
2625
"k8s.io/client-go/tools/cache"
2726
"k8s.io/client-go/tools/record"
2827
"k8s.io/client-go/util/workqueue"
2928
"k8s.io/utils/clock"
3029

30+
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
3131
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
3232
cmclient "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned"
3333
cmlisters "github.com/cert-manager/cert-manager/pkg/client/listers/certmanager/v1"
@@ -72,7 +72,7 @@ type Controller struct {
7272
// we need to wait for Secrets to be synced to avoid a situation where CA issuer's Secret
7373
// is not yet in cached at a time when issuance is attempted,
7474
// more details at https://github.com/cert-manager/cert-manager/issues/5216
75-
secretLister corelisters.SecretLister
75+
secretLister internalinformers.SecretLister
7676

7777
queue workqueue.RateLimitingInterface
7878

@@ -132,7 +132,7 @@ func (c *Controller) Register(ctx *controllerpkg.Context) (workqueue.RateLimitin
132132
// create a queue used to queue up items to be processed
133133
c.queue = workqueue.NewNamedRateLimitingQueue(controllerpkg.DefaultItemBasedRateLimiter(), componentName)
134134

135-
secretsInformer := ctx.KubeSharedInformerFactory.Core().V1().Secrets()
135+
secretsInformer := ctx.KubeSharedInformerFactory.Secrets()
136136
issuerInformer := ctx.SharedInformerFactory.Certmanager().V1().Issuers()
137137
c.issuerLister = issuerInformer.Lister()
138138
c.secretLister = secretsInformer.Lister()

pkg/controller/certificaterequests/selfsigned/selfsigned.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ import (
2525

2626
corev1 "k8s.io/api/core/v1"
2727
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
28-
corelisters "k8s.io/client-go/listers/core/v1"
2928
"k8s.io/client-go/tools/cache"
3029
"k8s.io/client-go/tools/record"
3130
"k8s.io/client-go/util/workqueue"
3231

32+
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
3333
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
3434
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
3535
controllerpkg "github.com/cert-manager/cert-manager/pkg/controller"
@@ -52,7 +52,7 @@ type signingFn func(*x509.Certificate, *x509.Certificate, crypto.PublicKey, inte
5252

5353
type SelfSigned struct {
5454
issuerOptions controllerpkg.IssuerOptions
55-
secretsLister corelisters.SecretLister
55+
secretsLister internalinformers.SecretLister
5656

5757
reporter *crutil.Reporter
5858
recorder record.EventRecorder
@@ -72,7 +72,7 @@ func init() {
7272
// Handle informed Secrets which may be referenced by the
7373
// "cert-manager.io/private-key-secret-name" annotation.
7474
func(ctx *controllerpkg.Context, log logr.Logger, queue workqueue.RateLimitingInterface) ([]cache.InformerSynced, error) {
75-
secretInformer := ctx.KubeSharedInformerFactory.Core().V1().Secrets().Informer()
75+
secretInformer := ctx.KubeSharedInformerFactory.Secrets().Informer()
7676
certificateRequestLister := ctx.SharedInformerFactory.Certmanager().V1().CertificateRequests().Lister()
7777
helper := issuer.NewHelper(
7878
ctx.SharedInformerFactory.Certmanager().V1().Issuers().Lister(),
@@ -95,7 +95,7 @@ func init() {
9595
func NewSelfSigned(ctx *controllerpkg.Context) certificaterequests.Issuer {
9696
return &SelfSigned{
9797
issuerOptions: ctx.IssuerOptions,
98-
secretsLister: ctx.KubeSharedInformerFactory.Core().V1().Secrets().Lister(),
98+
secretsLister: ctx.KubeSharedInformerFactory.Secrets().Lister(),
9999
reporter: crutil.NewReporter(ctx.Clock, ctx.Recorder),
100100
recorder: ctx.Recorder,
101101
signingFn: pki.SignCertificate,

pkg/controller/certificaterequests/vault/vault.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import (
2020
"context"
2121

2222
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
23-
corelisters "k8s.io/client-go/listers/core/v1"
2423

24+
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
2525
vaultinternal "github.com/cert-manager/cert-manager/internal/vault"
2626
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
2727
v1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
@@ -42,7 +42,7 @@ const (
4242
type Vault struct {
4343
issuerOptions controllerpkg.IssuerOptions
4444
createTokenFn func(ns string) vaultinternal.CreateToken
45-
secretsLister corelisters.SecretLister
45+
secretsLister internalinformers.SecretLister
4646
reporter *crutil.Reporter
4747

4848
vaultClientBuilder vaultinternal.ClientBuilder
@@ -64,7 +64,7 @@ func NewVault(ctx *controllerpkg.Context) certificaterequests.Issuer {
6464
createTokenFn: func(ns string) vaultinternal.CreateToken {
6565
return ctx.Client.CoreV1().ServiceAccounts(ns).CreateToken
6666
},
67-
secretsLister: ctx.KubeSharedInformerFactory.Core().V1().Secrets().Lister(),
67+
secretsLister: ctx.KubeSharedInformerFactory.Secrets().Lister(),
6868
reporter: crutil.NewReporter(ctx.Clock, ctx.Recorder),
6969
vaultClientBuilder: vaultinternal.New,
7070
}

pkg/controller/certificaterequests/vault/vault_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,17 @@ import (
3131
corev1 "k8s.io/api/core/v1"
3232
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3333
"k8s.io/apimachinery/pkg/runtime"
34-
corelisters "k8s.io/client-go/listers/core/v1"
3534
coretesting "k8s.io/client-go/testing"
3635
fakeclock "k8s.io/utils/clock/testing"
3736

37+
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
3838
internalvault "github.com/cert-manager/cert-manager/internal/vault"
3939
fakevault "github.com/cert-manager/cert-manager/internal/vault/fake"
4040
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
4141
"github.com/cert-manager/cert-manager/pkg/apis/certmanager"
4242
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
4343
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
44-
"github.com/cert-manager/cert-manager/pkg/controller"
44+
controllerpkg "github.com/cert-manager/cert-manager/pkg/controller"
4545
"github.com/cert-manager/cert-manager/pkg/controller/certificaterequests"
4646
testpkg "github.com/cert-manager/cert-manager/pkg/controller/test"
4747
"github.com/cert-manager/cert-manager/pkg/util/pki"
@@ -518,15 +518,15 @@ func runTest(t *testing.T, test testT) {
518518
vault := NewVault(test.builder.Context).(*Vault)
519519

520520
if test.fakeVault != nil {
521-
vault.vaultClientBuilder = func(ns string, _ func(ns string) internalvault.CreateToken, sl corelisters.SecretLister,
521+
vault.vaultClientBuilder = func(ns string, _ func(ns string) internalvault.CreateToken, sl internalinformers.SecretLister,
522522
iss cmapi.GenericIssuer) (internalvault.Interface, error) {
523523
return test.fakeVault.New(ns, sl, iss)
524524
}
525525
}
526526

527527
controller := certificaterequests.New(
528528
apiutil.IssuerVault,
529-
func(*controller.Context) certificaterequests.Issuer { return vault },
529+
func(*controllerpkg.Context) certificaterequests.Issuer { return vault },
530530
)
531531

532532
if _, _, err := controller.Register(test.builder.Context); err != nil {

pkg/controller/certificaterequests/venafi/venafi.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ import (
2323

2424
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
2525
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26-
corelisters "k8s.io/client-go/listers/core/v1"
2726

2827
"github.com/Venafi/vcert/v4/pkg/endpoint"
2928

29+
internalinformers "github.com/cert-manager/cert-manager/internal/informers"
3030
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
3131
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
3232
clientset "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned"
@@ -47,7 +47,7 @@ const (
4747

4848
type Venafi struct {
4949
issuerOptions controllerpkg.IssuerOptions
50-
secretsLister corelisters.SecretLister
50+
secretsLister internalinformers.SecretLister
5151
reporter *crutil.Reporter
5252
cmClient clientset.Interface
5353

@@ -68,7 +68,7 @@ func init() {
6868
func NewVenafi(ctx *controllerpkg.Context) certificaterequests.Issuer {
6969
return &Venafi{
7070
issuerOptions: ctx.IssuerOptions,
71-
secretsLister: ctx.KubeSharedInformerFactory.Core().V1().Secrets().Lister(),
71+
secretsLister: ctx.KubeSharedInformerFactory.Secrets().Lister(),
7272
reporter: crutil.NewReporter(ctx.Clock, ctx.Recorder),
7373
clientBuilder: venaficlient.New,
7474
metrics: ctx.Metrics,

0 commit comments

Comments
 (0)