Skip to content

Commit cbdb583

Browse files
committed
extend documentation
On-behalf-of: @SAP christoph.mewes@sap.com
1 parent 47d5aaa commit cbdb583

File tree

5 files changed

+595
-335
lines changed

5 files changed

+595
-335
lines changed

docs/content/faq.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,17 @@ schema from the `APIExport`.
3232

3333
## Does the Sync Agent handle permission claims?
3434

35-
Only those required for its own operation. If you configure a namespaced resource to sync, it will
36-
automatically add a claim for `namespaces` in kcp, plus it will add either `configmaps` or `secrets`
37-
if related resources are configured in a `PublishedResource`. But you cannot specify additional
38-
permissions claims.
35+
Only those required for its own operation. The syncagent will add the following permission claims
36+
to the APIExport it manages:
37+
38+
* `events` (always)
39+
* `namespaces` (always)
40+
* `core.kcp.io/logicalclusters` (if `enableWorkspacePaths` is set in a `PublishedResource`)
41+
* any resource used as related resources (most often this means `configmaps` or `secrets`, but could
42+
be any resource)
43+
44+
The syncagent will always overwrite the entire list of permission claims, i.e. you cannot have custom
45+
claims in an APIExport.
3946

4047
## I am seeing errors in the agent logs, what's going on?
4148

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
nav:
22
- index.md
3+
- related-resources.md
34
- templating.md
45
- api-lifecycle.md
56
- technical-details.md

docs/content/publish-resources/index.md

Lines changed: 7 additions & 331 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ usually originate on the service cluster and could be for example connection det
3535
by Crossplane, but could also originate in the user workspace and just be additional, auxiliary
3636
resources that need to be synced down to the service cluster.
3737

38-
### `PublishedResource`
38+
## `PublishedResource`
3939

4040
In its simplest form (which is rarely practical) a `PublishedResource` looks like this:
4141

@@ -287,333 +287,7 @@ resource represents usually one, but can be multiple objects to synchronize betw
287287
and service cluster. While the main published resource sync is always workspace->service cluster,
288288
related resources can originate on either side and so either can work as the source of truth.
289289

290-
At the moment, only `ConfigMaps` and `Secrets` are allowed related resource kinds.
291-
292-
For each related resource, the Sync Agent needs to be told how to find the object on the origin side
293-
and where to create it on the destination side. There are multiple options that you can choose from.
294-
295-
By default all related objects live in the same namespace as the primary object (their owner/parent).
296-
If the primary object is cluster scoped, admins must configure additional rules to specify what
297-
namespace the ConfigMap/Secret shall be read from and created in.
298-
299-
Related resources are always optional. Even if references (see below) are used and their path
300-
expression points to a non-existing field in the primary object (e.g. `spec.secretName` is configured,
301-
but that field does not exist in Certificate object), this will simply be treated as "not _yet_
302-
existing" and not create an error.
303-
304-
#### References
305-
306-
A reference is a JSONPath-like expression (more precisely, it follows the [gjson syntax](https://github.com/tidwall/gjson?tab=readme-ov-file#path-syntax))
307-
that are evaluated on both sides of the synchronization. You configure a single path expression
308-
(like `spec.secretName`) and the sync agent will evaluate it in the original primary object (in kcp)
309-
and again in the copied primary object (on the service cluster). Since the primary object has already
310-
been mutated, the `spec.secretName` is already rewritten/adjusted to work on the service cluster
311-
(for example it was changed from `my-secret` to `jk23h4wz47329rz2r72r92-secret` on the service
312-
cluster side). By doing it this way, admins only have to think about mutations and rewrites once
313-
(when configuring the primary object in the PublishedResource) and the path will yield 2 ready to
314-
use values (`my-secret` and the computed value).
315-
316-
References can either return a single scalar (strings or integers that will be auto-converted to a
317-
string) (like in `spec.secretName`) or a list of strings/numbers (like `spec.users.#.name`). A
318-
reference must return the same number of items on both the local and remote object, otherwise the
319-
agent will not be able to map local related names to remote related names correctly.
320-
321-
A regular expression can be configured to be applied to each found value (i.e. if the reference returns
322-
a list of values, the regular expression is applied to each individual value).
323-
324-
Here's an example on how to use references to locate the related object.
325-
326-
{% raw %}
327-
```yaml
328-
apiVersion: syncagent.kcp.io/v1alpha1
329-
kind: PublishedResource
330-
metadata:
331-
name: publish-certmanager-certs
332-
spec:
333-
resource:
334-
kind: Certificate
335-
apiGroup: cert-manager.io
336-
versions: [v1]
337-
338-
naming:
339-
# this is where our CA and Issuer live in this example
340-
namespace: kube-system
341-
# need to adjust it to prevent collisions (normally clustername is the namespace)
342-
name: "{{ .ClusterName }}-{{ .Object.metadata.namespace | sha3short }}-{{ .Object.metadata.name | sha3short }}"
343-
344-
related:
345-
- # unique name for this related resource. The name must be unique within
346-
# one PublishedResource and is the key by which consumers (end users)
347-
# can identify and consume the related resource. Common names are
348-
# "connection-details" or "credentials".
349-
identifier: tls-secret
350-
351-
# "service" or "kcp"
352-
origin: service
353-
354-
# for now, only "Secret" and "ConfigMap" are supported;
355-
# there is no GVK projection for related resources
356-
kind: Secret
357-
358-
# configure where in the parent object we can find the child object
359-
object:
360-
# Object can use either reference, labelSelector or template. In this
361-
# example we use references.
362-
reference:
363-
# This path is evaluated in both the local and remote objects, to figure out
364-
# the local and remote names for the related object. This saves us from having
365-
# to remember mutated fields before their mutation (similar to the last-known
366-
# annotation).
367-
path: spec.secretName
368-
369-
# namespace part is optional; if not configured,
370-
# Sync Agent assumes the same namespace as the owning resource
371-
# namespace:
372-
# reference:
373-
# path: spec.secretName
374-
# regex:
375-
# pattern: '...'
376-
# replacement: '...'
377-
```
378-
{% endraw %}
379-
380-
#### Templates
381-
382-
Similar to references, [Go templates](https://pkg.go.dev/text/template) can also be used to determine
383-
the names of related objects on both sides of the sync. In fact, templates can be thought of as more
384-
powerful references since they allow for minimal logic to be embedded in them. Templates also do not
385-
necessarily have to select a value from the object (like a reference does), but can use any kind of
386-
logic to determine the names.
387-
388-
Like references, templates can also only be used to select a single object per related resource.
389-
390-
A template gets the following data injected into it:
391-
392-
```go
393-
type localObjectNamingContext struct {
394-
// Side is set to either one of the possible origin values to indicate for
395-
// which cluster the template is currently being evaluated for.
396-
Side syncagentv1alpha1.RelatedResourceOrigin
397-
// Object is the primary object belonging to the related object. Since related
398-
// object templates are evaluated twice (once for the origin side and once
399-
// for the destination side), object is the primary object on the side the
400-
// template is evaluated for.
401-
Object map[string]any
402-
// ClusterName is the internal cluster identifier (e.g. "34hg2j4gh24jdfgf")
403-
// of the kcp workspace that the synchronization is currently processing. This
404-
// value is set for both evaluations, regardless of side.
405-
ClusterName logicalcluster.Name
406-
// ClusterPath is the workspace path (e.g. "root:customer:projectx"). This
407-
// value is set for both evaluations, regardless of side.
408-
ClusterPath logicalcluster.Path
409-
}
410-
```
411-
412-
In the simplest form, a template can replace a reference:
413-
414-
* reference: `.spec.secretName`
415-
* Go template: {% raw %}`{{ .Object.spec.secretName }}`{% endraw %}
416-
417-
Just like with references, the configured template is evaluated twice, once for each side of the
418-
synchronization. You can use the `Side` variable to allow for fully customized names on each side:
419-
420-
{% raw %}
421-
```yaml
422-
spec:
423-
...
424-
related:
425-
- identifier: tls-secret
426-
# ..omitting other fields..
427-
object:
428-
template:
429-
template: `{{ if eq .Side "kcp" }}name-in-kcp{{ else }}name-on-service-cluster{{ end }}`
430-
```
431-
{% endraw %}
432-
433-
See [Templating](templating.md) for more information on how to use templates in PublishedResources.
434-
435-
#### Label Selectors
436-
437-
In some cases, the primary object does not have a link to its child/children objects. In these cases,
438-
a label selector can be used. This allows to configure the labels that any related object must have
439-
to be included.
440-
441-
Notably, this allows for _multiple_ objects that are synced for a single configured related resource.
442-
The sync agent will not prevent misconfigurations, so great care must be taken when configuring
443-
selectors to not accidentally include too many objects.
444-
445-
Additionally, it is assumed that
446-
447-
* Primary objects synced from kcp to a service cluster will be renamed, to prevent naming collisions.
448-
* The renamed objects on the service cluster might contain private, sensitive information that should
449-
not be leaked into kcp workspaces.
450-
* When there is no explicit name being requested (like by setting `spec.secretName`), it can be
451-
assumed that the operator on the service cluster that is actually processing the primary object
452-
will use the primary object's name (at least in parts) to construct the names of related objects,
453-
for example a Certificate `yaddasupersecretyadda` might automatically get a Secret created named
454-
`yaddasupersecretyadda-secret`.
455-
456-
Since the name of the related object must not leak into a kcp workspace, admins who configure a
457-
label selector also always have to provide a naming scheme for the copies of the related objects on
458-
the destination side.
459-
460-
Namespaces work the same as with references, i.e. by default the same namespace as the primary object
461-
is assumed. However you can actually also use label selectors to find the origin _namespaces_
462-
dynamically. So you can configure two label selectors, and then agent will first use the namespace
463-
selector to find all applicable namespaces, and then use the other label selector _in each of the
464-
applicable namespaces_ to finally locate the related objects. How useful this is depends a lot on
465-
how peculiar the underlying operators on the service clusters are.
466-
467-
Here is an example on how to use label selectors:
468-
469-
{% raw %}
470-
```yaml
471-
apiVersion: syncagent.kcp.io/v1alpha1
472-
kind: PublishedResource
473-
metadata:
474-
name: publish-certmanager-certs
475-
spec:
476-
resource:
477-
kind: Certificate
478-
apiGroup: cert-manager.io
479-
versions: [v1]
480-
481-
naming:
482-
namespace: kube-system
483-
name: "{{ .ClusterName }}-{{ .Object.metadata.namespace | sha3short }}-{{ .Object.metadata.name | sha3short }}"
484-
485-
related:
486-
- identifier: tls-secrets
487-
488-
# "service" or "kcp"
489-
origin: service
490-
491-
# for now, only "Secret" and "ConfigMap" are supported;
492-
# there is no GVK projection for related resources
493-
kind: Secret
494-
495-
# configure where in the parent object we can find the child object
496-
object:
497-
# A selector is a standard Kubernetes label selector, supporting
498-
# matchLabels and matchExpressions.
499-
selector:
500-
matchLabels:
501-
my-key: my-value
502-
another: pair
503-
# Within matchLabels, keys and values are treated as Go templates.
504-
# In this example, since the Secret originates on the service cluster
505-
# (see "origin" above), we use LocalObject to determine the value
506-
# for the selector. In case the object was heavily mutated during the
507-
# sync, this will give access to the mutated values on the service
508-
# cluster side.
509-
'{{ shasum "test" }}': '{{ .LocalObject.spec.username }}'
510-
511-
# You also need to provide rules on how objects found by this selector
512-
# should be named on the destination side of the sync. You can choose
513-
# to define a rewrite rule that keeps the original name from the origin
514-
# side, but this may leak undesirable internals to the users.
515-
# Rewrites are either using regular expressions or templated strings,
516-
# never both.
517-
# The rewrite config is applied to each individual found object.
518-
rewrite:
519-
regex:
520-
pattern: "foo-(.+)"
521-
replacement: "bar-\\1"
522-
523-
# or
524-
template:
525-
template: "{{ .Value }}-foo"
526-
527-
# Like with references, the namespace can (or must) be configured explicitly.
528-
# You do not need to also use label selectors here, you can mix and match
529-
# freely.
530-
# namespace:
531-
# reference:
532-
# path: metadata.namespace
533-
# regex:
534-
# pattern: '...'
535-
# replacement: '...'
536-
```
537-
{% endraw %}
538-
539-
There are two possible usages of Go templates when using label selectors. See [Templating](templating.md)
540-
for more information on how to use templates in PublishedResources in general.
541-
542-
##### Selector Templates
543-
544-
Each template rendered as part of a `matchLabels` selector gets the following data injected:
545-
546-
```go
547-
type relatedObjectLabelContext struct {
548-
// LocalObject is the primary object copy on the local side of the sync
549-
// (i.e. on the service cluster).
550-
LocalObject map[string]any
551-
// RemoteObject is the primary object original, in kcp.
552-
RemoteObject map[string]any
553-
// ClusterName is the internal cluster identifier (e.g. "34hg2j4gh24jdfgf")
554-
// of the kcp workspace that the synchronization is currently processing
555-
// (where the remote object exists).
556-
ClusterName logicalcluster.Name
557-
// ClusterPath is the workspace path (e.g. "root:customer:projectx").
558-
ClusterPath logicalcluster.Path
559-
}
560-
```
561-
562-
Note that in contrast to the `template` way of selecting objects, the templates here in the label
563-
selector are only evaluated once, on the origin side of the sync. The names of the destination side
564-
are determined using the rewrite mechanism (which might also be a Go template, see next section).
565-
566-
##### Rewrite Rules
567-
568-
Each found related object on the origin side needs to have its own name on the destination side. To
569-
map from the origin to the destination side, regular expressions (see example snippet) or Go
570-
templates can be used.
571-
572-
If a template is configured, it is evaluated once for every found related object. The template gets
573-
the following data injected into it:
574-
575-
```go
576-
type relatedObjectLabelRewriteContext struct {
577-
// Value is either the a found namespace name (when a label selector was
578-
// used to select the source namespaces for related objects) or the name of
579-
// a found object (when a label selector was used to find objects). In the
580-
// former case, the template should return the new namespace to use on the
581-
// destination side, in the latter case it should return the new object name
582-
// to use on the destination side.
583-
Value string
584-
// When a rewrite is used to rewrite object names, RelatedObject is the
585-
// original related object (found on the origin side). This enables you to
586-
// ignore the given Value entirely and just select anything from the object
587-
// itself.
588-
// RelatedObject is nil when the rewrite is performed for a namespace.
589-
RelatedObject map[string]any
590-
// LocalObject is the primary object copy on the local side of the sync
591-
// (i.e. on the service cluster).
592-
LocalObject map[string]any
593-
// RemoteObject is the primary object original, in kcp.
594-
RemoteObject map[string]any
595-
// ClusterName is the internal cluster identifier (e.g. "34hg2j4gh24jdfgf")
596-
// of the kcp workspace that the synchronization is currently processing
597-
// (where the remote object exists).
598-
ClusterName logicalcluster.Name
599-
// ClusterPath is the workspace path (e.g. "root:customer:projectx").
600-
ClusterPath logicalcluster.Path
601-
}
602-
```
603-
604-
Regarding `Value`: The agent allows to individually configure rules for finding object _names_ and
605-
object _namespaces_. Often the namespace is not configured because the related objects live in the
606-
same namespace as their owning, primary object.
607-
608-
When a label selector is configured to find namespaces, the rewrite template will be evaluated once
609-
for each found namespace. In this case the `.Value` is the name of the found namespace. Remember, the
610-
template's job is to map the found namespace to the new namespace on the destination side of the sync.
611-
612-
Once the namespaces have been determined, the agent will look for matching objects in each namespace
613-
individually. For each namespace it will again follow the configured source, may it be a selector,
614-
template or reference. If again a label selector is used, it will be applied in each namespace and
615-
the configured rewrite rule will be evaluated once per found object. In this case, `.Value` is the
616-
name of found object.
290+
More information is available in the [related resources guide](./related-resources.md).
617291

618292
## Examples
619293

@@ -650,9 +324,11 @@ spec:
650324
name: "{{ .ClusterName }}-{{ .Object.metadata.namespace | sha3short }}-{{ .Object.metadata.name | sha3short }}"
651325
652326
related:
653-
- origin: service # service or kcp
654-
kind: Secret # for now, only "Secret" and "ConfigMap" are supported;
655-
# there is no GVK projection for related resources
327+
- identifier: tls-secrets
328+
origin: service # service or kcp
329+
group: ""
330+
version: v1
331+
resource: secrets
656332
657333
# configure where in the parent object we can find
658334
# the name/namespace of the related resource (the child)

0 commit comments

Comments
 (0)