Migration from HAProxy to OORT
Switching away from a deprecated HAProxy solution
Initially the default CI deployments provided by Jutro's pipeline generator have been based on a HAProxy Ingress. However, the Atmos team is going to drop support for this implementation of Ingress and the recommendation has been made to switch to the OORT-backed solution instead. This guide aims to support the owners of existing Jutro apps with CI pipelines already generated with the process of migrating to the new solution.
Setting up a gateway config
- Start from creating a gateway manifest file:
touch k8s/gateway.yml
- Populate the file with the first config's skeleton:
apiVersion: oort.ccs.guidewire.com/v1alpha1
kind: GatewayConfig
metadata:
namespace: ${NAMESPACE}
name: <%=appNameLowercase=%>-${DEPLOY_ENV}-gateway
spec:
profiles:
- name: Default
cors:
origins:
- .*\.guidewire\.net
- .*localhost.*
methods:
- GET
- OPTIONS
headers:
- '*'
credentials: true
request_transformer:
add:
headers:
- Host:tenant-<%=podName=%>-<%=s3FrontendBucketName=%>-${S3_SUFFIX}.s3-website.us-west-2.amazonaws.com
response_transformer:
add:
headers:
- Content-Security-Policy:frame-ancestors https://*.guidewire.net
path_configs:
- name: Default
paths:
- /**
profiles:
- Default
service_ref: <%=serviceName=%>
service_alias: <%=serviceAlias=%>
enable_sub_domain: false
Content-Security-Policy:frame-ancestors https://*.guidewire.net setting prevents clickjacking attacks. Do not modify or remove it. For more information see the OWASP page on clickjacking.- Replace app-specific parts of the above config with values appropriate for your app. In case of doubts, it might be a good idea to simply copy the existing corresponding values from the
k8s/ingress.ymlfile.
<%=appsNameLowercase=%>- Lowercased descriptive name of your app. Appropriate value can be figured out from thek8s/ingress.ymlfile, specifically themetadata.nameproperty there.<%=podName=%>- Name of your Atmos tenant. Typically your pod's name.<%=s3FrontendBucketName=%>- Name of your apps bucket. Typically constructed as the name of your app (same value as<%=appsNameLowercase=%>) with-bucketsuffix appended.<%=serviceName=%>- Name of yourExternalNameservice. Can be extracted out from your app'sExternalNameservice defined ink8s/service.ymlfile - specifically themetadata.nameproperty there.<%=serviceAlias=%>- Main part of the URL under which your app's deploment should be available, eventually combined with the default OORT's suffix for the final URL (for example,<%=serviceAlias=%>.dev.ccs.guidewire.netfor the deployments todevcluster or<%=serviceAlias=%>.int.ccs.guidewire.netfor the deployments tointcluster).
spec.profiles[0].request_transformer.add.headers[0] property can be copied from the existing k8s/ingress.yml file's Ingress config, specifically the value specified for its metadata.annotations['ingress.kubernetes.io/config-backend'] property, next to the http-request set-header Host phrase there.For example based on the existing ingress.yml file like:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
namespace: ${NAMESPACE}
name: myjutroapp-${DEPLOY_ENV}-ingress
annotations:
ingress.kubernetes.io/config-backend: |
http-request set-header Host tenant-mypod-myjutroapp-${S3_SUFFIX}.s3-website.us-west-2.amazonaws.com
ingress.kubernetes.io/enable-cors: \"true\"
ingress.kubernetes.io/cors-allow-methods: GET,OPTIONS
ingress.kubernetes.io/cors-allow-credentials: \"true\"
ingress.kubernetes.io/cors-allow-headers: GW-Tenant,GW-Request-GRN,Accept,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,Pragma
spec:
rules:
- host: 'myjutroapp-${NAMESPACE}.${DEPLOY_ENV}.ccs.guidewire.net'
http:
paths:
- backend:
serviceName: myjutroapp-external-name-service
servicePort: 80
path: /
We can have the following gateway.yml file created:
apiVersion: oort.ccs.guidewire.com/v1alpha1
kind: GatewayConfig
metadata:
namespace: ${NAMESPACE}
name: myjutroapp-${DEPLOY_ENV}-gateway
spec:
profiles:
- name: Default
cors:
origins:
- .*\.guidewire\.net
- .*localhost.*
methods:
- GET
- OPTIONS
headers:
- '*'
credentials: true
request_transformer:
add:
headers:
- Host:tenant-mypod-myjutroapp-${S3_SUFFIX}.s3-website.us-west-2.amazonaws.com
response_transformer:
add:
headers:
- Content-Security-Policy:frame-ancestors https://*.guidewire.net
path_configs:
- name: Default
paths:
- /**
profiles:
- Default
service_ref: myjutroapp-external-name-service
service_alias: myjutroapp-${NAMESPACE}
enable_sub_domain: false
Note: make sure to apply the appropriate adjustments corresponding to the content of your existing
ingress.ymlfile
- Based on the newly created
gateway.ymlfile let's also create a similar file dedicated to prod deployments.
touch k8s/gateway-prod.yml
Initially it can get fulfilled with the content previously added to the gateway.yml file, with the only two differences being spec.profiles[0].request_transformer.add.headers[0] property adjusted (make sure to replace the placeholders with your own values):
Host:tenant-<%=podName=%>-<%=s3FrontendBucketName=%>-${S3_SUFFIX}.s3-website.us-east-2.amazonaws.com
and an extra spec.base_domain prop added to it:
base_domain: us-east-2.service.guidewire.net
-
In the same file, an extra
internalendpoint needs to be exposed as well. For that, again in the same file:5.1. Copy its existing content, add
---line right below it and then again below it paste the copied content. Specifically for the cloned block apply the following adjustments:5.2. Append
-internalsuffix to themetadata.nameproperty.5.3. Add a new
spec.profiles[0].internal_onlyproperty:internal_only: true5.4. Adjust the
base_domainto start with theinternal.prefix:internal.us-east-2.service.guidewire.net
The final structure of gateway-prod.yml for our example case can look like the following example:
apiVersion: oort.ccs.guidewire.com/v1alpha1
kind: GatewayConfig
metadata:
namespace: ${NAMESPACE}
name: myjutroapp-${DEPLOY_ENV}-gateway
spec:
profiles:
- name: Default
cors:
origins:
- .*\.guidewire\.net
methods:
- GET
- OPTIONS
headers:
- '*'
credentials: true
request_transformer:
add:
headers:
- Host:tenant-mypod-myjutroapp-${S3_SUFFIX}.s3-website.us-east-2.amazonaws.com
response_transformer:
add:
headers:
- Content-Security-Policy:frame-ancestors https://*.guidewire.net
path_configs:
- name: Default
paths:
- /**
profiles:
- Default
service_ref: myjutroapp-external-name-service
service_alias: myjutroapp-${NAMESPACE}
base_domain: us-east-2.service.guidewire.net
enable_sub_domain: false
---
apiVersion: oort.ccs.guidewire.com/v1alpha1
kind: GatewayConfig
metadata:
namespace: ${NAMESPACE}
name: myjutroapp-${DEPLOY_ENV}-gateway-internal
spec:
profiles:
- name: Default
internal_only: true
cors:
origins:
- .*\.guidewire\.net
methods:
- GET
- OPTIONS
headers:
- '*'
credentials: true
request_transformer:
add:
headers:
- Host:tenant-mypod-myjutroapp-${S3_SUFFIX}.s3-website.us-east-2.amazonaws.com
response_transformer:
add:
headers:
- Content-Security-Policy:frame-ancestors https://*.guidewire.net
path_configs:
- name: Default
paths:
- /**
profiles:
- Default
service_ref: myjutroapp-external-name-service
service_alias: myjutroapp-${NAMESPACE}
base_domain: internal.us-east-2.service.guidewire.net
enable_sub_domain: false
ingress-prod.yml file.-
In the
.teamcity/settings.ktsTeamCity config file, adjust the existing references toingress[-prod].ymlfiles and switch them to the newly createdgateway[-prod].yml. -
Once your new manifests are adjusted to reflect the existing
ingress[-prod].ymlconfigs and are confirmed to meet your needs, remove the oldingress.ymlandingress-prod.ymlfiles. Also make sure to delete the existing HAProxy-based resources from the appropriate clusters before fully switching to OORT, in order to avoid conflicts with exposing 2 configs using the same URL underneath.