From bd369a097389814ce70f87545d7b0131f23d5bed Mon Sep 17 00:00:00 2001 From: kahirokunn Date: Tue, 10 Dec 2024 09:50:03 +0900 Subject: [PATCH] feat: add creation of ReferenceGrant objects for custom backend refs in Gateway API This feature is used only when referencing Services in another namespace using custom backend refs in Gateway API. It ensures proper cross-namespace access by automatically creating the necessary ReferenceGrant objects. Signed-off-by: kahirokunn --- charts/flagger/templates/rbac.yaml | 2 + .../v1beta1/referencegrant_types.go | 145 ++++++++++++++++++ pkg/apis/gatewayapi/v1beta1/register.go | 2 + .../v1beta1/zz_generated.deepcopy.go | 125 +++++++++++++++ .../v1beta1/fake/fake_gatewayapi_client.go | 4 + .../v1beta1/fake/fake_referencegrant.go | 134 ++++++++++++++++ .../gatewayapi/v1beta1/gatewayapi_client.go | 5 + .../gatewayapi/v1beta1/generated_expansion.go | 2 + .../gatewayapi/v1beta1/referencegrant.go | 67 ++++++++ .../gatewayapi/v1beta1/interface.go | 7 + .../gatewayapi/v1beta1/referencegrant.go | 90 +++++++++++ .../informers/externalversions/generic.go | 2 + .../gatewayapi/v1beta1/expansion_generated.go | 8 + .../gatewayapi/v1beta1/referencegrant.go | 70 +++++++++ pkg/router/gateway_api.go | 55 ++++++- 15 files changed, 717 insertions(+), 1 deletion(-) create mode 100644 pkg/apis/gatewayapi/v1beta1/referencegrant_types.go create mode 100644 pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/fake/fake_referencegrant.go create mode 100644 pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/referencegrant.go create mode 100644 pkg/client/informers/externalversions/gatewayapi/v1beta1/referencegrant.go create mode 100644 pkg/client/listers/gatewayapi/v1beta1/referencegrant.go diff --git a/charts/flagger/templates/rbac.yaml b/charts/flagger/templates/rbac.yaml index ae9c70155..964daed0b 100644 --- a/charts/flagger/templates/rbac.yaml +++ b/charts/flagger/templates/rbac.yaml @@ -226,6 +226,8 @@ rules: resources: - httproutes - httproutes/finalizers + - referencegrants + - referencegrants/finalizers verbs: - get - list diff --git a/pkg/apis/gatewayapi/v1beta1/referencegrant_types.go b/pkg/apis/gatewayapi/v1beta1/referencegrant_types.go new file mode 100644 index 000000000..443bc77cf --- /dev/null +++ b/pkg/apis/gatewayapi/v1beta1/referencegrant_types.go @@ -0,0 +1,145 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true +// +kubebuilder:resource:categories=gateway-api,shortName=refgrant +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` +// +kubebuilder:storageversion + +// ReferenceGrant identifies kinds of resources in other namespaces that are +// trusted to reference the specified kinds of resources in the same namespace +// as the policy. +// +// Each ReferenceGrant can be used to represent a unique trust relationship. +// Additional Reference Grants can be used to add to the set of trusted +// sources of inbound references for the namespace they are defined within. +// +// All cross-namespace references in Gateway API (with the exception of cross-namespace +// Gateway-route attachment) require a ReferenceGrant. +// +// ReferenceGrant is a form of runtime verification allowing users to assert +// which cross-namespace object references are permitted. Implementations that +// support ReferenceGrant MUST NOT permit cross-namespace references which have +// no grant, and MUST respond to the removal of a grant by revoking the access +// that the grant allowed. +type ReferenceGrant struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of ReferenceGrant. + Spec ReferenceGrantSpec `json:"spec,omitempty"` + + // Note that `Status` sub-resource has been excluded at the + // moment as it was difficult to work out the design. + // `Status` sub-resource may be added in future. +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true +// ReferenceGrantList contains a list of ReferenceGrant. +type ReferenceGrantList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ReferenceGrant `json:"items"` +} + +// ReferenceGrantSpec identifies a cross namespace relationship that is trusted +// for Gateway API. +type ReferenceGrantSpec struct { + // From describes the trusted namespaces and kinds that can reference the + // resources described in "To". Each entry in this list MUST be considered + // to be an additional place that references can be valid from, or to put + // this another way, entries MUST be combined using OR. + // + // Support: Core + // + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=16 + From []ReferenceGrantFrom `json:"from"` + + // To describes the resources that may be referenced by the resources + // described in "From". Each entry in this list MUST be considered to be an + // additional place that references can be valid to, or to put this another + // way, entries MUST be combined using OR. + // + // Support: Core + // + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=16 + To []ReferenceGrantTo `json:"to"` +} + +// ReferenceGrantFrom describes trusted namespaces and kinds. +type ReferenceGrantFrom struct { + // Group is the group of the referent. + // When empty, the Kubernetes core API group is inferred. + // + // Support: Core + Group Group `json:"group"` + + // Kind is the kind of the referent. Although implementations may support + // additional resources, the following types are part of the "Core" + // support level for this field. + // + // When used to permit a SecretObjectReference: + // + // * Gateway + // + // When used to permit a BackendObjectReference: + // + // * GRPCRoute + // * HTTPRoute + // * TCPRoute + // * TLSRoute + // * UDPRoute + Kind Kind `json:"kind"` + + // Namespace is the namespace of the referent. + // + // Support: Core + Namespace Namespace `json:"namespace"` +} + +// ReferenceGrantTo describes what Kinds are allowed as targets of the +// references. +type ReferenceGrantTo struct { + // Group is the group of the referent. + // When empty, the Kubernetes core API group is inferred. + // + // Support: Core + Group Group `json:"group"` + + // Kind is the kind of the referent. Although implementations may support + // additional resources, the following types are part of the "Core" + // support level for this field: + // + // * Secret when used to permit a SecretObjectReference + // * Service when used to permit a BackendObjectReference + Kind Kind `json:"kind"` + + // Name is the name of the referent. When unspecified, this policy + // refers to all resources of the specified Group and Kind in the local + // namespace. + // + // +optional + Name *ObjectName `json:"name,omitempty"` +} diff --git a/pkg/apis/gatewayapi/v1beta1/register.go b/pkg/apis/gatewayapi/v1beta1/register.go index 930a1d8cf..231d8f792 100644 --- a/pkg/apis/gatewayapi/v1beta1/register.go +++ b/pkg/apis/gatewayapi/v1beta1/register.go @@ -33,6 +33,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &HTTPRoute{}, &HTTPRouteList{}, + &ReferenceGrant{}, + &ReferenceGrantList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/pkg/apis/gatewayapi/v1beta1/zz_generated.deepcopy.go b/pkg/apis/gatewayapi/v1beta1/zz_generated.deepcopy.go index 7938a9141..6825cb294 100644 --- a/pkg/apis/gatewayapi/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/gatewayapi/v1beta1/zz_generated.deepcopy.go @@ -643,6 +643,131 @@ func (in *ParentReference) DeepCopy() *ParentReference { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReferenceGrant) DeepCopyInto(out *ReferenceGrant) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReferenceGrant. +func (in *ReferenceGrant) DeepCopy() *ReferenceGrant { + if in == nil { + return nil + } + out := new(ReferenceGrant) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ReferenceGrant) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReferenceGrantFrom) DeepCopyInto(out *ReferenceGrantFrom) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReferenceGrantFrom. +func (in *ReferenceGrantFrom) DeepCopy() *ReferenceGrantFrom { + if in == nil { + return nil + } + out := new(ReferenceGrantFrom) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReferenceGrantList) DeepCopyInto(out *ReferenceGrantList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ReferenceGrant, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReferenceGrantList. +func (in *ReferenceGrantList) DeepCopy() *ReferenceGrantList { + if in == nil { + return nil + } + out := new(ReferenceGrantList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ReferenceGrantList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReferenceGrantSpec) DeepCopyInto(out *ReferenceGrantSpec) { + *out = *in + if in.From != nil { + in, out := &in.From, &out.From + *out = make([]ReferenceGrantFrom, len(*in)) + copy(*out, *in) + } + if in.To != nil { + in, out := &in.To, &out.To + *out = make([]ReferenceGrantTo, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReferenceGrantSpec. +func (in *ReferenceGrantSpec) DeepCopy() *ReferenceGrantSpec { + if in == nil { + return nil + } + out := new(ReferenceGrantSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReferenceGrantTo) DeepCopyInto(out *ReferenceGrantTo) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(ObjectName) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReferenceGrantTo. +func (in *ReferenceGrantTo) DeepCopy() *ReferenceGrantTo { + if in == nil { + return nil + } + out := new(ReferenceGrantTo) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RouteParentStatus) DeepCopyInto(out *RouteParentStatus) { *out = *in diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/fake/fake_gatewayapi_client.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/fake/fake_gatewayapi_client.go index 18d99e74a..7447c9048 100644 --- a/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/fake/fake_gatewayapi_client.go +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/fake/fake_gatewayapi_client.go @@ -32,6 +32,10 @@ func (c *FakeGatewayapiV1beta1) HTTPRoutes(namespace string) v1beta1.HTTPRouteIn return &FakeHTTPRoutes{c, namespace} } +func (c *FakeGatewayapiV1beta1) ReferenceGrants(namespace string) v1beta1.ReferenceGrantInterface { + return &FakeReferenceGrants{c, namespace} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeGatewayapiV1beta1) RESTClient() rest.Interface { diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/fake/fake_referencegrant.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/fake/fake_referencegrant.go new file mode 100644 index 000000000..bf6ef0b86 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/fake/fake_referencegrant.go @@ -0,0 +1,134 @@ +/* +Copyright 2020 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1beta1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeReferenceGrants implements ReferenceGrantInterface +type FakeReferenceGrants struct { + Fake *FakeGatewayapiV1beta1 + ns string +} + +var referencegrantsResource = v1beta1.SchemeGroupVersion.WithResource("referencegrants") + +var referencegrantsKind = v1beta1.SchemeGroupVersion.WithKind("ReferenceGrant") + +// Get takes name of the referenceGrant, and returns the corresponding referenceGrant object, and an error if there is any. +func (c *FakeReferenceGrants) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.ReferenceGrant, err error) { + emptyResult := &v1beta1.ReferenceGrant{} + obj, err := c.Fake. + Invokes(testing.NewGetActionWithOptions(referencegrantsResource, c.ns, name, options), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1beta1.ReferenceGrant), err +} + +// List takes label and field selectors, and returns the list of ReferenceGrants that match those selectors. +func (c *FakeReferenceGrants) List(ctx context.Context, opts v1.ListOptions) (result *v1beta1.ReferenceGrantList, err error) { + emptyResult := &v1beta1.ReferenceGrantList{} + obj, err := c.Fake. + Invokes(testing.NewListActionWithOptions(referencegrantsResource, referencegrantsKind, c.ns, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1beta1.ReferenceGrantList{ListMeta: obj.(*v1beta1.ReferenceGrantList).ListMeta} + for _, item := range obj.(*v1beta1.ReferenceGrantList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested referenceGrants. +func (c *FakeReferenceGrants) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchActionWithOptions(referencegrantsResource, c.ns, opts)) + +} + +// Create takes the representation of a referenceGrant and creates it. Returns the server's representation of the referenceGrant, and an error, if there is any. +func (c *FakeReferenceGrants) Create(ctx context.Context, referenceGrant *v1beta1.ReferenceGrant, opts v1.CreateOptions) (result *v1beta1.ReferenceGrant, err error) { + emptyResult := &v1beta1.ReferenceGrant{} + obj, err := c.Fake. + Invokes(testing.NewCreateActionWithOptions(referencegrantsResource, c.ns, referenceGrant, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1beta1.ReferenceGrant), err +} + +// Update takes the representation of a referenceGrant and updates it. Returns the server's representation of the referenceGrant, and an error, if there is any. +func (c *FakeReferenceGrants) Update(ctx context.Context, referenceGrant *v1beta1.ReferenceGrant, opts v1.UpdateOptions) (result *v1beta1.ReferenceGrant, err error) { + emptyResult := &v1beta1.ReferenceGrant{} + obj, err := c.Fake. + Invokes(testing.NewUpdateActionWithOptions(referencegrantsResource, c.ns, referenceGrant, opts), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1beta1.ReferenceGrant), err +} + +// Delete takes name of the referenceGrant and deletes it. Returns an error if one occurs. +func (c *FakeReferenceGrants) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(referencegrantsResource, c.ns, name, opts), &v1beta1.ReferenceGrant{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeReferenceGrants) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionActionWithOptions(referencegrantsResource, c.ns, opts, listOpts) + + _, err := c.Fake.Invokes(action, &v1beta1.ReferenceGrantList{}) + return err +} + +// Patch applies the patch and returns the patched referenceGrant. +func (c *FakeReferenceGrants) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.ReferenceGrant, err error) { + emptyResult := &v1beta1.ReferenceGrant{} + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceActionWithOptions(referencegrantsResource, c.ns, name, pt, data, opts, subresources...), emptyResult) + + if obj == nil { + return emptyResult, err + } + return obj.(*v1beta1.ReferenceGrant), err +} diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/gatewayapi_client.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/gatewayapi_client.go index 33f1724af..5743ad66a 100644 --- a/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/gatewayapi_client.go +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/gatewayapi_client.go @@ -29,6 +29,7 @@ import ( type GatewayapiV1beta1Interface interface { RESTClient() rest.Interface HTTPRoutesGetter + ReferenceGrantsGetter } // GatewayapiV1beta1Client is used to interact with features provided by the gatewayapi group. @@ -40,6 +41,10 @@ func (c *GatewayapiV1beta1Client) HTTPRoutes(namespace string) HTTPRouteInterfac return newHTTPRoutes(c, namespace) } +func (c *GatewayapiV1beta1Client) ReferenceGrants(namespace string) ReferenceGrantInterface { + return newReferenceGrants(c, namespace) +} + // NewForConfig creates a new GatewayapiV1beta1Client for the given config. // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), // where httpClient was generated with rest.HTTPClientFor(c). diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/generated_expansion.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/generated_expansion.go index 7cd07e728..b43e0c844 100644 --- a/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/generated_expansion.go @@ -19,3 +19,5 @@ limitations under the License. package v1beta1 type HTTPRouteExpansion interface{} + +type ReferenceGrantExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/referencegrant.go b/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/referencegrant.go new file mode 100644 index 000000000..76a62e58c --- /dev/null +++ b/pkg/client/clientset/versioned/typed/gatewayapi/v1beta1/referencegrant.go @@ -0,0 +1,67 @@ +/* +Copyright 2020 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + + v1beta1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1" + scheme "github.com/fluxcd/flagger/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + gentype "k8s.io/client-go/gentype" +) + +// ReferenceGrantsGetter has a method to return a ReferenceGrantInterface. +// A group's client should implement this interface. +type ReferenceGrantsGetter interface { + ReferenceGrants(namespace string) ReferenceGrantInterface +} + +// ReferenceGrantInterface has methods to work with ReferenceGrant resources. +type ReferenceGrantInterface interface { + Create(ctx context.Context, referenceGrant *v1beta1.ReferenceGrant, opts v1.CreateOptions) (*v1beta1.ReferenceGrant, error) + Update(ctx context.Context, referenceGrant *v1beta1.ReferenceGrant, opts v1.UpdateOptions) (*v1beta1.ReferenceGrant, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta1.ReferenceGrant, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta1.ReferenceGrantList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1beta1.ReferenceGrant, err error) + ReferenceGrantExpansion +} + +// referenceGrants implements ReferenceGrantInterface +type referenceGrants struct { + *gentype.ClientWithList[*v1beta1.ReferenceGrant, *v1beta1.ReferenceGrantList] +} + +// newReferenceGrants returns a ReferenceGrants +func newReferenceGrants(c *GatewayapiV1beta1Client, namespace string) *referenceGrants { + return &referenceGrants{ + gentype.NewClientWithList[*v1beta1.ReferenceGrant, *v1beta1.ReferenceGrantList]( + "referencegrants", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *v1beta1.ReferenceGrant { return &v1beta1.ReferenceGrant{} }, + func() *v1beta1.ReferenceGrantList { return &v1beta1.ReferenceGrantList{} }), + } +} diff --git a/pkg/client/informers/externalversions/gatewayapi/v1beta1/interface.go b/pkg/client/informers/externalversions/gatewayapi/v1beta1/interface.go index 8b782c6ea..f858a4b0a 100644 --- a/pkg/client/informers/externalversions/gatewayapi/v1beta1/interface.go +++ b/pkg/client/informers/externalversions/gatewayapi/v1beta1/interface.go @@ -26,6 +26,8 @@ import ( type Interface interface { // HTTPRoutes returns a HTTPRouteInformer. HTTPRoutes() HTTPRouteInformer + // ReferenceGrants returns a ReferenceGrantInformer. + ReferenceGrants() ReferenceGrantInformer } type version struct { @@ -43,3 +45,8 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList func (v *version) HTTPRoutes() HTTPRouteInformer { return &hTTPRouteInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } + +// ReferenceGrants returns a ReferenceGrantInformer. +func (v *version) ReferenceGrants() ReferenceGrantInformer { + return &referenceGrantInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/gatewayapi/v1beta1/referencegrant.go b/pkg/client/informers/externalversions/gatewayapi/v1beta1/referencegrant.go new file mode 100644 index 000000000..464e5af79 --- /dev/null +++ b/pkg/client/informers/externalversions/gatewayapi/v1beta1/referencegrant.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "context" + time "time" + + gatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1" + versioned "github.com/fluxcd/flagger/pkg/client/clientset/versioned" + internalinterfaces "github.com/fluxcd/flagger/pkg/client/informers/externalversions/internalinterfaces" + v1beta1 "github.com/fluxcd/flagger/pkg/client/listers/gatewayapi/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ReferenceGrantInformer provides access to a shared informer and lister for +// ReferenceGrants. +type ReferenceGrantInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1beta1.ReferenceGrantLister +} + +type referenceGrantInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewReferenceGrantInformer constructs a new informer for ReferenceGrant type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewReferenceGrantInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredReferenceGrantInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredReferenceGrantInformer constructs a new informer for ReferenceGrant type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredReferenceGrantInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.GatewayapiV1beta1().ReferenceGrants(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.GatewayapiV1beta1().ReferenceGrants(namespace).Watch(context.TODO(), options) + }, + }, + &gatewayapiv1beta1.ReferenceGrant{}, + resyncPeriod, + indexers, + ) +} + +func (f *referenceGrantInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredReferenceGrantInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *referenceGrantInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&gatewayapiv1beta1.ReferenceGrant{}, f.defaultInformer) +} + +func (f *referenceGrantInformer) Lister() v1beta1.ReferenceGrantLister { + return v1beta1.NewReferenceGrantLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 78431793a..dd2c062e1 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -106,6 +106,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource // Group=gatewayapi, Version=v1beta1 case gatewayapiv1beta1.SchemeGroupVersion.WithResource("httproutes"): return &genericInformer{resource: resource.GroupResource(), informer: f.Gatewayapi().V1beta1().HTTPRoutes().Informer()}, nil + case gatewayapiv1beta1.SchemeGroupVersion.WithResource("referencegrants"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Gatewayapi().V1beta1().ReferenceGrants().Informer()}, nil // Group=gloo.solo.io, Version=v1 case gloov1.SchemeGroupVersion.WithResource("upstreams"): diff --git a/pkg/client/listers/gatewayapi/v1beta1/expansion_generated.go b/pkg/client/listers/gatewayapi/v1beta1/expansion_generated.go index 2d673fb05..44376cc74 100644 --- a/pkg/client/listers/gatewayapi/v1beta1/expansion_generated.go +++ b/pkg/client/listers/gatewayapi/v1beta1/expansion_generated.go @@ -25,3 +25,11 @@ type HTTPRouteListerExpansion interface{} // HTTPRouteNamespaceListerExpansion allows custom methods to be added to // HTTPRouteNamespaceLister. type HTTPRouteNamespaceListerExpansion interface{} + +// ReferenceGrantListerExpansion allows custom methods to be added to +// ReferenceGrantLister. +type ReferenceGrantListerExpansion interface{} + +// ReferenceGrantNamespaceListerExpansion allows custom methods to be added to +// ReferenceGrantNamespaceLister. +type ReferenceGrantNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/gatewayapi/v1beta1/referencegrant.go b/pkg/client/listers/gatewayapi/v1beta1/referencegrant.go new file mode 100644 index 000000000..891df708a --- /dev/null +++ b/pkg/client/listers/gatewayapi/v1beta1/referencegrant.go @@ -0,0 +1,70 @@ +/* +Copyright 2020 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1beta1 + +import ( + v1beta1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/listers" + "k8s.io/client-go/tools/cache" +) + +// ReferenceGrantLister helps list ReferenceGrants. +// All objects returned here must be treated as read-only. +type ReferenceGrantLister interface { + // List lists all ReferenceGrants in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1beta1.ReferenceGrant, err error) + // ReferenceGrants returns an object that can list and get ReferenceGrants. + ReferenceGrants(namespace string) ReferenceGrantNamespaceLister + ReferenceGrantListerExpansion +} + +// referenceGrantLister implements the ReferenceGrantLister interface. +type referenceGrantLister struct { + listers.ResourceIndexer[*v1beta1.ReferenceGrant] +} + +// NewReferenceGrantLister returns a new ReferenceGrantLister. +func NewReferenceGrantLister(indexer cache.Indexer) ReferenceGrantLister { + return &referenceGrantLister{listers.New[*v1beta1.ReferenceGrant](indexer, v1beta1.Resource("referencegrant"))} +} + +// ReferenceGrants returns an object that can list and get ReferenceGrants. +func (s *referenceGrantLister) ReferenceGrants(namespace string) ReferenceGrantNamespaceLister { + return referenceGrantNamespaceLister{listers.NewNamespaced[*v1beta1.ReferenceGrant](s.ResourceIndexer, namespace)} +} + +// ReferenceGrantNamespaceLister helps list and get ReferenceGrants. +// All objects returned here must be treated as read-only. +type ReferenceGrantNamespaceLister interface { + // List lists all ReferenceGrants in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1beta1.ReferenceGrant, err error) + // Get retrieves the ReferenceGrant from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1beta1.ReferenceGrant, error) + ReferenceGrantNamespaceListerExpansion +} + +// referenceGrantNamespaceLister implements the ReferenceGrantNamespaceLister +// interface. +type referenceGrantNamespaceLister struct { + listers.ResourceIndexer[*v1beta1.ReferenceGrant] +} diff --git a/pkg/router/gateway_api.go b/pkg/router/gateway_api.go index 25b91210a..56ad04d03 100644 --- a/pkg/router/gateway_api.go +++ b/pkg/router/gateway_api.go @@ -25,7 +25,7 @@ import ( flaggerv1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1" v1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1" - "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1" + v1beta1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1" istiov1beta1 "github.com/fluxcd/flagger/pkg/apis/istio/v1beta1" clientset "github.com/fluxcd/flagger/pkg/client/clientset/versioned" "github.com/google/go-cmp/cmp" @@ -211,6 +211,59 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error { } } + // auto create reference grants + svcNamespaces := []string{} + + for _, rule := range httpRouteSpec.Rules { + for _, backendRef := range rule.BackendRefs { + if backendRef.Namespace != nil { + svcNamespaces = append(svcNamespaces, string(*backendRef.Namespace)) + } + } + } + + // sort and remove duplicates + slices.Sort(svcNamespaces) + svcNamespaces = slices.Compact(svcNamespaces) + + referenceGrants := []*v1beta1.ReferenceGrant{} + + for _, svcNamespace := range svcNamespaces { + if svcNamespace != hrNamespace { + rg := &v1beta1.ReferenceGrant{ + ObjectMeta: metav1.ObjectMeta{ + Name: canarySvcName, + Namespace: svcNamespace, + }, + Spec: v1beta1.ReferenceGrantSpec{ + From: []v1beta1.ReferenceGrantFrom{ + { + Group: "gateway.networking.k8s.io", + Kind: "HTTPRoute", + Namespace: v1beta1.Namespace(hrNamespace), + }, + }, + To: []v1beta1.ReferenceGrantTo{ + { + Group: "", + Kind: "Service", + }, + }, + }, + } + referenceGrants = append(referenceGrants, rg) + } + } + + for _, rg := range referenceGrants { + _, err := gwr.gatewayAPIClient.GatewayapiV1beta1().ReferenceGrants(rg.Namespace).Create(context.TODO(), rg, metav1.CreateOptions{}) + if err == nil { + gwr.logger.Infof("ReferenceGrant %s.%s has been created", rg.Name, rg.Namespace) + } else if !errors.IsAlreadyExists(err) { + return fmt.Errorf("ReferenceGrant %s.%s creation error: %w", rg.Name, rg.Namespace, err) + } + } + if httpRoute != nil { specDiff := cmp.Diff( httpRoute.Spec, httpRouteSpec,