Skip to content

Commit 516722e

Browse files
committedJul 10, 2023
Initial commit
0 parents  commit 516722e

18 files changed

+2097
-0
lines changed
 

‎.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
vendor/
2+
bin/

‎README.md

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# gwctl
2+
3+
gwctl is a tool that improves the usability of the Gateway API by providing a better way to view and manage policies ([GEP-713](https://gateway-api.sigs.k8s.io/geps/gep-713)). The aim is to make it available as a standalone binary, a kubectl plugin, and a library.
4+
5+
gwctl allows you to view all Gateway API policy types that are present in a cluster, as well as all "policy bindings" in a namespace (or across all namespaces). It also shows you the attached policies when you view any Gateway resource (like HTTPRoute, Gateway, GatewayClass, etc.)
6+
7+
Please note that gwctl is <b>still under development and may have bugs</b>. There may be changes at various places, including the command-line interface, the output format, and the supported features.
8+
9+
In the future, gwctl may be able to read status from the policy resource to determine if it has been applied correctly.
10+
11+
## Try it out!
12+
13+
```bash
14+
# Clone the gwctl repository
15+
git clone https://github.com/gauravkghildiyal/gwctl.git
16+
17+
# Go to the gwctl directory
18+
cd gwctl
19+
20+
# Ensure vendor depedencies
21+
go mod tidy
22+
go mod vendor
23+
24+
# Build the gwctl binary
25+
go build -o bin/gwctl cmd/gwctl/main.go
26+
27+
# Add binary to PATH
28+
export PATH=./bin:${PATH}
29+
30+
# OPTIONAL: Create sample resources
31+
kubectl apply -f samples/crds.yaml
32+
kubectl apply -f samples/examples.yaml
33+
34+
# Start using!
35+
gwctl --help
36+
```
37+
38+
## Examples
39+
Here are some examples of how gwctl can be used:
40+
41+
```bash
42+
# List all policies in the cluster. This will also give the resource they bind to.
43+
gwctl get policies -A
44+
45+
# List all available policy types
46+
gwctl get policycrds
47+
48+
# Describe all HTTPRoutes in namespace ns2
49+
gwctl describe httproutes -n ns2
50+
51+
# Describe a single HTTPRoute in default namespace
52+
gwctl describe httproutes demo-httproute-1
53+
54+
# Describe all Gateways across all namespaces.
55+
gwctl describe gateways -A
56+
57+
# Describe a single GatewayClass
58+
gwctl describe gatewayclasses foo-com-external-gateway-class
59+
```
60+
61+
Here are some commands with their sample output:
62+
```bash
63+
❯ gwctl get policies -A
64+
POLICYNAME POLICYKIND TARGETNAME TARGETKIND
65+
demo-health-check-1 HealthCheckPolicy demo-gateway-1 Gateway
66+
demo-retry-policy-1 RetryOnPolicy demo-gateway-1 Gateway
67+
demo-retry-policy-2 RetryOnPolicy demo-httproute-2 HTTPRoute
68+
demo-timeout-policy-1 TimeoutPolicy foo-com-external-gateway-class GatewayClass
69+
demo-tls-min-version-policy-1 TLSMinimumVersionPolicy demo-httproute-1 HTTPRoute
70+
demo-tls-min-version-policy-2 TLSMinimumVersionPolicy demo-gateway-2 Gateway
71+
72+
❯ gwctl describe httproutes -n ns2
73+
Name: demo-httproute-3
74+
Namespace: ns2
75+
Hostnames:
76+
- example.com
77+
ParentRefs:
78+
- Kind: Gateway
79+
Group: gateway.networking.k8s.io
80+
Name: demo-gateway-2
81+
DirectlyAttachedPolicies:
82+
InheritedPolicies:
83+
- Kind: TLSMinimumVersionPolicy
84+
Group: baz.com
85+
Name: demo-tls-min-version-policy-2
86+
Target:
87+
Kind: Gateway
88+
Group: gateway.networking.k8s.io
89+
Name: demo-gateway-2
90+
91+
92+
Name: demo-httproute-4
93+
Namespace: ns2
94+
Hostnames:
95+
- demo.com
96+
ParentRefs:
97+
- Kind: Gateway
98+
Group: gateway.networking.k8s.io
99+
Name: demo-gateway-1
100+
DirectlyAttachedPolicies:
101+
InheritedPolicies:
102+
- Kind: HealthCheckPolicy
103+
Group: foo.com
104+
Name: demo-health-check-1
105+
Target:
106+
Kind: Gateway
107+
Group: gateway.networking.k8s.io
108+
Name: demo-gateway-1
109+
- Kind: RetryOnPolicy
110+
Group: foo.com
111+
Name: demo-retry-policy-1
112+
Target:
113+
Kind: Gateway
114+
Group: gateway.networking.k8s.io
115+
Name: demo-gateway-1
116+
- Kind: TimeoutPolicy
117+
Group: bar.com
118+
Name: demo-timeout-policy-1
119+
Target:
120+
Kind: GatewayClass
121+
Group: gateway.networking.k8s.io
122+
Name: foo-com-external-gateway-class
123+
```
124+
125+
---
126+
127+
## Areas that definitely need some work:
128+
* Add tests.
129+
* Add some more tests.
130+
* Re-evalute the minimum information that we need to print for resource descriptions.
131+
* Improve/define library interfaces.
132+
* There's several areas which could be generified instead of being repetitive.
133+

‎cmd/gwctl/main.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package main
2+
3+
import (
4+
_ "embed"
5+
"fmt"
6+
"math/rand"
7+
"os"
8+
"os/exec"
9+
"time"
10+
11+
"k8s.io/client-go/dynamic"
12+
"k8s.io/client-go/tools/clientcmd"
13+
"sigs.k8s.io/controller-runtime/pkg/client"
14+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
15+
gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
16+
17+
"github.com/gauravkghildiyal/gwctl/pkg/cmd"
18+
"github.com/gauravkghildiyal/gwctl/pkg/types"
19+
"github.com/spf13/cobra"
20+
)
21+
22+
func main() {
23+
// TODO: Replace proxying with proper authentication through kubeconfig.
24+
port := rand.Intn(30000) + 30000
25+
if err := exec.Command("kubectl", "proxy", "-p", fmt.Sprintf("%v", port)).Start(); err != nil {
26+
panic(err)
27+
}
28+
time.Sleep(100 * time.Millisecond)
29+
30+
restConfig, err := clientcmd.BuildConfigFromFlags(fmt.Sprintf("http://localhost:%v", port), "")
31+
if err != nil {
32+
panic(fmt.Sprintf("Failed to get restConfig from BuildConfigFromFlags: %v", err))
33+
}
34+
35+
client, err := client.New(restConfig, client.Options{})
36+
if err != nil {
37+
panic(fmt.Sprintf("Error initializing Kubernetes client: %v", err))
38+
}
39+
gatewayv1alpha2.AddToScheme(client.Scheme())
40+
gatewayv1beta1.AddToScheme(client.Scheme())
41+
42+
dc := dynamic.NewForConfigOrDie(restConfig)
43+
44+
clients := &types.Clients{
45+
Client: client,
46+
DC: dc,
47+
}
48+
49+
rootCmd := &cobra.Command{
50+
Use: "gwctl",
51+
}
52+
rootCmd.AddCommand(cmd.NewGetCommand(clients))
53+
rootCmd.AddCommand(cmd.NewDescribeCommand(clients))
54+
if err := rootCmd.Execute(); err != nil {
55+
os.Exit(1)
56+
}
57+
}

‎go.mod

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
module github.com/gauravkghildiyal/gwctl
2+
3+
go 1.21
4+
5+
require (
6+
github.com/spf13/cobra v1.7.0
7+
k8s.io/apiextensions-apiserver v0.27.3
8+
k8s.io/apimachinery v0.27.3
9+
k8s.io/client-go v0.27.3
10+
sigs.k8s.io/controller-runtime v0.14.6
11+
sigs.k8s.io/gateway-api v0.7.1
12+
)
13+
14+
require (
15+
github.com/davecgh/go-spew v1.1.1 // indirect
16+
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
17+
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
18+
github.com/go-logr/logr v1.2.3 // indirect
19+
github.com/go-openapi/jsonpointer v0.19.6 // indirect
20+
github.com/go-openapi/jsonreference v0.20.1 // indirect
21+
github.com/go-openapi/swag v0.22.3 // indirect
22+
github.com/gogo/protobuf v1.3.2 // indirect
23+
github.com/golang/protobuf v1.5.3 // indirect
24+
github.com/google/gnostic v0.5.7-v3refs // indirect
25+
github.com/google/go-cmp v0.5.9 // indirect
26+
github.com/google/gofuzz v1.1.0 // indirect
27+
github.com/google/uuid v1.3.0 // indirect
28+
github.com/imdario/mergo v0.3.6 // indirect
29+
github.com/inconshreveable/mousetrap v1.1.0 // indirect
30+
github.com/josharian/intern v1.0.0 // indirect
31+
github.com/json-iterator/go v1.1.12 // indirect
32+
github.com/mailru/easyjson v0.7.7 // indirect
33+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
34+
github.com/modern-go/reflect2 v1.0.2 // indirect
35+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
36+
github.com/pkg/errors v0.9.1 // indirect
37+
github.com/rogpeppe/go-internal v1.11.0 // indirect
38+
github.com/spf13/pflag v1.0.5 // indirect
39+
golang.org/x/net v0.8.0 // indirect
40+
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect
41+
golang.org/x/sys v0.6.0 // indirect
42+
golang.org/x/term v0.6.0 // indirect
43+
golang.org/x/text v0.8.0 // indirect
44+
golang.org/x/time v0.3.0 // indirect
45+
google.golang.org/appengine v1.6.7 // indirect
46+
google.golang.org/protobuf v1.28.1 // indirect
47+
gopkg.in/inf.v0 v0.9.1 // indirect
48+
gopkg.in/yaml.v2 v2.4.0 // indirect
49+
gopkg.in/yaml.v3 v3.0.1 // indirect
50+
k8s.io/api v0.27.3 // indirect
51+
k8s.io/klog/v2 v2.100.1 // indirect
52+
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
53+
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
54+
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
55+
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
56+
sigs.k8s.io/yaml v1.3.0 // indirect
57+
)

‎go.sum

+536
Large diffs are not rendered by default.

‎pkg/cmd/describe.go

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
8+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
9+
10+
"github.com/gauravkghildiyal/gwctl/pkg/resources/gatewayclasses"
11+
"github.com/gauravkghildiyal/gwctl/pkg/resources/gateways"
12+
"github.com/gauravkghildiyal/gwctl/pkg/resources/httproutes"
13+
"github.com/gauravkghildiyal/gwctl/pkg/resources/policies"
14+
"github.com/gauravkghildiyal/gwctl/pkg/types"
15+
"github.com/spf13/cobra"
16+
)
17+
18+
type describeFlags struct {
19+
namespace string
20+
allNamespaces bool
21+
}
22+
23+
func NewDescribeCommand(clients *types.Clients) *cobra.Command {
24+
flags := &describeFlags{}
25+
26+
cmd := &cobra.Command{
27+
Use: "describe {policies|httproutes|gateways|gatewayclasses} RESOURCE_NAME",
28+
Short: "Show details of a specific resource or group of resources",
29+
Args: cobra.RangeArgs(1, 2),
30+
Run: func(cmd *cobra.Command, args []string) {
31+
runDescribe(args, clients, flags)
32+
},
33+
}
34+
cmd.Flags().StringVarP(&flags.namespace, "namespace", "n", "default", "")
35+
cmd.Flags().BoolVarP(&flags.allNamespaces, "all-namespaces", "A", false, "If present, list requested resources from all namespaces.")
36+
37+
return cmd
38+
}
39+
40+
func runDescribe(args []string, clients *types.Clients, flags *describeFlags) {
41+
kind := args[0]
42+
ns := flags.namespace
43+
if flags.allNamespaces {
44+
ns = ""
45+
}
46+
47+
switch kind {
48+
case "policy", "policies":
49+
policyList, err := policies.List(context.TODO(), clients, ns)
50+
if err != nil {
51+
panic(err)
52+
}
53+
policies.PrintDescribeView(clients, policyList)
54+
case "httproute", "httproutes":
55+
var httpRoutes []gatewayv1alpha2.HTTPRoute
56+
if len(args) == 1 {
57+
var err error
58+
httpRoutes, err = httproutes.List(context.TODO(), clients, ns)
59+
if err != nil {
60+
panic(err)
61+
}
62+
} else {
63+
httpRoute, err := httproutes.Get(context.TODO(), clients, ns, args[1])
64+
if err != nil {
65+
panic(err)
66+
}
67+
httpRoutes = []gatewayv1alpha2.HTTPRoute{httpRoute}
68+
}
69+
httproutes.PrintDescribeView(context.TODO(), clients, httpRoutes)
70+
case "gateway", "gateways":
71+
var gws []gatewayv1alpha2.Gateway
72+
if len(args) == 1 {
73+
var err error
74+
gws, err = gateways.List(context.TODO(), clients, ns)
75+
if err != nil {
76+
panic(err)
77+
}
78+
} else {
79+
gw, err := gateways.Get(context.TODO(), clients, ns, args[1])
80+
if err != nil {
81+
panic(err)
82+
}
83+
gws = []gatewayv1alpha2.Gateway{gw}
84+
}
85+
gateways.PrintDescribeView(context.TODO(), clients, gws)
86+
case "gatewayclass", "gatewayclasses":
87+
var gwClasses []gatewayv1alpha2.GatewayClass
88+
if len(args) == 1 {
89+
var err error
90+
gwClasses, err = gatewayclasses.List(context.TODO(), clients)
91+
if err != nil {
92+
panic(err)
93+
}
94+
} else {
95+
gwc, err := gatewayclasses.Get(context.TODO(), clients, args[1])
96+
if err != nil {
97+
panic(err)
98+
}
99+
gwClasses = []gatewayv1alpha2.GatewayClass{gwc}
100+
}
101+
gatewayclasses.PrintDescribeView(context.TODO(), clients, gwClasses)
102+
default:
103+
fmt.Fprintf(os.Stderr, "Unrecognized RESOURCE_TYPE\n")
104+
}
105+
}

‎pkg/cmd/get.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
8+
"github.com/gauravkghildiyal/gwctl/pkg/resources/policies"
9+
"github.com/gauravkghildiyal/gwctl/pkg/types"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
type getFlags struct {
14+
namespace string
15+
allNamespaces bool
16+
}
17+
18+
func NewGetCommand(clients *types.Clients) *cobra.Command {
19+
flags := &getFlags{}
20+
21+
cmd := &cobra.Command{
22+
Use: "get {policies|policycrds}",
23+
Short: "Display one or many resources",
24+
Args: cobra.ExactArgs(1),
25+
Run: func(cmd *cobra.Command, args []string) {
26+
runGet(args, clients, flags)
27+
},
28+
}
29+
cmd.Flags().StringVarP(&flags.namespace, "namespace", "n", "default", "")
30+
cmd.Flags().BoolVarP(&flags.allNamespaces, "all-namespaces", "A", false, "If present, list requested resources from all namespaces.")
31+
32+
return cmd
33+
}
34+
35+
func runGet(args []string, clients *types.Clients, flags *getFlags) {
36+
kind := args[0]
37+
ns := flags.namespace
38+
if flags.allNamespaces {
39+
ns = ""
40+
}
41+
42+
switch kind {
43+
case "policy", "policies":
44+
p, err := policies.List(context.TODO(), clients, ns)
45+
if err != nil {
46+
panic(err)
47+
}
48+
policies.Print(p)
49+
case "policycrds":
50+
p, err := policies.ListCRDs(context.TODO(), clients)
51+
if err != nil {
52+
panic(err)
53+
}
54+
policies.PrintCRDs(p)
55+
default:
56+
fmt.Fprintf(os.Stderr, "Unrecognized RESOURCE_TYPE\n")
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Name: {{ .GatewayClass.Name }}
2+
ControllerName: {{ .GatewayClass.Spec.ControllerName }}
3+
Description: {{ .GatewayClass.Spec.Description }}
4+
DirectlyAttachedPolicies:
5+
{{- range .DirectlyAttachedPolicies }}
6+
- Kind: {{ .GroupVersionKind.Kind }}
7+
Group: {{ .GroupVersionKind.Group }}
8+
Name: {{ .GetName }}
9+
{{- end }}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package gatewayclasses
2+
3+
import (
4+
"context"
5+
_ "embed"
6+
"fmt"
7+
"html/template"
8+
"os"
9+
10+
"github.com/gauravkghildiyal/gwctl/pkg/resources/policies"
11+
"github.com/gauravkghildiyal/gwctl/pkg/types"
12+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
13+
apimachinerytypes "k8s.io/apimachinery/pkg/types"
14+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
15+
)
16+
17+
func List(ctx context.Context, dc *types.Clients) ([]gatewayv1alpha2.GatewayClass, error) {
18+
gwcList := &gatewayv1alpha2.GatewayClassList{}
19+
if err := dc.Client.List(ctx, gwcList); err != nil {
20+
return []gatewayv1alpha2.GatewayClass{}, nil
21+
}
22+
23+
return gwcList.Items, nil
24+
}
25+
26+
func Get(ctx context.Context, clients *types.Clients, name string) (gatewayv1alpha2.GatewayClass, error) {
27+
gwc := &gatewayv1alpha2.GatewayClass{}
28+
nn := apimachinerytypes.NamespacedName{Name: name}
29+
if err := clients.Client.Get(ctx, nn, gwc); err != nil {
30+
return gatewayv1alpha2.GatewayClass{}, nil
31+
}
32+
33+
return *gwc, nil
34+
}
35+
36+
func GetAttachedPolicies(ctx context.Context, clients *types.Clients, name string) ([]unstructured.Unstructured, error) {
37+
gw := &gatewayv1alpha2.GatewayClass{}
38+
gvks, _, err := clients.Client.Scheme().ObjectKinds(gw)
39+
if err != nil {
40+
return []unstructured.Unstructured{}, nil
41+
}
42+
43+
targetRef := gatewayv1alpha2.PolicyTargetReference{
44+
Group: gatewayv1alpha2.Group(gvks[0].Group),
45+
Kind: gatewayv1alpha2.Kind(gvks[0].Kind),
46+
Name: gatewayv1alpha2.ObjectName(name),
47+
}
48+
return policies.ListAttachedTo(ctx, clients, targetRef)
49+
}
50+
51+
func GetAllPolicies(ctx context.Context, clients *types.Clients, name string) ([]unstructured.Unstructured, error) {
52+
return GetAttachedPolicies(ctx, clients, name)
53+
}
54+
55+
type describeView struct {
56+
GatewayClass gatewayv1alpha2.GatewayClass
57+
DirectlyAttachedPolicies []unstructured.Unstructured
58+
}
59+
60+
//go:embed gatewayclass.tmpl
61+
var gatewayClassTmpl string
62+
63+
func PrintDescribeView(ctx context.Context, clients *types.Clients, gwClasses []gatewayv1alpha2.GatewayClass) {
64+
for i, gwc := range gwClasses {
65+
directlyAttachedPolicies, err := GetAttachedPolicies(ctx, clients, gwc.Name)
66+
if err != nil {
67+
panic(err)
68+
}
69+
70+
view := &describeView{
71+
GatewayClass: gwc,
72+
DirectlyAttachedPolicies: directlyAttachedPolicies,
73+
}
74+
75+
tmpl := template.Must(template.New("").Parse(gatewayClassTmpl))
76+
if err := tmpl.Execute(os.Stdout, view); err != nil {
77+
fmt.Fprintf(os.Stderr, "failed to execute template: %v", err)
78+
}
79+
80+
if i+1 != len(gwClasses) {
81+
fmt.Printf("\n\n")
82+
}
83+
}
84+
}

‎pkg/resources/gateways/gateway.tmpl

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Name: {{ .Gateway.Name }}
2+
Namespace: {{ .Gateway.Namespace }}
3+
GatewayClass: {{ .Gateway.Spec.GatewayClassName }}
4+
DirectlyAttachedPolicies:
5+
{{- range .DirectlyAttachedPolicies }}
6+
- Kind: {{ .GroupVersionKind.Kind }}
7+
Group: {{ .GroupVersionKind.Group }}
8+
Name: {{ .GetName }}
9+
{{- end }}
10+
InheritedPolicies:
11+
{{- range .InheritedPolicies }}
12+
- Kind: {{ .GroupVersionKind.Kind }}
13+
Group: {{ .GroupVersionKind.Group }}
14+
Name: {{ .GetName }}
15+
{{- end }}

‎pkg/resources/gateways/gateways.go

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package gateways
2+
3+
import (
4+
"context"
5+
_ "embed"
6+
"fmt"
7+
"html/template"
8+
"os"
9+
10+
"github.com/gauravkghildiyal/gwctl/pkg/resources/gatewayclasses"
11+
"github.com/gauravkghildiyal/gwctl/pkg/resources/policies"
12+
"github.com/gauravkghildiyal/gwctl/pkg/types"
13+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
14+
apimachinerytypes "k8s.io/apimachinery/pkg/types"
15+
"sigs.k8s.io/controller-runtime/pkg/client"
16+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
17+
)
18+
19+
func List(ctx context.Context, dc *types.Clients, namespace string) ([]gatewayv1alpha2.Gateway, error) {
20+
gwList := &gatewayv1alpha2.GatewayList{}
21+
if err := dc.Client.List(ctx, gwList, client.InNamespace(namespace)); err != nil {
22+
return []gatewayv1alpha2.Gateway{}, nil
23+
}
24+
25+
return gwList.Items, nil
26+
}
27+
28+
func Get(ctx context.Context, clients *types.Clients, namespace, name string) (gatewayv1alpha2.Gateway, error) {
29+
gw := &gatewayv1alpha2.Gateway{}
30+
nn := apimachinerytypes.NamespacedName{Namespace: namespace, Name: name}
31+
if err := clients.Client.Get(ctx, nn, gw); err != nil {
32+
return gatewayv1alpha2.Gateway{}, nil
33+
}
34+
35+
return *gw, nil
36+
}
37+
38+
func GetAttachedPolicies(ctx context.Context, clients *types.Clients, namespace, name string) ([]unstructured.Unstructured, error) {
39+
gw := &gatewayv1alpha2.Gateway{}
40+
gvks, _, err := clients.Client.Scheme().ObjectKinds(gw)
41+
if err != nil {
42+
return []unstructured.Unstructured{}, nil
43+
}
44+
45+
ns := gatewayv1alpha2.Namespace(namespace)
46+
targetRef := gatewayv1alpha2.PolicyTargetReference{
47+
Group: gatewayv1alpha2.Group(gvks[0].Group),
48+
Kind: gatewayv1alpha2.Kind(gvks[0].Kind),
49+
Name: gatewayv1alpha2.ObjectName(name),
50+
Namespace: &ns,
51+
}
52+
return policies.ListAttachedTo(ctx, clients, targetRef)
53+
}
54+
55+
func GetInheritedPolicies(ctx context.Context, clients *types.Clients, namespace, name string) ([]unstructured.Unstructured, error) {
56+
gw, err := Get(ctx, clients, namespace, name)
57+
if err != nil {
58+
return []unstructured.Unstructured{}, err
59+
}
60+
61+
return gatewayclasses.GetAllPolicies(ctx, clients, string(gw.Spec.GatewayClassName))
62+
}
63+
64+
func GetAllPolicies(ctx context.Context, clients *types.Clients, namespace, name string) ([]unstructured.Unstructured, error) {
65+
var result []unstructured.Unstructured
66+
policies, err := GetAttachedPolicies(ctx, clients, namespace, name)
67+
if err != nil {
68+
return result, err
69+
}
70+
result = append(result, policies...)
71+
72+
policies, err = GetInheritedPolicies(ctx, clients, namespace, name)
73+
if err != nil {
74+
return result, err
75+
}
76+
result = append(result, policies...)
77+
return result, nil
78+
}
79+
80+
type describeView struct {
81+
Gateway gatewayv1alpha2.Gateway
82+
DirectlyAttachedPolicies []unstructured.Unstructured
83+
InheritedPolicies []types.GenericPolicy
84+
}
85+
86+
//go:embed gateway.tmpl
87+
var gatewayTmpl string
88+
89+
func PrintDescribeView(ctx context.Context, clients *types.Clients, gws []gatewayv1alpha2.Gateway) {
90+
for i, gw := range gws {
91+
directlyAttachedPolicies, err := GetAttachedPolicies(ctx, clients, gw.Namespace, gw.Name)
92+
if err != nil {
93+
panic(err)
94+
}
95+
inheritedPolicies, err := GetInheritedPolicies(ctx, clients, gw.Namespace, gw.Name)
96+
if err != nil {
97+
panic(err)
98+
}
99+
100+
view := &describeView{
101+
Gateway: gw,
102+
DirectlyAttachedPolicies: directlyAttachedPolicies,
103+
InheritedPolicies: policies.UnstructuredToGeneric(clients, inheritedPolicies),
104+
}
105+
106+
tmpl := template.Must(template.New("").Parse(gatewayTmpl))
107+
if err := tmpl.Execute(os.Stdout, view); err != nil {
108+
fmt.Fprintf(os.Stderr, "failed to execute template: %v", err)
109+
}
110+
111+
if i+1 != len(gws) {
112+
fmt.Printf("\n\n")
113+
}
114+
}
115+
}
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
Name: {{ .HTTPRoute.Name }}
2+
Namespace: {{ .HTTPRoute.Namespace }}
3+
Hostnames:
4+
{{- range .HTTPRoute.Spec.Hostnames }}
5+
- {{ . }}
6+
{{- end }}
7+
ParentRefs:
8+
{{- range .HTTPRoute.Spec.ParentRefs }}
9+
- Kind: {{ .Kind }}
10+
Group: {{ .Group }}
11+
Name: {{ .Name }}
12+
{{- end }}
13+
DirectlyAttachedPolicies:
14+
{{- range .DirectlyAttachedPolicies }}
15+
- Kind: {{ .GroupVersionKind.Kind }}
16+
Group: {{ .GroupVersionKind.Group }}
17+
Name: {{ .GetName }}
18+
{{- end }}
19+
InheritedPolicies:
20+
{{- range .InheritedPolicies }}
21+
- Kind: {{ .GroupVersionKind.Kind }}
22+
Group: {{ .GroupVersionKind.Group }}
23+
Name: {{ .GetName }}
24+
Target:
25+
Kind: {{ .Spec.TargetRef.Kind }}
26+
Group: {{ .Spec.TargetRef.Group }}
27+
Name: {{ .Spec.TargetRef.Name }}
28+
{{- if .Spec.TargetRef.Namespace }}
29+
Namespace: {{ .Spec.TargetRef.Namespace }}
30+
{{- end }}
31+
{{- end }}
+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package httproutes
2+
3+
import (
4+
"context"
5+
_ "embed"
6+
"fmt"
7+
"html/template"
8+
"os"
9+
10+
"github.com/gauravkghildiyal/gwctl/pkg/resources/gateways"
11+
"github.com/gauravkghildiyal/gwctl/pkg/resources/policies"
12+
"github.com/gauravkghildiyal/gwctl/pkg/types"
13+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
14+
apimachinerytypes "k8s.io/apimachinery/pkg/types"
15+
"sigs.k8s.io/controller-runtime/pkg/client"
16+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
17+
)
18+
19+
func List(ctx context.Context, dc *types.Clients, namespace string) ([]gatewayv1alpha2.HTTPRoute, error) {
20+
httpRoutes := &gatewayv1alpha2.HTTPRouteList{}
21+
if err := dc.Client.List(ctx, httpRoutes, client.InNamespace(namespace)); err != nil {
22+
return []gatewayv1alpha2.HTTPRoute{}, nil
23+
}
24+
25+
return httpRoutes.Items, nil
26+
}
27+
28+
func Get(ctx context.Context, clients *types.Clients, namespace, name string) (gatewayv1alpha2.HTTPRoute, error) {
29+
httpRoute := &gatewayv1alpha2.HTTPRoute{}
30+
nn := apimachinerytypes.NamespacedName{Namespace: namespace, Name: name}
31+
if err := clients.Client.Get(ctx, nn, httpRoute); err != nil {
32+
return gatewayv1alpha2.HTTPRoute{}, nil
33+
}
34+
35+
return *httpRoute, nil
36+
}
37+
38+
func GetAttachedPolicies(ctx context.Context, clients *types.Clients, namespace, name string) ([]unstructured.Unstructured, error) {
39+
httpRoute := &gatewayv1alpha2.HTTPRoute{}
40+
gvks, _, err := clients.Client.Scheme().ObjectKinds(httpRoute)
41+
if err != nil {
42+
return []unstructured.Unstructured{}, nil
43+
}
44+
45+
ns := gatewayv1alpha2.Namespace(namespace)
46+
targetRef := gatewayv1alpha2.PolicyTargetReference{
47+
Group: gatewayv1alpha2.Group(gvks[0].Group),
48+
Kind: gatewayv1alpha2.Kind(gvks[0].Kind),
49+
Name: gatewayv1alpha2.ObjectName(name),
50+
Namespace: &ns,
51+
}
52+
return policies.ListAttachedTo(ctx, clients, targetRef)
53+
}
54+
55+
func GetInheritedPolicies(ctx context.Context, clients *types.Clients, namespace, name string) ([]unstructured.Unstructured, error) {
56+
var result []unstructured.Unstructured
57+
58+
httpRoute, err := Get(ctx, clients, namespace, name)
59+
if err != nil {
60+
return result, err
61+
}
62+
63+
for _, parentRef := range httpRoute.Spec.ParentRefs {
64+
ns := namespace
65+
if parentRef.Namespace != nil {
66+
ns = string(*parentRef.Namespace)
67+
}
68+
policies, err := gateways.GetAllPolicies(ctx, clients, string(ns), string(parentRef.Name))
69+
if err != nil {
70+
return result, err
71+
}
72+
result = append(result, policies...)
73+
}
74+
return result, nil
75+
}
76+
77+
type describeView struct {
78+
HTTPRoute gatewayv1alpha2.HTTPRoute
79+
DirectlyAttachedPolicies []unstructured.Unstructured
80+
InheritedPolicies []types.GenericPolicy
81+
}
82+
83+
//go:embed httproute.tmpl
84+
var httpRouteTmpl string
85+
86+
func PrintDescribeView(ctx context.Context, clients *types.Clients, httpRoutes []gatewayv1alpha2.HTTPRoute) {
87+
for i, httpRoute := range httpRoutes {
88+
directlyAttachedPolicies, err := GetAttachedPolicies(ctx, clients, httpRoute.Namespace, httpRoute.Name)
89+
if err != nil {
90+
panic(err)
91+
}
92+
inheritedPolicies, err := GetInheritedPolicies(ctx, clients, httpRoute.Namespace, httpRoute.Name)
93+
if err != nil {
94+
panic(err)
95+
}
96+
97+
view := &describeView{
98+
HTTPRoute: httpRoute,
99+
DirectlyAttachedPolicies: directlyAttachedPolicies,
100+
InheritedPolicies: policies.UnstructuredToGeneric(clients, inheritedPolicies),
101+
}
102+
103+
tmpl := template.Must(template.New("").Parse(httpRouteTmpl))
104+
if err := tmpl.Execute(os.Stdout, view); err != nil {
105+
fmt.Fprintf(os.Stderr, "failed to execute template: %v", err)
106+
}
107+
108+
if i+1 != len(httpRoutes) {
109+
fmt.Printf("\n\n")
110+
}
111+
}
112+
}

‎pkg/resources/policies/policies.go

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package policies
2+
3+
import (
4+
"context"
5+
_ "embed"
6+
"fmt"
7+
"html/template"
8+
"os"
9+
"strings"
10+
"text/tabwriter"
11+
12+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
15+
"k8s.io/apimachinery/pkg/runtime"
16+
"k8s.io/apimachinery/pkg/runtime/schema"
17+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
18+
19+
"github.com/gauravkghildiyal/gwctl/pkg/types"
20+
)
21+
22+
const (
23+
gatewayPolicyLabelKey = "gateway.networking.k8s.io/policy"
24+
)
25+
26+
func ListCRDs(ctx context.Context, clients *types.Clients) ([]apiextensionsv1.CustomResourceDefinition, error) {
27+
gvr := schema.GroupVersionResource{Group: "apiextensions.k8s.io", Version: "v1", Resource: "customresourcedefinitions"}
28+
o := metav1.ListOptions{
29+
LabelSelector: fmt.Sprintf("%v=true", gatewayPolicyLabelKey),
30+
}
31+
unstructuredCRDs, err := clients.DC.Resource(gvr).List(ctx, o)
32+
if err != nil {
33+
return []apiextensionsv1.CustomResourceDefinition{}, fmt.Errorf("failed to list CRDs: %v", err)
34+
}
35+
36+
crds := &apiextensionsv1.CustomResourceDefinitionList{}
37+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredCRDs.UnstructuredContent(), crds); err != nil {
38+
return []apiextensionsv1.CustomResourceDefinition{}, fmt.Errorf("failed to convert unstructured CRDs to structured: %v", err)
39+
}
40+
41+
return crds.Items, nil
42+
}
43+
44+
func List(ctx context.Context, clients *types.Clients, namespace string) ([]unstructured.Unstructured, error) {
45+
var result []unstructured.Unstructured
46+
47+
policyCRDs, err := ListCRDs(ctx, clients)
48+
if err != nil {
49+
return result, err
50+
}
51+
52+
for _, policyCRD := range policyCRDs {
53+
gvr := schema.GroupVersionResource{
54+
Group: policyCRD.Spec.Group,
55+
Version: policyCRD.Spec.Versions[0].Name,
56+
Resource: policyCRD.Spec.Names.Plural, // CRD Kinds directy map to the Resource.
57+
}
58+
ns := namespace
59+
if policyCRD.Spec.Scope == apiextensionsv1.ClusterScoped {
60+
ns = "" // Ignore namespace if the CRD is cluster scoped
61+
}
62+
policies, err := clients.DC.Resource(gvr).Namespace(ns).List(ctx, metav1.ListOptions{})
63+
if err != nil {
64+
return result, err
65+
}
66+
67+
result = append(result, policies.Items...)
68+
}
69+
70+
return result, nil
71+
}
72+
73+
func ListAttachedTo(ctx context.Context, clients *types.Clients, targetRef gatewayv1alpha2.PolicyTargetReference) ([]unstructured.Unstructured, error) {
74+
var result []unstructured.Unstructured
75+
76+
var ns string
77+
if targetRef.Namespace != nil {
78+
ns = string(*targetRef.Namespace)
79+
}
80+
policies, err := List(ctx, clients, ns)
81+
if err != nil {
82+
return result, err
83+
}
84+
85+
for _, policy := range policies {
86+
structuredPolicy := &types.GenericPolicy{}
87+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(policy.UnstructuredContent(), structuredPolicy); err != nil {
88+
return result, fmt.Errorf("failed to convert unstructured policy resource to structured: %v", err)
89+
}
90+
if structuredPolicy.IsAttachedTo(targetRef) {
91+
result = append(result, policy)
92+
}
93+
}
94+
95+
return result, nil
96+
}
97+
98+
func UnstructuredToGeneric(clients *types.Clients, policies []unstructured.Unstructured) []types.GenericPolicy {
99+
var result []types.GenericPolicy
100+
101+
for _, policy := range policies {
102+
structuredPolicy := &types.GenericPolicy{}
103+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(policy.UnstructuredContent(), structuredPolicy); err != nil {
104+
structuredPolicy.Spec.TargetRef.Group = "<Unknown>"
105+
structuredPolicy.Spec.TargetRef.Kind = "<Unknown>"
106+
structuredPolicy.Spec.TargetRef.Name = "<Unknown>"
107+
}
108+
result = append(result, *structuredPolicy)
109+
}
110+
111+
return result
112+
}
113+
114+
func Print(policies []unstructured.Unstructured) {
115+
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
116+
row := []string{"POLICYNAME", "POLICYKIND", "TARGETNAME", "TARGETKIND"}
117+
tw.Write([]byte(strings.Join(row, "\t") + "\n"))
118+
119+
for _, policy := range policies {
120+
structuredPolicy := &types.GenericPolicy{}
121+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(policy.UnstructuredContent(), structuredPolicy); err != nil {
122+
panic(err)
123+
}
124+
row := []string{structuredPolicy.Name, structuredPolicy.Kind, string(structuredPolicy.Spec.TargetRef.Name), string(structuredPolicy.Spec.TargetRef.Kind)}
125+
tw.Write([]byte(strings.Join(row, "\t") + "\n"))
126+
}
127+
tw.Flush()
128+
}
129+
130+
func PrintCRDs(crds []apiextensionsv1.CustomResourceDefinition) {
131+
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
132+
row := []string{"CRD_NAME", "CRD_GROUP", "CRD_KIND"}
133+
tw.Write([]byte(strings.Join(row, "\t") + "\n"))
134+
135+
for _, crd := range crds {
136+
row := []string{crd.Name, crd.Spec.Group, crd.Spec.Names.Kind}
137+
tw.Write([]byte(strings.Join(row, "\t") + "\n"))
138+
}
139+
tw.Flush()
140+
}
141+
142+
//go:embed policy.tmpl
143+
var policyTmpl string
144+
145+
func PrintDescribeView(clients *types.Clients, policies []unstructured.Unstructured) {
146+
structuredPolicies := UnstructuredToGeneric(clients, policies)
147+
148+
for i, policy := range structuredPolicies {
149+
tmpl := template.Must(template.New("").Parse(policyTmpl))
150+
if err := tmpl.Execute(os.Stdout, policy); err != nil {
151+
fmt.Fprintf(os.Stderr, "failed to execute template: %v", err)
152+
}
153+
154+
if i+1 != len(policies) {
155+
fmt.Printf("\n\n")
156+
}
157+
}
158+
}

‎pkg/resources/policies/policy.tmpl

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Name: {{ .Name }}
2+
Namespace: {{ .Namespace }}
3+
Group: {{ .GroupVersionKind.Group }}
4+
Kind: {{ .GroupVersionKind.Kind }}
5+
TargetRef:
6+
Group: {{ .Spec.TargetRef.Group }}
7+
Kind: {{ .Spec.TargetRef.Kind }}
8+
Name: {{ .Spec.TargetRef.Name }}
9+
{{- if .Spec.TargetRef.Namespace }}
10+
Namespace: {{ .Spec.TargetRef.Namespace }}
11+
{{- end }}

‎pkg/types/types.go

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package types
2+
3+
import (
4+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5+
"k8s.io/client-go/dynamic"
6+
"sigs.k8s.io/controller-runtime/pkg/client"
7+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
8+
)
9+
10+
type Clients struct {
11+
Client client.Client
12+
DC dynamic.Interface
13+
}
14+
15+
type GenericPolicy struct {
16+
metav1.TypeMeta `json:",inline"`
17+
metav1.ObjectMeta `json:"metadata,omitempty"`
18+
Spec GenericPolicySpec
19+
}
20+
21+
type GenericPolicySpec struct {
22+
TargetRef gatewayv1alpha2.PolicyTargetReference
23+
}
24+
25+
func (p *GenericPolicy) IsAttachedTo(targetRef gatewayv1alpha2.PolicyTargetReference) bool {
26+
if p.Spec.TargetRef.Group != targetRef.Group || p.Spec.TargetRef.Kind != targetRef.Kind || p.Spec.TargetRef.Name != targetRef.Name {
27+
return false
28+
}
29+
if p.Spec.TargetRef.Namespace == targetRef.Namespace {
30+
return true
31+
}
32+
if targetRef.Namespace == nil {
33+
return false
34+
}
35+
ns := p.Namespace
36+
if p.Spec.TargetRef.Namespace != nil {
37+
ns = string(*p.Spec.TargetRef.Namespace)
38+
}
39+
if ns != string(*targetRef.Namespace) {
40+
return false
41+
}
42+
return true
43+
}

‎samples/crds.yaml

+348
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
apiVersion: apiextensions.k8s.io/v1
2+
kind: CustomResourceDefinition
3+
metadata:
4+
labels:
5+
gateway.networking.k8s.io/policy: "true"
6+
name: healthcheckpolicies.foo.com
7+
spec:
8+
group: foo.com
9+
names:
10+
kind: HealthCheckPolicy
11+
listKind: HealthCheckPolicyList
12+
plural: healthcheckpolicies
13+
singular: healthcheckpolicy
14+
scope: Namespaced
15+
versions:
16+
- name: v1
17+
schema:
18+
openAPIV3Schema:
19+
description: "healthcheckpolicy"
20+
properties:
21+
apiVersion:
22+
description: 'APIVersion defines the versioned schema of this representation
23+
of an object. Servers should convert recognized schemas to the latest
24+
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
25+
type: string
26+
kind:
27+
description: 'Kind is a string value representing the REST resource this
28+
object represents. Servers may infer this from the endpoint the client
29+
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
30+
type: string
31+
metadata:
32+
type: object
33+
spec:
34+
description: Spec
35+
properties:
36+
default:
37+
description: Default defines default policy configuration for the
38+
targeted resource.
39+
properties:
40+
sampleField:
41+
description: sampleField
42+
type: string
43+
type: object
44+
targetRef:
45+
description: TargetRef identifies an API object to apply policy to.
46+
properties:
47+
group:
48+
description: Group is the group of the target resource.
49+
maxLength: 253
50+
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
51+
type: string
52+
kind:
53+
description: Kind is kind of the target resource.
54+
maxLength: 63
55+
minLength: 1
56+
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
57+
type: string
58+
name:
59+
description: Name is the name of the target resource.
60+
maxLength: 253
61+
minLength: 1
62+
type: string
63+
namespace:
64+
description: Namespace is the namespace of the referent. When
65+
unspecified, the local namespace is inferred. Even when policy
66+
targets a resource in a different namespace, it MUST only apply
67+
to traffic originating from the same namespace as the policy.
68+
maxLength: 63
69+
minLength: 1
70+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
71+
type: string
72+
required:
73+
- group
74+
- kind
75+
- name
76+
type: object
77+
required:
78+
- targetRef
79+
type: object
80+
required:
81+
- spec
82+
type: object
83+
served: true
84+
storage: true
85+
subresources:
86+
status: {}
87+
---
88+
apiVersion: apiextensions.k8s.io/v1
89+
kind: CustomResourceDefinition
90+
metadata:
91+
labels:
92+
gateway.networking.k8s.io/policy: "true"
93+
name: timeoutpolicies.bar.com
94+
spec:
95+
group: bar.com
96+
names:
97+
kind: TimeoutPolicy
98+
listKind: TimeoutPolicyList
99+
plural: timeoutpolicies
100+
singular: timeoutpolicy
101+
scope: Cluster
102+
versions:
103+
- name: v1
104+
schema:
105+
openAPIV3Schema:
106+
description: "timeoutpolicy"
107+
properties:
108+
apiVersion:
109+
description: 'APIVersion defines the versioned schema of this representation
110+
of an object. Servers should convert recognized schemas to the latest
111+
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
112+
type: string
113+
kind:
114+
description: 'Kind is a string value representing the REST resource this
115+
object represents. Servers may infer this from the endpoint the client
116+
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
117+
type: string
118+
metadata:
119+
type: object
120+
spec:
121+
description: Spec
122+
properties:
123+
default:
124+
description: Default defines default policy configuration for the
125+
targeted resource.
126+
properties:
127+
sampleField:
128+
description: sampleField
129+
type: string
130+
type: object
131+
targetRef:
132+
description: TargetRef identifies an API object to apply policy to.
133+
properties:
134+
group:
135+
description: Group is the group of the target resource.
136+
maxLength: 253
137+
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
138+
type: string
139+
kind:
140+
description: Kind is kind of the target resource.
141+
maxLength: 63
142+
minLength: 1
143+
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
144+
type: string
145+
name:
146+
description: Name is the name of the target resource.
147+
maxLength: 253
148+
minLength: 1
149+
type: string
150+
namespace:
151+
description: Namespace is the namespace of the referent. When
152+
unspecified, the local namespace is inferred. Even when policy
153+
targets a resource in a different namespace, it MUST only apply
154+
to traffic originating from the same namespace as the policy.
155+
maxLength: 63
156+
minLength: 1
157+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
158+
type: string
159+
required:
160+
- group
161+
- kind
162+
- name
163+
type: object
164+
required:
165+
- targetRef
166+
type: object
167+
required:
168+
- spec
169+
type: object
170+
served: true
171+
storage: true
172+
subresources:
173+
status: {}
174+
---
175+
apiVersion: apiextensions.k8s.io/v1
176+
kind: CustomResourceDefinition
177+
metadata:
178+
labels:
179+
gateway.networking.k8s.io/policy: "true"
180+
name: retryonpolicies.foo.com
181+
spec:
182+
group: foo.com
183+
names:
184+
kind: RetryOnPolicy
185+
listKind: RetryOnPolicyList
186+
plural: retryonpolicies
187+
singular: retryonpolicy
188+
scope: Namespaced
189+
versions:
190+
- name: v1
191+
schema:
192+
openAPIV3Schema:
193+
description: "retryonpolicy"
194+
properties:
195+
apiVersion:
196+
description: 'APIVersion defines the versioned schema of this representation
197+
of an object. Servers should convert recognized schemas to the latest
198+
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
199+
type: string
200+
kind:
201+
description: 'Kind is a string value representing the REST resource this
202+
object represents. Servers may infer this from the endpoint the client
203+
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
204+
type: string
205+
metadata:
206+
type: object
207+
spec:
208+
description: Spec
209+
properties:
210+
default:
211+
description: Default defines default policy configuration for the
212+
targeted resource.
213+
properties:
214+
sampleField:
215+
description: sampleField
216+
type: string
217+
type: object
218+
targetRef:
219+
description: TargetRef identifies an API object to apply policy to.
220+
properties:
221+
group:
222+
description: Group is the group of the target resource.
223+
maxLength: 253
224+
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
225+
type: string
226+
kind:
227+
description: Kind is kind of the target resource.
228+
maxLength: 63
229+
minLength: 1
230+
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
231+
type: string
232+
name:
233+
description: Name is the name of the target resource.
234+
maxLength: 253
235+
minLength: 1
236+
type: string
237+
namespace:
238+
description: Namespace is the namespace of the referent. When
239+
unspecified, the local namespace is inferred. Even when policy
240+
targets a resource in a different namespace, it MUST only apply
241+
to traffic originating from the same namespace as the policy.
242+
maxLength: 63
243+
minLength: 1
244+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
245+
type: string
246+
required:
247+
- group
248+
- kind
249+
- name
250+
type: object
251+
required:
252+
- targetRef
253+
type: object
254+
required:
255+
- spec
256+
type: object
257+
served: true
258+
storage: true
259+
subresources:
260+
status: {}
261+
---
262+
apiVersion: apiextensions.k8s.io/v1
263+
kind: CustomResourceDefinition
264+
metadata:
265+
labels:
266+
gateway.networking.k8s.io/policy: "true"
267+
name: tlsminimumversionpolicies.baz.com
268+
spec:
269+
group: baz.com
270+
names:
271+
kind: TLSMinimumVersionPolicy
272+
listKind: TLSMinimumVersionPolicyList
273+
plural: tlsminimumversionpolicies
274+
singular: tlsminimumversionpolicy
275+
scope: Namespaced
276+
versions:
277+
- name: v1
278+
schema:
279+
openAPIV3Schema:
280+
description: "tlsminimumversionpolicy"
281+
properties:
282+
apiVersion:
283+
description: 'APIVersion defines the versioned schema of this representation
284+
of an object. Servers should convert recognized schemas to the latest
285+
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
286+
type: string
287+
kind:
288+
description: 'Kind is a string value representing the REST resource this
289+
object represents. Servers may infer this from the endpoint the client
290+
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
291+
type: string
292+
metadata:
293+
type: object
294+
spec:
295+
description: Spec
296+
properties:
297+
default:
298+
description: Default defines default policy configuration for the
299+
targeted resource.
300+
properties:
301+
sampleField:
302+
description: sampleField
303+
type: string
304+
type: object
305+
targetRef:
306+
description: TargetRef identifies an API object to apply policy to.
307+
properties:
308+
group:
309+
description: Group is the group of the target resource.
310+
maxLength: 253
311+
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
312+
type: string
313+
kind:
314+
description: Kind is kind of the target resource.
315+
maxLength: 63
316+
minLength: 1
317+
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
318+
type: string
319+
name:
320+
description: Name is the name of the target resource.
321+
maxLength: 253
322+
minLength: 1
323+
type: string
324+
namespace:
325+
description: Namespace is the namespace of the referent. When
326+
unspecified, the local namespace is inferred. Even when policy
327+
targets a resource in a different namespace, it MUST only apply
328+
to traffic originating from the same namespace as the policy.
329+
maxLength: 63
330+
minLength: 1
331+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
332+
type: string
333+
required:
334+
- group
335+
- kind
336+
- name
337+
type: object
338+
required:
339+
- targetRef
340+
type: object
341+
required:
342+
- spec
343+
type: object
344+
served: true
345+
storage: true
346+
subresources:
347+
status: {}
348+
---

‎samples/examples.yaml

+223
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
apiVersion: v1
2+
kind: Namespace
3+
metadata:
4+
name: ns2
5+
---
6+
apiVersion: gateway.networking.k8s.io/v1beta1
7+
kind: GatewayClass
8+
metadata:
9+
name: foo-com-external-gateway-class
10+
spec:
11+
controllerName: foo.com/external-gateway-class
12+
description: Create an external load balancer
13+
---
14+
apiVersion: gateway.networking.k8s.io/v1beta1
15+
kind: GatewayClass
16+
metadata:
17+
name: bar-com-internal-gateway-class
18+
spec:
19+
controllerName: bar.baz/internal-gateway-class
20+
description: Create an internal load balancer
21+
---
22+
kind: Gateway
23+
apiVersion: gateway.networking.k8s.io/v1beta1
24+
metadata:
25+
name: demo-gateway-1
26+
namespace: default
27+
spec:
28+
gatewayClassName: foo-com-external-gateway-class
29+
listeners:
30+
- name: http
31+
protocol: HTTP
32+
port: 80
33+
---
34+
kind: Gateway
35+
apiVersion: gateway.networking.k8s.io/v1beta1
36+
metadata:
37+
name: demo-gateway-2
38+
namespace: ns2
39+
spec:
40+
gatewayClassName: bar-com-internal-gateway-class
41+
listeners:
42+
- name: http
43+
protocol: HTTP
44+
port: 80
45+
---
46+
kind: HTTPRoute
47+
apiVersion: gateway.networking.k8s.io/v1beta1
48+
metadata:
49+
name: demo-httproute-1
50+
namespace: default
51+
spec:
52+
parentRefs:
53+
- kind: Gateway
54+
name: demo-gateway-1
55+
hostnames:
56+
- "demo.com"
57+
rules:
58+
- matches:
59+
- path:
60+
type: PathPrefix
61+
value: /example
62+
backendRefs:
63+
- name: demo-svc
64+
port: 80
65+
---
66+
kind: HTTPRoute
67+
apiVersion: gateway.networking.k8s.io/v1beta1
68+
metadata:
69+
name: demo-httproute-2
70+
namespace: default
71+
spec:
72+
parentRefs:
73+
- kind: Gateway
74+
name: demo-gateway-1
75+
hostnames:
76+
- "example.com"
77+
rules:
78+
- matches:
79+
- path:
80+
type: PathPrefix
81+
value: /example
82+
backendRefs:
83+
- name: demo-svc
84+
port: 80
85+
---
86+
kind: HTTPRoute
87+
apiVersion: gateway.networking.k8s.io/v1beta1
88+
metadata:
89+
name: demo-httproute-3
90+
namespace: default
91+
spec:
92+
parentRefs:
93+
- kind: Gateway
94+
name: demo-gateway-1
95+
hostnames:
96+
- "demo.com"
97+
rules:
98+
- matches:
99+
- path:
100+
type: PathPrefix
101+
value: /example
102+
backendRefs:
103+
- name: demo-svc
104+
port: 80
105+
---
106+
kind: HTTPRoute
107+
apiVersion: gateway.networking.k8s.io/v1beta1
108+
metadata:
109+
name: demo-httproute-3
110+
namespace: ns2
111+
spec:
112+
parentRefs:
113+
- kind: Gateway
114+
name: demo-gateway-2
115+
hostnames:
116+
- "example.com"
117+
rules:
118+
- matches:
119+
- path:
120+
type: PathPrefix
121+
value: /example
122+
backendRefs:
123+
- name: demo-svc
124+
port: 80
125+
---
126+
kind: HTTPRoute
127+
apiVersion: gateway.networking.k8s.io/v1beta1
128+
metadata:
129+
name: demo-httproute-4
130+
namespace: ns2
131+
spec:
132+
parentRefs:
133+
- kind: Gateway
134+
name: demo-gateway-1
135+
namespace: default
136+
hostnames:
137+
- "demo.com"
138+
rules:
139+
- matches:
140+
- path:
141+
type: PathPrefix
142+
value: /example
143+
backendRefs:
144+
- name: demo-svc
145+
port: 80
146+
---
147+
apiVersion: bar.com/v1
148+
kind: TimeoutPolicy
149+
metadata:
150+
name: demo-timeout-policy-1
151+
spec:
152+
targetRef:
153+
group: "gateway.networking.k8s.io"
154+
kind: GatewayClass
155+
name: foo-com-external-gateway-class
156+
default:
157+
sampleField: "hello"
158+
---
159+
apiVersion: foo.com/v1
160+
kind: RetryOnPolicy
161+
metadata:
162+
name: demo-retry-policy-1
163+
namespace: default
164+
spec:
165+
targetRef:
166+
group: "gateway.networking.k8s.io"
167+
kind: Gateway
168+
name: demo-gateway-1
169+
default:
170+
sampleField: "hello"
171+
---
172+
apiVersion: baz.com/v1
173+
kind: TLSMinimumVersionPolicy
174+
metadata:
175+
name: demo-tls-min-version-policy-1
176+
namespace: default
177+
spec:
178+
targetRef:
179+
group: "gateway.networking.k8s.io"
180+
kind: HTTPRoute
181+
name: demo-httproute-1
182+
default:
183+
sampleField: "hello"
184+
---
185+
apiVersion: foo.com/v1
186+
kind: RetryOnPolicy
187+
metadata:
188+
name: demo-retry-policy-2
189+
namespace: default
190+
spec:
191+
targetRef:
192+
group: "gateway.networking.k8s.io"
193+
kind: HTTPRoute
194+
name: demo-httproute-2
195+
default:
196+
sampleField: "hello"
197+
---
198+
apiVersion: foo.com/v1
199+
kind: HealthCheckPolicy
200+
metadata:
201+
name: demo-health-check-1
202+
namespace: default
203+
spec:
204+
targetRef:
205+
group: "gateway.networking.k8s.io"
206+
kind: Gateway
207+
name: demo-gateway-1
208+
default:
209+
sampleField: "hello"
210+
---
211+
apiVersion: baz.com/v1
212+
kind: TLSMinimumVersionPolicy
213+
metadata:
214+
name: demo-tls-min-version-policy-2
215+
namespace: ns2
216+
spec:
217+
targetRef:
218+
group: "gateway.networking.k8s.io"
219+
kind: Gateway
220+
name: demo-gateway-2
221+
default:
222+
sampleField: "hello"
223+
---

0 commit comments

Comments
 (0)
Please sign in to comment.