Skip to content

Commit 4f3c96f

Browse files
committed
Add bound service account token e2e test
Signed-off-by: Max Cao <[email protected]>
1 parent de4e9ae commit 4f3c96f

File tree

2 files changed

+358
-2
lines changed

2 files changed

+358
-2
lines changed

pkg/util/env_resolver.go

-2
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,9 @@ func GetBoundServiceAccountTokenExpiry() (*time.Duration, error) {
103103
}
104104
if expiry == nil {
105105
return ptr.To[time.Duration](time.Hour), nil // if blank, default to 1 hour
106-
107106
}
108107
if *expiry < time.Hour || *expiry > 6*time.Hour {
109108
return nil, fmt.Errorf("invalid value for %s: %s, must be between 1h and 6h", BoundServiceAccountTokenExpiryEnvVar, expiry.String()) // Must be between 1 hour and 6 hours
110-
111109
}
112110
return expiry, nil
113111
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
//go:build e2e
2+
// +build e2e
3+
4+
package trigger_auth_bound_service_account_token_test
5+
6+
import (
7+
"fmt"
8+
"testing"
9+
10+
"github.com/joho/godotenv"
11+
"github.com/stretchr/testify/assert"
12+
"k8s.io/client-go/kubernetes"
13+
14+
. "github.com/kedacore/keda/v2/tests/helper"
15+
)
16+
17+
// Load environment variables from .env file
18+
var _ = godotenv.Load("../../.env")
19+
20+
const (
21+
testName = "trigger-auth-bound-service-account-token-test"
22+
)
23+
24+
var (
25+
testNamespace = fmt.Sprintf("%s-ns", testName)
26+
deploymentName = fmt.Sprintf("%s-deployment", testName)
27+
metricsServerDeploymentName = fmt.Sprintf("%s-metrics-server", testName)
28+
triggerAuthName = fmt.Sprintf("%s-ta", testName)
29+
scaledObjectName = fmt.Sprintf("%s-so", testName)
30+
metricsServerServiceName = fmt.Sprintf("%s-service", testName)
31+
metricsServerEndpoint = fmt.Sprintf("http://%s.%s.svc.cluster.local:8080/api/value", metricsServerServiceName, testNamespace)
32+
serviceAccountName = fmt.Sprintf("%s-sa", testName)
33+
serviceAccountTokenCreationRole = fmt.Sprintf("%s-sa-role", testName)
34+
serviceAccountTokenCreationRoleBinding = fmt.Sprintf("%s-sa-role-binding", testName)
35+
minReplicaCount = 0
36+
maxReplicaCount = 1
37+
)
38+
39+
type templateData struct {
40+
TestNamespace string
41+
ServiceAccountName string
42+
ServiceAccountTokenCreationRole string
43+
ServiceAccountTokenCreationRoleBinding string
44+
DeploymentName string
45+
MetricsServerDeploymentName string
46+
MetricsServerServiceName string
47+
TriggerAuthName string
48+
ScaledObjectName string
49+
MetricsServerEndpoint string
50+
MetricValue int
51+
MinReplicaCount string
52+
MaxReplicaCount string
53+
}
54+
55+
const (
56+
serviceAccountTemplate = `
57+
apiVersion: v1
58+
kind: ServiceAccount
59+
metadata:
60+
name: {{.ServiceAccountName}}
61+
namespace: {{.TestNamespace}}
62+
`
63+
// arbitrary k8s rbac permissions that the test metrics-api container requires requesters to have
64+
serviceAccountClusterRoleTemplate = `
65+
apiVersion: rbac.authorization.k8s.io/v1
66+
kind: ClusterRole
67+
metadata:
68+
name: {{.ServiceAccountName}}
69+
rules:
70+
- nonResourceURLs:
71+
- /api/value
72+
verbs:
73+
- get
74+
`
75+
serviceAccountClusterRoleBindingTemplate = `
76+
apiVersion: rbac.authorization.k8s.io/v1
77+
kind: ClusterRoleBinding
78+
metadata:
79+
name: {{.ServiceAccountName}}
80+
roleRef:
81+
apiGroup: rbac.authorization.k8s.io
82+
kind: ClusterRole
83+
name: {{.ServiceAccountName}}
84+
subjects:
85+
- kind: ServiceAccount
86+
name: {{.ServiceAccountName}}
87+
namespace: {{.TestNamespace}}
88+
`
89+
serviceAccountTokenCreationRoleTemplate = `
90+
apiVersion: rbac.authorization.k8s.io/v1
91+
kind: Role
92+
metadata:
93+
name: {{.ServiceAccountTokenCreationRole}}
94+
namespace: {{.TestNamespace}}
95+
rules:
96+
- apiGroups:
97+
- ""
98+
resources:
99+
- serviceaccounts/token
100+
verbs:
101+
- create
102+
- get
103+
`
104+
serviceAccountTokenCreationRoleBindingTemplate = `
105+
apiVersion: rbac.authorization.k8s.io/v1
106+
kind: RoleBinding
107+
metadata:
108+
name: {{.ServiceAccountTokenCreationRoleBinding}}
109+
namespace: {{.TestNamespace}}
110+
roleRef:
111+
apiGroup: rbac.authorization.k8s.io
112+
kind: Role
113+
name: {{.ServiceAccountTokenCreationRole}}
114+
subjects:
115+
- kind: ServiceAccount
116+
name: keda-operator
117+
namespace: keda
118+
`
119+
tokenReviewAndSubjectAccessReviewClusterRoleTemplate = `
120+
apiVersion: rbac.authorization.k8s.io/v1
121+
kind: ClusterRole
122+
metadata:
123+
name: token-review-and-subject-access-review-role
124+
rules:
125+
- apiGroups:
126+
- "authentication.k8s.io"
127+
resources:
128+
- tokenreviews
129+
verbs:
130+
- create
131+
- apiGroups:
132+
- authorization.k8s.io
133+
resources:
134+
- subjectaccessreviews
135+
verbs:
136+
- create
137+
`
138+
tokenReviewAndSubjectAccessReviewClusterRoleBindingTemplate = `
139+
apiVersion: rbac.authorization.k8s.io/v1
140+
kind: ClusterRoleBinding
141+
metadata:
142+
name: token-review-and-subject-access-review-role-binding
143+
roleRef:
144+
apiGroup: rbac.authorization.k8s.io
145+
kind: ClusterRole
146+
name: token-review-and-subject-access-review-role
147+
subjects:
148+
- kind: ServiceAccount
149+
name: default
150+
namespace: {{.TestNamespace}}
151+
`
152+
// TODO(macao): replace the image when https://github.com/kedacore/test-tools/pull/186 merged
153+
metricsServerDeploymentTemplate = `
154+
apiVersion: apps/v1
155+
kind: Deployment
156+
metadata:
157+
labels:
158+
app: {{.MetricsServerDeploymentName}}
159+
name: {{.MetricsServerDeploymentName}}
160+
namespace: {{.TestNamespace}}
161+
spec:
162+
replicas: 1
163+
selector:
164+
matchLabels:
165+
app: {{.MetricsServerDeploymentName}}
166+
template:
167+
metadata:
168+
labels:
169+
app: {{.MetricsServerDeploymentName}}
170+
type: keda-testing
171+
spec:
172+
containers:
173+
- name: k8s-protected-metrics-api
174+
image: quay.io/macao/metrics-api:latest
175+
imagePullPolicy: Always
176+
securityContext:
177+
allowPrivilegeEscalation: false
178+
runAsNonRoot: true
179+
capabilities:
180+
drop:
181+
- ALL
182+
seccompProfile:
183+
type: RuntimeDefault
184+
`
185+
metricsServerService = `
186+
apiVersion: v1
187+
kind: Service
188+
metadata:
189+
name: {{.MetricsServerServiceName}}
190+
namespace: {{.TestNamespace}}
191+
spec:
192+
ports:
193+
- name: http
194+
port: 8080
195+
targetPort: 8080
196+
selector:
197+
app: {{.MetricsServerDeploymentName}}
198+
`
199+
deploymentTemplate = `
200+
apiVersion: apps/v1
201+
kind: Deployment
202+
metadata:
203+
labels:
204+
app: {{.DeploymentName}}
205+
name: {{.DeploymentName}}
206+
namespace: {{.TestNamespace}}
207+
spec:
208+
replicas: 0
209+
selector:
210+
matchLabels:
211+
app: {{.DeploymentName}}
212+
template:
213+
metadata:
214+
labels:
215+
app: {{.DeploymentName}}
216+
type: keda-testing
217+
spec:
218+
containers:
219+
- name: prom-test-app
220+
image: ghcr.io/kedacore/tests-prometheus:latest
221+
imagePullPolicy: Always
222+
securityContext:
223+
allowPrivilegeEscalation: false
224+
runAsNonRoot: true
225+
capabilities:
226+
drop:
227+
- ALL
228+
seccompProfile:
229+
type: RuntimeDefault
230+
`
231+
triggerAuthTemplate = `
232+
apiVersion: keda.sh/v1alpha1
233+
kind: TriggerAuthentication
234+
metadata:
235+
name: {{.TriggerAuthName}}
236+
namespace: {{.TestNamespace}}
237+
spec:
238+
boundServiceAccountToken:
239+
- parameter: token
240+
serviceAccountName: {{.ServiceAccountName}}
241+
`
242+
scaledObjectTemplate = `
243+
apiVersion: keda.sh/v1alpha1
244+
kind: ScaledObject
245+
metadata:
246+
name: {{.ScaledObjectName}}
247+
namespace: {{.TestNamespace}}
248+
spec:
249+
scaleTargetRef:
250+
name: {{.DeploymentName}}
251+
pollingInterval: 5
252+
minReplicaCount: 0
253+
maxReplicaCount: 1
254+
cooldownPeriod: 10
255+
triggers:
256+
- type: metrics-api
257+
metadata:
258+
targetValue: "10"
259+
url: "{{.MetricsServerEndpoint}}"
260+
valueLocation: 'value'
261+
authMode: "bearer"
262+
authenticationRef:
263+
name: {{.TriggerAuthName}}
264+
`
265+
updateMetricTemplate = `apiVersion: batch/v1
266+
kind: Job
267+
metadata:
268+
name: update-metric-value
269+
namespace: {{.TestNamespace}}
270+
spec:
271+
ttlSecondsAfterFinished: 0
272+
template:
273+
spec:
274+
containers:
275+
- name: curl-client
276+
image: curlimages/curl
277+
imagePullPolicy: Always
278+
command: ["curl", "-X", "POST", "{{.MetricsServerEndpoint}}/{{.MetricValue}}"]
279+
restartPolicy: Never`
280+
)
281+
282+
func TestScaler(t *testing.T) {
283+
// setup
284+
// ctx := context.Background()
285+
t.Log("--- setting up ---")
286+
287+
// Create kubernetes resources
288+
kc := GetKubernetesClient(t)
289+
data, templates := getTemplateData()
290+
291+
CreateKubernetesResources(t, kc, testNamespace, data, templates)
292+
293+
// wait for metrics server to be ready; scale target to start at 0 replicas
294+
assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, metricsServerDeploymentName, testNamespace, 1, 60, 1),
295+
"replica count should be 1 after 1 minute")
296+
assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, deploymentName, testNamespace, 0, 60, 1),
297+
"replica count should be 0 after 1 minute")
298+
299+
// test scaling
300+
testScaleOut(t, kc, data)
301+
testScaleIn(t, kc, data)
302+
303+
// cleanup
304+
DeleteKubernetesResources(t, testNamespace, data, templates)
305+
}
306+
307+
func getTemplateData() (templateData, []Template) {
308+
return templateData{
309+
TestNamespace: testNamespace,
310+
ServiceAccountName: serviceAccountName,
311+
ServiceAccountTokenCreationRole: serviceAccountTokenCreationRole,
312+
ServiceAccountTokenCreationRoleBinding: serviceAccountTokenCreationRoleBinding,
313+
MetricsServerDeploymentName: metricsServerDeploymentName,
314+
MetricsServerEndpoint: metricsServerEndpoint,
315+
MetricsServerServiceName: metricsServerServiceName,
316+
DeploymentName: deploymentName,
317+
TriggerAuthName: triggerAuthName,
318+
ScaledObjectName: scaledObjectName,
319+
MinReplicaCount: fmt.Sprintf("%d", minReplicaCount),
320+
MaxReplicaCount: fmt.Sprintf("%d", maxReplicaCount),
321+
MetricValue: 1,
322+
}, []Template{
323+
// required for the keda to act as the service account which has the necessary permissions
324+
{Name: "serviceAccountTemplate", Config: serviceAccountTemplate},
325+
{Name: "serviceAccountClusterRoleTemplate", Config: serviceAccountClusterRoleTemplate},
326+
{Name: "serviceAccountClusterRoleBindingTemplate", Config: serviceAccountClusterRoleBindingTemplate},
327+
// required for the keda to request token creations for the service account
328+
{Name: "serviceAccountTokenCreationRoleTemplate", Config: serviceAccountTokenCreationRoleTemplate},
329+
{Name: "serviceAccountTokenCreationRoleBindingTemplate", Config: serviceAccountTokenCreationRoleBindingTemplate},
330+
// required for the metrics-api container to delegate authenticate/authorize requests to k8s apiserver
331+
{Name: "tokenReviewAndSubjectAccessReviewClusterRoleTemplate", Config: tokenReviewAndSubjectAccessReviewClusterRoleTemplate},
332+
{Name: "tokenReviewAndSubjectAccessReviewClusterRoleBindingTemplate", Config: tokenReviewAndSubjectAccessReviewClusterRoleBindingTemplate},
333+
{Name: "metricsServerDeploymentTemplate", Config: metricsServerDeploymentTemplate},
334+
{Name: "metricsServerService", Config: metricsServerService},
335+
// scale target and trigger auths
336+
{Name: "deploymentTemplate", Config: deploymentTemplate},
337+
{Name: "triggerAuthTemplate", Config: triggerAuthTemplate},
338+
{Name: "scaledObjectTemplate", Config: scaledObjectTemplate},
339+
}
340+
}
341+
342+
func testScaleOut(t *testing.T, kc *kubernetes.Clientset, data templateData) {
343+
t.Log("--- testing scale out ---")
344+
data.MetricValue = 50
345+
KubectlReplaceWithTemplate(t, data, "updateMetricTemplate", updateMetricTemplate)
346+
347+
assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, deploymentName, testNamespace, maxReplicaCount, 60, maxReplicaCount),
348+
"replica count should be %d after 3 minutes", maxReplicaCount)
349+
}
350+
351+
func testScaleIn(t *testing.T, kc *kubernetes.Clientset, data templateData) {
352+
t.Log("--- testing scale in ---")
353+
data.MetricValue = 0
354+
KubectlReplaceWithTemplate(t, data, "updateMetricTemplate", updateMetricTemplate)
355+
356+
assert.True(t, WaitForDeploymentReplicaReadyCount(t, kc, deploymentName, testNamespace, minReplicaCount, 60, 3),
357+
"replica count should be %d after 3 minutes", minReplicaCount)
358+
}

0 commit comments

Comments
 (0)