Skip to content

Commit f70d096

Browse files
committed
feat: support file path to pulsarconnection
1 parent 30ac0ad commit f70d096

File tree

7 files changed

+114
-33
lines changed

7 files changed

+114
-33
lines changed

api/v1alpha1/common.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,18 @@ type SecretKeyRef struct {
3131
Key string `json:"key"`
3232
}
3333

34-
// ValueOrSecretRef is a string or a secret reference of the authentication
34+
// ValueOrSecretRef is a string value, a secret reference, or a local file path for the authentication config.
3535
type ValueOrSecretRef struct {
3636
// +optional
3737
Value *string `json:"value,omitempty"`
3838

3939
// +optional
4040
SecretRef *SecretKeyRef `json:"secretRef,omitempty"`
41+
42+
// File points to a local file path whose contents will be used as the value.
43+
// This is useful when running the operator locally without creating Kubernetes secrets.
44+
// +optional
45+
File *string `json:"file,omitempty"`
4146
}
4247

4348
// PulsarAuthentication defines the authentication configuration for Pulsar resources.

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/resource.streamnative.io_pulsarconnections.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ spec:
143143
For confidential clients, this would be the client secret.
144144
For public clients using JWT authentication, this would be the path to the JSON credentials file.
145145
properties:
146+
file:
147+
description: |-
148+
File points to a local file path whose contents will be used as the value.
149+
This is useful when running the operator locally without creating Kubernetes secrets.
150+
type: string
146151
secretRef:
147152
description: SecretKeyRef indicates a secret name and
148153
key
@@ -187,6 +192,11 @@ spec:
187192
This can be either a direct token value or a reference to a secret containing the token.
188193
If using a secret, the token should be stored under the specified key in the secret.
189194
properties:
195+
file:
196+
description: |-
197+
File points to a local file path whose contents will be used as the value.
198+
This is useful when running the operator locally without creating Kubernetes secrets.
199+
type: string
190200
secretRef:
191201
description: SecretKeyRef indicates a secret name and key
192202
properties:

docs/pulsar_connection.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ The `authentication` field supports two methods: JWT Token and OAuth2. Each meth
3535
|-------|-------------|------|----------|
3636
| `value` | Direct string value | `*string` | No |
3737
| `secretRef` | Reference to a Kubernetes Secret | `*SecretKeyRef` | No |
38+
| `file` | Local file path whose contents are used as the value | `*string` | No |
39+
| `file` | Local file path whose contents are used as the value | `*string` | No |
3840

3941
#### SecretKeyRef
4042

@@ -57,7 +59,7 @@ Note: Only one authentication method (either `token` or `oauth2`) should be spec
5759

5860
### JWT Token Authentication
5961

60-
JWT Token authentication can be configured using either a direct value or a Kubernetes Secret reference.
62+
JWT Token authentication can be configured using a direct value, a local file path, or a Kubernetes Secret reference.
6163

6264
#### Using a direct value
6365

@@ -95,7 +97,7 @@ authentication:
9597

9698
### OAuth2 Authentication
9799

98-
OAuth2 authentication can be configured using either a direct value or a Kubernetes Secret reference.
100+
OAuth2 authentication can be configured using a direct value, a local file path, or a Kubernetes Secret reference.
99101

100102
#### Using a direct value
101103

@@ -234,7 +236,7 @@ Note: Only one authentication method (either `token` or `oauth2`) should be spec
234236

235237
### JWT Token Authentication
236238

237-
JWT Token authentication can be configured using either a direct value or a Kubernetes Secret reference.
239+
JWT Token authentication can be configured using a direct value, a local file path, or a Kubernetes Secret reference.
238240

239241
#### Using a direct value
240242

@@ -272,7 +274,7 @@ authentication:
272274

273275
### OAuth2 Authentication
274276

275-
OAuth2 authentication can be configured using either a direct value or a Kubernetes Secret reference.
277+
OAuth2 authentication can be configured using a direct value, a local file path, or a Kubernetes Secret reference.
276278

277279
#### Using a direct value
278280

@@ -614,4 +616,4 @@ kubectl apply -f connection.yaml
614616
kubectl -n test delete pulsarconnection.resource.streamnative.io test-pulsar-connection
615617
```
616618

617-
Please be noticed, because the Pulsar Resources Operator are using the connection to manage pulsar resources, If you delete the pulsar connection, it will only be deleted after the resources CRs are deleted
619+
Please be noticed, because the Pulsar Resources Operator are using the connection to manage pulsar resources, If you delete the pulsar connection, it will only be deleted after the resources CRs are deleted

pkg/admin/interface.go

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -353,13 +353,17 @@ type PulsarAdminConfig struct {
353353
// Either Token or OAuth2 configuration must be provided
354354
// The Token used for authentication.
355355
Token string
356+
// TokenFilePath points to a local file that contains the token.
357+
TokenFilePath string
356358

357359
// OAuth2 related configuration used for authentication.
358360
IssuerEndpoint string
359361
ClientID string
360362
Audience string
361363
Key string
362-
Scope string
364+
// KeyFilePath points to a local file that contains the OAuth2 credentials JSON.
365+
KeyFilePath string
366+
Scope string
363367

364368
// TLS Authentication related configuration
365369
ClientCertificatePath string
@@ -375,7 +379,7 @@ func NewPulsarAdmin(conf PulsarAdminConfig) (PulsarAdmin, error) {
375379
var err error
376380
var adminClient admin.Client
377381

378-
config := &config.Config{
382+
cfg := &config.Config{
379383
WebServiceURL: conf.WebServiceURL,
380384
TLSAllowInsecureConnection: conf.TLSAllowInsecureConnection,
381385
TLSEnableHostnameVerification: conf.TLSEnableHostnameVerification,
@@ -385,10 +389,11 @@ func NewPulsarAdmin(conf PulsarAdminConfig) (PulsarAdmin, error) {
385389
}
386390

387391
if conf.PulsarAPIVersion != nil {
388-
config.PulsarAPIVersion = *conf.PulsarAPIVersion
392+
cfg.PulsarAPIVersion = *conf.PulsarAPIVersion
389393
}
390394

391-
if conf.Key != "" {
395+
keyFilePath = conf.KeyFilePath
396+
if keyFilePath == "" && conf.Key != "" {
392397
keyFile, err = os.CreateTemp("", "oauth2-key-")
393398
if err != nil {
394399
return nil, err
@@ -398,12 +403,14 @@ func NewPulsarAdmin(conf PulsarAdminConfig) (PulsarAdmin, error) {
398403
if err != nil {
399404
return nil, err
400405
}
406+
}
401407

402-
config.IssuerEndpoint = conf.IssuerEndpoint
403-
config.ClientID = conf.ClientID
404-
config.Audience = conf.Audience
405-
config.KeyFile = keyFilePath
406-
config.Scope = conf.Scope
408+
if keyFilePath != "" {
409+
cfg.IssuerEndpoint = conf.IssuerEndpoint
410+
cfg.ClientID = conf.ClientID
411+
cfg.Audience = conf.Audience
412+
cfg.KeyFile = keyFilePath
413+
cfg.Scope = conf.Scope
407414

408415
oauthProvider, err := auth.NewAuthenticationOAuth2WithFlow(oauth2.Issuer{
409416
IssuerEndpoint: conf.IssuerEndpoint,
@@ -417,27 +424,34 @@ func NewPulsarAdmin(conf PulsarAdminConfig) (PulsarAdmin, error) {
417424
if err != nil {
418425
return nil, err
419426
}
420-
adminClient, err = admin.NewPulsarClientWithAuthProvider(config, oauthProvider)
427+
adminClient, err = admin.NewPulsarClientWithAuthProvider(cfg, oauthProvider)
428+
if err != nil {
429+
return nil, err
430+
}
431+
} else if conf.TokenFilePath != "" {
432+
cfg.TokenFile = conf.TokenFilePath
433+
434+
adminClient, err = admin.New(cfg)
421435
if err != nil {
422436
return nil, err
423437
}
424438
} else if conf.Token != "" {
425-
config.Token = conf.Token
439+
cfg.Token = conf.Token
426440

427-
adminClient, err = admin.New(config)
441+
adminClient, err = admin.New(cfg)
428442
if err != nil {
429443
return nil, err
430444
}
431445
} else if conf.ClientCertificatePath != "" {
432-
config.AuthPlugin = auth.TLSPluginName
433-
config.AuthParams = fmt.Sprintf("{\"tlsCertFile\": %q, \"tlsKeyFile\": %q}", conf.ClientCertificatePath, conf.ClientCertificateKeyPath)
446+
cfg.AuthPlugin = auth.TLSPluginName
447+
cfg.AuthParams = fmt.Sprintf("{\"tlsCertFile\": %q, \"tlsKeyFile\": %q}", conf.ClientCertificatePath, conf.ClientCertificateKeyPath)
434448

435-
adminClient, err = admin.New(config)
449+
adminClient, err = admin.New(cfg)
436450
if err != nil {
437451
return nil, err
438452
}
439453
} else {
440-
adminClient, err = admin.New(config)
454+
adminClient, err = admin.New(cfg)
441455
if err != nil {
442456
return nil, err
443457
}

pkg/connection/reconcile_geo_replication.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,16 @@ func createParams(ctx context.Context, destConnection *resourcev1alpha1.PulsarCo
319319
hasAuth := false
320320
if auth := destConnection.Spec.Authentication; auth != nil {
321321
if auth.Token != nil {
322-
value, err := GetValue(ctx, client, destConnection.Namespace, auth.Token)
322+
value, filePath, err := GetValue(ctx, client, destConnection.Namespace, auth.Token)
323323
if err != nil {
324324
return nil, err
325325
}
326+
if value == nil && filePath != nil {
327+
value, err = valueFromFile(*filePath)
328+
if err != nil {
329+
return nil, err
330+
}
331+
}
326332
if value != nil {
327333
clusterParam.AuthPlugin = resourcev1alpha1.AuthPluginToken
328334
clusterParam.AuthParameters = "token:" + *value
@@ -337,10 +343,19 @@ func createParams(ctx context.Context, destConnection *resourcev1alpha1.PulsarCo
337343
ClientID: oauth2.ClientID,
338344
}
339345
if oauth2.Key != nil {
340-
value, err := GetValue(ctx, client, destConnection.Namespace, oauth2.Key)
346+
value, filePath, err := GetValue(ctx, client, destConnection.Namespace, oauth2.Key)
341347
if err != nil {
342348
return nil, err
343349
}
350+
if value == nil && filePath != nil {
351+
value, err = valueFromFile(*filePath)
352+
if err != nil {
353+
return nil, err
354+
}
355+
}
356+
if value == nil && filePath == nil {
357+
return nil, fmt.Errorf("OAuth2 key is empty")
358+
}
344359
if value != nil {
345360
paramsJSON.PrivateKey = "data:application/json;base64," + base64.StdEncoding.EncodeToString([]byte(*value))
346361
clusterParam.AuthPlugin = resourcev1alpha1.AuthPluginOAuth2

pkg/connection/reconciler.go

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package connection
1717
import (
1818
"context"
1919
"fmt"
20+
"os"
2021

2122
"github.com/apache/pulsar-client-go/pulsaradmin/pkg/admin/config"
2223

@@ -270,21 +271,38 @@ func NewErrorCondition(generation int64, msg string) *metav1.Condition {
270271
}
271272
}
272273

273-
// GetValue get the authentication token value or secret
274+
// GetValue resolves the authentication value from direct input, secret, or file reference.
275+
// It returns either the resolved string value or a file path when provided.
274276
func GetValue(ctx context.Context, k8sClient client.Client, namespace string,
275-
vRef *resourcev1alpha1.ValueOrSecretRef) (*string, error) {
277+
vRef *resourcev1alpha1.ValueOrSecretRef) (*string, *string, error) {
278+
if vRef == nil {
279+
return nil, nil, nil
280+
}
276281
if value := vRef.Value; value != nil {
277-
return value, nil
278-
} else if ref := vRef.SecretRef; ref != nil {
282+
return value, nil, nil
283+
}
284+
if filePath := vRef.File; filePath != nil {
285+
return nil, filePath, nil
286+
}
287+
if ref := vRef.SecretRef; ref != nil {
279288
secret := &corev1.Secret{}
280289
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: ref.Name}, secret); err != nil {
281-
return nil, err
290+
return nil, nil, err
282291
}
283292
if value, exists := secret.Data[ref.Key]; exists {
284-
return ptr.To(string(value)), nil
293+
return ptr.To(string(value)), nil, nil
285294
}
286295
}
287-
return nil, nil
296+
return nil, nil, nil
297+
}
298+
299+
func valueFromFile(filePath string) (*string, error) {
300+
content, err := os.ReadFile(filePath)
301+
if err != nil {
302+
return nil, err
303+
}
304+
value := string(content)
305+
return &value, nil
288306
}
289307

290308
// MakePulsarAdminConfig create pulsar admin configuration
@@ -329,10 +347,14 @@ func MakePulsarAdminConfig(ctx context.Context, connection *resourcev1alpha1.Pul
329347
hasAuth := false
330348
if authn := connection.Spec.Authentication; authn != nil {
331349
if token := authn.Token; token != nil {
332-
value, err := GetValue(ctx, k8sClient, connection.Namespace, token)
350+
value, filePath, err := GetValue(ctx, k8sClient, connection.Namespace, token)
333351
if err != nil {
334352
return nil, err
335353
}
354+
if filePath != nil {
355+
cfg.TokenFilePath = *filePath
356+
hasAuth = true
357+
}
336358
if value != nil {
337359
cfg.Token = *value
338360
hasAuth = true
@@ -346,12 +368,20 @@ func MakePulsarAdminConfig(ctx context.Context, connection *resourcev1alpha1.Pul
346368
if oauth2.Key == nil {
347369
return nil, fmt.Errorf("oauth2 key must not be empty")
348370
}
349-
value, err := GetValue(ctx, k8sClient, connection.Namespace, oauth2.Key)
371+
value, filePath, err := GetValue(ctx, k8sClient, connection.Namespace, oauth2.Key)
350372
if err != nil {
351373
return nil, err
352374
}
375+
if value == nil && filePath == nil {
376+
return nil, fmt.Errorf("oauth2 key must not be empty")
377+
}
353378
if value != nil {
354379
cfg.Key = *value
380+
hasAuth = true
381+
}
382+
if filePath != nil {
383+
cfg.KeyFilePath = *filePath
384+
hasAuth = true
355385
}
356386
}
357387
if tls := authn.TLS; tls != nil {

0 commit comments

Comments
 (0)