Hey Sanguita, I am not fully clear on what the question is, but it looks like you are trying to dynamically generate the TargetEndpoint. This is definitely possible … but the information has to be there in the template input context to start with. Since you are using overlays already … you could put this information in an overlay document.
For example, you could have a target.yaml overlay file that looks like this:
overlay: "1.0.0"
actions:
- target: "$" # This JSONPath targets the root (top level) of the document
update: # This operation adds or merges the 'value' into the target
x-target:
# Provide EITHER a direct URL...
URL: https://my-single-backend.example.com/api/v1
# ...OR a LoadBalancer configuration for one or more servers
LoadBalancer:
Algorithm: RoundRobin # Other options: Weighted, LeastConnection
Server:
- name: backend-server-1
# You can add other server properties like 'weight' or 'sslInfo' here
- name: backend-server-2
# Optional section for health monitoring
HealthMonitor:
IsEnabled: true
IntervalInSec: 10
TimeoutInSec: 5
Protocol: HTTP # Can be TCP, HTTP, or HTTPS
Request:
Path: /health
Port: 8080 # The port on the backend server to check
Verb: GET
SuccessCodes: # HTTP codes that indicate a healthy response
- '200'
- '201'
FailureThreshold: 3 # Number of failed checks before marking as unhealthy
And an apiproxy.yaml template that looks like this:
(Note, I took the OAS3 sample template form the apigee-go-gen repo, and made the target dynamic based on your description)
APIProxy:
.revision: 1
.name: {{ slug_make ($.Values.spec | dig "info" "x-serviceName" $.Values.spec.info.title) }}
DisplayName: {{ $.Values.spec.info.title }}
Description: |-
{{ $.Values.spec.info.description | nindent 4 }}
Policies: {{ include "./policies.yaml" . | nindent 2 }}
ProxyEndpoints:
- ProxyEndpoint:
.name: default
PreFlow:
.name: PreFlow
Request:
- Step:
Name: Spike-Arrest
- Step:
Name: OAS-Validate
Flows:
#{{- range $path, $pathItem := $.Values.spec.paths }}
#{{- range $verb, $opItem := $pathItem }}
#{{- if not (regexMatch "^(post|get|put|delete|trace|options|head|patch)$" $verb) }}
#{{- continue }}
#{{- end }}
#{{- if eq (include "get_visibility" $opItem) "INTERNAL" }}
#{{- fmt_printf "Skipping internal operation '%s' (%s %s)\n" $opItem.operationId $verb $path }}
#{{- continue }}
#{{- end }}
- Flow:
.name: {{ $opItem.operationId }}
Condition: (proxy.pathsuffix MatchesPath "{{ regexReplaceAll "{[^}]*}" $path "*" }}") and (request.verb = "{{ $verb | upper }}")
#{{- end }}
#{{- end }}
- Flow:
.name: CatchAll
Request:
- Step:
Name: RF-CatchAll
PostClientFlow:
.name: SamplePostClientFlow
Description: Processed after the response is sent back to the client.
Response:
Step:
Name: ML-Logging-OK
HTTPProxyConnection:
BasePath: {{ include "get_basepath" (index $.Values.spec.servers 0 "url") }}
RouteRule:
.name: default
TargetEndpoint: default
TargetEndpoints:
- TargetEndpoint:
.name: default
PreFlow:
.name: PreFlow
Flows: []
PostFlow:
.name: PostFlow
# --- START: x-target LOGIC ---
# Create a variable for the x-target extension, if it exists
#{{- $target := dig "x-target" "" $.Values.spec -}}
#{{- if and $target (or $target.URL $target.LoadBalancer) }}
# x-target extension is present, use it to build the connection
HTTPTargetConnection:
#{{- if $target.URL }}
# Case 1: A simple URL is provided
URL: {{ $target.URL }}
#{{- else if $target.LoadBalancer }}
# Case 2: A LoadBalancer is provided
LoadBalancer:
- Algorithm: {{ $target.LoadBalancer.Algorithm | default "RoundRobin" }}
#{{- if and $target.HealthMonitor }}
# Map the HealthMonitor's threshold to MaxFailures
- MaxFailures: {{ $target.HealthMonitor.FailureThreshold | default "3" }}
#{{- end }}
- RetryEnabled: true
#{{- range $server := $target.LoadBalancer.Server }}
- Server:
.name: {{ $server.name }}
IsFallback: false
#{{- if $server.weight }}
Weight: {{ $server.weight }}
#{{- end }}
#{{- end }}
#{{- end }}
#{{- if and $target.HealthMonitor $target.HealthMonitor.IsEnabled }}
# Optional: Add HealthMonitor block if it's enabled in x-target
HealthMonitor:
Enabled: true
IntervalInSec: {{ $target.HealthMonitor.IntervalInSec }}
# {{- if eq $target.HealthMonitor.Protocol "TCP" }}
# Case A: TCP Health Monitor
TCPMonitor:
ConnectTimeoutInSec: {{ $target.HealthMonitor.TimeoutInSec | default 5 }}
Port: {{ $target.HealthMonitor.Request.Port }}
#{{- else }}
# Case B: HTTP or HTTPS Health Monitor
HTTPMonitor:
Request:
ConnectTimeoutInSec: {{ $target.HealthMonitor.TimeoutInSec | default 5 }}
SocketReadTimeoutInSec: {{ $target.HealthMonitor.TimeoutInSec | default 10 }}
Port: {{ $target.HealthMonitor.Request.Port }}
Verb: {{ $target.HealthMonitor.Request.Verb | default "GET" }}
Path: {{ $target.HealthMonitor.Request.Path }}
#{{- if eq $target.HealthMonitor.Protocol "HTTPS" }}
IsSSL: true
#{{- end }}
SuccessResponse:
#{{- range $code := $target.HealthMonitor.SuccessCodes }}
ResponseCode: {{ $code }}
#{{- end }}
#{{- end }}
#{{- end }}
#{{- else }}
# Fallback: x-target is NOT present, use original logic
HTTPTargetConnection:
#{{- $scheme := include "get_scheme" (index $.Values.spec.servers 0 "url") }}
#{{- if eq $scheme "https" }}
SSLInfo:
Enabled: true
Enforce: true
IgnoreValidationErrors: true
#{{- end }}
URL: {{ include "get_target_url" $.Values.spec.servers }}
#{{- end }}
# --- END: x-target LOGIC ---
Resources:
- Resource:
Type: oas
#{{ os_writefile "./openapi.yaml" $.Values.spec_string }}
#{{ remove_oas_extensions "./openapi.yaml" }}
Path: ./openapi.yaml
- Resource:
Type: properties
#{{ os_copyfile "./test.properties" "./resources/test.properties" }}
Path: ./test.properties
As you described, first, you apply the overlay to the OpenAPI Description, like this:
apigee-go-gen transform oas-overlay \
--spec ./examples/specs/oas3/petstore.yaml \
--overlay target.yaml \
--output ./out/petstore-overlaid.yaml
Then, render the API proxy as usual
apigee-go-gen render apiproxy \
--template ${TEMPLATES}/oas3-dynamic-target/apiproxy.yaml \
--include ${TEMPLATES}/oas3/*.tmpl \
--set-oas spec=./out/petstore-overlaid.yaml \
--output ./out/apiproxies/petstore.zip
Hope this helps.