Skip to content

Commit 380d4fd

Browse files
authored
Merge pull request #794 from jetstack/VC-52159-ngts-cert-ownership-agent
VC-52159: add certOwnership Helm flag to discovery-agent chart
2 parents 63b00bf + 22cbdf7 commit 380d4fd

10 files changed

Lines changed: 78 additions & 7 deletions

File tree

deploy/charts/discovery-agent/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ A short description of the cluster where the agent is deployed (optional).
3535
3636
This description will be associated with the data that the agent uploads to the backend.
3737
38+
#### **config.claimableCerts** ~ `bool`
39+
> Default value:
40+
> ```yaml
41+
> false
42+
> ```
43+
44+
Whether discovered certs can be claimed by other tenants (optional). true = certs are left unassigned, available for any tenant to claim. false (default) = certs are owned by this cluster's tenant.
3845
#### **config.period** ~ `string`
3946
> Default value:
4047
> ```yaml

deploy/charts/discovery-agent/templates/configmap.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ data:
99
config.yaml: |-
1010
cluster_name: {{ required "config.clusterName is required" .Values.config.clusterName | quote }}
1111
cluster_description: {{ .Values.config.clusterDescription | quote }}
12+
{{- if .Values.config.claimableCerts }}
13+
claimable_certs: true
14+
{{- end }}
1215
period: {{ .Values.config.period | quote }}
1316
{{- with .Values.config.excludeAnnotationKeysRegex }}
1417
exclude-annotation-keys-regex:

deploy/charts/discovery-agent/values.schema.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@
9494
"helm-values.config": {
9595
"additionalProperties": false,
9696
"properties": {
97+
"claimableCerts": {
98+
"$ref": "#/$defs/helm-values.config.claimableCerts"
99+
},
97100
"clientID": {
98101
"$ref": "#/$defs/helm-values.config.clientID"
99102
},
@@ -127,6 +130,11 @@
127130
},
128131
"type": "object"
129132
},
133+
"helm-values.config.claimableCerts": {
134+
"default": false,
135+
"description": "Whether discovered certs can be claimed by other tenants (optional). true = certs are left unassigned, available for any tenant to claim. false (default) = certs are owned by this cluster's tenant.",
136+
"type": "boolean"
137+
},
130138
"helm-values.config.clientID": {
131139
"default": "",
132140
"description": "Deprecated: Client ID for the configured service account. The client ID should be provided in the \"clientID\" field of the authentication secret (see config.secretName). This field is provided for compatibility for users migrating from the \"venafi-kubernetes-agent\" chart.",

deploy/charts/discovery-agent/values.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ config:
1919
# +docs:property
2020
clusterDescription: ""
2121

22+
# Whether discovered certs can be claimed by other tenants (optional).
23+
# true = certs are left unassigned, available for any tenant to claim.
24+
# false (default) = certs are owned by this cluster's tenant.
25+
claimableCerts: false
26+
2227
# How often to push data to the remote server
2328
# +docs:property
2429
period: "0h1m0s"

pkg/agent/config.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,13 @@ type Config struct {
5555
ClusterName string `yaml:"cluster_name"`
5656
// ClusterDescription is a short description of the Kubernetes cluster where the
5757
// agent is running.
58-
ClusterDescription string `yaml:"cluster_description"`
59-
DataGatherers []DataGatherer `yaml:"data-gatherers"`
60-
VenafiCloud *VenafiCloudConfig `yaml:"venafi-cloud,omitempty"`
58+
ClusterDescription string `yaml:"cluster_description"`
59+
// ClaimableCerts controls whether discovered certs can be claimed by other tenants.
60+
// true = certs are left unassigned, available for any tenant to claim.
61+
// false (default) = certs are owned by this cluster's tenant.
62+
ClaimableCerts bool `yaml:"claimable_certs"`
63+
DataGatherers []DataGatherer `yaml:"data-gatherers"`
64+
VenafiCloud *VenafiCloudConfig `yaml:"venafi-cloud,omitempty"`
6165

6266
// For testing purposes.
6367
InputPath string `yaml:"input-path"`
@@ -419,6 +423,11 @@ type CombinedConfig struct {
419423
// the agent is running.
420424
ClusterDescription string
421425

426+
// ClaimableCerts controls whether discovered certs can be claimed by other tenants.
427+
// true = certs are left unassigned, available for any tenant to claim.
428+
// false (default) = certs are owned by this cluster's tenant.
429+
ClaimableCerts bool
430+
422431
// VenafiCloudVenafiConnection mode only.
423432
VenConnName string
424433
VenConnNS string
@@ -733,6 +742,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
733742
res.ClusterID = clusterID
734743
res.ClusterName = clusterName
735744
res.ClusterDescription = cfg.ClusterDescription
745+
res.ClaimableCerts = cfg.ClaimableCerts
736746
}
737747

738748
// Validation of `data-gatherers`.

pkg/agent/config_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,9 +1081,24 @@ func Test_ValidateAndCombineConfig_NGTS(t *testing.T) {
10811081
assert.Equal(t, "test-tsg-123", got.TSGID)
10821082
assert.Equal(t, "test-cluster", got.ClusterName)
10831083
assert.Equal(t, "Test NGTS cluster", got.ClusterDescription)
1084+
assert.Equal(t, false, got.ClaimableCerts)
10841085
assert.IsType(t, &client.NGTSClient{}, cl)
10851086
})
10861087

1088+
t.Run("ngts: claimable_certs flows from config into CombinedConfig", func(t *testing.T) {
1089+
t.Setenv("POD_NAMESPACE", "venafi")
1090+
privKeyPath := withFile(t, fakePrivKeyPEM)
1091+
got, _, err := ValidateAndCombineConfig(discardLogs(),
1092+
withConfig(testutil.Undent(`
1093+
period: 1h
1094+
cluster_name: test-cluster
1095+
claimable_certs: true
1096+
`)),
1097+
withCmdLineFlags("--ngts", "--tsg-id", "test-tsg-123", "--client-id", "test-client-id", "--private-key-path", privKeyPath))
1098+
require.NoError(t, err)
1099+
assert.Equal(t, true, got.ClaimableCerts)
1100+
})
1101+
10871102
t.Run("ngts: valid configuration with custom server URL", func(t *testing.T) {
10881103
t.Setenv("POD_NAMESPACE", "venafi")
10891104
privKeyPath := withFile(t, fakePrivKeyPEM)

pkg/agent/run.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ func postData(ctx context.Context, config CombinedConfig, preflightClient client
459459
err := preflightClient.PostDataReadingsWithOptions(ctx, readings, client.Options{
460460
ClusterName: config.ClusterName,
461461
ClusterDescription: config.ClusterDescription,
462+
ClaimableCerts: config.ClaimableCerts,
462463
// orgID and clusterID are not required for Venafi Cloud auth
463464
OrgID: config.OrganizationID,
464465
ClusterID: config.ClusterID,

pkg/client/client.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ type (
2323

2424
// Used for Venafi Cloud and MachineHub mode.
2525
ClusterDescription string
26+
27+
// ClaimableCerts controls whether discovered certs can be claimed by other tenants.
28+
// true = certs are left unassigned, available for any tenant to claim.
29+
// false (default) = certs are owned by this cluster's tenant.
30+
ClaimableCerts bool
2631
}
2732

2833
// The Client interface describes types that perform requests against the Jetstack Secure backend.

pkg/client/client_ngts.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,11 @@ func (c *NGTSClient) PostDataReadingsWithOptions(ctx context.Context, readings [
247247
query.Add("description", base64.RawURLEncoding.EncodeToString([]byte(stripHTML.Sanitize(opts.ClusterDescription))))
248248
}
249249

250+
if opts.ClaimableCerts {
251+
// The TLSPK backend reads "certOwnership=unassigned" — this is the backend contract.
252+
query.Add("certOwnership", "unassigned")
253+
}
254+
250255
uploadURL.RawQuery = query.Encode()
251256

252257
klog.FromContext(ctx).V(2).Info(

pkg/client/client_ngts_test.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package client
22

33
import (
4-
"context"
54
"encoding/json"
65
"fmt"
76
"net/http"
@@ -331,20 +330,33 @@ func TestNGTSClient_PostDataReadingsWithOptions(t *testing.T) {
331330
ClusterDescription: "Test cluster description",
332331
}
333332

334-
err = client.PostDataReadingsWithOptions(context.Background(), readings, opts)
333+
err = client.PostDataReadingsWithOptions(t.Context(), readings, opts)
335334
require.NoError(t, err)
336335

337336
// Verify the upload request
338337
assert.NotNil(t, receivedRequest)
339338
assert.Equal(t, "/"+ngtsUploadEndpoint, receivedRequest.URL.Path)
340339
assert.Contains(t, receivedRequest.URL.RawQuery, "name=test-cluster")
341340
assert.Equal(t, "Bearer test-access-token", receivedRequest.Header.Get("Authorization"))
341+
// certOwnership not set — must NOT appear in query
342+
assert.NotContains(t, receivedRequest.URL.RawQuery, "certOwnership")
342343

343344
// Verify the payload
344345
var payload api.DataReadingsPost
345346
err = json.Unmarshal(receivedBody, &payload)
346347
require.NoError(t, err)
347348
assert.Equal(t, 1, len(payload.DataReadings))
349+
350+
// Verify claimableCerts=true is included when set
351+
t.Run("claimableCerts: true sends certOwnership=unassigned to backend", func(t *testing.T) {
352+
optsUnassigned := Options{
353+
ClusterName: "test-cluster",
354+
ClaimableCerts: true,
355+
}
356+
err = client.PostDataReadingsWithOptions(t.Context(), readings, optsUnassigned)
357+
require.NoError(t, err)
358+
assert.Contains(t, receivedRequest.URL.RawQuery, "certOwnership=unassigned")
359+
})
348360
}
349361

350362
func TestNGTSClient_AuthenticationFlow(t *testing.T) {
@@ -384,7 +396,7 @@ func TestNGTSClient_AuthenticationFlow(t *testing.T) {
384396
opts := Options{ClusterName: "test"}
385397

386398
for range 3 {
387-
err = client.PostDataReadingsWithOptions(context.Background(), readings, opts)
399+
err = client.PostDataReadingsWithOptions(t.Context(), readings, opts)
388400
require.NoError(t, err)
389401
}
390402

@@ -448,7 +460,7 @@ func TestNGTSClient_ErrorHandling(t *testing.T) {
448460
readings := []*api.DataReading{{DataGatherer: "test", Data: &api.DynamicData{}}}
449461
opts := Options{ClusterName: "test"}
450462

451-
err = client.PostDataReadingsWithOptions(context.Background(), readings, opts)
463+
err = client.PostDataReadingsWithOptions(t.Context(), readings, opts)
452464
require.Error(t, err)
453465
assert.Contains(t, err.Error(), tt.expectedErrMsg)
454466
})

0 commit comments

Comments
 (0)