Skip to content

Commit f89160c

Browse files
authored
Merge pull request #65 from embik/docs-reorg
Reorganise docs for a better reading experience
2 parents cbce4fc + 0471ca7 commit f89160c

File tree

9 files changed

+161
-134
lines changed

9 files changed

+161
-134
lines changed

docs/content/.pages

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
nav:
2+
- Home: README.md
3+
- Getting Started: getting-started.md
4+
- Publishing Resources: publish-resources
5+
- Consuming Services: consuming-services.md
6+
- FAQ: faq.md
7+
- Contributing: contributing

docs/content/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ able to delete and recreate objects to follow this GVK change, which is a level
109109
simply do not want to deal with at this point in time. Also, `APIResourceSchemas` are immutable
110110
themselves.
111111

112-
More information is available in the [Publishing Resources](./publish-resources.md) guide.
112+
More information is available in the [Publishing Resources](./publish-resources/index.md) guide.
113113

114114
### APIExports
115115

File renamed without changes.

docs/content/getting-started.md

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,32 @@ $ kubectl create --filename apiexport.yaml
3939
apiexport/test.example.com created
4040
```
4141

42+
### Optional: Create Initial APIBinding
43+
44+
To save resources, kcp doesn't start API endpoints for `APIExports` that are not in use (i.e. that don't
45+
have an active `APIBinding`). To avoid cryptic errors in the Sync Agent logs about resources not being found,
46+
you can create an initial `APIBinding` in the same (or another) workspace as your `APIExport`.
47+
48+
It could look like this:
49+
50+
```yaml
51+
apiVersion: apis.kcp.io/v1alpha1
52+
kind: APIBinding
53+
metadata:
54+
name: test.example.com
55+
spec:
56+
reference:
57+
export:
58+
name: test.example.com
59+
```
60+
61+
While still being in your `:workspace:you:want:to:create:it` workspace, you could create the `APIBinding` like this:
62+
63+
```sh
64+
$ kubectl create --filename apibinding.yaml
65+
apibinding/test.example.com created
66+
```
67+
4268
## Sync Agent Installation
4369

4470
The Sync Agent can be installed into any namespace, but in our example we are going with `kcp-system`.
@@ -47,7 +73,7 @@ to, but that is the common setup. Ultimately the Sync Agent synchronizes data be
4773
endpoints.
4874

4975
Now that the `APIExport` is created, switch to the Kubernetes cluster from which you wish to
50-
[publish resources](publish-resources.md). You will need to ensure that a kubeconfig with access to
76+
[publish resources](./publish-resources/index.md). You will need to ensure that a kubeconfig with access to
5177
the kcp workspace that the `APIExport` has been created in is stored as a `Secret` on this cluster.
5278
Make sure that the kubeconfig points to the right workspace (not necessarily the `root` workspace).
5379

@@ -237,7 +263,7 @@ subjects:
237263
## Publish Resources
238264

239265
Once the Sync Agent Pods are up and running, you should be able to follow the
240-
[Publishing Resources](publish-resources.md) guide.
266+
[Publishing Resources](./publish-resources/index.md) guide.
241267

242268
## Consume Service
243269

docs/content/publish-resources/.pages

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
nav:
2+
- index.md
3+
- api-lifecycle.md
4+
- technical-details.md

docs/content/publish-resources.md renamed to docs/content/publish-resources/index.md

Lines changed: 0 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -491,124 +491,3 @@ spec:
491491
# replacement: '...'
492492
```
493493

494-
## Technical Details
495-
496-
The following sections go into more details of the behind the scenes magic.
497-
498-
### Synchronization
499-
500-
Even though the whole configuration is written from the standpoint of the service owner, the actual
501-
synchronization logic considers the kcp side as the canonical source of truth. The Sync Agent
502-
continuously tries to make the local objects look like the ones in kcp, while pushing status updates
503-
back into kcp (if the given `PublishedResource` (i.e. CRD) has a `status` subresource enabled).
504-
505-
### Local <-> Remote Connection
506-
507-
The Sync Agent tries to keep sync-related metadata on the service cluster, away from the consumers.
508-
This is both to prevent vandalism and to hide implementation details.
509-
510-
To ensure stability against future changes, once the Sync Agent has determined how a local object
511-
should be named, it will remember this decision in the object's metadata. This is so that on future
512-
reconciliations, the (potentially costly, but probably not) renaming logic does not need to be
513-
applied again. This allows the Sync Agent to change defaults and also allows the service owner to make
514-
changes to the naming rules without breaking existing objects.
515-
516-
Since we do not want to store metadata on the kcp side, we instead rely on label selectors on
517-
the local objects. Each object on the service cluster has a label for the remote cluster name,
518-
namespace and object name, and when trying to find the matching local object, the Sync Agent simply
519-
does a label-based search.
520-
521-
There is currently no sync-related metadata available on source objects (in kcp workspaces), as this
522-
would either be annotations (untyped strings...) or require schema changes to allow additional
523-
fields in basically random CRDs.
524-
525-
Note that fields like `generation` or `resourceVersion` are not relevant for any of the sync logic.
526-
527-
### Reconcile Loop
528-
529-
The sync loop can be divided into 5 parts:
530-
531-
1. find the local object
532-
2. handle deletion
533-
3. ensure the destination object exists
534-
4. ensure the destination object's content matches the source object
535-
5. synchronize related resources the same way (repeat 1-4 for each related resource)
536-
537-
#### Phase 1: Find the Local Object
538-
539-
For this, as mentioned in the connection chapter above, the Sync Agent tries to follow label selectors
540-
on the service cluster. This helps prevent cluttering with consumer workspaces with sync metadata.
541-
If no object is found to match the labels, that's fine and the loop will continue with phase 2,
542-
in which a possible Conflict error (if labels broke) is handled gracefully.
543-
544-
The remote object in the workspace becomes the `source object` and its local equivalent on the
545-
service cluster is called the `destination object`.
546-
547-
#### Phase 2: Handle Deletion
548-
549-
A finalizer is used in the kcp workspaces to prevent orphans in the service cluster side. This
550-
is the only real evidence in the kcp side that the Sync Agent is even doing things. When a remote
551-
(source) object is deleted, the corresponding local object is deleted as well. Once the local object
552-
is gone, the finalizer is removed from the source object.
553-
554-
#### Phase 3: Ensure Object Existence
555-
556-
We have a source object and now need to create the destination. This chart shows what's happening.
557-
558-
```mermaid
559-
graph TB
560-
A(source object):::state --> B([cleanup if in deletion]):::step
561-
B --> C([ensure finalizer on source object]):::step
562-
C --> D{exists local object?}
563-
564-
D -- yes --> I("continue with next phase…"):::state
565-
D -- no --> E([apply projection]):::step
566-
567-
subgraph "ensure dest object exists"
568-
E --> G([ensure resulting namespace exists]):::step
569-
G --> H([create local object]):::step
570-
H --> H_err{Errors?}
571-
H_err -- Conflict --> J([attempt to adopt existing object]):::step
572-
end
573-
574-
H_err -- success --> I
575-
J --> I
576-
577-
classDef step color:#77F
578-
classDef state color:#F77
579-
```
580-
581-
After we followed through with these steps, both the source and destination objects exists and we
582-
can continue with phase 4.
583-
584-
Resource adoption happens when creation of the initial local object fails. This can happen when labels
585-
get mangled. If such a conflict happens, the Sync Agent will "adopt" the existing local object by
586-
adding / fixing the labels on it, so that for the next reconciliation it will be found and updated.
587-
588-
#### Phase 4: Content Synchronization
589-
590-
Content synchronization is rather simple, really.
591-
592-
First the source "spec" is used to patch the local object. Note that this step is called "spec", but
593-
should actually be called "all top-level elements besides `apiVersion`, `kind`, `status` and
594-
`metadata`, but still including some labels and annotations"; so if you were to publish RBAC objects,
595-
the syncer would include `roleRef` field, for example).
596-
597-
To allow proper patch generation, the last known state is kept on the local object, similar to how
598-
`kubectl` creates an annotation for it. This is required for the Sync Agent to properly detect changes
599-
made by mutation webhooks on the service cluster.
600-
601-
If the published resource (CRD) has a `status` subresource enabled (not just a `status` field in its
602-
scheme, it must be a real subresource), then the Sync Agent will copy the status from the local object
603-
back up to the remote (source) object.
604-
605-
#### Phase 5: Sync Related Resources
606-
607-
The same logic for synchronizing the main published resource applies to their related resources as
608-
well. The only difference is that the source side can be either remote (workspace) or local
609-
(service cluster).
610-
611-
Since the Sync Agent tries its best to keep sync-related data out of kcp workspaces, the last known
612-
state for related resources is _not_ kept together with the destination object in the kcp workspaces.
613-
Instead all known states (from the main object and all related resources) is kept in a single Secret
614-
on the service cluster side.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Technical Details
2+
3+
The following sections go into more details of the behind the scenes magic.
4+
5+
## Synchronization
6+
7+
Even though the whole configuration is written from the standpoint of the service owner, the actual
8+
synchronization logic considers the kcp side as the canonical source of truth. The Sync Agent
9+
continuously tries to make the local objects look like the ones in kcp, while pushing status updates
10+
back into kcp (if the given `PublishedResource` (i.e. CRD) has a `status` subresource enabled).
11+
12+
## Local <-> Remote Connection
13+
14+
The Sync Agent tries to keep sync-related metadata on the service cluster, away from the consumers.
15+
This is both to prevent vandalism and to hide implementation details.
16+
17+
To ensure stability against future changes, once the Sync Agent has determined how a local object
18+
should be named, it will remember this decision in the object's metadata. This is so that on future
19+
reconciliations, the (potentially costly, but probably not) renaming logic does not need to be
20+
applied again. This allows the Sync Agent to change defaults and also allows the service owner to make
21+
changes to the naming rules without breaking existing objects.
22+
23+
Since we do not want to store metadata on the kcp side, we instead rely on label selectors on
24+
the local objects. Each object on the service cluster has a label for the remote cluster name,
25+
namespace and object name, and when trying to find the matching local object, the Sync Agent simply
26+
does a label-based search.
27+
28+
There is currently no sync-related metadata available on source objects (in kcp workspaces), as this
29+
would either be annotations (untyped strings...) or require schema changes to allow additional
30+
fields in basically random CRDs.
31+
32+
Note that fields like `generation` or `resourceVersion` are not relevant for any of the sync logic.
33+
34+
## Reconcile Loop
35+
36+
The sync loop can be divided into 5 parts:
37+
38+
1. find the local object
39+
2. handle deletion
40+
3. ensure the destination object exists
41+
4. ensure the destination object's content matches the source object
42+
5. synchronize related resources the same way (repeat 1-4 for each related resource)
43+
44+
### Phase 1: Find the Local Object
45+
46+
For this, as mentioned in the connection chapter above, the Sync Agent tries to follow label selectors
47+
on the service cluster. This helps prevent cluttering with consumer workspaces with sync metadata.
48+
If no object is found to match the labels, that's fine and the loop will continue with phase 2,
49+
in which a possible Conflict error (if labels broke) is handled gracefully.
50+
51+
The remote object in the workspace becomes the `source object` and its local equivalent on the
52+
service cluster is called the `destination object`.
53+
54+
### Phase 2: Handle Deletion
55+
56+
A finalizer is used in the kcp workspaces to prevent orphans in the service cluster side. This
57+
is the only real evidence in the kcp side that the Sync Agent is even doing things. When a remote
58+
(source) object is deleted, the corresponding local object is deleted as well. Once the local object
59+
is gone, the finalizer is removed from the source object.
60+
61+
### Phase 3: Ensure Object Existence
62+
63+
We have a source object and now need to create the destination. This chart shows what's happening.
64+
65+
```mermaid
66+
graph TB
67+
A(source object):::state --> B([cleanup if in deletion]):::step
68+
B --> C([ensure finalizer on source object]):::step
69+
C --> D{exists local object?}
70+
71+
D -- yes --> I("continue with next phase…"):::state
72+
D -- no --> E([apply projection]):::step
73+
74+
subgraph "ensure dest object exists"
75+
E --> G([ensure resulting namespace exists]):::step
76+
G --> H([create local object]):::step
77+
H --> H_err{Errors?}
78+
H_err -- Conflict --> J([attempt to adopt existing object]):::step
79+
end
80+
81+
H_err -- success --> I
82+
J --> I
83+
84+
classDef step color:#77F
85+
classDef state color:#F77
86+
```
87+
88+
After we followed through with these steps, both the source and destination objects exists and we
89+
can continue with phase 4.
90+
91+
Resource adoption happens when creation of the initial local object fails. This can happen when labels
92+
get mangled. If such a conflict happens, the Sync Agent will "adopt" the existing local object by
93+
adding / fixing the labels on it, so that for the next reconciliation it will be found and updated.
94+
95+
### Phase 4: Content Synchronization
96+
97+
Content synchronization is rather simple, really.
98+
99+
First the source "spec" is used to patch the local object. Note that this step is called "spec", but
100+
should actually be called "all top-level elements besides `apiVersion`, `kind`, `status` and
101+
`metadata`, but still including some labels and annotations"; so if you were to publish RBAC objects,
102+
the syncer would include `roleRef` field, for example).
103+
104+
To allow proper patch generation, the last known state is kept on the local object, similar to how
105+
`kubectl` creates an annotation for it. This is required for the Sync Agent to properly detect changes
106+
made by mutation webhooks on the service cluster.
107+
108+
If the published resource (CRD) has a `status` subresource enabled (not just a `status` field in its
109+
scheme, it must be a real subresource), then the Sync Agent will copy the status from the local object
110+
back up to the remote (source) object.
111+
112+
### Phase 5: Sync Related Resources
113+
114+
The same logic for synchronizing the main published resource applies to their related resources as
115+
well. The only difference is that the source side can be either remote (workspace) or local
116+
(service cluster).
117+
118+
Since the Sync Agent tries its best to keep sync-related data out of kcp workspaces, the last known
119+
state for related resources is _not_ kept together with the destination object in the kcp workspaces.
120+
Instead all known states (from the main object and all related resources) is kept in a single Secret
121+
on the service cluster side.

docs/mkdocs.yml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,6 @@ repo_url: https://github.com/kcp-dev/api-syncagent
1717
repo_name: kcp-dev/api-syncagent
1818
site_url: https://docs.kcp.io/api-syncagent/
1919

20-
# Site navigation
21-
nav:
22-
- Home: README.md
23-
- Getting Started: getting-started.md
24-
- Publishing Resources: publish-resources.md
25-
- Consuming Services: consuming-services.md
26-
- API Lifecycle: api-lifecycle.md
27-
- FAQ: faq.md
28-
- Release Process: releasing.md
29-
3020
# Site content
3121
docs_dir: 'content'
3222
# Where to generate

0 commit comments

Comments
 (0)