Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(test): add unit-test to vcluster controller #49

Merged
merged 1 commit into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
52 changes: 42 additions & 10 deletions controllers/vcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,49 @@ import (
"github.com/loft-sh/cluster-api-provider-vcluster/pkg/util/vclustervalues"
)

type ClientConfigGetter interface {
NewForConfig(restConfig *rest.Config) (kubernetes.Interface, error)
}

type clientConfigGetter struct {
}

func (c *clientConfigGetter) NewForConfig(restConfig *rest.Config) (kubernetes.Interface, error) {
return kubernetes.NewForConfig(restConfig)
}

func NewClientConfigGetter() ClientConfigGetter {
return &clientConfigGetter{}
}

type HTTPClientGetter interface {
ClientFor(r http.RoundTripper, timeout time.Duration) *http.Client
}

type httpClientGetter struct {
}

func (h *httpClientGetter) ClientFor(r http.RoundTripper, timeout time.Duration) *http.Client {
return &http.Client{
Timeout: timeout,
Transport: r,
}
}

func NewHTTPClientGetter() HTTPClientGetter {
return &httpClientGetter{}
}

// VClusterReconciler reconciles a VCluster object
type VClusterReconciler struct {
client.Client
HelmClient helm.Client
HelmSecrets *helm.Secrets
Log logr.Logger
Scheme *runtime.Scheme
clusterKindExists bool
HelmClient helm.Client
HelmSecrets *helm.Secrets
Log logr.Logger
Scheme *runtime.Scheme
ClientConfigGetter ClientConfigGetter
HTTPClientGetter HTTPClientGetter
clusterKindExists bool
}

type Credentials struct {
Expand Down Expand Up @@ -347,7 +382,7 @@ func (r *VClusterReconciler) syncVClusterKubeconfig(ctx context.Context, vCluste
return nil, err
}

kubeClient, err := kubernetes.NewForConfig(restConfig)
kubeClient, err := r.ClientConfigGetter.NewForConfig(restConfig)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -433,10 +468,7 @@ func (r *VClusterReconciler) checkReadyz(vCluster *v1alpha1.VCluster, restConfig
if err != nil {
return false, err
}
client := http.Client{
Timeout: 10 * time.Second,
Transport: transport,
}
client := r.HTTPClientGetter.ClientFor(transport, 10*time.Second)
resp, err := client.Get(fmt.Sprintf("https://%s:%d/readyz", vCluster.Spec.ControlPlaneEndpoint.Host, vCluster.Spec.ControlPlaneEndpoint.Port))
r.Log.V(1).Info("ready check done", "namespace", vCluster.Namespace, "name", vCluster.Name, "duration", time.Since(t))
if err != nil {
Expand Down
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ require (
github.com/golangci/golangci-lint v1.56.2
github.com/loft-sh/log v0.0.0-20240219160058-26d83ffb46ac
github.com/loft-sh/utils v0.0.29
github.com/onsi/ginkgo/v2 v2.15.0
github.com/onsi/gomega v1.31.1
github.com/stretchr/testify v1.8.4
k8s.io/apimachinery v0.26.1
k8s.io/apiserver v0.26.1
k8s.io/client-go v0.26.1
Expand Down Expand Up @@ -84,6 +87,7 @@ require (
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/go-toolsmith/astcast v1.1.0 // indirect
github.com/go-toolsmith/astcopy v1.1.0 // indirect
github.com/go-toolsmith/astequal v1.2.0 // indirect
Expand All @@ -105,6 +109,7 @@ require (
github.com/golangci/revgrep v0.5.2 // indirect
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect
github.com/google/gnostic v0.6.9 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/gordonklaus/ineffassign v0.1.0 // indirect
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
github.com/gostaticanalysis/comment v1.4.2 // indirect
Expand Down Expand Up @@ -183,7 +188,6 @@ require (
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect
github.com/tdakkota/asciicheck v0.2.0 // indirect
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
Expand Down Expand Up @@ -506,7 +507,6 @@ github.com/nunnatsa/ginkgolinter v0.15.2 h1:N2ORxUxPU56R9gsfLIlVVvCv/V/VVou5qVI1
github.com/nunnatsa/ginkgolinter v0.15.2/go.mod h1:oYxE7dt1vZI8cK2rZOs3RgTaBN2vggkqnENmoJ8kVvc=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
Expand Down Expand Up @@ -633,6 +633,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
Expand Down
12 changes: 7 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,13 @@ func main() {
}

if err = (&controllers.VClusterReconciler{
Client: mgr.GetClient(),
HelmClient: helm.NewClient(rawConfig),
HelmSecrets: helm.NewSecrets(mgr.GetClient()),
Log: log,
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
HelmClient: helm.NewClient(rawConfig),
HelmSecrets: helm.NewSecrets(mgr.GetClient()),
Log: log,
Scheme: mgr.GetScheme(),
ClientConfigGetter: controllers.NewClientConfigGetter(),
HTTPClientGetter: controllers.NewHTTPClientGetter(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "VCluster")
os.Exit(1)
Expand Down
115 changes: 115 additions & 0 deletions test/controllerstest/controllers_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package controllerstest

import (
"context"
"testing"
"time"

"github.com/loft-sh/cluster-api-provider-vcluster/api/v1alpha1"
"github.com/loft-sh/cluster-api-provider-vcluster/controllers"
"github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"

fakeclientset "k8s.io/client-go/kubernetes/fake"
ctrl "sigs.k8s.io/controller-runtime"
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
)

var (
kubeconfigBytes = []byte(`
kind: Config
apiVersion: v1
clusters:
- cluster:
api-version: v1
server: https://test:443
certificate-authority: test.crt
name: kubeconfig-cluster
users:
- name: kubeconfig-user
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrakNDQVRlZ0F3SUJBZ0lJT2FQRzhMc21MNWd3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOekE0TURBNE1qRXpNQjRYRFRJME1ESXhOVEUwTkRNek0xb1hEVEkxTURJeApOREUwTkRNek0xb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJDbysyRzRzQ0pjaTVZTlMKMkp6VTd5ZnEzSUR0dE1tcnU2bGtGV2NMR2FJSVRTVDZPbFdzaDdaYkJRb3FrTkk5c3dTOStCWHptV2FOQ1FzRgp1Q0ZaL0F1alNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCUyt0MG1hMFR2ZHN5d2RuVGpYd0ExWis0eFZJakFLQmdncWhrak9QUVFEQWdOSkFEQkcKQWlFQThjZXNlcWhjOFpGU0Z3TERzdDJYUS9lU0xiVWFuNnNYenhFeHFtSlNEbXNDSVFEcDdJWmRJd3FaVmY2WQpQMWRaOWwzeE9JTDFRL2Y5VXdNVC9aOFRaZEZJa2c9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQpNSUlCZGpDQ0FSMmdBd0lCQWdJQkFEQUtCZ2dxaGtqT1BRUURBakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwClpXNTBMV05oUURFM01EZ3dNRGd5TVRNd0hoY05NalF3TWpFMU1UUTBNek16V2hjTk16UXdNakV5TVRRME16TXoKV2pBak1TRXdId1lEVlFRRERCaHJNM010WTJ4cFpXNTBMV05oUURFM01EZ3dNRGd5TVRNd1dUQVRCZ2NxaGtqTwpQUUlCQmdncWhrak9QUU1CQndOQ0FBVCtZbTVnL0o4TzIwQ0llSFB4Z2hRWTBXajl3QVZzc0QxdHRzS0VnMFFRCjA3UDNLZEttV3AzS3BvV3FkdkN4dTNFMkp4ZDBGVDh5eG1IOVJiamVXRW90bzBJd1FEQU9CZ05WSFE4QkFmOEUKQkFNQ0FxUXdEd1lEVlIwVEFRSC9CQVV3QXdFQi96QWRCZ05WSFE0RUZnUVV2cmRKbXRFNzNiTXNIWjA0MThBTgpXZnVNVlNJd0NnWUlLb1pJemowRUF3SURSd0F3UkFJZ1VldS9yVnBmc1NoUUZmSjIyb05CMVhwY1djUWFPY2FBCnF4ZGg0dzhGdHBRQ0lIdmVTRE00clN2V3ZGZktROXRWTDRFZkpUdDc2cWliMFMyY2FBdDQwUHNGCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
client-key-data: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSVBWS2JlQzJua2JaZ1UxZUNaS2NxUHpnSXd0MWxtOGcxZFNRaENoaHRURWVvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFS2o3WWJpd0lseUxsZzFMWW5OVHZKK3JjZ08yMHlhdTdxV1FWWndzWm9naE5KUG82VmF5SAp0bHNGQ2lxUTBqMnpCTDM0RmZPWlpvMEpDd1c0SVZuOEN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
`)
)

func TestRunControllersTests(t *testing.T) {
gomega.RegisterFailHandler(ginkgo.Fail)
ginkgo.RunSpecs(t, "controllers suite")
}

var _ = ginkgo.Describe("Vcluster Controller test", func() {
ginkgo.Context("Reconcile", func() {
var (
reconciler *controllers.VClusterReconciler
ctx context.Context
scheme *runtime.Scheme
hemlClient *MockHelmClient
)

ginkgo.BeforeEach(func() {
scheme = runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)
gomega.Expect(err).NotTo(gomega.HaveOccurred())

err = corev1.AddToScheme(scheme)
gomega.Expect(err).NotTo(gomega.HaveOccurred())

ctx = context.Background()
hemlClient = &MockHelmClient{}
})

ginkgo.It("reconcile successfully", func() {
vCluster := &v1alpha1.VCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "test-vcluster",
Namespace: "default",
},
Spec: v1alpha1.VClusterSpec{},
}
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: vCluster.Namespace,
Name: "vc-test-vcluster",
},
Data: map[string][]byte{
"config": kubeconfigBytes,
},
}
hemlClient.On("Upgrade").Return(nil)
f := fakeclientset.NewSimpleClientset()

_, err := f.CoreV1().ServiceAccounts("default").Create(context.Background(), &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
Namespace: "default",
},
}, metav1.CreateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())

reconciler = &controllers.VClusterReconciler{
Client: fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects(vCluster, secret).Build(),
HelmClient: hemlClient,
Scheme: scheme,
ClientConfigGetter: &fakeConfigGetter{
fake: f,
},
HTTPClientGetter: &fakeHTTPClientGetter{},
}
req := ctrl.Request{
NamespacedName: types.NamespacedName{
Name: vCluster.Name,
Namespace: vCluster.Namespace,
},
}
result, err := reconciler.Reconcile(ctx, req)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(result.RequeueAfter).Should(gomega.Equal(time.Minute))
})
})

})
33 changes: 33 additions & 0 deletions test/controllerstest/fake.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package controllerstest

import (
"fmt"
"net/http"
"time"

"net/http/httptest"

"k8s.io/client-go/kubernetes"
fakeclientset "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/rest"
restfake "k8s.io/client-go/rest/fake"
)

type fakeConfigGetter struct {
fake *fakeclientset.Clientset
}

func (f *fakeConfigGetter) NewForConfig(_ *rest.Config) (kubernetes.Interface, error) {
return f.fake, nil
}

type fakeHTTPClientGetter struct {
}

func (f *fakeHTTPClientGetter) ClientFor(_ http.RoundTripper, _ time.Duration) *http.Client {
return restfake.CreateHTTPClient(func(*http.Request) (*http.Response, error) {
recorder := httptest.NewRecorder()
fmt.Fprint(recorder, "ok")
return recorder.Result(), nil
})
}
35 changes: 35 additions & 0 deletions test/controllerstest/mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package controllerstest

import (
"github.com/loft-sh/cluster-api-provider-vcluster/pkg/helm"
"github.com/stretchr/testify/mock"
)

type MockHelmClient struct {
mock.Mock
}

func (m *MockHelmClient) Install(_, _ string, _ helm.UpgradeOptions) error {
args := m.Called()
return args.Error(0)
}

func (m *MockHelmClient) Upgrade(_, _ string, _ helm.UpgradeOptions) error {
args := m.Called()
return args.Error(0)
}

func (m *MockHelmClient) Rollback(_, _ string, _ string) error {
args := m.Called()
return args.Error(0)
}

func (m *MockHelmClient) Delete(_, _ string) error {
args := m.Called()
return args.Error(0)
}

func (m *MockHelmClient) Exists(_, _ string) (bool, error) {
args := m.Called()
return args.Bool(0), args.Error(1)
}
Loading
Loading