From 249845aea65278592eaf238a797dc8a5b7584deb Mon Sep 17 00:00:00 2001 From: Manabu McCloskey Date: Mon, 22 Apr 2024 23:14:54 +0000 Subject: [PATCH] add tests and consts Signed-off-by: Manabu McCloskey --- api/v1alpha1/gitrepository_types.go | 8 +- pkg/controllers/custompackage/controller.go | 7 +- .../custompackage/controller_test.go | 9 +- pkg/controllers/gitrepository/controller.go | 9 +- .../gitrepository/git_repository.go | 7 +- pkg/controllers/gitrepository/gitea.go | 2 +- pkg/controllers/gitrepository/github.go | 56 ++--- pkg/controllers/gitrepository/github_test.go | 197 ++++++++++++++++++ pkg/controllers/localbuild/controller.go | 7 +- pkg/controllers/localbuild/gitea.go | 2 +- .../idpbuilder.cnoe.io_gitrepositories.yaml | 3 + 11 files changed, 261 insertions(+), 46 deletions(-) create mode 100644 pkg/controllers/gitrepository/github_test.go diff --git a/api/v1alpha1/gitrepository_types.go b/api/v1alpha1/gitrepository_types.go index df671fb8..457c110c 100644 --- a/api/v1alpha1/gitrepository_types.go +++ b/api/v1alpha1/gitrepository_types.go @@ -5,8 +5,9 @@ import ( ) const ( - GitProviderGitea = "gitea" - GitProviderGitHub = "github" + GitProviderGitea = "gitea" + GitProviderGitHub = "github" + GiteaAdminUserName = "giteaAdmin" ) type GitRepositorySpec struct { @@ -42,7 +43,8 @@ type Provider struct { // +kubebuilder:validation:Pattern=`^https?:\/\/.+$` GitURL string `json:"gitURL"` // InternalGitURL is the base URL of Git server accessible within the cluster only. - InternalGitURL string `json:"internalGitURL"` + InternalGitURL string `json:"internalGitURL"` + OrganizationName string `json:"organizationName"` } type SecretReference struct { diff --git a/pkg/controllers/custompackage/controller.go b/pkg/controllers/custompackage/controller.go index 52918c1f..a0c65685 100644 --- a/pkg/controllers/custompackage/controller.go +++ b/pkg/controllers/custompackage/controller.go @@ -207,9 +207,10 @@ func (r *Reconciler) reconcileGitRepo(ctx context.Context, resource *v1alpha1.Cu Path: absPath, }, Provider: v1alpha1.Provider{ - Name: v1alpha1.GitProviderGitea, - GitURL: resource.Spec.GitServerURL, - InternalGitURL: resource.Spec.InternalGitServeURL, + Name: v1alpha1.GitProviderGitea, + GitURL: resource.Spec.GitServerURL, + InternalGitURL: resource.Spec.InternalGitServeURL, + OrganizationName: v1alpha1.GiteaAdminUserName, }, SecretRef: resource.Spec.GitServerAuthSecretRef, } diff --git a/pkg/controllers/custompackage/controller_test.go b/pkg/controllers/custompackage/controller_test.go index 62c632ec..6e9afa88 100644 --- a/pkg/controllers/custompackage/controller_test.go +++ b/pkg/controllers/custompackage/controller_test.go @@ -13,6 +13,7 @@ import ( argov1alpha1 "github.com/cnoe-io/argocd-api/api/argo/application/v1alpha1" "github.com/cnoe-io/idpbuilder/api/v1alpha1" + "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8sruntime "k8s.io/apimachinery/pkg/runtime" @@ -172,12 +173,14 @@ func TestReconcileCustomPkg(t *testing.T) { Path: p, }, Provider: v1alpha1.Provider{ - Name: v1alpha1.GitProviderGitea, - GitURL: "https://cnoe.io", - InternalGitURL: "http://internal.cnoe.io", + Name: v1alpha1.GitProviderGitea, + GitURL: "https://cnoe.io", + InternalGitURL: "http://internal.cnoe.io", + OrganizationName: v1alpha1.GiteaAdminUserName, }, }, } + assert.Equal(t, repo.Spec, expectedRepo.Spec) ok := reflect.DeepEqual(repo.Spec, expectedRepo.Spec) if !ok { t.Fatalf("expected spec does not match") diff --git a/pkg/controllers/gitrepository/controller.go b/pkg/controllers/gitrepository/controller.go index c4c4ef81..1de085e0 100644 --- a/pkg/controllers/gitrepository/controller.go +++ b/pkg/controllers/gitrepository/controller.go @@ -51,7 +51,7 @@ func getRepositoryName(repo v1alpha1.GitRepository) string { } func getOrganizationName(repo v1alpha1.GitRepository) string { - return "giteaAdmin" + return repo.Spec.Provider.OrganizationName } func GetGitProvider(ctx context.Context, repo *v1alpha1.GitRepository, kubeClient client.Client, scheme *runtime.Scheme, tmplConfig util.CorePackageTemplateConfig) (gitProvider, error) { @@ -73,9 +73,10 @@ func GetGitProvider(ctx context.Context, repo *v1alpha1.GitRepository, kubeClien }, nil case v1alpha1.GitProviderGitHub: return &gitHubProvider{ - Client: kubeClient, - Scheme: scheme, - config: tmplConfig, + Client: kubeClient, + Scheme: scheme, + config: tmplConfig, + gitHubClient: newGitHubClient(nil), }, nil } return nil, fmt.Errorf("invalid git provider %s ", repo.Spec.Provider.Name) diff --git a/pkg/controllers/gitrepository/git_repository.go b/pkg/controllers/gitrepository/git_repository.go index 5e30936b..cc4bbf08 100644 --- a/pkg/controllers/gitrepository/git_repository.go +++ b/pkg/controllers/gitrepository/git_repository.go @@ -20,9 +20,10 @@ type GiteaClient interface { SetContext(ctx context.Context) } -type gitHubClient interface { - getRepo(ctx context.Context, owner, repo string) (*github.Repository, error) - createRepo(ctx context.Context, owner, repo string) (*github.Repository, error) +type gitHubClientI interface { + getRepo(ctx context.Context, owner, repo string) (*github.Repository, *github.Response, error) + createRepo(ctx context.Context, owner string, req *github.Repository) (*github.Repository, *github.Response, error) + setToken(token string) error } type repoInfo struct { diff --git a/pkg/controllers/gitrepository/gitea.go b/pkg/controllers/gitrepository/gitea.go index 0155b461..8a5b3b65 100644 --- a/pkg/controllers/gitrepository/gitea.go +++ b/pkg/controllers/gitrepository/gitea.go @@ -143,5 +143,5 @@ func NewGiteaClient(url string, options ...gitea.ClientOption) (GiteaClient, err } func getInternalGiteaRepositoryURL(namespace, name, baseUrl string) string { - return fmt.Sprintf("%s/giteaAdmin/%s-%s.git", baseUrl, namespace, name) + return fmt.Sprintf("%s/%s/%s-%s.git", baseUrl, v1alpha1.GiteaAdminUserName, namespace, name) } diff --git a/pkg/controllers/gitrepository/github.go b/pkg/controllers/gitrepository/github.go index e3cbdadf..6766f4ba 100644 --- a/pkg/controllers/gitrepository/github.go +++ b/pkg/controllers/gitrepository/github.go @@ -19,42 +19,39 @@ const ( ) type ghClient struct { - client *github.Client + c *github.Client } -func (g *ghClient) getRepo(ctx context.Context, owner, repo string) (*github.Repository, error) { - r, resp, err := g.client.Repositories.Get(ctx, owner, repo) - if resp.StatusCode == http.StatusNotFound { - return nil, notFoundError{} - } - return r, err +func (g *ghClient) getRepo(ctx context.Context, owner, repo string) (*github.Repository, *github.Response, error) { + return g.c.Repositories.Get(ctx, owner, repo) } -func (g *ghClient) createRepo(ctx context.Context, owner, repo string) (*github.Repository, error) { - r := github.Repository{ - Name: &repo, - Private: github.Bool(false), - } - rp, _, err := g.client.Repositories.Create(ctx, owner, &r) - if err != nil { - return nil, err - } - return rp, nil +func (g *ghClient) createRepo(ctx context.Context, owner string, req *github.Repository) (*github.Repository, *github.Response, error) { + return g.c.Repositories.Create(ctx, owner, req) +} + +func (g *ghClient) setToken(token string) error { + g.c = g.c.WithAuthToken(token) + return nil } type gitHubProvider struct { client.Client Scheme *runtime.Scheme - gitHubClient gitHubClient + gitHubClient gitHubClientI config util.CorePackageTemplateConfig } func (g *gitHubProvider) createRepository(ctx context.Context, repo *v1alpha1.GitRepository) (repoInfo, error) { - - r, err := g.gitHubClient.createRepo(ctx, getOrganizationName(*repo), getRepositoryName(*repo)) + req := github.Repository{ + Name: github.String(getRepositoryName(*repo)), + Private: github.Bool(true), + } + r, _, err := g.gitHubClient.createRepo(ctx, getOrganizationName(*repo), &req) if err != nil { return repoInfo{}, fmt.Errorf("creating repo: %w", err) } + return repoInfo{ name: *r.Name, cloneUrl: *r.CloneURL, @@ -64,11 +61,15 @@ func (g *gitHubProvider) createRepository(ctx context.Context, repo *v1alpha1.Gi } func (g *gitHubProvider) getRepository(ctx context.Context, repo *v1alpha1.GitRepository) (repoInfo, error) { - - r, err := g.gitHubClient.createRepo(ctx, getOrganizationName(*repo), getRepositoryName(*repo)) + r, resp, err := g.gitHubClient.getRepo(ctx, getOrganizationName(*repo), getRepositoryName(*repo)) if err != nil { - return repoInfo{}, fmt.Errorf("creating repo: %w", err) + if resp != nil && resp.StatusCode == http.StatusNotFound { + return repoInfo{}, notFoundError{} + } else { + return repoInfo{}, fmt.Errorf("getting repo: %w", err) + } } + return repoInfo{ name: *r.Name, cloneUrl: *r.CloneURL, @@ -98,10 +99,15 @@ func (g *gitHubProvider) getProviderCredentials(ctx context.Context, repo *v1alp } func (g *gitHubProvider) setProviderCredentials(ctx context.Context, repo *v1alpha1.GitRepository, creds gitProviderCredentials) error { - g.gitHubClient = &ghClient{client: github.NewClient(nil).WithAuthToken(creds.accessToken)} - return nil + return g.gitHubClient.setToken(creds.accessToken) } func (g *gitHubProvider) updateRepoContent(ctx context.Context, repo *v1alpha1.GitRepository, repoInfo repoInfo, creds gitProviderCredentials) error { return updateRepoContent(ctx, repo, repoInfo, creds, g.Scheme, g.config) } + +func newGitHubClient(httpClient *http.Client) gitHubClientI { + return &ghClient{ + c: github.NewClient(httpClient), + } +} diff --git a/pkg/controllers/gitrepository/github_test.go b/pkg/controllers/gitrepository/github_test.go new file mode 100644 index 00000000..416b9be6 --- /dev/null +++ b/pkg/controllers/gitrepository/github_test.go @@ -0,0 +1,197 @@ +package gitrepository + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/cnoe-io/idpbuilder/api/v1alpha1" + "github.com/google/go-github/v61/github" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type fakeGH struct { + mock.Mock +} + +func (f *fakeGH) getRepo(ctx context.Context, owner, repo string) (*github.Repository, *github.Response, error) { + args := f.Called(ctx, owner, repo) + return args.Get(0).(*github.Repository), args.Get(1).(*github.Response), args.Error(2) +} + +func (f *fakeGH) createRepo(ctx context.Context, owner string, req *github.Repository) (*github.Repository, *github.Response, error) { + args := f.Called(ctx, owner, req) + return args.Get(0).(*github.Repository), args.Get(1).(*github.Response), args.Error(2) +} + +func (f *fakeGH) setToken(token string) error { + return nil +} + +func newResponse(r http.Response) *github.Response { + response := &github.Response{Response: &r} + return response +} + +type fakeKubeClient struct { + mock.Mock + client.Client +} + +func (f *fakeKubeClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { + args := f.Called(ctx, key, obj, opts) + return args.Error(0) +} + +func TestGitHubCreateRepository(t *testing.T) { + fakeGH := new(fakeGH) + ctx := context.Background() + gh := gitHubProvider{ + Client: &fakeClient{}, + gitHubClient: fakeGH, + } + repoExpected := repoInfo{ + name: "repo1", + cloneUrl: "", + internalGitRepositoryUrl: "", + fullName: "owner/test-test", + } + resource := v1alpha1.GitRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: v1alpha1.GitRepositorySpec{ + Source: v1alpha1.GitRepositorySource{ + Path: "ac", + Type: "local", + }, + Provider: v1alpha1.Provider{ + Name: "github", + OrganizationName: "owner", + }, + }, + } + + expectedInput := &github.Repository{ + Name: github.String(getRepositoryName(resource)), + Private: github.Bool(true), + } + + fakeGH.On("createRepo", ctx, "owner", expectedInput).Return( + &github.Repository{ + Name: &repoExpected.name, + CloneURL: &repoExpected.cloneUrl, + FullName: &repoExpected.fullName, + }, + newResponse(http.Response{StatusCode: http.StatusOK}), + nil, + ) + + resp, err := gh.createRepository(ctx, &resource) + assert.Nil(t, err) + assert.Equal(t, repoExpected, resp) + fakeGH.AssertExpectations(t) +} + +func TestGitHubGetProviderCredentials(t *testing.T) { + fakeK8sClient := new(fakeKubeClient) + ctx := context.Background() + gh := gitHubProvider{ + Client: fakeK8sClient, + } + + resource := v1alpha1.GitRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: v1alpha1.GitRepositorySpec{ + SecretRef: v1alpha1.SecretReference{ + Name: "test", + Namespace: "testNS", + }, + }, + } + inputSecret := &v1.Secret{} + fakeK8sClient.On("Get", ctx, types.NamespacedName{ + Namespace: "testNS", + Name: "test", + }, inputSecret, []client.GetOption(nil)).Run(func(args mock.Arguments) { + sec := args.Get(2).(*v1.Secret) + sec.Data = make(map[string][]byte, 1) + sec.Data[gitHubTokenKey] = []byte("token") + }).Return(nil) + + creds, err := gh.getProviderCredentials(ctx, &resource) + assert.Nil(t, err) + assert.Equal(t, creds.accessToken, "token") + fakeK8sClient.AssertExpectations(t) + +} + +func TestGitHubGetRepository(t *testing.T) { + fakeGH := new(fakeGH) + ctx := context.Background() + gh := gitHubProvider{ + Client: &fakeClient{}, + gitHubClient: fakeGH, + } + + repoExpected := repoInfo{ + name: "repo1", + cloneUrl: "", + internalGitRepositoryUrl: "", + fullName: "owner/test-test", + } + + fakeGetRepo := fakeGH.On("getRepo", ctx, "owner", "test-test").Return( + &github.Repository{ + Name: &repoExpected.name, + CloneURL: &repoExpected.cloneUrl, + FullName: &repoExpected.fullName, + }, + newResponse(http.Response{StatusCode: http.StatusOK}), + nil, + ) + + resource := v1alpha1.GitRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: v1alpha1.GitRepositorySpec{ + Source: v1alpha1.GitRepositorySource{ + Path: "ac", + Type: "local", + }, + Provider: v1alpha1.Provider{ + Name: "github", + OrganizationName: "owner", + }, + }, + } + + resp, err := gh.getRepository(ctx, &resource) + assert.Nil(t, err) + assert.Equal(t, repoExpected, resp) + fakeGH.AssertExpectations(t) + + fakeGetRepo.Unset() + fakeGH.On("getRepo", ctx, "owner", "test-test").Return( + &github.Repository{}, + newResponse(http.Response{StatusCode: http.StatusNotFound}), + fmt.Errorf("some error"), + ) + + resp, err = gh.getRepository(ctx, &resource) + assert.Equal(t, notFoundError{}, err) + assert.Equal(t, repoInfo{}, resp) + fakeGH.AssertExpectations(t) +} diff --git a/pkg/controllers/localbuild/controller.go b/pkg/controllers/localbuild/controller.go index 19a2745e..dc9b4259 100644 --- a/pkg/controllers/localbuild/controller.go +++ b/pkg/controllers/localbuild/controller.go @@ -385,9 +385,10 @@ func (r *LocalbuildReconciler) reconcileGitRepo(ctx context.Context, resource *v Type: repoType, }, Provider: v1alpha1.Provider{ - Name: v1alpha1.GitProviderGitea, - GitURL: resource.Status.Gitea.ExternalURL, - InternalGitURL: resource.Status.Gitea.InternalURL, + Name: v1alpha1.GitProviderGitea, + GitURL: resource.Status.Gitea.ExternalURL, + InternalGitURL: resource.Status.Gitea.InternalURL, + OrganizationName: v1alpha1.GiteaAdminUserName, }, SecretRef: v1alpha1.SecretReference{ Name: resource.Status.Gitea.AdminUserSecretName, diff --git a/pkg/controllers/localbuild/gitea.go b/pkg/controllers/localbuild/gitea.go index 38e6453d..58cd5efc 100644 --- a/pkg/controllers/localbuild/gitea.go +++ b/pkg/controllers/localbuild/gitea.go @@ -49,7 +49,7 @@ func newGiteAdminSecret() (corev1.Secret, error) { Namespace: giteaNamespace, }, StringData: map[string]string{ - "username": "giteaAdmin", + "username": v1alpha1.GiteaAdminUserName, "password": pass, }, }, nil diff --git a/pkg/controllers/resources/idpbuilder.cnoe.io_gitrepositories.yaml b/pkg/controllers/resources/idpbuilder.cnoe.io_gitrepositories.yaml index a3dec0d0..b334960a 100644 --- a/pkg/controllers/resources/idpbuilder.cnoe.io_gitrepositories.yaml +++ b/pkg/controllers/resources/idpbuilder.cnoe.io_gitrepositories.yaml @@ -67,10 +67,13 @@ spec: - gitea - github type: string + organizationName: + type: string required: - gitURL - internalGitURL - name + - organizationName type: object secretRef: description: SecretRef is the reference to secret that contain Git