-
Notifications
You must be signed in to change notification settings - Fork 11
Migration v1 to v2
v2 makes four breaking changes, notably to the TwingateResource CRD, to better support multiple Identity Firewall protocols and integration with the Gateway. Start with Does this affect you? to see which of them apply to your install.
Throughout this page, a TwingateResource is the Kubernetes object and a Twingate Resource is what it registers with Twingate, identified by spec.id. The distinction is what decides whether access is interrupted: an object that keeps its spec.id has its Twingate Resource updated in place, while one that is replaced has its Twingate Resource deprovisioned and a new one registered.
<release> in the object names below is your Helm release name, twop in the command examples. If your release name already contains gateway, or you set the subchart's gateway.fullnameOverride, the subchart drops the -gateway suffix and the object names differ; confirm them with kubectl get twingateresource.
<release> in the object names below is your Helm release name, twop in the command examples. They assume the subchart's fullname is <release>-gateway, which differs if your release name already contains gateway or you set gateway.nameOverride or gateway.fullnameOverride; confirm the names with kubectl get twingateresource.
| If your install has | What you have to do | Access impact |
|---|---|---|
| Any install | Apply the new CRDs, then run helm upgrade
|
None |
The bundled gateway subchart (gateway.enabled: true) |
Upgrade through v1.3 first, then restructure the subchart values | None |
| The bundled gateway subchart, and you cannot reach v1.3 | Disable the subchart, delete the in-cluster Kubernetes resource, then re-provision it | Kubernetes API access is down until it is re-provisioned |
A hand-authored TwingateResource of type: Kubernetes
|
Bind it to a TwingateGateway by patching spec.gatewayRef onto it |
None |
A TwingateResourceAccess outside the default namespace whose resourceRef omits namespace
|
Delete it and recreate it with an explicit resourceRef.namespace, while still on v1 (step 1) |
That grant is down between the delete and the recreate |
Legacy twingate.com/resource* annotations on a Service
|
Rename them to the resource.twingate.com* form, while still on v1 (step 2) |
Network resources are interrupted until the replacement reconciles |
The bundled gateway subchart no longer configures in-cluster Kubernetes access by annotating the Gateway Service. It renders the TwingateGateway, its TwingateCertificateAuthority, and the TwingateResource directly, so the twingate.resource block and its extraAnnotations are replaced by fields under twingateOperator.gateway.* and twingateOperator.kubernetesResource.*. See Gateway subchart values for the before and after.
The proxy serving a Kubernetes resource is now a separate TwingateGateway object, referenced with spec.gatewayRef, in place of the inline spec.proxy block (see Identity Firewall).
-
spec.proxyis removed from the CRD, along with itsaddress,certificateAuthorityCert, andcertificateAuthorityCertSecretReffields. -
spec.gatewayRefis required onKubernetesandWebAppresources, and rejected onNetworkones.
Applying the new CRDs does not rewrite stored objects, but from that point a Kubernetes resource without gatewayRef is frozen: it can be read, and the only write the API server accepts is one that adds gatewayRef (which also drops spec.proxy). Keep that window short; see If you hand-authored Kubernetes resources.
A reference that omits namespace now resolves in the declaring object's namespace instead of the default namespace, following Kubernetes convention. On a TwingateResourceAccess:
-
resourceRefhas to be fixed. It carried no default in the v1 CRD, so nothing is stored on existing objects and resolution moves the moment the v2 operator runs, silently granting or withdrawing access. Pin it while still on v1, in Before you upgrade, step 1. -
groupRefneeds no action. The v1 CRD storednamespace: defaulton every object, so it keeps resolving to thedefaultnamespace no matter which namespace theTwingateResourceAccessis in.
See Namespace resolution for the detail.
The old twingate.com/resource* annotations no longer work; use the resource.twingate.com* form. The mapping is:
| v1 (removed) | v2 |
|---|---|
twingate.com/resource |
resource.twingate.com |
twingate.com/resource-name |
resource.twingate.com/name |
twingate.com/resource-alias |
resource.twingate.com/alias |
twingate.com/resource-isVisible |
resource.twingate.com/isVisible |
twingate.com/resource-isBrowserShortcutEnabled |
resource.twingate.com/isBrowserShortcutEnabled |
twingate.com/resource-securityPolicyId |
resource.twingate.com/securityPolicyId |
twingate.com/resource-syncLabels |
resource.twingate.com/syncLabels |
Apply both while still running v1. Neither needs the v2 operator. Step 1 has to land before the v2 operator starts, because that is when resourceRef resolution moves. Doing it before you apply the new CRDs is less work: the v1 CRDs still inject namespace: default into groupRef on write, and once they are replaced you have to set that namespace yourself.
-
Pin
resourceRefreferences. If aTwingateResourceAccessoutside thedefaultnamespace references aTwingateResourcewithout naming its namespace, delete it and recreate it with an explicitresourceRef.namespace.resourceRefis immutable, so it cannot be patched in place, and the principal loses access to the resource until the object is back.Recreate it while still on v1 so
groupRefkeeps its storednamespace: default. If you instead recreate it after applying the v2 CRDs, setgroupRef.namespaceexplicitly whenever theTwingateGroupis not in theTwingateResourceAccess's own namespace. -
Rename legacy annotations. Rename any legacy annotations on your
Serviceobjects to theresource.twingate.com*form using the mapping in Legacy Service annotations removed.On
Networkresources the rename replaces the generatedTwingateResource, so its Twingate Resource is deprovisioned and a new one is registered under a newspec.id. Access through it is interrupted until the replacement reconciles.TwingateResourceAccessobjects reference the generated object by its name,<service>-resource, which does not change, so they rebind with no edits.
Complete Before you upgrade on v1, then follow the steps below.
If you also hand-authored any TwingateResource of type: Kubernetes, each has to be bound to a TwingateGateway; see If you hand-authored Kubernetes resources.
-
Upgrade to v1.3 (bundled gateway subchart only). Skip this step if
gateway.enabledisfalse. v1.3 prepares the in-cluster Kubernetes resource so the v2 subchart can take it over instead of re-creating it; see Why the upgrade goes through v1.3.Keep your existing v1 values here; restructuring them early drops the
resource.twingate.comannotations from theService, which removes the in-cluster Kubernetes resource.helm upgrade twop oci://ghcr.io/twingate/helmcharts/twingate-operator --version 1.3.0 --install --wait -f ./values.yaml
v1.3 stamps the ownership metadata when it starts, and the pod reporting ready does not mean it has finished. To check that it landed, the command below prints your release name once the stamp is in place and nothing before that:
kubectl get twingateresource <release>-gateway-resource \ -o jsonpath='{.metadata.annotations.meta\.helm\.sh/release-name}{"\n"}'
-
Apply the new CRDs. Helm does not upgrade CRDs, and they are not published on their own; they ship inside the chart, so you must apply them yourself or the new fields and validations will be missing. Pull the same chart you are about to install and apply the CRDs from its
crds/directory so they match the chart version:helm pull oci://ghcr.io/twingate/helmcharts/twingate-operator --version 2.0.0 --untar kubectl apply -f twingate-operator/crds/
-
Upgrade the operator. If you use the bundled gateway subchart, restructure its values first: the subchart now renders the Gateway custom resources directly instead of annotating the Gateway
Service, so the old block has to be replaced (see Gateway subchart values).gateway.enabledstaystruethroughout.This upgrade also registers a
TwingateCertificateAuthority, which the v1 subchart never created. Its name has to be unique across your Twingate network and is immutable once set, so this is where the name is fixed. The subchart derives one by hashing the cluster and the release namespace, which needs a live cluster read: rendering without one, as with Argo CD,helm template, or--dry-run=client, fails until you setgateway.twingateOperator.gateway.certificateAuthority.name. The failure message gives that path astwingateOperator.gateway.certificateAuthority.name, which is relative to the subchart. See Bundled subchart.helm upgrade twop oci://ghcr.io/twingate/helmcharts/twingate-operator --version 2.0.0 --install --wait -f ./values.yaml
Disable the subchart first so in-cluster access can be recreated cleanly under the new model. Unlike the v1.3 path, nothing here preserves the existing object: it is recreated rather than adopted, so it keeps the Kubernetes object name <release>-gateway-resource but spec.id names a new Twingate Resource.
Warning
Twingate access to the cluster's Kubernetes API is down from the moment the subchart is disabled in step 1 until step 3 re-provisions it. Plan a maintenance window.
-
Set
gateway.enabled: falseand upgrade, still on v1. -
Delete the in-cluster Kubernetes resource:
kubectl delete twingateresource <release>-gateway-resource --ignore-not-found
-
Run Upgrade to v2 from step 2, with
gateway.enabled: truerestored.
Step 1 deletes the Gateway Service, and the generated TwingateResource is garbage-collected behind it through its owner reference, so step 2 is usually a no-op. It is there because helm upgrade does not wait for that collection: if the object is still present when step 3 renders one under the same name, the upgrade aborts with an ownership error, since the v1 operator created it and it carries no Helm metadata.
If the delete does not return, the operator could not deregister the resource from Twingate; check its log. Clearing metadata.finalizers forces the object out, but then the Twingate Resource is left behind and has to be deleted in the admin console.
Only applies if you hand-authored a TwingateResource of type: Kubernetes. These are backed by the removed spec.proxy field, so each one has to be bound to a TwingateGateway instead.
Stand up that TwingateGateway before you patch. It carries the certificate authority that spec.proxy.certificateAuthorityCert used to hold, so it is a prerequisite, not a follow-up (see Identity Firewall). If the bundled subchart is providing it, that is step 3 of Upgrade to v2, so these objects stay frozen from step 2 until you patch; the sooner you have the Gateway ready, the shorter that window.
Then patch spec.gatewayRef onto the existing object rather than replacing it:
kubectl patch twingateresource my-cluster --type=merge \
-p '{"spec":{"gatewayRef":{"name":"my-gateway"}}}'spec.proxy is dropped on the same write, so it does not need removing. The object keeps its spec.id, so the operator updates the Twingate Resource it already registered instead of registering a new one, and the access granted to it is preserved. Do not delete and re-create the object: that deprovisions its Twingate Resource and takes access down until the replacement reconciles.
Set gatewayRef.namespace as well when the Gateway is in another namespace.
Before (v1 subchart, annotation-based):
# values.yaml
twingateOperator:
network: "<network slug>"
apiKey: "<api key>"
remoteNetworkId: "<remote network id>"
gateway:
enabled: true
twingate:
network: "<network slug>"
# v1: Kubernetes resource configured via annotations here; removed in v2
resource:
enabled: true
extraAnnotations:
resource.twingate.com/name: "My Kubernetes Cluster"
resource.twingate.com/alias: "my-k8s-cluster.int"After (v2 subchart, resource fields):
# values.yaml
twingateOperator:
network: "<network slug>"
apiKey: "<api key>"
remoteNetworkId: "<remote network id>"
gateway:
enabled: true
twingate:
network: "<network slug>"
# v2: replaces the v1 twingate.resource block
twingateOperator:
gateway:
enabled: true
# Required only when rendering without cluster access, as with Argo CD:
# certificateAuthority:
# name: "<a name unique within your Twingate network>"
kubernetesResource:
enabled: true
name: "My Kubernetes Cluster"
alias: "my-k8s-cluster.int"What moved:
-
gateway.twingate.resource.enabledsplits into two toggles:gateway.twingateOperator.gateway.enabledrenders the Gateway, andgateway.twingateOperator.kubernetesResource.enabledrenders the in-cluster Kubernetes resource. The operator chart already defaults both totrue, so they only need setting to turn something off. Enabling the resource without the Gateway fails the render rather than skipping it. - The
resource.twingate.com/*annotations undergateway.twingate.resource.extraAnnotationsbecome fields undergateway.twingateOperator.kubernetesResource(name,alias,securityPolicyId, and so on). Any otherTwingateResourcespec field passes through as well, apart fromtype,address, andgatewayRef, which the subchart sets itself, andenabled, which is the subchart's own toggle.namepasses through but defaults to<release>-gateway Kubernetes clusterrather than being left unset. -
remoteNetworkIdis the one exception to that pass-through: set it asgateway.twingateOperator.remoteNetworkId, which the subchart applies to the Gateway and the resource together. Setting it underkubernetesResourcefails the render withtwingateOperator.kubernetesResource.remoteNetworkId is not supported, set twingateOperator.remoteNetworkId instead.
Remove the old twingate.resource block; the v2 subchart's schema rejects it.
- v1.3 stamps Helm's ownership metadata onto the
TwingateResourcethe operator generated from the GatewayService. Without it, Helm refuses to take over an object it did not create. With it, the v2 subchart adopts the existing object instead of creating a second one, so it keeps itsspec.id, and the Twingate Resource and the access granted to it are preserved. - v1.3 also stops deleting that object when the subchart drops the
resource.twingate.comannotations. In v1.2 that removal deprovisions the Twingate Resource in the middle of the upgrade. - The v2 subchart declares
gatewayRefon the resource, but Helm 3 adopts an existing object without patching its fields, so on an adopted resource the field starts out unset. The v2 operator binds the resource to theTwingateGatewayrendered from the sameServiceand drops the now-staleServiceowner reference, retrying until that Gateway exists, so no manual step is required. Clients that use server-side apply, such as Helm 4 and Argo CD, set the field themselves.
Two fields matter for objects created under v1, both on TwingateResourceAccess:
-
spec.resourceRef.namespaceneeds fixing. It had no default in the v1 CRD, so nothing is stored on existing objects. The fallback to thedefaultnamespace lived in the operator, which is why this is the one field that changes with no edit: resolution moves to the declaring namespace as soon as the v2 operator is running. -
spec.groupRef.namespaceneeds no action. It carrieddefault: default, so the API server stampednamespace: defaultonto it on write. Existing objects still carry that value and keep resolving to thedefaultnamespace whichever operator version runs; objects written after the new CRDs are applied carry no stamped value, so theirgroupRefresolves in the declaring namespace once the v2 operator is running.
Both fields are immutable as a whole, so neither can be edited in place. That is why fixing resourceRef means deleting and recreating the TwingateResourceAccess, and why an existing groupRef cannot later be pointed at a TwingateGroup outside the default namespace without doing the same.
Set twingateOperator.gateway.certificateAuthority.name to a name unique within your
Twingate network. The chart derives one from the kube-system namespace UID, which is
unavailable when rendering without cluster access such as helm template, --dry-run=client
or Argo CD.
The subchart cannot derive the certificate authority name without a live cluster read. Set gateway.twingateOperator.gateway.certificateAuthority.name in the operator chart's values.yaml; the path in the message is relative to the subchart. See Bundled subchart for the values.yaml shape.
Error: UPGRADE FAILED: Unable to continue with update: TwingateResource
"twop-gateway-resource" in namespace "default" exists and cannot be imported into the
current release: invalid ownership metadata; annotation validation error: missing key
"meta.helm.sh/release-name": must be set to "twop"; annotation validation error: missing
key "meta.helm.sh/release-namespace": must be set to "default"
Helm found a TwingateResource it does not own: the ownership metadata is stamped by v1.3, and v1.3 was skipped.
Recover by running If you cannot upgrade through v1.3 from step 1, still on your v1 chart. Do not just fix the metadata and retry: the v2 subchart stops annotating the Gateway Service, and your pre-1.3 operator deletes the generated TwingateResource as soon as those annotations come off. That operator is still running when Helm updates the Service, so it would delete the object the v2 chart has just taken over. Step 1 of that section deletes the Service while you are still on v1, which leaves the old operator nothing to react to.
If you never set a name for the in-cluster Kubernetes resource, the name it shows in the Twingate Admin Console changes: v1 fell back to the Kubernetes object name <release>-gateway-resource, and the v2 subchart names it <release>-gateway Kubernetes cluster instead. This is a display change only, as the resource itself is the same one and keeps its access. Set gateway.twingateOperator.kubernetesResource.name to keep the old name or choose your own.
Copyright © 2023 Twingate.