Skip to content

Commit

Permalink
Create AppRepository and secret in specific namespace, defaulting to … (
Browse files Browse the repository at this point in the history
#1499)

* Create AppRepository and secret in specific namespace, defaulting to kubeapps
  • Loading branch information
absoludity authored Feb 4, 2020
1 parent 1dcabc7 commit 5c94484
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 8 deletions.
30 changes: 30 additions & 0 deletions chart/kubeapps/templates/apprepository-rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ rules:
- jobs
verbs:
- create
---
# Kubeapps can read and watch its own AppRepository resources cluster-wide.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: "kubeapps:controller:apprepository-reader-{{ .Release.Namespace }}"
labels:
app: {{ template "kubeapps.apprepository.fullname" . }}
chart: {{ template "kubeapps.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
rules:
- apiGroups:
- kubeapps.com
resources:
Expand Down Expand Up @@ -61,6 +73,24 @@ subjects:
name: {{ template "kubeapps.apprepository.fullname" . }}
namespace: {{ .Release.Namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: "kubeapps:controller:apprepository-reader-{{ .Release.Namespace }}"
labels:
app: {{ template "kubeapps.apprepository.fullname" . }}
chart: {{ template "kubeapps.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: "kubeapps:controller:apprepository-reader-{{ .Release.Namespace }}"
subjects:
- kind: ServiceAccount
name: {{ template "kubeapps.apprepository.fullname" . }}
namespace: {{ .Release.Namespace }}
---
# Define role, but no binding, so users can be bound to this role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
Expand Down
13 changes: 10 additions & 3 deletions pkg/apprepo/apprepos_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type appRepositoryRequest struct {

type appRepositoryRequestDetails struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
RepoURL string `json:"repoURL"`
AuthHeader string `json:"authHeader"`
CustomCA string `json:"customCA"`
Expand Down Expand Up @@ -162,10 +163,14 @@ func (a *appRepositoriesHandler) Create(w http.ResponseWriter, req *http.Request
}

appRepo := appRepositoryForRequest(appRepoRequest)
if appRepo.ObjectMeta.Namespace == "" {
appRepo.ObjectMeta.Namespace = a.kubeappsNamespace
}

// TODO(mnelson): validate both required data and request for index
// https://github.com/kubeapps/kubeapps/issues/1330

appRepo, err = clientset.KubeappsV1alpha1().AppRepositories(a.kubeappsNamespace).Create(appRepo)
appRepo, err = clientset.KubeappsV1alpha1().AppRepositories(appRepo.ObjectMeta.Namespace).Create(appRepo)

if err != nil {
if statusErr, ok := err.(*errors.StatusError); ok {
Expand Down Expand Up @@ -238,7 +243,8 @@ func appRepositoryForRequest(appRepoRequest appRepositoryRequest) *v1alpha1.AppR

return &v1alpha1.AppRepository{
ObjectMeta: metav1.ObjectMeta{
Name: appRepo.Name,
Name: appRepo.Name,
Namespace: appRepo.Namespace,
},
Spec: v1alpha1.AppRepositorySpec{
URL: appRepo.RepoURL,
Expand Down Expand Up @@ -267,7 +273,8 @@ func secretForRequest(appRepoRequest appRepositoryRequest, appRepo *v1alpha1.App
blockOwnerDeletion := true
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretNameForRepo(appRepo.Name),
Name: secretNameForRepo(appRepo.Name),
Namespace: appRepoDetails.Namespace,
OwnerReferences: []metav1.OwnerReference{
metav1.OwnerReference{
APIVersion: "kubeapps.com/v1alpha1",
Expand Down
57 changes: 52 additions & 5 deletions pkg/apprepo/apprepos_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,17 @@ func TestAppRepositoryCreate(t *testing.T) {
expectedCode int
}{
{
name: "it creates an app repository",
name: "it creates an app repository in the default kubeappsNamespace",
kubeappsNamespace: "kubeapps",
requestData: `{"appRepository": {"name": "test-repo", "url": "http://example.com/test-repo"}}`,
expectedCode: http.StatusCreated,
},
{
name: "it creates an app repository in a specific namespace",
kubeappsNamespace: "kubeapps",
requestData: `{"appRepository": {"name": "test-repo", "url": "http://example.com/test-repo", "namespace": "my-namespace"}}`,
expectedCode: http.StatusCreated,
},
{
name: "it creates an app repository with an empty template",
kubeappsNamespace: "kubeapps",
Expand Down Expand Up @@ -111,7 +117,7 @@ func TestAppRepositoryCreate(t *testing.T) {
expectedCode: http.StatusBadRequest,
},
{
name: "it results in an Unauthorized response if the kubeapps namespace is not set",
name: "it results in an Unauthorized response if neither the kubeapps namespace or a specific namespace is set",
requestData: `{"appRepository": {"name": "bitnami"}}`,
kubeappsNamespace: "",
expectedCode: http.StatusUnauthorized,
Expand Down Expand Up @@ -142,7 +148,7 @@ func TestAppRepositoryCreate(t *testing.T) {
handler.Create(response, req)

if got, want := response.Code, tc.expectedCode; got != want {
t.Errorf("got: %d, want: %d", got, want)
t.Errorf("got: %d, want: %d\nBody: %s", got, want, response.Body)
}

if response.Code == 201 {
Expand All @@ -154,9 +160,13 @@ func TestAppRepositoryCreate(t *testing.T) {

// Ensure the expected AppRepository is stored
requestAppRepo := appRepositoryForRequest(appRepoRequest)
requestAppRepo.ObjectMeta.Namespace = tc.kubeappsNamespace
// Default to the kubeapps namespace if not included in request.
// TODO(mnelson, #1256): remove once frontend always sends namespace.
if requestAppRepo.ObjectMeta.Namespace == "" {
requestAppRepo.ObjectMeta.Namespace = tc.kubeappsNamespace
}

responseAppRepo, err := cs.KubeappsV1alpha1().AppRepositories(tc.kubeappsNamespace).Get(requestAppRepo.ObjectMeta.Name, metav1.GetOptions{})
responseAppRepo, err := cs.KubeappsV1alpha1().AppRepositories(requestAppRepo.ObjectMeta.Namespace).Get(requestAppRepo.ObjectMeta.Name, metav1.GetOptions{})
if err != nil {
t.Errorf("expected data %v not present: %+v", requestAppRepo, err)
}
Expand Down Expand Up @@ -236,6 +246,24 @@ func TestAppRepositoryForRequest(t *testing.T) {
},
},
},
{
name: "it creates an app repo in a specific namespace",
request: appRepositoryRequestDetails{
Name: "test-repo",
Namespace: "my-namespace",
RepoURL: "http://example.com/test-repo",
},
appRepo: v1alpha1.AppRepository{
ObjectMeta: metav1.ObjectMeta{
Name: "test-repo",
Namespace: "my-namespace",
},
Spec: v1alpha1.AppRepositorySpec{
URL: "http://example.com/test-repo",
Type: "helm",
},
},
},
{
name: "it creates an app repo with auth header",
request: appRepositoryRequestDetails{
Expand Down Expand Up @@ -416,6 +444,25 @@ func TestSecretForRequest(t *testing.T) {
},
},
},
{
name: "it creates a secret in a specific namespace",
request: appRepositoryRequestDetails{
Name: "test-repo",
Namespace: "my-namespace",
RepoURL: "http://example.com/test-repo",
AuthHeader: "testing",
},
secret: &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "apprepo-test-repo-secrets",
Namespace: "my-namespace",
OwnerReferences: ownerRefs,
},
StringData: map[string]string{
"authorizationHeader": "testing",
},
},
},
}

for _, tc := range testCases {
Expand Down

0 comments on commit 5c94484

Please sign in to comment.