This document provides guidance on using virtual service templates to reuse common configurations across multiple virtual services.
Virtual service templates provide a way to reuse common configurations across multiple virtual services. Templates define a base configuration that can be extended or modified by individual virtual services. This mechanism helps maintain consistency and reduces duplication in your Envoy configuration.
When a virtual service references a template, the following process occurs:
The merging process happens during resource building, before the configuration is sent to Envoy. The template is applied to the virtual service’s spec, and then the resulting configuration is used to build the Envoy resources.
Template options allow you to control how specific fields from the template are handled when merging with the virtual service configuration. There are three modifiers available:
Each template option specifies a field path and a modifier. The field path identifies the field to apply the modifier to, and the modifier determines how the field is handled during merging.
The ExtraFields feature allows templates to define additional configurable fields that virtual services can provide values for. This enables parameterized templates where the configuration can be customized based on the values provided by the virtual service.
ExtraFields are defined in the template with the following properties:
When a virtual service uses a template with ExtraFields, it must provide values for all required fields and can optionally provide values for non-required fields. The system validates that:
Here’s a basic example of using a template:
apiVersion: envoy.kaasops.io/v1alpha1
kind: VirtualService
metadata:
name: demo-virtual-service
annotations:
envoy.kaasops.io/node-id: test
spec:
template:
name: https-template
virtualHost:
domains:
- example.com
Template definition:
apiVersion: envoy.kaasops.io/v1alpha1
kind: VirtualServiceTemplate
metadata:
name: https-template
spec:
listener:
name: https
accessLogConfig:
name: access-log-config
tlsConfig:
autoDiscovery: true
virtualHost:
name: test-virtual-host
routes:
- match:
prefix: "/"
direct_response:
status: 200
body:
inline_string: "{\"message\":\"Hello from template\"}"
In this example, the virtual service inherits all configuration from the template and adds the domains field to the virtualHost. The resulting configuration will have:
Here’s an example using template options to customize how fields are merged:
apiVersion: envoy.kaasops.io/v1alpha1
kind: VirtualService
metadata:
name: demo-virtual-service
annotations:
envoy.kaasops.io/node-id: test
spec:
template:
name: https-template
templateOptions:
- field: accessLogConfig
modifier: delete
- field: additionalHttpFilters
modifier: replace
additionalHttpFilters:
- my-filter-1
- my-filter-2
virtualHost:
domains:
- example.com
In this example:
accessLogConfig field from the template is deleted (not used in the final configuration)additionalHttpFilters field is replaced entirely with the new list instead of being mergedvirtualHost field is merged with the template’s virtual host (default behavior)You can override specific fields from the template by providing them in the virtual service:
apiVersion: envoy.kaasops.io/v1alpha1
kind: VirtualService
metadata:
name: demo-virtual-service
annotations:
envoy.kaasops.io/node-id: test
spec:
template:
name: https-template
accessLogConfig:
name: access-log-config-2 # Overrides the template's accessLogConfig
virtualHost:
domains:
- exc.kaasops.io
In this example, the accessLogConfig from the template is overridden with a different access log configuration.
You can delete a field from the template and replace it with a different type of configuration:
apiVersion: envoy.kaasops.io/v1alpha1
kind: VirtualService
metadata:
name: demo-virtual-service
annotations:
envoy.kaasops.io/node-id: test
spec:
template:
name: https-template
templateOptions:
- field: accessLogConfig
modifier: delete
accessLog: # Different type of access log configuration
name: envoy.access_loggers
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
path: /tmp/app.log
log_format:
json_format:
message: "%LOCAL_REPLY_BODY%"
status: "%RESPONSE_CODE%"
duration: "%DURATION%"
virtualHost:
domains:
- exc.kaasops.io
In this example, the accessLogConfig reference from the template is removed, and instead, an inline accessLog configuration is defined in the virtual service.
Here’s an example of a template with ExtraFields:
apiVersion: envoy.kaasops.io/v1alpha1
kind: VirtualServiceTemplate
metadata:
name: api-gateway-template
spec:
listener:
name: https
tlsConfig:
autoDiscovery: true
virtualHost:
name: api-gateway
routes:
- match:
prefix: "/api"
route:
cluster: ""
extraFields:
- name: api_service
description: "The name of the API service cluster"
type: "string"
required: true
- name: rate_limit
description: "Rate limit in requests per second"
type: "number"
default: "100"
- name: auth_enabled
description: "Enable authentication"
type: "boolean"
default: "true"
- name: log_level
description: "Logging level"
type: "enum"
enum: ["debug", "info", "warn", "error"]
default: "info"
And a virtual service using this template:
apiVersion: envoy.kaasops.io/v1alpha1
kind: VirtualService
metadata:
name: user-api-service
annotations:
envoy.kaasops.io/node-id: test
spec:
template:
name: api-gateway-template
extraFields:
api_service: "user-service"
rate_limit: "200"
auth_enabled: "false"
log_level: "debug"
virtualHost:
domains:
- api.example.com
In this example:
api_service (required), rate_limit, auth_enabled, and log_level (enum)api_service value in the route configuration with variable substitution (``)Here’s a more complex example with multiple ExtraFields and variable substitution:
apiVersion: envoy.kaasops.io/v1alpha1
kind: VirtualServiceTemplate
metadata:
name: microservice-template
spec:
listener:
name: https
tlsConfig:
autoDiscovery: true
virtualHost:
name: "-service"
domains:
- "."
routes:
- match:
prefix: "/api/v"
route:
cluster: "-v"
timeout: "s"
- match:
prefix: "/health"
direct_response:
status: 200
body:
inline_string: "{\"status\":\"healthy\"}"
extraFields:
- name: service_name
description: "The name of the microservice"
type: "string"
required: true
- name: domain
description: "The domain for the service"
type: "string"
required: true
- name: api_version
description: "API version number"
type: "number"
default: "1"
- name: timeout
description: "Request timeout in seconds"
type: "number"
default: "30"
And a virtual service using this template:
apiVersion: envoy.kaasops.io/v1alpha1
kind: VirtualService
metadata:
name: payment-service
annotations:
envoy.kaasops.io/node-id: test
spec:
template:
name: microservice-template
extraFields:
service_name: "payment"
domain: "example.com"
api_version: "2"
timeout: "15"
additionalHttpFilters:
- name: envoy.filters.http.ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
domain: "payment-ratelimit"
rate_limit_service:
grpc_service:
envoy_grpc:
cluster_name: ratelimit-service
In this example:
service_name, domain, api_version, and timeoutWhen merging nested fields, the behavior depends on the field type:
For objects, fields are merged recursively. If a field exists in both the template and the virtual service, the value from the virtual service takes precedence for primitive types. For nested objects, the merging continues recursively.
Example:
Template:
virtualHost:
name: test-virtual-host
routes:
- match:
prefix: "/"
direct_response:
status: 200
Virtual Service:
virtualHost:
domains:
- example.com
routes:
- match:
prefix: "/api"
direct_response:
status: 201
Result (merged):
virtualHost:
name: test-virtual-host
domains:
- example.com
routes:
- match:
prefix: "/"
direct_response:
status: 200
- match:
prefix: "/api"
direct_response:
status: 201
By default, lists are merged by appending items from the virtual service to the items from the template. If you want to replace the entire list, use the replace modifier.
Example with default merging:
Template:
additionalHttpFilters:
- template-filter-1
- template-filter-2
Virtual Service:
additionalHttpFilters:
- service-filter-1
Result (merged):
additionalHttpFilters:
- template-filter-1
- template-filter-2
- service-filter-1
Example with replace modifier:
Template:
additionalHttpFilters:
- template-filter-1
- template-filter-2
Virtual Service:
templateOptions:
- field: additionalHttpFilters
modifier: replace
additionalHttpFilters:
- service-filter-1
Result (replaced):
additionalHttpFilters:
- service-filter-1
When a template includes ExtraFields, it can use the values provided by the virtual service for variable substitution in the template configuration. This is done using Go template syntax with the `` notation.
Here’s an example of how variable substitution works:
Template:
apiVersion: envoy.kaasops.io/v1alpha1
kind: VirtualServiceTemplate
metadata:
name: dynamic-route-template
spec:
virtualHost:
name: "-host"
routes:
- match:
prefix: "/"
route:
cluster: ""
timeout: "s"
extraFields:
- name: service_name
description: "Service name"
type: "string"
required: true
- name: path_prefix
description: "URL path prefix"
type: "string"
required: true
- name: target_cluster
description: "Target cluster name"
type: "string"
required: true
- name: timeout
description: "Request timeout in seconds"
type: "number"
default: "30"
Virtual Service:
apiVersion: envoy.kaasops.io/v1alpha1
kind: VirtualService
metadata:
name: user-service
spec:
template:
name: dynamic-route-template
extraFields:
service_name: "user"
path_prefix: "users"
target_cluster: "user-service"
timeout: "15"
virtualHost:
domains:
- api.example.com
Result (after variable substitution and merging):
apiVersion: envoy.kaasops.io/v1alpha1
kind: VirtualService
metadata:
name: user-service
spec:
virtualHost:
name: "user-host"
domains:
- api.example.com
routes:
- match:
prefix: "/users"
route:
cluster: "user-service"
timeout: "15s"
Variable substitution happens before the merging process, so the template is first rendered with the provided ExtraFields values, and then the virtual service’s configuration is merged with the rendered template.