Skip to content

Commit

Permalink
feat(egctl): add support for egctl to translate from gateway-api reso…
Browse files Browse the repository at this point in the history
…urces to IR (#2799)

* Added an option to translate to IR representation.

Signed-off-by: Lior Okman <[email protected]>

* Added a unit test, and made sure that existing services have an IP
address.

Signed-off-by: Lior Okman <[email protected]>

* Add omitempty where needed.

Signed-off-by: Lior Okman <[email protected]>

* Make gen-check happy

Signed-off-by: Lior Okman <[email protected]>

* Added some documentation.

Signed-off-by: Lior Okman <[email protected]>

---------

Signed-off-by: Lior Okman <[email protected]>
  • Loading branch information
liorokman authored Mar 7, 2024
1 parent fe4de9f commit 73eba18
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 2 deletions.
124 changes: 124 additions & 0 deletions internal/cmd/egctl/testdata/translate/out/quickstart.all.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
gateways:
- metadata:
creationTimestamp: null
name: eg
namespace: envoy-gateway-system
spec:
gatewayClassName: eg
listeners:
- name: http
port: 80
protocol: HTTP
status:
listeners:
- attachedRoutes: 1
conditions:
- lastTransitionTime: null
message: Sending translated listener configuration to the data plane
reason: Programmed
status: "True"
type: Programmed
- lastTransitionTime: null
message: Listener has been successfully translated
reason: Accepted
status: "True"
type: Accepted
- lastTransitionTime: null
message: Listener references have been resolved
reason: ResolvedRefs
status: "True"
type: ResolvedRefs
name: http
supportedKinds:
- group: gateway.networking.k8s.io
kind: HTTPRoute
- group: gateway.networking.k8s.io
kind: GRPCRoute
httpRoutes:
- kind: HTTPRoute
metadata:
creationTimestamp: null
name: backend
namespace: envoy-gateway-system
spec:
hostnames:
- www.example.com
parentRefs:
- name: eg
rules:
- backendRefs:
- group: ""
kind: Service
name: backend
port: 3000
weight: 1
matches:
- path:
type: PathPrefix
value: /
status:
parents:
- conditions:
- lastTransitionTime: null
message: Route is accepted
reason: Accepted
status: "True"
type: Accepted
- lastTransitionTime: null
message: Resolved all the Object references for the Route
reason: ResolvedRefs
status: "True"
type: ResolvedRefs
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parentRef:
name: eg
infraIR:
envoy-gateway-system/eg:
proxy:
listeners:
- address: null
name: envoy-gateway-system/eg/http
ports:
- containerPort: 10080
name: http
protocol: HTTP
servicePort: 80
metadata:
labels:
gateway.envoyproxy.io/owning-gateway-name: eg
gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway-system
name: envoy-gateway-system/eg
xdsIR:
envoy-gateway-system/eg:
accessLog:
text:
- path: /dev/stdout
http:
- address: 0.0.0.0
hostnames:
- '*'
isHTTP2: false
name: envoy-gateway-system/eg/http
path:
escapedSlashesAction: UnescapeAndRedirect
mergeSlashes: true
port: 10080
routes:
- backendWeights:
invalid: 0
valid: 0
destination:
name: httproute/envoy-gateway-system/backend/rule/0
settings:
- endpoints:
- host: 7.7.7.7
port: 3000
protocol: HTTP
weight: 1
hostname: www.example.com
isHTTP2: false
name: httproute/envoy-gateway-system/backend/rule/0/match/0/www_example_com
pathMatch:
distinct: false
name: ""
prefix: /
45 changes: 43 additions & 2 deletions internal/cmd/egctl/translate.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,14 @@ import (
const (
gatewayAPIType = "gateway-api"
xdsType = "xds"
irType = "ir"
)

type TranslationResult struct {
gatewayapi.Resources
Xds map[string]interface{} `json:"xds,omitempty"`
XdsIR gatewayapi.XdsIRMap `json:"xdsIR,omitempty" yaml:"xdsIR,omitempty"`
InfraIR gatewayapi.InfraIRMap `json:"infraIR,omitempty" yaml:"infraIR,omitempty"`
Xds map[string]interface{} `json:"xds,omitempty"`
}

func newTranslateCommand() *cobra.Command {
Expand Down Expand Up @@ -94,6 +97,9 @@ func newTranslateCommand() *cobra.Command {
# Translate Gateway API Resources into All xDS Resources in YAML output,
# also print the Gateway API Resources with updated status in the same output.
egctl experimental translate --from gateway-api --to gateway-api,xds --type all --output yaml --file <input file>
# Translate Gateway API Resources into IR in YAML output,
egctl experimental translate --from gateway-api --to ir --output yaml --file <input file>
`,
RunE: func(cmd *cobra.Command, args []string) error {
return translate(cmd.OutOrStdout(), inFile, inType, outTypes, output, resourceType, addMissingResources, dnsDomain)
Expand Down Expand Up @@ -131,7 +137,7 @@ func getValidInputTypesStr() string {
}

func validOutputTypes() []string {
return []string{xdsType, gatewayAPIType}
return []string{xdsType, gatewayAPIType, irType}
}

func findInvalidOutputType(outTypes []string) string {
Expand Down Expand Up @@ -244,6 +250,15 @@ func translate(w io.Writer, inFile, inType string, outTypes []string, output, re
}
result.Xds = res
}
if outType == irType {
res, err := translateGatewayAPIToIR(resources)
if err != nil {
return err
}
result.Resources = res.Resources
result.XdsIR = res.XdsIR
result.InfraIR = res.InfraIR
}
}
// Print
if err = printOutput(w, result, output); err != nil {
Expand All @@ -255,6 +270,32 @@ func translate(w io.Writer, inFile, inType string, outTypes []string, output, re
return fmt.Errorf("unable to find translate from input type %s to output type %s", inType, outTypes)
}

func translateGatewayAPIToIR(resources *gatewayapi.Resources) (*gatewayapi.TranslateResult, error) {
if resources.GatewayClass == nil {
return nil, fmt.Errorf("the GatewayClass resource is required")
}

t := &gatewayapi.Translator{
GatewayControllerName: egv1a1.GatewayControllerName,
GatewayClassName: gwapiv1.ObjectName(resources.GatewayClass.Name),
GlobalRateLimitEnabled: true,
EndpointRoutingDisabled: true,
EnvoyPatchPolicyEnabled: true,
}

// Fix the services in the resources section so that they have an IP address - this prevents nasty
// errors in the translation.
for _, svc := range resources.Services {
if svc.Spec.ClusterIP == "" {
svc.Spec.ClusterIP = "10.96.1.2"
}
}

result := t.Translate(resources)

return result, nil
}

func translateGatewayAPIToGatewayAPI(resources *gatewayapi.Resources) (gatewayapi.Resources, error) {
if resources.GatewayClass == nil {
return gatewayapi.Resources{}, fmt.Errorf("the GatewayClass resource is required")
Expand Down
7 changes: 7 additions & 0 deletions internal/cmd/egctl/translate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,13 @@ func TestTranslate(t *testing.T) {
expect: true,
extraArgs: []string{"--add-missing-resources"},
},
{
name: "quickstart",
from: "gateway-api",
to: "ir",
output: yamlOutput,
expect: true,
},
{
name: "quickstart",
from: "gateway-api",
Expand Down
17 changes: 17 additions & 0 deletions site/content/en/latest/user/operations/egctl.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ title: "Use egctl"

This subcommand allows users to translate from an input configuration type to an output configuration type.

The `translate` subcommand can translate Kubernetes resources to:
* Gateway API resources
This is useful in order to see how validation would occur if these resources were applied to Kubernetes.

Use the `--to gateway-api` parameter to translate to Gateway API resources.

* Envoy Gateway intermediate representation (IR)
This represents Envoy Gateway's translation of the Gateway API resources.

Use the `--to ir` parameter to translate to Envoy Gateway intermediate representation.

* Envoy Proxy xDS
This is the xDS configuration provided to Envoy Proxy.

Use the `--to xds` parameter to translate to Envoy Proxy xDS.


In the below example, we will translate the Kubernetes resources (including the Gateway API resources) into xDS
resources.

Expand Down

0 comments on commit 73eba18

Please sign in to comment.