Skip to content

Commit 7778ff2

Browse files
committed
Namespace will now be extracted from request URL when not provided in the body
Signed-off-by: Fabian von Feilitzsch <[email protected]>
1 parent 35313e8 commit 7778ff2

File tree

9 files changed

+368
-103
lines changed

9 files changed

+368
-103
lines changed

internal/ansible/proxy/inject_owner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func (i *injectOwnerReferenceHandler) ServeHTTP(w http.ResponseWriter, req *http
136136
ownerObject.SetGroupVersionKind(ownerGVK)
137137
ownerObject.SetNamespace(owner.Namespace)
138138
ownerObject.SetName(owner.Name)
139-
addOwnerRef, err := k8sutil.SupportsOwnerReference(i.restMapper, ownerObject, data)
139+
addOwnerRef, err := k8sutil.SupportsOwnerReference(i.restMapper, ownerObject, data, r.Namespace)
140140
if err != nil {
141141
m := "Could not determine if we should add owner ref"
142142
log.Error(err, m)
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright 2021 The Operator-SDK Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package proxy
16+
17+
import (
18+
"bytes"
19+
"context"
20+
"encoding/json"
21+
"fmt"
22+
"io/ioutil"
23+
"net/http"
24+
"testing"
25+
26+
. "github.com/onsi/ginkgo"
27+
. "github.com/onsi/gomega"
28+
29+
corev1 "k8s.io/api/core/v1"
30+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31+
32+
"github.com/operator-framework/operator-sdk/internal/ansible/proxy/kubeconfig"
33+
)
34+
35+
var _ = Describe("injectOwnerReferenceHandler", func() {
36+
37+
Describe("ServeHTTP", func() {
38+
It("Should inject ownerReferences even when namespace is not explicitly set", func() {
39+
if testing.Short() {
40+
Skip("skipping ansible owner reference injection testing in short mode")
41+
}
42+
cm := corev1.ConfigMap{
43+
TypeMeta: metav1.TypeMeta{
44+
APIVersion: "v1",
45+
Kind: "ConfigMap",
46+
},
47+
ObjectMeta: metav1.ObjectMeta{
48+
Name: "test-owner-ref-injection",
49+
},
50+
Data: map[string]string{
51+
"hello": "world",
52+
},
53+
}
54+
55+
body, err := json.Marshal(cm)
56+
if err != nil {
57+
Fail("Failed to marshal body")
58+
}
59+
60+
req, err := http.NewRequest("POST", "http://localhost:8888/api/v1/namespaces/default/configmaps", bytes.NewReader(body))
61+
62+
po, err := createTestPod("test-injection", "default", testClient)
63+
if err != nil {
64+
Fail(fmt.Sprintf("Failed to create pod: %v", err))
65+
}
66+
defer func() {
67+
if err := testClient.Delete(context.Background(), po); err != nil {
68+
Fail(fmt.Sprintf("Failed to delete the pod: %v", err))
69+
}
70+
}()
71+
72+
username, err := kubeconfig.EncodeOwnerRef(
73+
metav1.OwnerReference{
74+
APIVersion: "v1",
75+
Kind: "Pod",
76+
Name: po.GetName(),
77+
UID: po.GetUID(),
78+
}, "default")
79+
if err != nil {
80+
Fail("Failed to encode owner reference")
81+
}
82+
req.SetBasicAuth(username, "unused")
83+
84+
httpClient := http.Client{}
85+
86+
defer func() {
87+
cleanupReq, err := http.NewRequest("DELETE", "http://localhost:8888/api/v1/namespaces/default/configmaps/test-owner-ref-injection", bytes.NewReader([]byte{}))
88+
if err != nil {
89+
Fail(fmt.Sprintf("Failed to delete configmap: %v", err))
90+
}
91+
_, err = httpClient.Do(cleanupReq)
92+
if err != nil {
93+
Fail(fmt.Sprintf("Failed to delete configmap: %v", err))
94+
}
95+
}()
96+
97+
resp, err := httpClient.Do(req)
98+
respBody, err := ioutil.ReadAll(resp.Body)
99+
if err != nil {
100+
Fail(fmt.Sprintf("Failed to read response body: %v", err))
101+
}
102+
var modifiedCM corev1.ConfigMap
103+
err = json.Unmarshal(respBody, &modifiedCM)
104+
if err != nil {
105+
Fail(fmt.Sprintf("Failed to unmarshal configmap: %v", err))
106+
}
107+
ownerRefs := modifiedCM.ObjectMeta.OwnerReferences
108+
109+
Expect(len(ownerRefs)).To(Equal(1))
110+
111+
ownerRef := ownerRefs[0]
112+
113+
Expect(ownerRef.APIVersion).To(Equal("v1"))
114+
Expect(ownerRef.Kind).To(Equal("Pod"))
115+
Expect(ownerRef.Name).To(Equal(po.GetName()))
116+
Expect(ownerRef.UID).To(Equal(po.GetUID()))
117+
})
118+
})
119+
})

internal/ansible/proxy/kubeconfig/kubeconfig.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,25 @@ type NamespacedOwnerReference struct {
6868
Namespace string
6969
}
7070

71+
func EncodeOwnerRef(ownerRef metav1.OwnerReference, namespace string) (string, error) {
72+
nsOwnerRef := NamespacedOwnerReference{OwnerReference: ownerRef, Namespace: namespace}
73+
ownerRefJSON, err := json.Marshal(nsOwnerRef)
74+
if err != nil {
75+
return "", err
76+
}
77+
return base64.URLEncoding.EncodeToString(ownerRefJSON), nil
78+
}
79+
7180
// Create renders a kubeconfig template and writes it to disk
7281
func Create(ownerRef metav1.OwnerReference, proxyURL string, namespace string) (*os.File, error) {
73-
nsOwnerRef := NamespacedOwnerReference{OwnerReference: ownerRef, Namespace: namespace}
7482
parsedURL, err := url.Parse(proxyURL)
7583
if err != nil {
7684
return nil, err
7785
}
78-
ownerRefJSON, err := json.Marshal(nsOwnerRef)
86+
username, err := EncodeOwnerRef(ownerRef, namespace)
7987
if err != nil {
8088
return nil, err
8189
}
82-
username := base64.URLEncoding.EncodeToString(ownerRefJSON)
8390
parsedURL.User = url.User(username)
8491
v := values{
8592
Username: username,
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright 2021 The Operator-SDK Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package proxy
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"testing"
21+
22+
. "github.com/onsi/ginkgo"
23+
. "github.com/onsi/gomega"
24+
"github.com/operator-framework/operator-sdk/internal/ansible/proxy/controllermap"
25+
kcorev1 "k8s.io/api/core/v1"
26+
kmetav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"sigs.k8s.io/controller-runtime/pkg/client"
28+
"sigs.k8s.io/controller-runtime/pkg/client/config"
29+
"sigs.k8s.io/controller-runtime/pkg/manager"
30+
)
31+
32+
var testMgr manager.Manager
33+
34+
var testClient client.Client
35+
36+
func TestProxy(t *testing.T) {
37+
RegisterFailHandler(Fail)
38+
RunSpecs(t, "Proxy Test Suite")
39+
}
40+
41+
var _ = BeforeSuite(func() {
42+
if testing.Short() {
43+
return
44+
}
45+
var err error
46+
testMgr, err = manager.New(config.GetConfigOrDie(), manager.Options{Namespace: "default"})
47+
if err != nil {
48+
Fail(fmt.Sprintf("Failed to instantiate manager: %v", err))
49+
}
50+
done := make(chan error)
51+
cMap := controllermap.NewControllerMap()
52+
err = Run(done, Options{
53+
Address: "localhost",
54+
Port: 8888,
55+
KubeConfig: testMgr.GetConfig(),
56+
Cache: nil,
57+
RESTMapper: testMgr.GetRESTMapper(),
58+
ControllerMap: cMap,
59+
WatchedNamespaces: []string{"test-watched-namespace"},
60+
OwnerInjection: true,
61+
})
62+
if err != nil {
63+
Fail(fmt.Sprintf("Error starting proxy: %v", err))
64+
}
65+
testClient, err = client.New(testMgr.GetConfig(), client.Options{})
66+
if err != nil {
67+
Fail(fmt.Sprintf("Failed to create the client: %v", err))
68+
}
69+
_, err = createTestNamespace("test-watched-namespace", testClient)
70+
if err != nil {
71+
Fail(fmt.Sprintf("Failed to create watched namespace: %v", err))
72+
}
73+
})
74+
75+
var _ = AfterSuite(func() {
76+
if testing.Short() {
77+
return
78+
}
79+
err := testClient.Delete(context.Background(), &kcorev1.Namespace{
80+
ObjectMeta: kmetav1.ObjectMeta{
81+
Name: "test-watched-namespace",
82+
Labels: map[string]string{
83+
"test-label": "test-watched-namespace",
84+
},
85+
},
86+
})
87+
88+
if err != nil {
89+
Fail(fmt.Sprintf("Failed to clean up namespace: %v:", err))
90+
}
91+
})
92+
93+
func createTestNamespace(name string, cl client.Client) (client.Object, error) {
94+
ns := &kcorev1.Namespace{
95+
ObjectMeta: kmetav1.ObjectMeta{
96+
Name: name,
97+
Labels: map[string]string{
98+
"test-label": name,
99+
},
100+
},
101+
}
102+
if err := cl.Create(context.Background(), ns); err != nil {
103+
return nil, err
104+
}
105+
return ns, nil
106+
}
107+
108+
func createTestPod(name, namespace string, cl client.Client) (client.Object, error) {
109+
three := int64(3)
110+
pod := &kcorev1.Pod{
111+
ObjectMeta: kmetav1.ObjectMeta{
112+
Name: name,
113+
Namespace: namespace,
114+
Labels: map[string]string{
115+
"test-label": name,
116+
},
117+
},
118+
Spec: kcorev1.PodSpec{
119+
Containers: []kcorev1.Container{{Name: "nginx", Image: "nginx"}},
120+
RestartPolicy: "Always",
121+
ActiveDeadlineSeconds: &three,
122+
},
123+
}
124+
if err := cl.Create(context.Background(), pod); err != nil {
125+
return nil, err
126+
}
127+
return pod, nil
128+
}

0 commit comments

Comments
 (0)