Customization
By default, the Portal Controller generates entities straight into the writer, no customization
needed. If you want to shape the entities a Promise (and its Resources) publish to a portal, you
can bind it to a PortalCustomization.
A PortalCustomization declares containers that run between generate and writer and edit the
generated documents in place:
generate → configure (this profile's containers, in order) → writer
The lane runs any container image you give it, so you can use your own tooling, an off-the-shelf image, or the helper described below.
If you would rather not write a container, Portal Patch is a helper image we ship that applies declarative recipes in this lane, covering setting owners and tags, removing form fields, and reshaping Cortex workflows. It is entirely optional, and you can mix it with your own containers in the same pipeline.
Creating a PortalCustomization
PortalCustomization (platform.syntasso.io/v1alpha1) is a cluster-scoped resource:
apiVersion: platform.syntasso.io/v1alpha1
kind: PortalCustomization
metadata:
name: backstage-add-team-owner
spec:
portalType: backstage # required: the portal type this profile applies to
promise:
configure:
containers:
- name: portal-patch
image: ghcr.io/syntasso/portal-patch:0.7.0
args:
- |
target:
kind: Component
patch:
spec:
owner: platform-team
resource:
configure:
containers:
- name: portal-patch
image: ghcr.io/syntasso/portal-patch:0.7.0
args:
- |
target:
kind: Component
patch:
metadata:
tags: [tier-1]
spec.portalType(required) is the portal type this profile applies to, for examplebackstage. It is never a specific portal, credential, or URL, those live on theSKEIntegration.spec.promise.configureruns for the Promise-level sync.spec.resource.configureruns once per bound Resource request.
Either lane may be omitted. A profile with only promise leaves Resource syncs untouched, and the
other way round.
The configure lane is a Kratix pipeline
Each lane is a full Kratix PipelineSpec, not just a list of containers. Alongside
containers[], which take the usual image, args, command, env, envFrom, volumeMounts,
resources and securityContext, you can set volumes, imagePullSecrets, rbac, jobOptions,
nodeSelector and tolerations.
Containers run in the order listed. They read the parent object from /kratix/input/object.yaml
and edit the generated documents in /kratix/output in place.
Do not write /kratix/metadata/destination-selectors.yaml from a configure container unless you
are deliberately redirecting output. The generate stage writes one entry for its output directory,
and duplicate entries for the same directory are rejected by the writer.
Configure containers run for the configure action only. The delete pipeline is writer-only, so a configure container never runs when a Promise or Resource is deleted.
Binding a Promise to a PortalCustomization
Label the Promise with the profile's name:
metadata:
labels:
kratix.io/portal-customization: backstage-add-team-owner
The label alone is enough to opt in. The portal type is derived from the profile's own
spec.portalType, so you do not also need a kratix.io/portal-<type> label.
A Promise may be bound to one PortalCustomization only. If more than one
kratix.io/portal-customization label is present, the Promise reports PortalBindingReady: False
with reason AmbiguousCustomization and does not sync.
To apply several sets of changes, list multiple containers in one profile rather than binding multiple profiles.
If you have more than one portal of the profile's type, use an instance suffix to say which portal the Promise should sync to. The suffix names the portal, not a second profile:
metadata:
labels:
kratix.io/portal-customization.staging: backstage-add-team-owner
Worked example: setting owner and tags without Portal Patch
Where a recipe will not do, any container that can read and write YAML works:
apiVersion: platform.syntasso.io/v1alpha1
kind: PortalCustomization
metadata:
name: cicd-backstage-customization
spec:
portalType: backstage
resource:
configure:
containers:
- name: set-owner-and-tags
image: ghcr.io/syntasso/kratix-pipeline-utility:v0.0.1
command: ["sh", "-c"]
args:
- |
set -e
file=/kratix/output/catalog-info.yaml
yq -i '.spec.owner = "platform-team"' "$file"
yq -i '.metadata.tags = (.metadata.tags // []) + ["tier-1", "managed-by-kratix"]' "$file"
Bind the Promise to it:
metadata:
labels:
kratix.io/portal-customization: cicd-backstage-customization
Status
A PortalCustomization reports a single Accepted condition. This reflects whether the resource
itself is structurally valid, Validated when it is and PortalTypeMissing when spec.portalType
is absent. It does not reflect whether any particular Promise's binding to it resolved.
Whether a Promise's binding resolved is reported on the Promise, via the PortalBindingReady
condition described in Installation.
PortalCustomization resources are cluster-wide. Give them unique, descriptive names.
Examples
- Portal Patch recipe reference for the full declarative grammar.
- Cortex examples for worked examples against Cortex documents.
