Skip to content

Commit

Permalink
Add 'GetPackageRepositories' impl (#3792)
Browse files Browse the repository at this point in the history
* Add 'GetPackageRepositories' impl

Signed-off-by: Antonio Gamez Diaz <agamez@vmware.com>

* Fix nil pointer

Signed-off-by: Antonio Gamez Diaz <agamez@vmware.com>

* Add TestGetPackageRepositories

Signed-off-by: Antonio Gamez Diaz <agamez@vmware.com>
  • Loading branch information
antgamdia committed Nov 24, 2021
1 parent f6098a0 commit a7acfa7
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,42 @@
// limitations under the License.
// */
package main

import (
"context"
"fmt"

"github.com/kubeapps/kubeapps/cmd/kubeapps-apis/gen/plugins/kapp_controller/packages/v1alpha1"
log "k8s.io/klog/v2"
)

// GetPackageRepositories returns the package repositories based on the request managed by the 'kapp_controller' plugin
func (s *Server) GetPackageRepositories(ctx context.Context, request *v1alpha1.GetPackageRepositoriesRequest) (*v1alpha1.GetPackageRepositoriesResponse, error) {
log.Infof("+kapp-controller GetPackageRepositories")

namespace := request.GetContext().GetNamespace()
cluster := request.GetContext().GetCluster()
if cluster == "" {
cluster = s.globalPackagingCluster
}

// retrieve the list of installed packages
pkgRepositories, err := s.getPkgRepositories(ctx, cluster, namespace)
if err != nil {
return nil, errorByStatus("get", "PackageRepository", "", err)
}

// convert the Carvel PackageRepository to our API PackageRepository struct
responseRepos := []*v1alpha1.PackageRepository{}
for _, repo := range pkgRepositories {
repo, err := getPackageRepository(repo)
if err != nil {
return nil, fmt.Errorf("unable to create the PackageRepository: %v", err)
}
responseRepos = append(responseRepos, repo)
}

return &v1alpha1.GetPackageRepositoriesResponse{
Repositories: responseRepos,
}, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"time"

corev1 "github.com/kubeapps/kubeapps/cmd/kubeapps-apis/gen/core/packages/v1alpha1"
"github.com/kubeapps/kubeapps/cmd/kubeapps-apis/gen/plugins/kapp_controller/packages/v1alpha1"
kappctrlv1alpha1 "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apis/kappctrl/v1alpha1"
packagingv1alpha1 "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apis/packaging/v1alpha1"
datapackagingv1alpha1 "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apiserver/apis/datapackaging/v1alpha1"
Expand Down Expand Up @@ -321,6 +322,37 @@ func (s *Server) getInstalledPackageDetail(pkgInstall *packagingv1alpha1.Package
return installedPackageDetail, nil
}

func getPackageRepository(pr *packagingv1alpha1.PackageRepository) (*v1alpha1.PackageRepository, error) {
// See the PackageRepository CR at
// https://carvel.dev/kapp-controller/docs/latest/packaging/#packagerepository-cr

repoURL := ""

// TODO(agamez): this is a temporary solution
if pr.Spec.Fetch != nil && pr.Spec.Fetch.ImgpkgBundle != nil {
repoURL = pr.Spec.Fetch.ImgpkgBundle.Image
} else if pr.Spec.Fetch != nil && pr.Spec.Fetch.Image != nil {
repoURL = pr.Spec.Fetch.Image.URL
} else if pr.Spec.Fetch != nil && pr.Spec.Fetch.HTTP != nil {
repoURL = pr.Spec.Fetch.HTTP.URL
} else if pr.Spec.Fetch != nil && pr.Spec.Fetch.Git != nil {
repoURL = pr.Spec.Fetch.Git.URL
}

if repoURL == "" {
return nil, fmt.Errorf("packagerepository without fetch of one of imgpkgBundle, image, http or git: %v", pr)
}

repo := &v1alpha1.PackageRepository{
Name: pr.Name,
Namespace: pr.Namespace,
Url: repoURL,
Plugin: &pluginDetail,
}

return repo, nil
}

func (s *Server) newSecret(installedPackageName, values, targetNamespace string) (*k8scorev1.Secret, error) {
return &k8scorev1.Secret{
TypeMeta: metav1.TypeMeta{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import (

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/kubeapps/kubeapps/cmd/kubeapps-apis/gen/core/packages/v1alpha1"
corev1 "github.com/kubeapps/kubeapps/cmd/kubeapps-apis/gen/core/packages/v1alpha1"
pluginv1 "github.com/kubeapps/kubeapps/cmd/kubeapps-apis/gen/core/plugins/v1alpha1"
"github.com/kubeapps/kubeapps/cmd/kubeapps-apis/gen/plugins/kapp_controller/packages/v1alpha1"
kappctrlv1alpha1 "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apis/kappctrl/v1alpha1"
packagingv1alpha1 "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apis/packaging/v1alpha1"
datapackagingv1alpha1 "github.com/vmware-tanzu/carvel-kapp-controller/pkg/apiserver/apis/datapackaging/v1alpha1"
Expand All @@ -45,19 +45,20 @@ var ignoreUnexported = cmpopts.IgnoreUnexported(
corev1.AvailablePackageDetail{},
corev1.AvailablePackageReference{},
corev1.AvailablePackageSummary{},
corev1.InstalledPackageSummary{},
corev1.Context{},
corev1.CreateInstalledPackageResponse{},
corev1.DeleteInstalledPackageResponse{},
corev1.InstalledPackageDetail{},
corev1.InstalledPackageReference{},
corev1.InstalledPackageStatus{},
corev1.InstalledPackageDetail{},
corev1.InstalledPackageSummary{},
corev1.Maintainer{},
corev1.PackageAppVersion{},
corev1.ReconciliationOptions{},
corev1.CreateInstalledPackageResponse{},
corev1.DeleteInstalledPackageResponse{},
corev1.UpdateInstalledPackageResponse{},
corev1.VersionReference{},
corev1.Context{},
corev1.Maintainer{},
corev1.PackageAppVersion{},
pluginv1.Plugin{},
v1alpha1.PackageRepository{},
)

var defaultContext = &corev1.Context{Cluster: "default", Namespace: "default"}
Expand Down Expand Up @@ -653,7 +654,7 @@ func TestGetAvailablePackageDetail(t *testing.T) {
AvailablePackageRef: &corev1.AvailablePackageReference{
Context: defaultContext,
Identifier: "tetris.foo.example.com",
Plugin: &pluginv1.Plugin{Name: "kapp_controller.packages", Version: "v1alpha1"},
Plugin: &pluginDetail,
},
},
statusCode: codes.OK,
Expand Down Expand Up @@ -704,7 +705,7 @@ func TestGetAvailablePackageDetail(t *testing.T) {
Version: &corev1.PackageAppVersion{
PkgVersion: "1.2.3",
},
Maintainers: []*v1alpha1.Maintainer{},
Maintainers: []*corev1.Maintainer{},
Readme: fmt.Sprintf(`## Details
Expand Down Expand Up @@ -733,7 +734,7 @@ func TestGetAvailablePackageDetail(t *testing.T) {
AvailablePackageRef: &corev1.AvailablePackageReference{
Context: defaultContext,
Identifier: "tetris.foo.example.com",
Plugin: &pluginv1.Plugin{Name: "kapp_controller.packages", Version: "v1alpha1"},
Plugin: &pluginDetail,
},
},
statusCode: codes.OK,
Expand Down Expand Up @@ -2442,3 +2443,114 @@ func TestDeleteInstalledPackage(t *testing.T) {
})
}
}

func TestGetPackageRepositories(t *testing.T) {
testCases := []struct {
name string
request *v1alpha1.GetPackageRepositoriesRequest
existingObjects []runtime.Object
expectedResponse []*v1alpha1.PackageRepository
expectedStatusCode codes.Code
}{
{
name: "returns expected repositories",
request: &v1alpha1.GetPackageRepositoriesRequest{
Context: &corev1.Context{
Cluster: "default",
Namespace: "default",
},
},
existingObjects: []runtime.Object{
&packagingv1alpha1.PackageRepository{
TypeMeta: metav1.TypeMeta{
Kind: pkgRepositoryResource,
APIVersion: packagingAPIVersion,
},
ObjectMeta: metav1.ObjectMeta{
Name: "repo-1",
Namespace: "default",
},
Spec: packagingv1alpha1.PackageRepositorySpec{
Fetch: &packagingv1alpha1.PackageRepositoryFetch{
ImgpkgBundle: &kappctrlv1alpha1.AppFetchImgpkgBundle{
Image: "projects.registry.example.com/repo-1/main@sha256:abcd",
},
},
},
Status: packagingv1alpha1.PackageRepositoryStatus{},
},
&packagingv1alpha1.PackageRepository{
TypeMeta: metav1.TypeMeta{
Kind: pkgRepositoryResource,
APIVersion: packagingAPIVersion,
},
ObjectMeta: metav1.ObjectMeta{
Name: "repo-2",
Namespace: "default",
},
Spec: packagingv1alpha1.PackageRepositorySpec{
Fetch: &packagingv1alpha1.PackageRepositoryFetch{
ImgpkgBundle: &kappctrlv1alpha1.AppFetchImgpkgBundle{
Image: "projects.registry.example.com/repo-2/main@sha256:abcd",
},
},
},
Status: packagingv1alpha1.PackageRepositoryStatus{},
},
},
expectedResponse: []*v1alpha1.PackageRepository{
{
Name: "repo-1",
Url: "projects.registry.example.com/repo-1/main@sha256:abcd",
Namespace: "default",
Plugin: &pluginDetail,
},
{
Name: "repo-2",
Url: "projects.registry.example.com/repo-2/main@sha256:abcd",
Namespace: "default",
Plugin: &pluginDetail,
},
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var unstructuredObjects []runtime.Object
for _, obj := range tc.existingObjects {
unstructuredContent, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
unstructuredObjects = append(unstructuredObjects, &unstructured.Unstructured{Object: unstructuredContent})
}

s := Server{
clientGetter: func(context.Context, string) (kubernetes.Interface, dynamic.Interface, error) {
return nil, dynfake.NewSimpleDynamicClientWithCustomListKinds(
runtime.NewScheme(),
map[schema.GroupVersionResource]string{
{Group: packagingv1alpha1.SchemeGroupVersion.Group, Version: packagingv1alpha1.SchemeGroupVersion.Version, Resource: pkgRepositoriesResource}: pkgRepositoryResource + "List",
},
unstructuredObjects...,
), nil
},
}

getPackageRepositoriesResponse, err := s.GetPackageRepositories(context.Background(), tc.request)

if got, want := status.Code(err), tc.expectedStatusCode; got != want {
t.Fatalf("got: %d, want: %d, err: %+v", got, want, err)
}

// Only check the response for OK status.
if tc.expectedStatusCode == codes.OK {
if getPackageRepositoriesResponse == nil {
t.Fatalf("got: nil, want: response")
} else {
if got, want := getPackageRepositoriesResponse.Repositories, tc.expectedResponse; !cmp.Equal(got, want, ignoreUnexported) {
t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(want, got, ignoreUnexported))
}
}
}
})
}
}

0 comments on commit a7acfa7

Please sign in to comment.