From ce0dd762f8066d4a40dcba41d4637f3ca56e967d Mon Sep 17 00:00:00 2001 From: Jenny Shu <28537278+jenshu@users.noreply.github.com> Date: Thu, 6 Mar 2025 12:12:44 -0500 Subject: [PATCH 1/7] Fix the release chart uri (#10765) Signed-off-by: Jenny Shu --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7d7f9806610..3c919e06c36 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -173,7 +173,7 @@ jobs: - name: Install the released chart run: | helm install --create-namespace --namespace kgateway-system kgateway \ - oci://${{ env.IMAGE_REGISTRY }}/kgateway-dev/charts/kgateway \ + oci://${{ env.IMAGE_REGISTRY }}/charts/kgateway \ --set image.registry=${{ env.IMAGE_REGISTRY }} \ --version ${{ needs.setup.outputs.version }} \ --wait --timeout 5m From a6a853948861ef30a0aefeeaf5671337d257fac0 Mon Sep 17 00:00:00 2001 From: Tim Flannagan Date: Thu, 6 Mar 2025 14:20:29 -0500 Subject: [PATCH 2/7] Fix broken go-generate-mocks target (#10769) Signed-off-by: timflannagan --- Makefile | 5 ++++- internal/kgateway/query/mocks/mock_queries.go | 2 +- internal/kgateway/query/query_test.go | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 3fbded8c83c..af5dc6bab5c 100644 --- a/Makefile +++ b/Makefile @@ -312,7 +312,10 @@ generated-code: update-licenses generated-code: fmt .PHONY: go-generate-all -go-generate-all: ## Run all go generate directives in the repo, including codegen for protos, mockgen, and more +go-generate-all: go-generate-apis go-generate-mocks + +.PHONY: go-generate-apis +go-generate-apis: ## Run all go generate directives in the repo, including codegen for protos, mockgen, and more GO111MODULE=on go generate ./hack/... .PHONY: go-generate-mocks diff --git a/internal/kgateway/query/mocks/mock_queries.go b/internal/kgateway/query/mocks/mock_queries.go index 12bf592659b..9834d6ca4e0 100644 --- a/internal/kgateway/query/mocks/mock_queries.go +++ b/internal/kgateway/query/mocks/mock_queries.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/kgateway-dev/kgateway/internal/kgateway/query (interfaces: GatewayQueries) +// Source: github.com/kgateway-dev/kgateway/v2/internal/kgateway/query (interfaces: GatewayQueries) // Package mocks is a generated GoMock package. package mocks diff --git a/internal/kgateway/query/query_test.go b/internal/kgateway/query/query_test.go index 6a5f77834e9..00ed06ab329 100644 --- a/internal/kgateway/query/query_test.go +++ b/internal/kgateway/query/query_test.go @@ -27,7 +27,7 @@ import ( "github.com/kgateway-dev/kgateway/v2/internal/kgateway/wellknown" ) -//go:generate go run github.com/golang/mock/mockgen -destination mocks/mock_queries.go -package mocks github.com/kgateway-dev/kgateway/internal/kgateway/query GatewayQueries +//go:generate go run github.com/golang/mock/mockgen -destination mocks/mock_queries.go -package mocks github.com/kgateway-dev/kgateway/v2/internal/kgateway/query GatewayQueries var _ = Describe("Query", func() { Describe("GetSecretRef", func() { From 62ac501cac979965720c08b920fcd55bbde0a63f Mon Sep 17 00:00:00 2001 From: Tim Flannagan Date: Thu, 6 Mar 2025 14:42:58 -0500 Subject: [PATCH 3/7] Remove the test/mocks directory (#10770) Signed-off-by: timflannagan --- test/mocks/cache/corecache.go | 190 ------ test/mocks/gloo/validation_client.go | 80 --- test/mocks/kubernetes/kubeinterface.go | 846 ------------------------- test/mocks/mocks.go | 7 - 4 files changed, 1123 deletions(-) delete mode 100644 test/mocks/cache/corecache.go delete mode 100644 test/mocks/gloo/validation_client.go delete mode 100644 test/mocks/kubernetes/kubeinterface.go delete mode 100644 test/mocks/mocks.go diff --git a/test/mocks/cache/corecache.go b/test/mocks/cache/corecache.go deleted file mode 100644 index 638ef271fa5..00000000000 --- a/test/mocks/cache/corecache.go +++ /dev/null @@ -1,190 +0,0 @@ -//go:build ignore - -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/solo-io/solo-kit/pkg/api/v1/clients/kube/cache (interfaces: KubeCoreCache) - -// Package mock_cache is a generated GoMock package. -package mock_cache - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - cache "github.com/solo-io/solo-kit/pkg/api/v1/clients/kube/cache" - v1 "k8s.io/client-go/listers/core/v1" -) - -// MockKubeCoreCache is a mock of KubeCoreCache interface. -type MockKubeCoreCache struct { - ctrl *gomock.Controller - recorder *MockKubeCoreCacheMockRecorder -} - -// MockKubeCoreCacheMockRecorder is the mock recorder for MockKubeCoreCache. -type MockKubeCoreCacheMockRecorder struct { - mock *MockKubeCoreCache -} - -// NewMockKubeCoreCache creates a new mock instance. -func NewMockKubeCoreCache(ctrl *gomock.Controller) *MockKubeCoreCache { - mock := &MockKubeCoreCache{ctrl: ctrl} - mock.recorder = &MockKubeCoreCacheMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockKubeCoreCache) EXPECT() *MockKubeCoreCacheMockRecorder { - return m.recorder -} - -// ConfigMapLister mocks base method. -func (m *MockKubeCoreCache) ConfigMapLister() v1.ConfigMapLister { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ConfigMapLister") - ret0, _ := ret[0].(v1.ConfigMapLister) - return ret0 -} - -// ConfigMapLister indicates an expected call of ConfigMapLister. -func (mr *MockKubeCoreCacheMockRecorder) ConfigMapLister() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigMapLister", reflect.TypeOf((*MockKubeCoreCache)(nil).ConfigMapLister)) -} - -// NamespaceLister mocks base method. -func (m *MockKubeCoreCache) NamespaceLister() v1.NamespaceLister { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NamespaceLister") - ret0, _ := ret[0].(v1.NamespaceLister) - return ret0 -} - -// NamespaceLister indicates an expected call of NamespaceLister. -func (mr *MockKubeCoreCacheMockRecorder) NamespaceLister() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NamespaceLister", reflect.TypeOf((*MockKubeCoreCache)(nil).NamespaceLister)) -} - -// NamespacedConfigMapLister mocks base method. -func (m *MockKubeCoreCache) NamespacedConfigMapLister(arg0 string) cache.ConfigMapLister { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NamespacedConfigMapLister", arg0) - ret0, _ := ret[0].(cache.ConfigMapLister) - return ret0 -} - -// NamespacedConfigMapLister indicates an expected call of NamespacedConfigMapLister. -func (mr *MockKubeCoreCacheMockRecorder) NamespacedConfigMapLister(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NamespacedConfigMapLister", reflect.TypeOf((*MockKubeCoreCache)(nil).NamespacedConfigMapLister), arg0) -} - -// NamespacedPodLister mocks base method. -func (m *MockKubeCoreCache) NamespacedPodLister(arg0 string) cache.PodLister { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NamespacedPodLister", arg0) - ret0, _ := ret[0].(cache.PodLister) - return ret0 -} - -// NamespacedPodLister indicates an expected call of NamespacedPodLister. -func (mr *MockKubeCoreCacheMockRecorder) NamespacedPodLister(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NamespacedPodLister", reflect.TypeOf((*MockKubeCoreCache)(nil).NamespacedPodLister), arg0) -} - -// NamespacedSecretLister mocks base method. -func (m *MockKubeCoreCache) NamespacedSecretLister(arg0 string) cache.SecretLister { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NamespacedSecretLister", arg0) - ret0, _ := ret[0].(cache.SecretLister) - return ret0 -} - -// NamespacedSecretLister indicates an expected call of NamespacedSecretLister. -func (mr *MockKubeCoreCacheMockRecorder) NamespacedSecretLister(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NamespacedSecretLister", reflect.TypeOf((*MockKubeCoreCache)(nil).NamespacedSecretLister), arg0) -} - -// NamespacedServiceLister mocks base method. -func (m *MockKubeCoreCache) NamespacedServiceLister(arg0 string) cache.ServiceLister { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NamespacedServiceLister", arg0) - ret0, _ := ret[0].(cache.ServiceLister) - return ret0 -} - -// NamespacedServiceLister indicates an expected call of NamespacedServiceLister. -func (mr *MockKubeCoreCacheMockRecorder) NamespacedServiceLister(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NamespacedServiceLister", reflect.TypeOf((*MockKubeCoreCache)(nil).NamespacedServiceLister), arg0) -} - -// PodLister mocks base method. -func (m *MockKubeCoreCache) PodLister() v1.PodLister { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PodLister") - ret0, _ := ret[0].(v1.PodLister) - return ret0 -} - -// PodLister indicates an expected call of PodLister. -func (mr *MockKubeCoreCacheMockRecorder) PodLister() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PodLister", reflect.TypeOf((*MockKubeCoreCache)(nil).PodLister)) -} - -// SecretLister mocks base method. -func (m *MockKubeCoreCache) SecretLister() v1.SecretLister { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SecretLister") - ret0, _ := ret[0].(v1.SecretLister) - return ret0 -} - -// SecretLister indicates an expected call of SecretLister. -func (mr *MockKubeCoreCacheMockRecorder) SecretLister() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SecretLister", reflect.TypeOf((*MockKubeCoreCache)(nil).SecretLister)) -} - -// ServiceLister mocks base method. -func (m *MockKubeCoreCache) ServiceLister() v1.ServiceLister { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ServiceLister") - ret0, _ := ret[0].(v1.ServiceLister) - return ret0 -} - -// ServiceLister indicates an expected call of ServiceLister. -func (mr *MockKubeCoreCacheMockRecorder) ServiceLister() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceLister", reflect.TypeOf((*MockKubeCoreCache)(nil).ServiceLister)) -} - -// Subscribe mocks base method. -func (m *MockKubeCoreCache) Subscribe() <-chan struct{} { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Subscribe") - ret0, _ := ret[0].(<-chan struct{}) - return ret0 -} - -// Subscribe indicates an expected call of Subscribe. -func (mr *MockKubeCoreCacheMockRecorder) Subscribe() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockKubeCoreCache)(nil).Subscribe)) -} - -// Unsubscribe mocks base method. -func (m *MockKubeCoreCache) Unsubscribe(arg0 <-chan struct{}) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "Unsubscribe", arg0) -} - -// Unsubscribe indicates an expected call of Unsubscribe. -func (mr *MockKubeCoreCacheMockRecorder) Unsubscribe(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unsubscribe", reflect.TypeOf((*MockKubeCoreCache)(nil).Unsubscribe), arg0) -} diff --git a/test/mocks/gloo/validation_client.go b/test/mocks/gloo/validation_client.go deleted file mode 100644 index e30223ae8fc..00000000000 --- a/test/mocks/gloo/validation_client.go +++ /dev/null @@ -1,80 +0,0 @@ -//go:build ignore - -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/kgateway-dev/kgateway/internal/gloo/pkg/api/grpc/validation (interfaces: GlooValidationServiceClient) - -// Package mock_validation is a generated GoMock package. -package mock_validation - -import ( - context "context" - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - grpc "google.golang.org/grpc" - - validation "github.com/kgateway-dev/kgateway/v2/internal/gloo/pkg/api/grpc/validation" -) - -// MockGlooValidationServiceClient is a mock of GlooValidationServiceClient interface. -type MockGlooValidationServiceClient struct { - ctrl *gomock.Controller - recorder *MockGlooValidationServiceClientMockRecorder -} - -// MockGlooValidationServiceClientMockRecorder is the mock recorder for MockGlooValidationServiceClient. -type MockGlooValidationServiceClientMockRecorder struct { - mock *MockGlooValidationServiceClient -} - -// NewMockGlooValidationServiceClient creates a new mock instance. -func NewMockGlooValidationServiceClient(ctrl *gomock.Controller) *MockGlooValidationServiceClient { - mock := &MockGlooValidationServiceClient{ctrl: ctrl} - mock.recorder = &MockGlooValidationServiceClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockGlooValidationServiceClient) EXPECT() *MockGlooValidationServiceClientMockRecorder { - return m.recorder -} - -// NotifyOnResync mocks base method. -func (m *MockGlooValidationServiceClient) NotifyOnResync(arg0 context.Context, arg1 *validation.NotifyOnResyncRequest, arg2 ...grpc.CallOption) (validation.GlooValidationService_NotifyOnResyncClient, error) { - m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "NotifyOnResync", varargs...) - ret0, _ := ret[0].(validation.GlooValidationService_NotifyOnResyncClient) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// NotifyOnResync indicates an expected call of NotifyOnResync. -func (mr *MockGlooValidationServiceClientMockRecorder) NotifyOnResync(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotifyOnResync", reflect.TypeOf((*MockGlooValidationServiceClient)(nil).NotifyOnResync), varargs...) -} - -// Validate mocks base method. -func (m *MockGlooValidationServiceClient) Validate(arg0 context.Context, arg1 *validation.GlooValidationServiceRequest, arg2 ...grpc.CallOption) (*validation.GlooValidationServiceResponse, error) { - m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "Validate", varargs...) - ret0, _ := ret[0].(*validation.GlooValidationServiceResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Validate indicates an expected call of Validate. -func (mr *MockGlooValidationServiceClientMockRecorder) Validate(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockGlooValidationServiceClient)(nil).Validate), varargs...) -} diff --git a/test/mocks/kubernetes/kubeinterface.go b/test/mocks/kubernetes/kubeinterface.go deleted file mode 100644 index ff690bbf916..00000000000 --- a/test/mocks/kubernetes/kubeinterface.go +++ /dev/null @@ -1,846 +0,0 @@ -//go:build ignore - -// Code generated by MockGen. DO NOT EDIT. -// Source: k8s.io/client-go/kubernetes (interfaces: Interface) - -// Package mock_kubernetes is a generated GoMock package. -package mock_kubernetes - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - discovery "k8s.io/client-go/discovery" - v1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1" - v1alpha1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1" - v1beta1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1" - v1alpha10 "k8s.io/client-go/kubernetes/typed/apiserverinternal/v1alpha1" - v10 "k8s.io/client-go/kubernetes/typed/apps/v1" - v1beta10 "k8s.io/client-go/kubernetes/typed/apps/v1beta1" - v1beta2 "k8s.io/client-go/kubernetes/typed/apps/v1beta2" - v11 "k8s.io/client-go/kubernetes/typed/authentication/v1" - v1alpha11 "k8s.io/client-go/kubernetes/typed/authentication/v1alpha1" - v1beta11 "k8s.io/client-go/kubernetes/typed/authentication/v1beta1" - v12 "k8s.io/client-go/kubernetes/typed/authorization/v1" - v1beta12 "k8s.io/client-go/kubernetes/typed/authorization/v1beta1" - v13 "k8s.io/client-go/kubernetes/typed/autoscaling/v1" - v2 "k8s.io/client-go/kubernetes/typed/autoscaling/v2" - v2beta1 "k8s.io/client-go/kubernetes/typed/autoscaling/v2beta1" - v2beta2 "k8s.io/client-go/kubernetes/typed/autoscaling/v2beta2" - v14 "k8s.io/client-go/kubernetes/typed/batch/v1" - v1beta13 "k8s.io/client-go/kubernetes/typed/batch/v1beta1" - v15 "k8s.io/client-go/kubernetes/typed/certificates/v1" - v1alpha12 "k8s.io/client-go/kubernetes/typed/certificates/v1alpha1" - v1beta14 "k8s.io/client-go/kubernetes/typed/certificates/v1beta1" - v16 "k8s.io/client-go/kubernetes/typed/coordination/v1" - v1alpha13 "k8s.io/client-go/kubernetes/typed/coordination/v1alpha1" - v1beta15 "k8s.io/client-go/kubernetes/typed/coordination/v1beta1" - v17 "k8s.io/client-go/kubernetes/typed/core/v1" - v18 "k8s.io/client-go/kubernetes/typed/discovery/v1" - v1beta16 "k8s.io/client-go/kubernetes/typed/discovery/v1beta1" - v19 "k8s.io/client-go/kubernetes/typed/events/v1" - v1beta17 "k8s.io/client-go/kubernetes/typed/events/v1beta1" - v1beta18 "k8s.io/client-go/kubernetes/typed/extensions/v1beta1" - v110 "k8s.io/client-go/kubernetes/typed/flowcontrol/v1" - v1beta19 "k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta1" - v1beta20 "k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta2" - v1beta3 "k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3" - v111 "k8s.io/client-go/kubernetes/typed/networking/v1" - v1alpha14 "k8s.io/client-go/kubernetes/typed/networking/v1alpha1" - v1beta110 "k8s.io/client-go/kubernetes/typed/networking/v1beta1" - v112 "k8s.io/client-go/kubernetes/typed/node/v1" - v1alpha15 "k8s.io/client-go/kubernetes/typed/node/v1alpha1" - v1beta111 "k8s.io/client-go/kubernetes/typed/node/v1beta1" - v113 "k8s.io/client-go/kubernetes/typed/policy/v1" - v1beta112 "k8s.io/client-go/kubernetes/typed/policy/v1beta1" - v114 "k8s.io/client-go/kubernetes/typed/rbac/v1" - v1alpha16 "k8s.io/client-go/kubernetes/typed/rbac/v1alpha1" - v1beta113 "k8s.io/client-go/kubernetes/typed/rbac/v1beta1" - v1alpha3 "k8s.io/client-go/kubernetes/typed/resource/v1alpha3" - v115 "k8s.io/client-go/kubernetes/typed/scheduling/v1" - v1alpha17 "k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1" - v1beta114 "k8s.io/client-go/kubernetes/typed/scheduling/v1beta1" - v116 "k8s.io/client-go/kubernetes/typed/storage/v1" - v1alpha18 "k8s.io/client-go/kubernetes/typed/storage/v1alpha1" - v1beta115 "k8s.io/client-go/kubernetes/typed/storage/v1beta1" - v1alpha19 "k8s.io/client-go/kubernetes/typed/storagemigration/v1alpha1" -) - -// MockInterface is a mock of Interface interface. -type MockInterface struct { - ctrl *gomock.Controller - recorder *MockInterfaceMockRecorder -} - -// MockInterfaceMockRecorder is the mock recorder for MockInterface. -type MockInterfaceMockRecorder struct { - mock *MockInterface -} - -// NewMockInterface creates a new mock instance. -func NewMockInterface(ctrl *gomock.Controller) *MockInterface { - mock := &MockInterface{ctrl: ctrl} - mock.recorder = &MockInterfaceMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { - return m.recorder -} - -// AdmissionregistrationV1 mocks base method. -func (m *MockInterface) AdmissionregistrationV1() v1.AdmissionregistrationV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AdmissionregistrationV1") - ret0, _ := ret[0].(v1.AdmissionregistrationV1Interface) - return ret0 -} - -// AdmissionregistrationV1 indicates an expected call of AdmissionregistrationV1. -func (mr *MockInterfaceMockRecorder) AdmissionregistrationV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdmissionregistrationV1", reflect.TypeOf((*MockInterface)(nil).AdmissionregistrationV1)) -} - -// AdmissionregistrationV1alpha1 mocks base method. -func (m *MockInterface) AdmissionregistrationV1alpha1() v1alpha1.AdmissionregistrationV1alpha1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AdmissionregistrationV1alpha1") - ret0, _ := ret[0].(v1alpha1.AdmissionregistrationV1alpha1Interface) - return ret0 -} - -// AdmissionregistrationV1alpha1 indicates an expected call of AdmissionregistrationV1alpha1. -func (mr *MockInterfaceMockRecorder) AdmissionregistrationV1alpha1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdmissionregistrationV1alpha1", reflect.TypeOf((*MockInterface)(nil).AdmissionregistrationV1alpha1)) -} - -// AdmissionregistrationV1beta1 mocks base method. -func (m *MockInterface) AdmissionregistrationV1beta1() v1beta1.AdmissionregistrationV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AdmissionregistrationV1beta1") - ret0, _ := ret[0].(v1beta1.AdmissionregistrationV1beta1Interface) - return ret0 -} - -// AdmissionregistrationV1beta1 indicates an expected call of AdmissionregistrationV1beta1. -func (mr *MockInterfaceMockRecorder) AdmissionregistrationV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdmissionregistrationV1beta1", reflect.TypeOf((*MockInterface)(nil).AdmissionregistrationV1beta1)) -} - -// AppsV1 mocks base method. -func (m *MockInterface) AppsV1() v10.AppsV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AppsV1") - ret0, _ := ret[0].(v10.AppsV1Interface) - return ret0 -} - -// AppsV1 indicates an expected call of AppsV1. -func (mr *MockInterfaceMockRecorder) AppsV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppsV1", reflect.TypeOf((*MockInterface)(nil).AppsV1)) -} - -// AppsV1beta1 mocks base method. -func (m *MockInterface) AppsV1beta1() v1beta10.AppsV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AppsV1beta1") - ret0, _ := ret[0].(v1beta10.AppsV1beta1Interface) - return ret0 -} - -// AppsV1beta1 indicates an expected call of AppsV1beta1. -func (mr *MockInterfaceMockRecorder) AppsV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppsV1beta1", reflect.TypeOf((*MockInterface)(nil).AppsV1beta1)) -} - -// AppsV1beta2 mocks base method. -func (m *MockInterface) AppsV1beta2() v1beta2.AppsV1beta2Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AppsV1beta2") - ret0, _ := ret[0].(v1beta2.AppsV1beta2Interface) - return ret0 -} - -// AppsV1beta2 indicates an expected call of AppsV1beta2. -func (mr *MockInterfaceMockRecorder) AppsV1beta2() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppsV1beta2", reflect.TypeOf((*MockInterface)(nil).AppsV1beta2)) -} - -// AuthenticationV1 mocks base method. -func (m *MockInterface) AuthenticationV1() v11.AuthenticationV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AuthenticationV1") - ret0, _ := ret[0].(v11.AuthenticationV1Interface) - return ret0 -} - -// AuthenticationV1 indicates an expected call of AuthenticationV1. -func (mr *MockInterfaceMockRecorder) AuthenticationV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticationV1", reflect.TypeOf((*MockInterface)(nil).AuthenticationV1)) -} - -// AuthenticationV1alpha1 mocks base method. -func (m *MockInterface) AuthenticationV1alpha1() v1alpha11.AuthenticationV1alpha1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AuthenticationV1alpha1") - ret0, _ := ret[0].(v1alpha11.AuthenticationV1alpha1Interface) - return ret0 -} - -// AuthenticationV1alpha1 indicates an expected call of AuthenticationV1alpha1. -func (mr *MockInterfaceMockRecorder) AuthenticationV1alpha1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticationV1alpha1", reflect.TypeOf((*MockInterface)(nil).AuthenticationV1alpha1)) -} - -// AuthenticationV1beta1 mocks base method. -func (m *MockInterface) AuthenticationV1beta1() v1beta11.AuthenticationV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AuthenticationV1beta1") - ret0, _ := ret[0].(v1beta11.AuthenticationV1beta1Interface) - return ret0 -} - -// AuthenticationV1beta1 indicates an expected call of AuthenticationV1beta1. -func (mr *MockInterfaceMockRecorder) AuthenticationV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticationV1beta1", reflect.TypeOf((*MockInterface)(nil).AuthenticationV1beta1)) -} - -// AuthorizationV1 mocks base method. -func (m *MockInterface) AuthorizationV1() v12.AuthorizationV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AuthorizationV1") - ret0, _ := ret[0].(v12.AuthorizationV1Interface) - return ret0 -} - -// AuthorizationV1 indicates an expected call of AuthorizationV1. -func (mr *MockInterfaceMockRecorder) AuthorizationV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthorizationV1", reflect.TypeOf((*MockInterface)(nil).AuthorizationV1)) -} - -// AuthorizationV1beta1 mocks base method. -func (m *MockInterface) AuthorizationV1beta1() v1beta12.AuthorizationV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AuthorizationV1beta1") - ret0, _ := ret[0].(v1beta12.AuthorizationV1beta1Interface) - return ret0 -} - -// AuthorizationV1beta1 indicates an expected call of AuthorizationV1beta1. -func (mr *MockInterfaceMockRecorder) AuthorizationV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthorizationV1beta1", reflect.TypeOf((*MockInterface)(nil).AuthorizationV1beta1)) -} - -// AutoscalingV1 mocks base method. -func (m *MockInterface) AutoscalingV1() v13.AutoscalingV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AutoscalingV1") - ret0, _ := ret[0].(v13.AutoscalingV1Interface) - return ret0 -} - -// AutoscalingV1 indicates an expected call of AutoscalingV1. -func (mr *MockInterfaceMockRecorder) AutoscalingV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AutoscalingV1", reflect.TypeOf((*MockInterface)(nil).AutoscalingV1)) -} - -// AutoscalingV2 mocks base method. -func (m *MockInterface) AutoscalingV2() v2.AutoscalingV2Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AutoscalingV2") - ret0, _ := ret[0].(v2.AutoscalingV2Interface) - return ret0 -} - -// AutoscalingV2 indicates an expected call of AutoscalingV2. -func (mr *MockInterfaceMockRecorder) AutoscalingV2() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AutoscalingV2", reflect.TypeOf((*MockInterface)(nil).AutoscalingV2)) -} - -// AutoscalingV2beta1 mocks base method. -func (m *MockInterface) AutoscalingV2beta1() v2beta1.AutoscalingV2beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AutoscalingV2beta1") - ret0, _ := ret[0].(v2beta1.AutoscalingV2beta1Interface) - return ret0 -} - -// AutoscalingV2beta1 indicates an expected call of AutoscalingV2beta1. -func (mr *MockInterfaceMockRecorder) AutoscalingV2beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AutoscalingV2beta1", reflect.TypeOf((*MockInterface)(nil).AutoscalingV2beta1)) -} - -// AutoscalingV2beta2 mocks base method. -func (m *MockInterface) AutoscalingV2beta2() v2beta2.AutoscalingV2beta2Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AutoscalingV2beta2") - ret0, _ := ret[0].(v2beta2.AutoscalingV2beta2Interface) - return ret0 -} - -// AutoscalingV2beta2 indicates an expected call of AutoscalingV2beta2. -func (mr *MockInterfaceMockRecorder) AutoscalingV2beta2() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AutoscalingV2beta2", reflect.TypeOf((*MockInterface)(nil).AutoscalingV2beta2)) -} - -// BatchV1 mocks base method. -func (m *MockInterface) BatchV1() v14.BatchV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BatchV1") - ret0, _ := ret[0].(v14.BatchV1Interface) - return ret0 -} - -// BatchV1 indicates an expected call of BatchV1. -func (mr *MockInterfaceMockRecorder) BatchV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchV1", reflect.TypeOf((*MockInterface)(nil).BatchV1)) -} - -// BatchV1beta1 mocks base method. -func (m *MockInterface) BatchV1beta1() v1beta13.BatchV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BatchV1beta1") - ret0, _ := ret[0].(v1beta13.BatchV1beta1Interface) - return ret0 -} - -// BatchV1beta1 indicates an expected call of BatchV1beta1. -func (mr *MockInterfaceMockRecorder) BatchV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchV1beta1", reflect.TypeOf((*MockInterface)(nil).BatchV1beta1)) -} - -// CertificatesV1 mocks base method. -func (m *MockInterface) CertificatesV1() v15.CertificatesV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CertificatesV1") - ret0, _ := ret[0].(v15.CertificatesV1Interface) - return ret0 -} - -// CertificatesV1 indicates an expected call of CertificatesV1. -func (mr *MockInterfaceMockRecorder) CertificatesV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CertificatesV1", reflect.TypeOf((*MockInterface)(nil).CertificatesV1)) -} - -// CertificatesV1alpha1 mocks base method. -func (m *MockInterface) CertificatesV1alpha1() v1alpha12.CertificatesV1alpha1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CertificatesV1alpha1") - ret0, _ := ret[0].(v1alpha12.CertificatesV1alpha1Interface) - return ret0 -} - -// CertificatesV1alpha1 indicates an expected call of CertificatesV1alpha1. -func (mr *MockInterfaceMockRecorder) CertificatesV1alpha1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CertificatesV1alpha1", reflect.TypeOf((*MockInterface)(nil).CertificatesV1alpha1)) -} - -// CertificatesV1beta1 mocks base method. -func (m *MockInterface) CertificatesV1beta1() v1beta14.CertificatesV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CertificatesV1beta1") - ret0, _ := ret[0].(v1beta14.CertificatesV1beta1Interface) - return ret0 -} - -// CertificatesV1beta1 indicates an expected call of CertificatesV1beta1. -func (mr *MockInterfaceMockRecorder) CertificatesV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CertificatesV1beta1", reflect.TypeOf((*MockInterface)(nil).CertificatesV1beta1)) -} - -// CoordinationV1 mocks base method. -func (m *MockInterface) CoordinationV1() v16.CoordinationV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CoordinationV1") - ret0, _ := ret[0].(v16.CoordinationV1Interface) - return ret0 -} - -// CoordinationV1 indicates an expected call of CoordinationV1. -func (mr *MockInterfaceMockRecorder) CoordinationV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CoordinationV1", reflect.TypeOf((*MockInterface)(nil).CoordinationV1)) -} - -// CoordinationV1alpha1 mocks base method. -func (m *MockInterface) CoordinationV1alpha1() v1alpha13.CoordinationV1alpha1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CoordinationV1alpha1") - ret0, _ := ret[0].(v1alpha13.CoordinationV1alpha1Interface) - return ret0 -} - -// CoordinationV1alpha1 indicates an expected call of CoordinationV1alpha1. -func (mr *MockInterfaceMockRecorder) CoordinationV1alpha1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CoordinationV1alpha1", reflect.TypeOf((*MockInterface)(nil).CoordinationV1alpha1)) -} - -// CoordinationV1beta1 mocks base method. -func (m *MockInterface) CoordinationV1beta1() v1beta15.CoordinationV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CoordinationV1beta1") - ret0, _ := ret[0].(v1beta15.CoordinationV1beta1Interface) - return ret0 -} - -// CoordinationV1beta1 indicates an expected call of CoordinationV1beta1. -func (mr *MockInterfaceMockRecorder) CoordinationV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CoordinationV1beta1", reflect.TypeOf((*MockInterface)(nil).CoordinationV1beta1)) -} - -// CoreV1 mocks base method. -func (m *MockInterface) CoreV1() v17.CoreV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CoreV1") - ret0, _ := ret[0].(v17.CoreV1Interface) - return ret0 -} - -// CoreV1 indicates an expected call of CoreV1. -func (mr *MockInterfaceMockRecorder) CoreV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CoreV1", reflect.TypeOf((*MockInterface)(nil).CoreV1)) -} - -// Discovery mocks base method. -func (m *MockInterface) Discovery() discovery.DiscoveryInterface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Discovery") - ret0, _ := ret[0].(discovery.DiscoveryInterface) - return ret0 -} - -// Discovery indicates an expected call of Discovery. -func (mr *MockInterfaceMockRecorder) Discovery() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Discovery", reflect.TypeOf((*MockInterface)(nil).Discovery)) -} - -// DiscoveryV1 mocks base method. -func (m *MockInterface) DiscoveryV1() v18.DiscoveryV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DiscoveryV1") - ret0, _ := ret[0].(v18.DiscoveryV1Interface) - return ret0 -} - -// DiscoveryV1 indicates an expected call of DiscoveryV1. -func (mr *MockInterfaceMockRecorder) DiscoveryV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscoveryV1", reflect.TypeOf((*MockInterface)(nil).DiscoveryV1)) -} - -// DiscoveryV1beta1 mocks base method. -func (m *MockInterface) DiscoveryV1beta1() v1beta16.DiscoveryV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DiscoveryV1beta1") - ret0, _ := ret[0].(v1beta16.DiscoveryV1beta1Interface) - return ret0 -} - -// DiscoveryV1beta1 indicates an expected call of DiscoveryV1beta1. -func (mr *MockInterfaceMockRecorder) DiscoveryV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscoveryV1beta1", reflect.TypeOf((*MockInterface)(nil).DiscoveryV1beta1)) -} - -// EventsV1 mocks base method. -func (m *MockInterface) EventsV1() v19.EventsV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EventsV1") - ret0, _ := ret[0].(v19.EventsV1Interface) - return ret0 -} - -// EventsV1 indicates an expected call of EventsV1. -func (mr *MockInterfaceMockRecorder) EventsV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EventsV1", reflect.TypeOf((*MockInterface)(nil).EventsV1)) -} - -// EventsV1beta1 mocks base method. -func (m *MockInterface) EventsV1beta1() v1beta17.EventsV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EventsV1beta1") - ret0, _ := ret[0].(v1beta17.EventsV1beta1Interface) - return ret0 -} - -// EventsV1beta1 indicates an expected call of EventsV1beta1. -func (mr *MockInterfaceMockRecorder) EventsV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EventsV1beta1", reflect.TypeOf((*MockInterface)(nil).EventsV1beta1)) -} - -// ExtensionsV1beta1 mocks base method. -func (m *MockInterface) ExtensionsV1beta1() v1beta18.ExtensionsV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExtensionsV1beta1") - ret0, _ := ret[0].(v1beta18.ExtensionsV1beta1Interface) - return ret0 -} - -// ExtensionsV1beta1 indicates an expected call of ExtensionsV1beta1. -func (mr *MockInterfaceMockRecorder) ExtensionsV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExtensionsV1beta1", reflect.TypeOf((*MockInterface)(nil).ExtensionsV1beta1)) -} - -// FlowcontrolV1 mocks base method. -func (m *MockInterface) FlowcontrolV1() v110.FlowcontrolV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FlowcontrolV1") - ret0, _ := ret[0].(v110.FlowcontrolV1Interface) - return ret0 -} - -// FlowcontrolV1 indicates an expected call of FlowcontrolV1. -func (mr *MockInterfaceMockRecorder) FlowcontrolV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlowcontrolV1", reflect.TypeOf((*MockInterface)(nil).FlowcontrolV1)) -} - -// FlowcontrolV1beta1 mocks base method. -func (m *MockInterface) FlowcontrolV1beta1() v1beta19.FlowcontrolV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FlowcontrolV1beta1") - ret0, _ := ret[0].(v1beta19.FlowcontrolV1beta1Interface) - return ret0 -} - -// FlowcontrolV1beta1 indicates an expected call of FlowcontrolV1beta1. -func (mr *MockInterfaceMockRecorder) FlowcontrolV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlowcontrolV1beta1", reflect.TypeOf((*MockInterface)(nil).FlowcontrolV1beta1)) -} - -// FlowcontrolV1beta2 mocks base method. -func (m *MockInterface) FlowcontrolV1beta2() v1beta20.FlowcontrolV1beta2Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FlowcontrolV1beta2") - ret0, _ := ret[0].(v1beta20.FlowcontrolV1beta2Interface) - return ret0 -} - -// FlowcontrolV1beta2 indicates an expected call of FlowcontrolV1beta2. -func (mr *MockInterfaceMockRecorder) FlowcontrolV1beta2() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlowcontrolV1beta2", reflect.TypeOf((*MockInterface)(nil).FlowcontrolV1beta2)) -} - -// FlowcontrolV1beta3 mocks base method. -func (m *MockInterface) FlowcontrolV1beta3() v1beta3.FlowcontrolV1beta3Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FlowcontrolV1beta3") - ret0, _ := ret[0].(v1beta3.FlowcontrolV1beta3Interface) - return ret0 -} - -// FlowcontrolV1beta3 indicates an expected call of FlowcontrolV1beta3. -func (mr *MockInterfaceMockRecorder) FlowcontrolV1beta3() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlowcontrolV1beta3", reflect.TypeOf((*MockInterface)(nil).FlowcontrolV1beta3)) -} - -// InternalV1alpha1 mocks base method. -func (m *MockInterface) InternalV1alpha1() v1alpha10.InternalV1alpha1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InternalV1alpha1") - ret0, _ := ret[0].(v1alpha10.InternalV1alpha1Interface) - return ret0 -} - -// InternalV1alpha1 indicates an expected call of InternalV1alpha1. -func (mr *MockInterfaceMockRecorder) InternalV1alpha1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InternalV1alpha1", reflect.TypeOf((*MockInterface)(nil).InternalV1alpha1)) -} - -// NetworkingV1 mocks base method. -func (m *MockInterface) NetworkingV1() v111.NetworkingV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NetworkingV1") - ret0, _ := ret[0].(v111.NetworkingV1Interface) - return ret0 -} - -// NetworkingV1 indicates an expected call of NetworkingV1. -func (mr *MockInterfaceMockRecorder) NetworkingV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkingV1", reflect.TypeOf((*MockInterface)(nil).NetworkingV1)) -} - -// NetworkingV1alpha1 mocks base method. -func (m *MockInterface) NetworkingV1alpha1() v1alpha14.NetworkingV1alpha1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NetworkingV1alpha1") - ret0, _ := ret[0].(v1alpha14.NetworkingV1alpha1Interface) - return ret0 -} - -// NetworkingV1alpha1 indicates an expected call of NetworkingV1alpha1. -func (mr *MockInterfaceMockRecorder) NetworkingV1alpha1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkingV1alpha1", reflect.TypeOf((*MockInterface)(nil).NetworkingV1alpha1)) -} - -// NetworkingV1beta1 mocks base method. -func (m *MockInterface) NetworkingV1beta1() v1beta110.NetworkingV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NetworkingV1beta1") - ret0, _ := ret[0].(v1beta110.NetworkingV1beta1Interface) - return ret0 -} - -// NetworkingV1beta1 indicates an expected call of NetworkingV1beta1. -func (mr *MockInterfaceMockRecorder) NetworkingV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkingV1beta1", reflect.TypeOf((*MockInterface)(nil).NetworkingV1beta1)) -} - -// NodeV1 mocks base method. -func (m *MockInterface) NodeV1() v112.NodeV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NodeV1") - ret0, _ := ret[0].(v112.NodeV1Interface) - return ret0 -} - -// NodeV1 indicates an expected call of NodeV1. -func (mr *MockInterfaceMockRecorder) NodeV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeV1", reflect.TypeOf((*MockInterface)(nil).NodeV1)) -} - -// NodeV1alpha1 mocks base method. -func (m *MockInterface) NodeV1alpha1() v1alpha15.NodeV1alpha1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NodeV1alpha1") - ret0, _ := ret[0].(v1alpha15.NodeV1alpha1Interface) - return ret0 -} - -// NodeV1alpha1 indicates an expected call of NodeV1alpha1. -func (mr *MockInterfaceMockRecorder) NodeV1alpha1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeV1alpha1", reflect.TypeOf((*MockInterface)(nil).NodeV1alpha1)) -} - -// NodeV1beta1 mocks base method. -func (m *MockInterface) NodeV1beta1() v1beta111.NodeV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NodeV1beta1") - ret0, _ := ret[0].(v1beta111.NodeV1beta1Interface) - return ret0 -} - -// NodeV1beta1 indicates an expected call of NodeV1beta1. -func (mr *MockInterfaceMockRecorder) NodeV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeV1beta1", reflect.TypeOf((*MockInterface)(nil).NodeV1beta1)) -} - -// PolicyV1 mocks base method. -func (m *MockInterface) PolicyV1() v113.PolicyV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PolicyV1") - ret0, _ := ret[0].(v113.PolicyV1Interface) - return ret0 -} - -// PolicyV1 indicates an expected call of PolicyV1. -func (mr *MockInterfaceMockRecorder) PolicyV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PolicyV1", reflect.TypeOf((*MockInterface)(nil).PolicyV1)) -} - -// PolicyV1beta1 mocks base method. -func (m *MockInterface) PolicyV1beta1() v1beta112.PolicyV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PolicyV1beta1") - ret0, _ := ret[0].(v1beta112.PolicyV1beta1Interface) - return ret0 -} - -// PolicyV1beta1 indicates an expected call of PolicyV1beta1. -func (mr *MockInterfaceMockRecorder) PolicyV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PolicyV1beta1", reflect.TypeOf((*MockInterface)(nil).PolicyV1beta1)) -} - -// RbacV1 mocks base method. -func (m *MockInterface) RbacV1() v114.RbacV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RbacV1") - ret0, _ := ret[0].(v114.RbacV1Interface) - return ret0 -} - -// RbacV1 indicates an expected call of RbacV1. -func (mr *MockInterfaceMockRecorder) RbacV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RbacV1", reflect.TypeOf((*MockInterface)(nil).RbacV1)) -} - -// RbacV1alpha1 mocks base method. -func (m *MockInterface) RbacV1alpha1() v1alpha16.RbacV1alpha1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RbacV1alpha1") - ret0, _ := ret[0].(v1alpha16.RbacV1alpha1Interface) - return ret0 -} - -// RbacV1alpha1 indicates an expected call of RbacV1alpha1. -func (mr *MockInterfaceMockRecorder) RbacV1alpha1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RbacV1alpha1", reflect.TypeOf((*MockInterface)(nil).RbacV1alpha1)) -} - -// RbacV1beta1 mocks base method. -func (m *MockInterface) RbacV1beta1() v1beta113.RbacV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RbacV1beta1") - ret0, _ := ret[0].(v1beta113.RbacV1beta1Interface) - return ret0 -} - -// RbacV1beta1 indicates an expected call of RbacV1beta1. -func (mr *MockInterfaceMockRecorder) RbacV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RbacV1beta1", reflect.TypeOf((*MockInterface)(nil).RbacV1beta1)) -} - -// ResourceV1alpha3 mocks base method. -func (m *MockInterface) ResourceV1alpha3() v1alpha3.ResourceV1alpha3Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ResourceV1alpha3") - ret0, _ := ret[0].(v1alpha3.ResourceV1alpha3Interface) - return ret0 -} - -// ResourceV1alpha3 indicates an expected call of ResourceV1alpha3. -func (mr *MockInterfaceMockRecorder) ResourceV1alpha3() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResourceV1alpha3", reflect.TypeOf((*MockInterface)(nil).ResourceV1alpha3)) -} - -// SchedulingV1 mocks base method. -func (m *MockInterface) SchedulingV1() v115.SchedulingV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SchedulingV1") - ret0, _ := ret[0].(v115.SchedulingV1Interface) - return ret0 -} - -// SchedulingV1 indicates an expected call of SchedulingV1. -func (mr *MockInterfaceMockRecorder) SchedulingV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedulingV1", reflect.TypeOf((*MockInterface)(nil).SchedulingV1)) -} - -// SchedulingV1alpha1 mocks base method. -func (m *MockInterface) SchedulingV1alpha1() v1alpha17.SchedulingV1alpha1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SchedulingV1alpha1") - ret0, _ := ret[0].(v1alpha17.SchedulingV1alpha1Interface) - return ret0 -} - -// SchedulingV1alpha1 indicates an expected call of SchedulingV1alpha1. -func (mr *MockInterfaceMockRecorder) SchedulingV1alpha1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedulingV1alpha1", reflect.TypeOf((*MockInterface)(nil).SchedulingV1alpha1)) -} - -// SchedulingV1beta1 mocks base method. -func (m *MockInterface) SchedulingV1beta1() v1beta114.SchedulingV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SchedulingV1beta1") - ret0, _ := ret[0].(v1beta114.SchedulingV1beta1Interface) - return ret0 -} - -// SchedulingV1beta1 indicates an expected call of SchedulingV1beta1. -func (mr *MockInterfaceMockRecorder) SchedulingV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedulingV1beta1", reflect.TypeOf((*MockInterface)(nil).SchedulingV1beta1)) -} - -// StorageV1 mocks base method. -func (m *MockInterface) StorageV1() v116.StorageV1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "StorageV1") - ret0, _ := ret[0].(v116.StorageV1Interface) - return ret0 -} - -// StorageV1 indicates an expected call of StorageV1. -func (mr *MockInterfaceMockRecorder) StorageV1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageV1", reflect.TypeOf((*MockInterface)(nil).StorageV1)) -} - -// StorageV1alpha1 mocks base method. -func (m *MockInterface) StorageV1alpha1() v1alpha18.StorageV1alpha1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "StorageV1alpha1") - ret0, _ := ret[0].(v1alpha18.StorageV1alpha1Interface) - return ret0 -} - -// StorageV1alpha1 indicates an expected call of StorageV1alpha1. -func (mr *MockInterfaceMockRecorder) StorageV1alpha1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageV1alpha1", reflect.TypeOf((*MockInterface)(nil).StorageV1alpha1)) -} - -// StorageV1beta1 mocks base method. -func (m *MockInterface) StorageV1beta1() v1beta115.StorageV1beta1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "StorageV1beta1") - ret0, _ := ret[0].(v1beta115.StorageV1beta1Interface) - return ret0 -} - -// StorageV1beta1 indicates an expected call of StorageV1beta1. -func (mr *MockInterfaceMockRecorder) StorageV1beta1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StorageV1beta1", reflect.TypeOf((*MockInterface)(nil).StorageV1beta1)) -} - -// StoragemigrationV1alpha1 mocks base method. -func (m *MockInterface) StoragemigrationV1alpha1() v1alpha19.StoragemigrationV1alpha1Interface { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "StoragemigrationV1alpha1") - ret0, _ := ret[0].(v1alpha19.StoragemigrationV1alpha1Interface) - return ret0 -} - -// StoragemigrationV1alpha1 indicates an expected call of StoragemigrationV1alpha1. -func (mr *MockInterfaceMockRecorder) StoragemigrationV1alpha1() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoragemigrationV1alpha1", reflect.TypeOf((*MockInterface)(nil).StoragemigrationV1alpha1)) -} diff --git a/test/mocks/mocks.go b/test/mocks/mocks.go deleted file mode 100644 index 0defb41530d..00000000000 --- a/test/mocks/mocks.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build ignore - -package mocks - -//go:generate go run github.com/golang/mock/mockgen -destination ./cache/corecache.go github.com/solo-io/solo-kit/pkg/api/v1/clients/kube/cache KubeCoreCache -//go:generate go run github.com/golang/mock/mockgen -destination ./kubernetes/kubeinterface.go k8s.io/client-go/kubernetes Interface -//go:generate go run github.com/golang/mock/mockgen -destination ./gloo/validation_client.go github.com/kgateway-dev/kgateway/internal/gloo/pkg/api/grpc/validation GlooValidationServiceClient From 8575ed88aba1cd7349425fda4017b90587f0e893 Mon Sep 17 00:00:00 2001 From: "Lawrence G." Date: Thu, 6 Mar 2025 14:16:53 -0600 Subject: [PATCH 4/7] Add global setting for dns lookup family (#10755) --- .../kgateway/extensions2/settings/settings.go | 8 ++ .../extensions2/settings/settings_test.go | 23 +++--- internal/kgateway/setup/ggv2setup_test.go | 28 +++++-- ...cesslog-filtercel-httplisteneropt-out.yaml | 1 + .../ai-anthropic-passthrough-out.yaml | 1 + .../ai-deepseek-prompt-guard-out.yaml | 1 + .../ai-openai-moderation-promptguard-out.yaml | 1 + .../testdata/ai-vertex-ai-streaming-out.yaml | 1 + .../setup/testdata/autodns/backend-out.yaml | 77 +++++++++++++++++++ .../setup/testdata/autodns/backend.yaml | 42 ++++++++++ .../kgateway/setup/testdata/backend-out.yaml | 1 + .../translator/irtranslator/backend.go | 56 ++++++++++++-- internal/kgateway/translator/translator.go | 1 + 13 files changed, 217 insertions(+), 24 deletions(-) create mode 100644 internal/kgateway/setup/testdata/autodns/backend-out.yaml create mode 100644 internal/kgateway/setup/testdata/autodns/backend.yaml diff --git a/internal/kgateway/extensions2/settings/settings.go b/internal/kgateway/extensions2/settings/settings.go index fb4c877a8ba..d638586a30f 100644 --- a/internal/kgateway/extensions2/settings/settings.go +++ b/internal/kgateway/extensions2/settings/settings.go @@ -5,6 +5,14 @@ import ( ) type Settings struct { + // Controls the DnsLookupFamily for all static clusters created via Backend resources. + // If not set, kgateway will default to "V4_PREFERRED". Note that this is different + // from the Envoy default of "AUTO", which is effectively "V6_PREFERRED". + // Supported values are: "ALL", "AUTO", "V4_PREFERRED", "V4_ONLY", "V6_ONLY" + // Details on the behavior of these options are available on the Envoy documentation: + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/cluster/v3/cluster.proto#enum-config-cluster-v3-cluster-dnslookupfamily + DnsLookupFamily string `split_words:"true" default:"V4_PREFERRED"` + EnableIstioIntegration bool `split_words:"true"` EnableAutoMtls bool `split_words:"true"` StsClusterName string `split_words:"true"` diff --git a/internal/kgateway/extensions2/settings/settings_test.go b/internal/kgateway/extensions2/settings/settings_test.go index 850ef66c258..c3c1fba67e9 100644 --- a/internal/kgateway/extensions2/settings/settings_test.go +++ b/internal/kgateway/extensions2/settings/settings_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/onsi/gomega" - . "github.com/onsi/gomega" "github.com/kgateway-dev/kgateway/v2/internal/kgateway/extensions2/settings" "github.com/kgateway-dev/kgateway/v2/internal/kgateway/wellknown" @@ -26,9 +25,12 @@ func TestSettings(t *testing.T) { expectedErrorStr string }{ { + // TODO: this test case does not fail when a new field is added to Settings + // but not updated here. should it? name: "defaults to empty or default values", envVars: map[string]string{}, expectedSettings: &settings.Settings{ + DnsLookupFamily: "V4_PREFERRED", EnableIstioIntegration: false, EnableAutoMtls: false, StsClusterName: "", @@ -40,6 +42,7 @@ func TestSettings(t *testing.T) { { name: "all values set", envVars: map[string]string{ + "KGW_DNS_LOOKUP_FAMILY": "V4_ONLY", "KGW_ENABLE_ISTIO_INTEGRATION": "true", "KGW_ENABLE_AUTO_MTLS": "true", "KGW_STS_CLUSTER_NAME": "my-cluster", @@ -48,6 +51,7 @@ func TestSettings(t *testing.T) { "KGW_XDS_SERVICE_PORT": "1234", }, expectedSettings: &settings.Settings{ + DnsLookupFamily: "V4_ONLY", EnableIstioIntegration: true, EnableAutoMtls: true, StsClusterName: "my-cluster", @@ -78,9 +82,10 @@ func TestSettings(t *testing.T) { "KGW_ENABLE_AUTO_MTLS": "true", }, expectedSettings: &settings.Settings{ - EnableAutoMtls: true, - XdsServiceName: wellknown.DefaultXdsService, - XdsServicePort: wellknown.DefaultXdsPort, + DnsLookupFamily: "V4_PREFERRED", + EnableAutoMtls: true, + XdsServiceName: wellknown.DefaultXdsService, + XdsServicePort: wellknown.DefaultXdsPort, }, }, } @@ -92,21 +97,21 @@ func TestSettings(t *testing.T) { t.Cleanup(func() { for k := range tc.envVars { err := os.Unsetenv(k) - g.Expect(err).NotTo(HaveOccurred()) + g.Expect(err).NotTo(gomega.HaveOccurred()) } }) for k, v := range tc.envVars { err := os.Setenv(k, v) - g.Expect(err).NotTo(HaveOccurred()) + g.Expect(err).NotTo(gomega.HaveOccurred()) } s, err := settings.BuildSettings() if tc.expectedErrorStr != "" { - g.Expect(err).To(HaveOccurred()) + g.Expect(err).To(gomega.HaveOccurred()) g.Expect(err.Error()).To(gomega.ContainSubstring(tc.expectedErrorStr)) } else { - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(s).To(Equal(tc.expectedSettings)) + g.Expect(err).NotTo(gomega.HaveOccurred()) + g.Expect(s).To(gomega.Equal(tc.expectedSettings)) } }) } diff --git a/internal/kgateway/setup/ggv2setup_test.go b/internal/kgateway/setup/ggv2setup_test.go index 9168d376eb7..ed68f2c075b 100644 --- a/internal/kgateway/setup/ggv2setup_test.go +++ b/internal/kgateway/setup/ggv2setup_test.go @@ -107,18 +107,32 @@ func init() { grpclog.SetLoggerV2(grpclog.NewLoggerV2WithVerbosity(writer, writer, writer, 100)) } +func TestWithAutoDns(t *testing.T) { + os.Setenv("KGW_DNS_LOOKUP_FAMILY", "AUTO") + t.Cleanup(func() { + os.Unsetenv("KGW_DNS_LOOKUP_FAMILY") + }) + runScenario(t, "testdata/autodns") +} + func TestScenarios(t *testing.T) { + // set global settings env vars; "default" ggv2setup_tests assume these are set to true + os.Setenv("KGW_ENABLE_ISTIO_INTEGRATION", "true") + os.Setenv("KGW_ENABLE_AUTO_MTLS", "true") + t.Cleanup(func() { + os.Unsetenv("KGW_ENABLE_ISTIO_INTEGRATION") + os.Unsetenv("KGW_ENABLE_AUTO_MTLS") + }) + runScenario(t, "testdata") +} + +func runScenario(t *testing.T, scenarioDir string) { proxy_syncer.UseDetailedUnmarshalling = true writer.set(t) os.Setenv("POD_NAMESPACE", "gwtest") // TODO: is this still needed? - // set global settings env vars; current ggv2setup_tests all assume these are set to true - os.Setenv("KGW_ENABLE_ISTIO_INTEGRATION", "true") - os.Setenv("KGW_ENABLE_AUTO_MTLS", "true") t.Cleanup(func() { os.Unsetenv("POD_NAMESPACE") - os.Unsetenv("KGW_ENABLE_ISTIO_INTEGRATION") - os.Unsetenv("KGW_ENABLE_AUTO_MTLS") }) testEnv := &envtest.Environment{ @@ -203,7 +217,7 @@ func TestScenarios(t *testing.T) { time.Sleep(time.Second) // list all yamls in test data - files, err := os.ReadDir("testdata") + files, err := os.ReadDir(scenarioDir) if err != nil { t.Fatalf("failed to read dir: %v", err) } @@ -211,7 +225,7 @@ func TestScenarios(t *testing.T) { // run tests with the yaml files (but not -out.yaml files)/s parentT := t if strings.HasSuffix(f.Name(), ".yaml") && !strings.HasSuffix(f.Name(), "-out.yaml") { - fullpath := filepath.Join("testdata", f.Name()) + fullpath := filepath.Join(scenarioDir, f.Name()) t.Run(strings.TrimSuffix(f.Name(), ".yaml"), func(t *testing.T) { writer.set(t) t.Cleanup(func() { diff --git a/internal/kgateway/setup/testdata/accesslog-filtercel-httplisteneropt-out.yaml b/internal/kgateway/setup/testdata/accesslog-filtercel-httplisteneropt-out.yaml index ed5366d16e8..7c9b4afd0ee 100644 --- a/internal/kgateway/setup/testdata/accesslog-filtercel-httplisteneropt-out.yaml +++ b/internal/kgateway/setup/testdata/accesslog-filtercel-httplisteneropt-out.yaml @@ -98,6 +98,7 @@ clusters: '@type': type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer type: EDS - connectTimeout: 5s + dnsLookupFamily: V4_PREFERRED loadAssignment: clusterName: backend_gwtest_log_0 endpoints: diff --git a/internal/kgateway/setup/testdata/ai-anthropic-passthrough-out.yaml b/internal/kgateway/setup/testdata/ai-anthropic-passthrough-out.yaml index bbfa016d80e..0cf75a43b25 100644 --- a/internal/kgateway/setup/testdata/ai-anthropic-passthrough-out.yaml +++ b/internal/kgateway/setup/testdata/ai-anthropic-passthrough-out.yaml @@ -64,6 +64,7 @@ clusters: '@type': type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer type: EDS - connectTimeout: 5s + dnsLookupFamily: V4_PREFERRED loadAssignment: clusterName: backend_gwtest_anthropic_0 endpoints: diff --git a/internal/kgateway/setup/testdata/ai-deepseek-prompt-guard-out.yaml b/internal/kgateway/setup/testdata/ai-deepseek-prompt-guard-out.yaml index 3671cbe8078..339ae8b0bc4 100644 --- a/internal/kgateway/setup/testdata/ai-deepseek-prompt-guard-out.yaml +++ b/internal/kgateway/setup/testdata/ai-deepseek-prompt-guard-out.yaml @@ -64,6 +64,7 @@ clusters: '@type': type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer type: EDS - connectTimeout: 5s + dnsLookupFamily: V4_PREFERRED loadAssignment: clusterName: backend_gwtest_deepseek_0 endpoints: diff --git a/internal/kgateway/setup/testdata/ai-openai-moderation-promptguard-out.yaml b/internal/kgateway/setup/testdata/ai-openai-moderation-promptguard-out.yaml index 696255fcf4d..7ea0695f9bc 100644 --- a/internal/kgateway/setup/testdata/ai-openai-moderation-promptguard-out.yaml +++ b/internal/kgateway/setup/testdata/ai-openai-moderation-promptguard-out.yaml @@ -64,6 +64,7 @@ clusters: '@type': type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer type: EDS - connectTimeout: 5s + dnsLookupFamily: V4_PREFERRED loadAssignment: clusterName: backend_gwtest_openai_0 endpoints: diff --git a/internal/kgateway/setup/testdata/ai-vertex-ai-streaming-out.yaml b/internal/kgateway/setup/testdata/ai-vertex-ai-streaming-out.yaml index 248cc422993..0b8a6b7e159 100644 --- a/internal/kgateway/setup/testdata/ai-vertex-ai-streaming-out.yaml +++ b/internal/kgateway/setup/testdata/ai-vertex-ai-streaming-out.yaml @@ -64,6 +64,7 @@ clusters: '@type': type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer type: EDS - connectTimeout: 5s + dnsLookupFamily: V4_PREFERRED loadAssignment: clusterName: backend_gwtest_vertexai_0 endpoints: diff --git a/internal/kgateway/setup/testdata/autodns/backend-out.yaml b/internal/kgateway/setup/testdata/autodns/backend-out.yaml new file mode 100644 index 00000000000..53af1c52229 --- /dev/null +++ b/internal/kgateway/setup/testdata/autodns/backend-out.yaml @@ -0,0 +1,77 @@ +clusters: +- connectTimeout: 5s + loadAssignment: + clusterName: backend_gwtest_static_0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 8080 + healthCheckConfig: + hostname: 1.2.3.4 + hostname: 1.2.3.4 + metadata: {} + name: backend_gwtest_static_0 + type: STATIC +- connectTimeout: 5s + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + metadata: {} + name: kube_default_kubernetes_443 + type: EDS +- connectTimeout: 5s + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + metadata: {} + name: kube_gwtest_http-backend_8080 + type: EDS +listeners: +- address: + socketAddress: + address: '::' + ipv4Compat: true + portValue: 8080 + filterChains: + - filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + mergeSlashes: true + normalizePath: true + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: http + statPrefix: http + useRemoteAddress: true + name: http + name: http +routes: +- ignorePortInHostMatching: true + name: http + virtualHosts: + - domains: + - www.example.com + name: http~www_example_com + routes: + - match: + prefix: / + name: http~www_example_com-route-0-httproute-route-to-upstream-gwtest-0-0-matcher-0 + route: + cluster: backend_gwtest_static_0 + clusterNotFoundResponseCode: INTERNAL_SERVER_ERROR + typedPerFilterConfig: + ai.extproc.kgateway.io: + '@type': type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExtProcPerRoute + disabled: true diff --git a/internal/kgateway/setup/testdata/autodns/backend.yaml b/internal/kgateway/setup/testdata/autodns/backend.yaml new file mode 100644 index 00000000000..0f2347f16ce --- /dev/null +++ b/internal/kgateway/setup/testdata/autodns/backend.yaml @@ -0,0 +1,42 @@ +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: http-gw-for-test + namespace: gwtest +spec: + gatewayClassName: kgateway + listeners: + - protocol: HTTP + port: 8080 + name: http + allowedRoutes: + namespaces: + from: All +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: route-to-upstream + namespace: gwtest +spec: + parentRefs: + - name: http-gw-for-test + hostnames: + - "www.example.com" + rules: + - backendRefs: + - name: static + kind: Backend + group: gateway.kgateway.dev +--- +apiVersion: gateway.kgateway.dev/v1alpha1 +kind: Backend +metadata: + name: static + namespace: gwtest +spec: + type: Static + static: + hosts: + - host: 1.2.3.4 + port: 8080 diff --git a/internal/kgateway/setup/testdata/backend-out.yaml b/internal/kgateway/setup/testdata/backend-out.yaml index 1217ff6aea6..42a980e0e22 100644 --- a/internal/kgateway/setup/testdata/backend-out.yaml +++ b/internal/kgateway/setup/testdata/backend-out.yaml @@ -49,6 +49,7 @@ clusters: '@type': type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer type: EDS - connectTimeout: 5s + dnsLookupFamily: V4_PREFERRED loadAssignment: clusterName: backend_gwtest_static_0 endpoints: diff --git a/internal/kgateway/translator/irtranslator/backend.go b/internal/kgateway/translator/irtranslator/backend.go index adf4e5b5e9d..806795f3f3d 100644 --- a/internal/kgateway/translator/irtranslator/backend.go +++ b/internal/kgateway/translator/irtranslator/backend.go @@ -5,7 +5,7 @@ import ( "errors" "time" - envoy_config_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" + clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_upstreams_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" "google.golang.org/protobuf/proto" @@ -15,6 +15,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/kgateway-dev/kgateway/v2/internal/kgateway/extensions2/common" extensionsplug "github.com/kgateway-dev/kgateway/v2/internal/kgateway/extensions2/plugin" "github.com/kgateway-dev/kgateway/v2/internal/kgateway/ir" "github.com/kgateway-dev/kgateway/v2/internal/kgateway/utils" @@ -27,13 +28,14 @@ var ( type BackendTranslator struct { ContributedBackends map[schema.GroupKind]ir.BackendInit ContributedPolicies map[schema.GroupKind]extensionsplug.PolicyPlugin + CommonCols *common.CommonCollections } func (t *BackendTranslator) TranslateBackend( kctx krt.HandlerContext, ucc ir.UniqlyConnectedClient, backend ir.BackendObjectIR, -) (*envoy_config_cluster_v3.Cluster, error) { +) (*clusterv3.Cluster, error) { gk := schema.GroupKind{ Group: backend.Group, Kind: backend.Kind, @@ -48,20 +50,58 @@ func (t *BackendTranslator) TranslateBackend( } out := initializeCluster(backend) - process.InitBackend(context.TODO(), backend, out) + processDnsLookupFamily(out, t.CommonCols) // now process backend policies - t.runPlugins(kctx, context.TODO(), ucc, backend, out) + t.runPolicies(kctx, context.TODO(), ucc, backend, out) return out, nil } -func (t *BackendTranslator) runPlugins( +// processDnsLookupFamily modifies clusters that use DNS-based discovery in the following way: +// 1. explicitly default to 'V4_PREFERRED' (as opposed to the envoy default of effectively V6_PREFERRED) +// 2. override to value defined in kgateway global setting if present +func processDnsLookupFamily(out *clusterv3.Cluster, cc *common.CommonCollections) { + cdt, ok := out.GetClusterDiscoveryType().(*clusterv3.Cluster_Type) + if !ok { + return + } + setDns := false + switch cdt.Type { + case clusterv3.Cluster_STATIC, clusterv3.Cluster_LOGICAL_DNS, clusterv3.Cluster_STRICT_DNS: + setDns = true + } + if !setDns { + return + } + + // irrespective of settings, default to V4_PREFERRED, overriding Envoy default + out.DnsLookupFamily = clusterv3.Cluster_V4_PREFERRED + + if cc == nil { + return + } + // if we have settings, use value from it + switch cc.Settings.DnsLookupFamily { + case "V4_PREFERRED": + out.DnsLookupFamily = clusterv3.Cluster_V4_PREFERRED + case "V4_ONLY": + out.DnsLookupFamily = clusterv3.Cluster_V4_ONLY + case "V6_ONLY": + out.DnsLookupFamily = clusterv3.Cluster_V6_ONLY + case "AUTO": + out.DnsLookupFamily = clusterv3.Cluster_AUTO + case "ALL": + out.DnsLookupFamily = clusterv3.Cluster_ALL + } +} + +func (t *BackendTranslator) runPolicies( kctx krt.HandlerContext, ctx context.Context, ucc ir.UniqlyConnectedClient, backend ir.BackendObjectIR, - out *envoy_config_cluster_v3.Cluster, + out *clusterv3.Cluster, ) { for gk, polImpl := range t.ContributedPolicies { // TODO: in theory it would be nice to do `ProcessBackend` once, and only do @@ -114,9 +154,9 @@ func translateAppProtocol(appProtocol ir.AppProtocol) map[string]*anypb.Any { // initializeCluster creates a default envoy cluster with minimal configuration, // that will then be augmented by various backend plugins -func initializeCluster(u ir.BackendObjectIR) *envoy_config_cluster_v3.Cluster { +func initializeCluster(u ir.BackendObjectIR) *clusterv3.Cluster { // circuitBreakers := t.settings.GetGloo().GetCircuitBreakers() - out := &envoy_config_cluster_v3.Cluster{ + out := &clusterv3.Cluster{ Name: u.ClusterName(), Metadata: new(envoy_config_core_v3.Metadata), // CircuitBreakers: getCircuitBreakers(upstream.GetCircuitBreakers(), circuitBreakers), diff --git a/internal/kgateway/translator/translator.go b/internal/kgateway/translator/translator.go index f07f6773fce..af597278918 100644 --- a/internal/kgateway/translator/translator.go +++ b/internal/kgateway/translator/translator.go @@ -78,6 +78,7 @@ func (s *CombinedTranslator) Init(ctx context.Context, routes *krtcollections.Ro s.backendTranslator = &irtranslator.BackendTranslator{ ContributedBackends: make(map[schema.GroupKind]ir.BackendInit), ContributedPolicies: s.extensions.ContributesPolicies, + CommonCols: s.commonCols, } for k, up := range s.extensions.ContributesBackends { s.backendTranslator.ContributedBackends[k] = up.BackendInit From 0304ef336cf49aa67118d2ce39ed0d7374da5cab Mon Sep 17 00:00:00 2001 From: Yuval Kohavi Date: Thu, 6 Mar 2025 17:29:36 -0500 Subject: [PATCH 5/7] route: don't use specific backend gvk here (#10772) --- internal/kgateway/translator/irtranslator/route.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/kgateway/translator/irtranslator/route.go b/internal/kgateway/translator/irtranslator/route.go index 9dfd944179a..f181166db11 100644 --- a/internal/kgateway/translator/irtranslator/route.go +++ b/internal/kgateway/translator/irtranslator/route.go @@ -21,7 +21,6 @@ import ( "github.com/kgateway-dev/kgateway/v2/internal/kgateway/reports" "github.com/kgateway-dev/kgateway/v2/internal/kgateway/translator/routeutils" "github.com/kgateway-dev/kgateway/v2/internal/kgateway/utils" - "github.com/kgateway-dev/kgateway/v2/internal/kgateway/wellknown" "github.com/kgateway-dev/kgateway/v2/pkg/utils/regexutils" ) @@ -249,8 +248,9 @@ func (h *httpRouteConfigurationTranslator) runBackendPolicies(ctx context.Contex func (h *httpRouteConfigurationTranslator) runBackend(ctx context.Context, in ir.HttpBackend, pCtx *ir.RouteBackendContext, outRoute *envoy_config_route_v3.Route) error { var errs []error if in.Backend.BackendObject != nil { - if in.Backend.BackendObject.GetGroupKind().Kind == wellknown.BackendGVK.Kind { - err := h.PluginPass[in.Backend.BackendObject.GetGroupKind()].ApplyForBackend(ctx, pCtx, in, outRoute) + backendPass := h.PluginPass[in.Backend.BackendObject.GetGroupKind()] + if backendPass != nil { + err := backendPass.ApplyForBackend(ctx, pCtx, in, outRoute) if err != nil { errs = append(errs, err) } From 1cf50fd69e36a01a20f34a3cdb189ab29599cdbd Mon Sep 17 00:00:00 2001 From: Nathan Fudenberg Date: Thu, 6 Mar 2025 22:34:10 -0500 Subject: [PATCH 6/7] Add transformation policy support (#10727) --- .github/workflows/pr-kubernetes-tests.yaml | 5 + .gitignore | 7 + .goreleaser.yaml | 4 + Makefile | 2 + .../api/v1alpha1/bodytransformation.go | 36 + .../api/v1alpha1/headertransformation.go | 38 ++ .../api/v1alpha1/routepolicyspec.go | 15 +- .../api/v1alpha1/transform.go | 62 ++ .../api/v1alpha1/transformationpolicy.go | 32 + api/applyconfiguration/internal/internal.go | 60 ++ api/applyconfiguration/utils.go | 8 + api/v1alpha1/route_policy_types.go | 78 ++- api/v1alpha1/zz_generated.deepcopy.go | 96 +++ hack/generate.sh | 2 + .../gateway.kgateway.dev_routepolicies.yaml | 119 ++++ internal/envoyinit/Dockerfile.envoyinit | 31 +- .../envoyinit/Dockerfile.envoyinit.distroless | 53 +- internal/envoyinit/README.md | 4 + internal/envoyinit/rustformations/Cargo.lock | 621 ++++++++++++++++++ internal/envoyinit/rustformations/Cargo.toml | 22 + .../src/http_simple_mutations.rs | 461 +++++++++++++ internal/envoyinit/rustformations/src/lib.rs | 41 ++ .../routepolicy/route_policy_plugin.go | 246 ++++++- .../routepolicy/transformation_plugin.go | 203 ++++++ .../kgateway/extensions2/settings/settings.go | 2 + .../extensions2/settings/settings_test.go | 3 + pkg/generated/openapi/zz_generated.openapi.go | 178 ++++- pkg/utils/kubeutils/portforward/options.go | 5 + .../e2e/features/transformation/suite.go | 248 +++++++ .../gateway-with-transformed-route.yaml | 47 ++ .../transformation/testdata/service.yaml | 47 ++ .../e2e/features/transformation/types.go | 33 + test/kubernetes/e2e/tests/kgateway_tests.go | 3 + test/kubernetes/testutils/assertions/envoy.go | 4 +- 34 files changed, 2770 insertions(+), 46 deletions(-) create mode 100644 api/applyconfiguration/api/v1alpha1/bodytransformation.go create mode 100644 api/applyconfiguration/api/v1alpha1/headertransformation.go create mode 100644 api/applyconfiguration/api/v1alpha1/transform.go create mode 100644 api/applyconfiguration/api/v1alpha1/transformationpolicy.go create mode 100644 internal/envoyinit/README.md create mode 100644 internal/envoyinit/rustformations/Cargo.lock create mode 100644 internal/envoyinit/rustformations/Cargo.toml create mode 100644 internal/envoyinit/rustformations/src/http_simple_mutations.rs create mode 100644 internal/envoyinit/rustformations/src/lib.rs create mode 100644 internal/kgateway/extensions2/plugins/routepolicy/transformation_plugin.go create mode 100644 test/kubernetes/e2e/features/transformation/suite.go create mode 100644 test/kubernetes/e2e/features/transformation/testdata/gateway-with-transformed-route.yaml create mode 100644 test/kubernetes/e2e/features/transformation/testdata/service.yaml create mode 100644 test/kubernetes/e2e/features/transformation/types.go diff --git a/.github/workflows/pr-kubernetes-tests.yaml b/.github/workflows/pr-kubernetes-tests.yaml index d310f2d2652..58a9bc5d49d 100644 --- a/.github/workflows/pr-kubernetes-tests.yaml +++ b/.github/workflows/pr-kubernetes-tests.yaml @@ -31,6 +31,11 @@ jobs: - cluster-name: 'cluster-one' go-test-args: '-v -timeout=25m' go-test-run-regex: '^TestKgateway$$/^BasicRouting$$|^TestKgateway$$/^Deployer$$|^TestKgateway$$/^HTTPRouteServices$$|^TestKgateway$$/^TCPRouteServices$$|^TestKgateway$$/^TLSRouteServices$$|^TestKgateway$$/^Backends$$' + # Mar 4, 2025: TODO minutes + - cluster-name: 'cluster-two' + go-test-args: '-v -timeout=25m' + go-test-run-regex: '^TestKgateway$$/^Transformation$$' + # # Dec 4, 2024: 23 minutes # - cluster-name: 'cluster-two' diff --git a/.gitignore b/.gitignore index d8044fd1490..d9843e942bf 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,10 @@ istio-*/* .bin/ # Added by goreleaser init: dist/ + +*.o +*.d +*-build + + +internal/envoyinit/rustformations/target/* diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 8ee953cc6c5..2b43f7000cb 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -107,8 +107,10 @@ dockers: - "--build-arg=GOARCH=arm64" - "--build-arg=ENTRYPOINT_SCRIPT=/internal/envoyinit/cmd/docker-entrypoint.sh" - "--build-arg=ENVOY_IMAGE={{ .Env.ENVOY_IMAGE }}" + - "--build-arg=RUSTFORMATIONS_DIR=/internal/envoyinit/rustformations" extra_files: - internal/envoyinit/cmd/docker-entrypoint.sh + - internal/envoyinit/rustformations - image_templates: - &envoyinit_amd_image "{{ .Env.IMAGE_REGISTRY }}/{{ .Env.ENVOYINIT_IMAGE_REPO }}:{{ .Env.VERSION }}-amd64" use: buildx @@ -121,8 +123,10 @@ dockers: - "--build-arg=GOARCH=amd64" - "--build-arg=ENTRYPOINT_SCRIPT=/internal/envoyinit/cmd/docker-entrypoint.sh" - "--build-arg=ENVOY_IMAGE={{ .Env.ENVOY_IMAGE }}" + - "--build-arg=RUSTFORMATIONS_DIR=/internal/envoyinit/rustformations" extra_files: - internal/envoyinit/cmd/docker-entrypoint.sh + - internal/envoyinit/rustformations docker_manifests: - name_template: "{{ .Env.IMAGE_REGISTRY }}/{{ .Env.CONTROLLER_IMAGE_REPO }}:{{ .Env.VERSION }}" image_templates: diff --git a/Makefile b/Makefile index af5dc6bab5c..bec2f3b3a8d 100644 --- a/Makefile +++ b/Makefile @@ -166,6 +166,7 @@ envoyversion: ENVOY_VERSION_TAG ?= $(shell echo $(ENVOY_IMAGE) | cut -d':' -f2) envoyversion: echo "Version is $(ENVOY_VERSION_TAG)" echo "Commit for envoyproxy is $(shell curl -s https://raw.githubusercontent.com/solo-io/envoy-gloo/refs/tags/v$(ENVOY_VERSION_TAG)/bazel/repository_locations.bzl | grep "envoy =" -A 4 | grep commit | cut -d'"' -f2)" + echo "Current ABI in envoyinit can be found in the cargo.toml's envoy-proxy-dynamic-modules-rust-sdk" #---------------------------------------------------------------------------------- # Ginkgo Tests #---------------------------------------------------------------------------------- @@ -462,6 +463,7 @@ envoyinit: $(ENVOYINIT_OUTPUT_DIR)/envoyinit-linux-$(GOARCH) # TODO(nfuden) cheat the process for now with -r but try to find a cleaner method $(ENVOYINIT_OUTPUT_DIR)/Dockerfile.envoyinit: internal/envoyinit/Dockerfile.envoyinit + cp -r ${ENVOYINIT_DIR}/rustformations $(ENVOYINIT_OUTPUT_DIR) cp $< $@ $(ENVOYINIT_OUTPUT_DIR)/docker-entrypoint.sh: internal/envoyinit/cmd/docker-entrypoint.sh diff --git a/api/applyconfiguration/api/v1alpha1/bodytransformation.go b/api/applyconfiguration/api/v1alpha1/bodytransformation.go new file mode 100644 index 00000000000..e4bec6bc622 --- /dev/null +++ b/api/applyconfiguration/api/v1alpha1/bodytransformation.go @@ -0,0 +1,36 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + apiv1alpha1 "github.com/kgateway-dev/kgateway/v2/api/v1alpha1" +) + +// BodyTransformationApplyConfiguration represents a declarative configuration of the BodyTransformation type for use +// with apply. +type BodyTransformationApplyConfiguration struct { + ParseAs *apiv1alpha1.BodyParseBehavior `json:"parseAs,omitempty"` + Value *apiv1alpha1.InjaTemplate `json:"value,omitempty"` +} + +// BodyTransformationApplyConfiguration constructs a declarative configuration of the BodyTransformation type for use with +// apply. +func BodyTransformation() *BodyTransformationApplyConfiguration { + return &BodyTransformationApplyConfiguration{} +} + +// WithParseAs sets the ParseAs field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ParseAs field is set to the value of the last call. +func (b *BodyTransformationApplyConfiguration) WithParseAs(value apiv1alpha1.BodyParseBehavior) *BodyTransformationApplyConfiguration { + b.ParseAs = &value + return b +} + +// WithValue sets the Value field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Value field is set to the value of the last call. +func (b *BodyTransformationApplyConfiguration) WithValue(value apiv1alpha1.InjaTemplate) *BodyTransformationApplyConfiguration { + b.Value = &value + return b +} diff --git a/api/applyconfiguration/api/v1alpha1/headertransformation.go b/api/applyconfiguration/api/v1alpha1/headertransformation.go new file mode 100644 index 00000000000..87b55107da5 --- /dev/null +++ b/api/applyconfiguration/api/v1alpha1/headertransformation.go @@ -0,0 +1,38 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "sigs.k8s.io/gateway-api/apis/v1" + + apiv1alpha1 "github.com/kgateway-dev/kgateway/v2/api/v1alpha1" +) + +// HeaderTransformationApplyConfiguration represents a declarative configuration of the HeaderTransformation type for use +// with apply. +type HeaderTransformationApplyConfiguration struct { + Name *v1.HeaderName `json:"name,omitempty"` + Value *apiv1alpha1.InjaTemplate `json:"value,omitempty"` +} + +// HeaderTransformationApplyConfiguration constructs a declarative configuration of the HeaderTransformation type for use with +// apply. +func HeaderTransformation() *HeaderTransformationApplyConfiguration { + return &HeaderTransformationApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *HeaderTransformationApplyConfiguration) WithName(value v1.HeaderName) *HeaderTransformationApplyConfiguration { + b.Name = &value + return b +} + +// WithValue sets the Value field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Value field is set to the value of the last call. +func (b *HeaderTransformationApplyConfiguration) WithValue(value apiv1alpha1.InjaTemplate) *HeaderTransformationApplyConfiguration { + b.Value = &value + return b +} diff --git a/api/applyconfiguration/api/v1alpha1/routepolicyspec.go b/api/applyconfiguration/api/v1alpha1/routepolicyspec.go index 7b4928183d9..97dd999ac65 100644 --- a/api/applyconfiguration/api/v1alpha1/routepolicyspec.go +++ b/api/applyconfiguration/api/v1alpha1/routepolicyspec.go @@ -5,9 +5,10 @@ package v1alpha1 // RoutePolicySpecApplyConfiguration represents a declarative configuration of the RoutePolicySpec type for use // with apply. type RoutePolicySpecApplyConfiguration struct { - TargetRef *LocalPolicyTargetReferenceApplyConfiguration `json:"targetRef,omitempty"` - Timeout *int `json:"timeout,omitempty"` - AI *AIRoutePolicyApplyConfiguration `json:"ai,omitempty"` + TargetRef *LocalPolicyTargetReferenceApplyConfiguration `json:"targetRef,omitempty"` + Timeout *int `json:"timeout,omitempty"` + AI *AIRoutePolicyApplyConfiguration `json:"ai,omitempty"` + Transformation *TransformationPolicyApplyConfiguration `json:"transformation,omitempty"` } // RoutePolicySpecApplyConfiguration constructs a declarative configuration of the RoutePolicySpec type for use with @@ -39,3 +40,11 @@ func (b *RoutePolicySpecApplyConfiguration) WithAI(value *AIRoutePolicyApplyConf b.AI = value return b } + +// WithTransformation sets the Transformation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Transformation field is set to the value of the last call. +func (b *RoutePolicySpecApplyConfiguration) WithTransformation(value *TransformationPolicyApplyConfiguration) *RoutePolicySpecApplyConfiguration { + b.Transformation = value + return b +} diff --git a/api/applyconfiguration/api/v1alpha1/transform.go b/api/applyconfiguration/api/v1alpha1/transform.go new file mode 100644 index 00000000000..b897b573787 --- /dev/null +++ b/api/applyconfiguration/api/v1alpha1/transform.go @@ -0,0 +1,62 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// TransformApplyConfiguration represents a declarative configuration of the Transform type for use +// with apply. +type TransformApplyConfiguration struct { + Set []HeaderTransformationApplyConfiguration `json:"set,omitempty"` + Add []HeaderTransformationApplyConfiguration `json:"add,omitempty"` + Remove []string `json:"remove,omitempty"` + Body *BodyTransformationApplyConfiguration `json:"body,omitempty"` +} + +// TransformApplyConfiguration constructs a declarative configuration of the Transform type for use with +// apply. +func Transform() *TransformApplyConfiguration { + return &TransformApplyConfiguration{} +} + +// WithSet adds the given value to the Set field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Set field. +func (b *TransformApplyConfiguration) WithSet(values ...*HeaderTransformationApplyConfiguration) *TransformApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithSet") + } + b.Set = append(b.Set, *values[i]) + } + return b +} + +// WithAdd adds the given value to the Add field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Add field. +func (b *TransformApplyConfiguration) WithAdd(values ...*HeaderTransformationApplyConfiguration) *TransformApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithAdd") + } + b.Add = append(b.Add, *values[i]) + } + return b +} + +// WithRemove adds the given value to the Remove field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Remove field. +func (b *TransformApplyConfiguration) WithRemove(values ...string) *TransformApplyConfiguration { + for i := range values { + b.Remove = append(b.Remove, values[i]) + } + return b +} + +// WithBody sets the Body field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Body field is set to the value of the last call. +func (b *TransformApplyConfiguration) WithBody(value *BodyTransformationApplyConfiguration) *TransformApplyConfiguration { + b.Body = value + return b +} diff --git a/api/applyconfiguration/api/v1alpha1/transformationpolicy.go b/api/applyconfiguration/api/v1alpha1/transformationpolicy.go new file mode 100644 index 00000000000..5bdb6abd63c --- /dev/null +++ b/api/applyconfiguration/api/v1alpha1/transformationpolicy.go @@ -0,0 +1,32 @@ +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// TransformationPolicyApplyConfiguration represents a declarative configuration of the TransformationPolicy type for use +// with apply. +type TransformationPolicyApplyConfiguration struct { + Request *TransformApplyConfiguration `json:"request,omitempty"` + Response *TransformApplyConfiguration `json:"response,omitempty"` +} + +// TransformationPolicyApplyConfiguration constructs a declarative configuration of the TransformationPolicy type for use with +// apply. +func TransformationPolicy() *TransformationPolicyApplyConfiguration { + return &TransformationPolicyApplyConfiguration{} +} + +// WithRequest sets the Request field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Request field is set to the value of the last call. +func (b *TransformationPolicyApplyConfiguration) WithRequest(value *TransformApplyConfiguration) *TransformationPolicyApplyConfiguration { + b.Request = value + return b +} + +// WithResponse sets the Response field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Response field is set to the value of the last call. +func (b *TransformationPolicyApplyConfiguration) WithResponse(value *TransformApplyConfiguration) *TransformationPolicyApplyConfiguration { + b.Response = value + return b +} diff --git a/api/applyconfiguration/internal/internal.go b/api/applyconfiguration/internal/internal.go index cb74d85a054..a531838aa6d 100644 --- a/api/applyconfiguration/internal/internal.go +++ b/api/applyconfiguration/internal/internal.go @@ -312,6 +312,16 @@ var schemaYAML = typed.YAMLObject(`types: elementRelationship: associative keys: - type +- name: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.BodyTransformation + map: + fields: + - name: parseAs + type: + scalar: string + default: "" + - name: value + type: + scalar: string - name: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.CELFilter map: fields: @@ -627,6 +637,15 @@ var schemaYAML = typed.YAMLObject(`types: type: namedType: io.k8s.sigs.gateway-api.apis.v1.HTTPHeaderMatch default: {} +- name: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.HeaderTransformation + map: + fields: + - name: name + type: + scalar: string + - name: value + type: + scalar: string - name: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.Host map: fields: @@ -1010,6 +1029,10 @@ var schemaYAML = typed.YAMLObject(`types: - name: timeout type: scalar: numeric + - name: transformation + type: + namedType: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.TransformationPolicy + default: {} - name: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.SdsBootstrap map: fields: @@ -1139,6 +1162,43 @@ var schemaYAML = typed.YAMLObject(`types: - name: vertexai type: namedType: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.VertexAIConfig +- name: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.Transform + map: + fields: + - name: add + type: + list: + elementType: + namedType: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.HeaderTransformation + elementRelationship: associative + keys: + - name + - name: body + type: + namedType: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.BodyTransformation + - name: remove + type: + list: + elementType: + scalar: string + elementRelationship: associative + - name: set + type: + list: + elementType: + namedType: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.HeaderTransformation + elementRelationship: associative + keys: + - name +- name: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.TransformationPolicy + map: + fields: + - name: request + type: + namedType: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.Transform + - name: response + type: + namedType: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.Transform - name: com.github.kgateway-dev.kgateway.v2.api.v1alpha1.VertexAIConfig map: fields: diff --git a/api/applyconfiguration/utils.go b/api/applyconfiguration/utils.go index 7fed619e851..16675633c5c 100644 --- a/api/applyconfiguration/utils.go +++ b/api/applyconfiguration/utils.go @@ -51,6 +51,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1alpha1.BackendSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("BackendStatus"): return &apiv1alpha1.BackendStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("BodyTransformation"): + return &apiv1alpha1.BodyTransformationApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("CELFilter"): return &apiv1alpha1.CELFilterApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("CustomLabel"): @@ -87,6 +89,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1alpha1.GrpcStatusFilterApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("HeaderFilter"): return &apiv1alpha1.HeaderFilterApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("HeaderTransformation"): + return &apiv1alpha1.HeaderTransformationApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("Host"): return &apiv1alpha1.HostApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("HTTPListenerPolicy"): @@ -159,6 +163,10 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1alpha1.StatusCodeFilterApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("SupportedLLMProvider"): return &apiv1alpha1.SupportedLLMProviderApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Transform"): + return &apiv1alpha1.TransformApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TransformationPolicy"): + return &apiv1alpha1.TransformationPolicyApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("VertexAIConfig"): return &apiv1alpha1.VertexAIConfigApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("Webhook"): diff --git a/api/v1alpha1/route_policy_types.go b/api/v1alpha1/route_policy_types.go index 6b2508cd18f..dedcb1c20fa 100644 --- a/api/v1alpha1/route_policy_types.go +++ b/api/v1alpha1/route_policy_types.go @@ -2,6 +2,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1 "sigs.k8s.io/gateway-api/apis/v1" ) // +kubebuilder:rbac:groups=gateway.kgateway.dev,resources=routepolicies,verbs=get;list;watch @@ -31,6 +32,79 @@ type RoutePolicyList struct { type RoutePolicySpec struct { TargetRef LocalPolicyTargetReference `json:"targetRef,omitempty"` // +kubebuilder:validation:Minimum=1 - Timeout int `json:"timeout,omitempty"` - AI *AIRoutePolicy `json:"ai,omitempty"` + Timeout int `json:"timeout,omitempty"` + AI *AIRoutePolicy `json:"ai,omitempty"` + Transformation TransformationPolicy `json:"transformation,omitempty"` +} + +// TransformationPolicy config is used to modify envoy behavior at a route level. +// These modifications can be performed on the request and response paths. +type TransformationPolicy struct { + // +optional + Request *Transform `json:"request,omitempty"` + // +optional + Response *Transform `json:"response,omitempty"` +} + +// Transform defines the operations to be performed by the transformation. +// These operations may include changing the actual request/response but may also cause side effects. +// Side effects may include setting info that can be used in future steps (e.g. dynamic metadata) and can cause envoy to buffer. +type Transform struct { + + // Set is a list of headers and the value they should be set to. + // +optional + // +listType=map + // +listMapKey=name + // +kubebuilder:validation:MaxItems=16 + Set []HeaderTransformation `json:"set,omitempty"` + + // Add is a list of headers to add to the request and what that value should be set to. + // If there is already a header with these values then append the value as an extra entry. + // +optional + // +listType=map + // +listMapKey=name + // +kubebuilder:validation:MaxItems=16 + Add []HeaderTransformation `json:"add,omitempty"` + + // Remove is a list of header names to remove from the request/response. + // +optional + // +listType=set + // +kubebuilder:validation:MaxItems=16 + Remove []string `json:"remove,omitempty"` + + // Body controls both how to parse the body and if needed how to set. + // +optional + // + // If empty, body will not be buffered. + Body *BodyTransformation `json:"body,omitempty"` +} + +type InjaTemplate string + +type HeaderTransformation struct { + // Name is the name of the header to interact with. + // +required + Name gwv1.HeaderName `json:"name,omitempty"` + // Value is the template to apply to generate the output value for the header. + Value InjaTemplate `json:"value,omitempty"` +} + +// BodyparseBehavior defines how the body should be parsed +// If set to json and the body is not json then the filter will not perform the transformation. +// +kubebuilder:validation:Enum=AsString;AsJson +type BodyParseBehavior string + +const ( + BodyParseBehaviorAsString BodyParseBehavior = "AsString" + BodyParseBehaviorAsJSON BodyParseBehavior = "AsJson" +) + +// BodyTransformation controls how the body should be parsed and transformed. +type BodyTransformation struct { + // ParseAs defines what auto formatting should be applied to the body. + // This can make interacting with keys within a json body much easier if AsJson is selected. + // +kubebuilder:default=AsString + ParseAs BodyParseBehavior `json:"parseAs"` + // Value is the template to apply to generate the output value for the body. + Value *InjaTemplate `json:"value,omitempty"` } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 92e12db6f59..880f286d38c 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -498,6 +498,26 @@ func (in *BackendStatus) DeepCopy() *BackendStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BodyTransformation) DeepCopyInto(out *BodyTransformation) { + *out = *in + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(InjaTemplate) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BodyTransformation. +func (in *BodyTransformation) DeepCopy() *BodyTransformation { + if in == nil { + return nil + } + out := new(BodyTransformation) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CELFilter) DeepCopyInto(out *CELFilter) { *out = *in @@ -1122,6 +1142,21 @@ func (in *HeaderFilter) DeepCopy() *HeaderFilter { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HeaderTransformation) DeepCopyInto(out *HeaderTransformation) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeaderTransformation. +func (in *HeaderTransformation) DeepCopy() *HeaderTransformation { + if in == nil { + return nil + } + out := new(HeaderTransformation) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Host) DeepCopyInto(out *Host) { *out = *in @@ -1885,6 +1920,7 @@ func (in *RoutePolicySpec) DeepCopyInto(out *RoutePolicySpec) { *out = new(AIRoutePolicy) (*in).DeepCopyInto(*out) } + in.Transformation.DeepCopyInto(&out.Transformation) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoutePolicySpec. @@ -2170,6 +2206,66 @@ func (in *SupportedLLMProvider) DeepCopy() *SupportedLLMProvider { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Transform) DeepCopyInto(out *Transform) { + *out = *in + if in.Set != nil { + in, out := &in.Set, &out.Set + *out = make([]HeaderTransformation, len(*in)) + copy(*out, *in) + } + if in.Add != nil { + in, out := &in.Add, &out.Add + *out = make([]HeaderTransformation, len(*in)) + copy(*out, *in) + } + if in.Remove != nil { + in, out := &in.Remove, &out.Remove + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Body != nil { + in, out := &in.Body, &out.Body + *out = new(BodyTransformation) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Transform. +func (in *Transform) DeepCopy() *Transform { + if in == nil { + return nil + } + out := new(Transform) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TransformationPolicy) DeepCopyInto(out *TransformationPolicy) { + *out = *in + if in.Request != nil { + in, out := &in.Request, &out.Request + *out = new(Transform) + (*in).DeepCopyInto(*out) + } + if in.Response != nil { + in, out := &in.Response, &out.Response + *out = new(Transform) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TransformationPolicy. +func (in *TransformationPolicy) DeepCopy() *TransformationPolicy { + if in == nil { + return nil + } + out := new(TransformationPolicy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VertexAIConfig) DeepCopyInto(out *VertexAIConfig) { *out = *in diff --git a/hack/generate.sh b/hack/generate.sh index 2242fad07e4..d128d4e3a04 100755 --- a/hack/generate.sh +++ b/hack/generate.sh @@ -72,6 +72,8 @@ go run k8s.io/code-generator/cmd/client-gen \ --output-pkg "${OUTPUT_PKG}/${CLIENTSET_PKG_NAME}" \ --apply-configuration-package "${APIS_PKG}/api/applyconfiguration" +go generate ${ROOT_DIR}/internal/envoyinit/hack/... + # fix imports of gen code go run golang.org/x/tools/cmd/goimports -w ${ROOT_DIR}/${CLIENT_GEN_DIR} go run golang.org/x/tools/cmd/goimports -w ${ROOT_DIR}/api diff --git a/install/helm/kgateway/crds/gateway.kgateway.dev_routepolicies.yaml b/install/helm/kgateway/crds/gateway.kgateway.dev_routepolicies.yaml index 698303427f0..3e8ae826f4d 100644 --- a/install/helm/kgateway/crds/gateway.kgateway.dev_routepolicies.yaml +++ b/install/helm/kgateway/crds/gateway.kgateway.dev_routepolicies.yaml @@ -293,6 +293,125 @@ spec: timeout: minimum: 1 type: integer + transformation: + properties: + request: + properties: + add: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + type: string + required: + - name + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + body: + properties: + parseAs: + default: AsString + enum: + - AsString + - AsJson + type: string + value: + type: string + required: + - parseAs + type: object + remove: + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + type: string + required: + - name + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + response: + properties: + add: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + type: string + required: + - name + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + body: + properties: + parseAs: + default: AsString + enum: + - AsString + - AsJson + type: string + value: + type: string + required: + - parseAs + type: object + remove: + items: + type: string + maxItems: 16 + type: array + x-kubernetes-list-type: set + set: + items: + properties: + name: + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + value: + type: string + required: + - name + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + type: object type: object status: properties: diff --git a/internal/envoyinit/Dockerfile.envoyinit b/internal/envoyinit/Dockerfile.envoyinit index 5a6daa37c50..883d21af243 100644 --- a/internal/envoyinit/Dockerfile.envoyinit +++ b/internal/envoyinit/Dockerfile.envoyinit @@ -1,7 +1,32 @@ ARG ENVOY_IMAGE -FROM $ENVOY_IMAGE + +FROM ghcr.io/rust-cross/cargo-zigbuild:0.19.88@sha256:a00024c136a365171892a186b1ee5552812a8f91f2ba9f5fe609c0d352238cca AS rust_builder + +WORKDIR /build + +RUN apt update && apt install -y clang + + + +# Then copy the rest of the source code and build the library. +# Paramerized to allow for supporting local, make and goreleaser builds +ARG RUSTFORMATIONS_DIR=./rustformations +COPY ${RUSTFORMATIONS_DIR}/Cargo.toml ${RUSTFORMATIONS_DIR}/Cargo.lock ./ +RUN mkdir src && echo "" > src/lib.rs +RUN cargo fetch +RUN rm -rf src + +# Then copy the rest of the source code and build the library. +COPY ./${RUSTFORMATIONS_DIR} . +RUN cargo zigbuild --target x86_64-unknown-linux-gnu + +# hard code amd64 for now as we only have that version of envoy-gloo +RUN cp /build/target/x86_64-unknown-linux-gnu/debug/librust_module.so /build/amd64_librust_module.so + +FROM $ENVOY_IMAGE AS envoy +# hard code amd64 for now as we only have that version of envoy-gloo ARG GOARCH=amd64 # eventually may matter for now https://unix.stackexchange.com/a/701288 # means its not too useful @@ -16,6 +41,10 @@ RUN apt-get update \ COPY envoyinit-linux-$GOARCH /usr/local/bin/envoyinit +ENV ENVOY_DYNAMIC_MODULES_SEARCH_PATH=/usr/local/lib +COPY --from=rust_builder /build/amd64_librust_module.so /usr/local/lib/librust_module.so + + # SDS-specific setup, only used if ENVOY_SIDECAR=true ARG ENTRYPOINT_SCRIPT=/docker-entrypoint.sh COPY $ENTRYPOINT_SCRIPT / diff --git a/internal/envoyinit/Dockerfile.envoyinit.distroless b/internal/envoyinit/Dockerfile.envoyinit.distroless index 15731afc0d8..dc8901689ee 100644 --- a/internal/envoyinit/Dockerfile.envoyinit.distroless +++ b/internal/envoyinit/Dockerfile.envoyinit.distroless @@ -1,23 +1,56 @@ ARG ENVOY_IMAGE ARG BASE_IMAGE -FROM $ENVOY_IMAGE as envoy -FROM $BASE_IMAGE -ARG GOARCH=amd64 +FROM ghcr.io/rust-cross/cargo-zigbuild:0.19.88@sha256:a00024c136a365171892a186b1ee5552812a8f91f2ba9f5fe609c0d352238cca AS rust_builder + +WORKDIR /build + +# bindgen requires libclang-dev. +RUN apt update && apt install -y clang + +# Then copy the rest of the source code and build the library. +# Paramerized to allow for supporting local, make and goreleaser builds +ARG RUSTFORMATIONS_DIR=./rustformations +COPY ${RUSTFORMATIONS_DIR}/Cargo.toml ${RUSTFORMATIONS_DIR}/Cargo.lock ./ +RUN mkdir src && echo "" > src/lib.rs +RUN cargo fetch +RUN rm -rf src + +# Then copy the rest of the source code and build the library. +COPY ./${RUSTFORMATIONS_DIR} . +RUN cargo zigbuild --target x86_64-unknown-linux-gnu + +# hard code amd64 for now as we only have that version of envoy-gloo +RUN cp /build/target/x86_64-unknown-linux-gnu/debug/librust_module.so /build/amd64_librust_module.so -COPY --from=envoy /usr/local/bin/envoy /usr/local/bin/envoy -# Copy over the required libraries -# lib64z1 - Required by libsaxon for xslt transformations -COPY --from=envoy /usr/lib/x86_64-linux-gnu/libz.so* /usr/lib/x86_64-linux-gnu/ + +FROM ${BASE_IMAGE} AS envoy +# hard code amd64 for now as we only have that version of envoy-gloo +ARG GOARCH=amd64 +# eventually may matter for now https://unix.stackexchange.com/a/701288 +# means its not too useful +ENV DEBIAN_FRONTEND=noninteractive + +# Update our deps to make cve toil lower +#install wget for our default probes +RUN apt-get update \ + && apt-get upgrade -y \ + && apt-get install wget -y \ + && rm -rf /var/log/*log /var/lib/apt/lists/* /var/log/apt/* /var/lib/dpkg/*-old /var/cache/debconf/*-old COPY envoyinit-linux-$GOARCH /usr/local/bin/envoyinit +ENV ENVOY_DYNAMIC_MODULES_SEARCH_PATH=/usr/local/lib +COPY --from=rust_builder /build/amd64_librust_module.so /usr/local/lib/librust_module.so + + # SDS-specific setup, only used if ENVOY_SIDECAR=true -COPY docker-entrypoint.sh / +ARG ENTRYPOINT_SCRIPT=/docker-entrypoint.sh +COPY $ENTRYPOINT_SCRIPT / USER 10101 -ENTRYPOINT [ "/docker-entrypoint.sh"] -CMD [] \ No newline at end of file +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD [] diff --git a/internal/envoyinit/README.md b/internal/envoyinit/README.md new file mode 100644 index 00000000000..b224c844224 --- /dev/null +++ b/internal/envoyinit/README.md @@ -0,0 +1,4 @@ +Based on https://github.com/envoyproxy/dynamic-modules-examples + + +The current envoy sha can be found via top level "make envoyversion" this will give you the sha to replace within envoyinit when the ENVOY_IMAGE is updated \ No newline at end of file diff --git a/internal/envoyinit/rustformations/Cargo.lock b/internal/envoyinit/rustformations/Cargo.lock new file mode 100644 index 00000000000..59e49ac43b9 --- /dev/null +++ b/internal/envoyinit/rustformations/Cargo.lock @@ -0,0 +1,621 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "envoy-proxy-dynamic-modules-rust-sdk" +version = "0.1.0" +source = "git+https://github.com/envoyproxy/envoy?rev=87b4ae8dde4a0cd35bc8c3584ec79d3625522c4d#87b4ae8dde4a0cd35bc8c3584ec79d3625522c4d" +dependencies = [ + "bindgen", + "mockall", +] + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi", + "windows-targets", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "log" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minijinja" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff7b8df5e85e30b87c2b0b3f58ba3a87b68e133738bf512a7713769326dbca9" +dependencies = [ + "serde", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mockall" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy 0.7.35", +] + +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha", + "rand_core", + "zerocopy 0.8.17", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" +dependencies = [ + "getrandom", + "zerocopy 0.8.17", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustformations" +version = "0.1.0" +dependencies = [ + "envoy-proxy-dynamic-modules-rust-sdk", + "matchers", + "minijinja", + "mockall", + "rand", + "serde", + "serde_json", + "tempfile", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + +[[package]] +name = "unicode-ident" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" + +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713" +dependencies = [ + "zerocopy-derive 0.8.17", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/internal/envoyinit/rustformations/Cargo.toml b/internal/envoyinit/rustformations/Cargo.toml new file mode 100644 index 00000000000..f3ab0f7e56f --- /dev/null +++ b/internal/envoyinit/rustformations/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "rustformations" +version = "0.1.0" +edition = "2021" + +[dependencies] +# The SDK version must match the Envoy version due to the strict compatibility requirements. +envoy-proxy-dynamic-modules-rust-sdk = { git = "https://github.com/envoyproxy/envoy", rev = "87b4ae8dde4a0cd35bc8c3584ec79d3625522c4d" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +rand = "0.9.0" +matchers = "0.2.0" +minijinja = { version = "2.7.0" } +tempfile = "3.16.0" +mockall = "0.13.1" + +[lib] +name = "rust_module" +path = "src/lib.rs" +crate-type = ["cdylib"] + + diff --git a/internal/envoyinit/rustformations/src/http_simple_mutations.rs b/internal/envoyinit/rustformations/src/http_simple_mutations.rs new file mode 100644 index 00000000000..50c8d6a8d36 --- /dev/null +++ b/internal/envoyinit/rustformations/src/http_simple_mutations.rs @@ -0,0 +1,461 @@ +use envoy_proxy_dynamic_modules_rust_sdk::*; +use minijinja::value::Rest; +use minijinja::{context, Environment, State}; +use mockall::*; + +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::hash::Hash; + +/// This implements the [`envoy_proxy_dynamic_modules_rust_sdk::HttpFilterConfig`] trait. +/// +/// The trait corresponds to a Envoy filter chain configuration. + +#[derive(Serialize, Deserialize)] +pub struct FilterConfig { + #[serde(default)] + request_headers_setter: Vec<(String, String)>, + #[serde(default)] + response_headers_setter: Vec<(String, String)>, + route_specific: HashMap, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct PerRouteConfig { + #[serde(default)] + request_headers_setter: Vec<(String, String)>, + #[serde(default)] + response_headers_setter: Vec<(String, String)>, +} + +impl FilterConfig { + /// This is the constructor for the [`FilterConfig`]. + /// + /// filter_config is the filter config from the Envoy config here: + /// https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/dynamic_modules/v3/dynamic_modules.proto#envoy-v3-api-msg-extensions-dynamic-modules-v3-dynamicmoduleconfig + pub fn new(filter_config: &str) -> Option { + let filter_config: FilterConfig = match serde_json::from_str(filter_config) { + // TODO(nfuden): Handle optional configuration entries more clenaly. Currently all values are required to be present + Ok(cfg) => cfg, + Err(err) => { + // TODO(nfuden): Dont panic if there is incorrect configuration + eprintln!("Error parsing filter config: {}", err); + return None; + } + }; + Some(filter_config) + } +} + +impl HttpFilterConfig for FilterConfig { + /// This is called for each new HTTP filter. + fn new_http_filter(&mut self, _envoy: &mut EC) -> Box> { + let mut env = Environment::new(); + + // could add in line like this if we wanted to + // env.add_function("substring", |input: &str, args: Rest| { + + env.add_function("substring", substring); + + // !! Standard string manipulation + // env.add_function("trim", trim); + // env.add_function("base64_encode", base64_encode); + // env.add_function("base64url_encode", base64url_encode); + // env.add_function("base64_decode", base64_decode); + // env.add_function("base64url_decode", base64url_decode); + // env.add_function("replace_with_random", replace_with_random); + // env.add_function("raw_string", raw_string); + // env.add_function("word_count", word_count); + + // !! Envoy context accessors + env.add_function("header", header); + // env.add_function("request_header", request_header); + // env.add_function("extraction", extraction); + // env.add_function("body", body); + // env.add_function("dynamic_metadata", dynamic_metadata); + + // !! Datasource Puller needed + // env.add_function("data_source", data_source); + + // !! Requires being in an upstream filter + // env.add_function("host_metadata", host_metadata); + // env.add_function("cluster_metadata", cluster_metadata); + + // !! Possibly not relevant old inja internal debug stuff + // env.add_function("context", context); + // env.add_function("env", env); + + // attempt to unmarshal the route_specific strings into RouteSpecificConfigs + // TODO(nfuden): remove this once upstream allows for real route specific configs + let mut specific = HashMap::new(); + for (key, value) in self.route_specific.iter() { + let route_specific: PerRouteConfig = match serde_json::from_str(value) { + Ok(cfg) => cfg, + Err(err) => { + eprintln!("Error parsing route specific config: {} {}", err, value); + continue; + } + }; + specific.insert(key.clone(), route_specific); + } + + // specific.extend(self.route_specific.into_iter()); + + Box::new(Filter { + request_headers_setter: self.request_headers_setter.clone(), + // request_headers_extractions: self.request_headers_extractions.clone(), + response_headers_setter: self.response_headers_setter.clone(), + // clone the hashmap + route_specific: specific, + env: env, + }) + } +} + +// substring can be called with either two or three arguments -- +// the first argument is the string to be modified, the second is the start position +// of the substring, and the optional third argument is the length of the substring. +// If the third argument is not provided, the substring will extend to the end of the string. +fn substring(input: &str, args: Rest) -> String { + if args.len() == 0 || args.len() > 2 { + return input.to_string(); + } + let start: usize = args[0].parse::().unwrap_or(0); + let end = if args.len() == 2 { + args[1].parse::().unwrap_or(input.len()) + } else { + input.len() + }; + + input[start..end].to_string() +} + +fn header(state: &State, key: &str) -> String { + let headers = state.lookup("headers"); + let Some(headers) = headers else { + return "".to_string(); + }; + + let Some(header_map) = >::deserialize(headers.clone()).ok() else { + return "".to_string(); + }; + header_map.get(key).cloned().unwrap_or_default() +} + +/// This implements the [`envoy_proxy_dynamic_modules_rust_sdk::HttpFilter`] trait. +/// +/// This sets the request and response headers to the values specified in the filter config. +pub struct Filter { + request_headers_setter: Vec<(String, String)>, + // request_headers_extractions: Vec<(String, String)>, + response_headers_setter: Vec<(String, String)>, + route_specific: HashMap, + env: Environment<'static>, +} + +/// This implements the [`envoy_proxy_dynamic_modules_rust_sdk::HttpFilter`] trait. +impl HttpFilter for Filter { + fn on_request_headers( + &mut self, + envoy_filter: &mut EHF, + _end_of_stream: bool, + ) -> abi::envoy_dynamic_module_type_on_http_filter_request_headers_status { + if !_end_of_stream { + return abi::envoy_dynamic_module_type_on_http_filter_request_headers_status::StopIteration; + } + + let mut setters = self.request_headers_setter.clone(); + // use the sub route version if appropriate as we dont have valid perroute config today + if self.route_specific.len() > 0 { + // check filter state for info + let route_name_data = envoy_filter + .get_dynamic_metadata_string("kgateway", "route") + .unwrap(); + let route_name = std::str::from_utf8(route_name_data.as_slice()).unwrap(); + setters = self + .route_specific + .get(route_name) + .unwrap() + .request_headers_setter + .clone(); + + // TODO(nfuden)remove + // add a debug to the setters + setters.append(&mut vec![("x-debuggs".to_string(), route_name.to_string())]); + } + + // TODO(nfuden): find someone who knows rust to see if we really need this Hash map for serialization + let mut headers = HashMap::new(); + for (key, val) in envoy_filter.get_request_headers() { + let Some(key) = std::str::from_utf8(key.as_slice()).ok() else { + continue; + }; + let value = std::str::from_utf8(val.as_slice()).unwrap().to_string(); + + headers.insert(key.to_string(), value); + } + + // let serialized_headers = serde_json::to_string(&headers).unwrap(); + for (key, value) in &setters { + let mut env = self.env.clone(); + env.add_template("temp", value).unwrap(); + let tmpl = env.get_template("temp").unwrap(); + let rendered = tmpl.render(context!(headers => headers)); + let mut rendered_str = "".to_string(); + if rendered.is_ok() { + rendered_str = rendered.unwrap(); + } else { + eprintln!("Error rendering template: {}", rendered.err().unwrap()); + } + envoy_filter.set_request_header(key, rendered_str.as_bytes()); + } + abi::envoy_dynamic_module_type_on_http_filter_request_headers_status::Continue + } + + fn on_response_headers( + &mut self, + envoy_filter: &mut EHF, + _end_of_stream: bool, + ) -> abi::envoy_dynamic_module_type_on_http_filter_response_headers_status { + // TODO(nfuden): find someone who knows rust to see if we really need this Hash map for serialization + let mut headers = HashMap::new(); + for (key, val) in envoy_filter.get_response_headers() { + let Some(key) = std::str::from_utf8(key.as_slice()).ok() else { + continue; + }; + let value = std::str::from_utf8(val.as_slice()).unwrap().to_string(); + + headers.insert(key.to_string(), value); + } + + let mut setters = self.response_headers_setter.clone(); + // use the sub route version if appropriate as we dont have valid perroute config today + if self.route_specific.len() > 0 { + // check filter state for info + let route_name_data = envoy_filter + .get_dynamic_metadata_string("kgateway", "route") + .unwrap(); + + // let route_name_slice = .as_slice(); + let route_name = std::str::from_utf8(route_name_data.as_slice()).unwrap(); + setters = self + .route_specific + .get(route_name) + .unwrap() + .response_headers_setter + .clone(); + + // TODO(nfuden)remove + // add a debug to the setters + setters.append(&mut vec![("x-debuggs".to_string(), route_name.to_string())]); + } + + for (key, value) in &setters { + let mut env = self.env.clone(); + env.add_template("temp", value).unwrap(); + let tmpl = env.get_template("temp").unwrap(); + let rendered = tmpl.render(context!(headers => headers)); + let mut rendered_str = "".to_string(); + if rendered.is_ok() { + rendered_str = rendered.unwrap(); + } else { + eprintln!("Error rendering template: {}", rendered.err().unwrap()); + } + envoy_filter.set_response_header(key, rendered_str.as_bytes()); + } + abi::envoy_dynamic_module_type_on_http_filter_response_headers_status::Continue + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_injected_functions() { + // bootstrap envoy config to feed into the new http filter call + struct EnvoyConfig {} + impl EnvoyHttpFilterConfig for EnvoyConfig {} + let mut envoy_config = EnvoyConfig {}; + + // get envoy's mockall impl for httpfilter + let mut envoy_filter = envoy_proxy_dynamic_modules_rust_sdk::MockEnvoyHttpFilter::new(); + + // construct the filter config + // most upstream tests start with the filter itself but we are tryign to add heavier logic + // to the config factory strat rather than running it on header calls + let mut filter_conf = FilterConfig { + request_headers_setter: vec![ + ( + "X-substring".to_string(), + "{{substring(\"ENVOYPROXY something\", 5, 10) }}".to_string(), + ), + ( + "X-substring-no-3rd".to_string(), + "{{substring(\"ENVOYPROXY something\", 5) }}".to_string(), + ), + ( + "X-donor-header-contents".to_string(), + "{{ header(\"x-donor\") }}".to_string(), + ), + ( + "X-donor-header-substringed".to_string(), + "{{ substring( header(\"x-donor\"), 0, 7)}}".to_string(), + ), + ], + response_headers_setter: vec![("X-Bar".to_string(), "foo".to_string())], + route_specific: HashMap::new(), + }; + let mut filter = filter_conf.new_http_filter(&mut envoy_config); + + envoy_filter.expect_get_request_headers().returning(|| { + vec![ + (EnvoyBuffer::new("host"), EnvoyBuffer::new("example.com")), + ( + EnvoyBuffer::new("x-donor"), + EnvoyBuffer::new("thedonorvalue"), + ), + ] + }); + + envoy_filter.expect_get_response_headers().returning(|| { + vec![ + (EnvoyBuffer::new("host"), EnvoyBuffer::new("example.com")), + ( + EnvoyBuffer::new("x-donor"), + EnvoyBuffer::new("thedonorvalue"), + ), + ] + }); + + let mut seq = Sequence::new(); + envoy_filter + .expect_set_request_header() + .times(1) + .in_sequence(&mut seq) + .returning(|key, value: &[u8]| { + assert_eq!(key, "X-substring"); + assert_eq!(std::str::from_utf8(value).unwrap(), "PROXY"); + return true; + }); + + envoy_filter + .expect_set_request_header() + .times(1) + .in_sequence(&mut seq) + .returning(|key, value: &[u8]| { + assert_eq!(key, "X-substring-no-3rd"); + assert_eq!(std::str::from_utf8(value).unwrap(), "PROXY something"); + return true; + }); + + envoy_filter + .expect_set_request_header() + .times(1) + .in_sequence(&mut seq) + .returning(|key, value: &[u8]| { + assert_eq!(key, "X-donor-header-contents"); + assert_eq!(std::str::from_utf8(value).unwrap(), "thedonorvalue"); + return true; + }); + + envoy_filter + .expect_set_request_header() + .times(1) + .in_sequence(&mut seq) + .returning(|key, value: &[u8]| { + assert_eq!(key, "X-donor-header-substringed"); + assert_eq!(std::str::from_utf8(value).unwrap(), "thedono"); + return true; + }); + + envoy_filter + .expect_set_response_header() + .returning(|key, value| { + assert_eq!(key, "X-Bar"); + assert_eq!(value, b"foo"); + return true; + }); + + assert_eq!( + filter.on_request_headers(&mut envoy_filter, true), + abi::envoy_dynamic_module_type_on_http_filter_request_headers_status::Continue + ); + assert_eq!( + filter.on_response_headers(&mut envoy_filter, true), + abi::envoy_dynamic_module_type_on_http_filter_response_headers_status::Continue + ); + } + #[test] + fn test_minininja_functionality() { + // bootstrap envoy config to feed into the new http filter call + struct EnvoyConfig {} + impl EnvoyHttpFilterConfig for EnvoyConfig {} + let mut envoy_config = EnvoyConfig {}; + + // get envoy's mockall impl for httpfilter + let mut envoy_filter = envoy_proxy_dynamic_modules_rust_sdk::MockEnvoyHttpFilter::new(); + + // construct the filter config + // most upstream tests start with the filter itself but we are tryign to add heavier logic + // to the config factory strat rather than running it on header calls + let mut filter_conf = FilterConfig { + request_headers_setter: vec![( + "X-if-truth".to_string(), + "{%- if true -%}supersuper{% endif %}".to_string(), + )], + response_headers_setter: vec![("X-Bar".to_string(), "foo".to_string())], + route_specific: HashMap::new(), + }; + let mut filter = filter_conf.new_http_filter(&mut envoy_config); + + envoy_filter.expect_get_request_headers().returning(|| { + vec![ + (EnvoyBuffer::new("host"), EnvoyBuffer::new("example.com")), + ( + EnvoyBuffer::new("x-donor"), + EnvoyBuffer::new("thedonorvalue"), + ), + ] + }); + + envoy_filter.expect_get_response_headers().returning(|| { + vec![ + (EnvoyBuffer::new("host"), EnvoyBuffer::new("example.com")), + ( + EnvoyBuffer::new("x-donor"), + EnvoyBuffer::new("thedonorvalue"), + ), + ] + }); + + let mut seq = Sequence::new(); + envoy_filter + .expect_set_request_header() + .times(1) + .in_sequence(&mut seq) + .returning(|key, value: &[u8]| { + assert_eq!(key, "X-if-truth"); + assert_eq!(std::str::from_utf8(value).unwrap(), "supersuper"); + return true; + }); + envoy_filter + .expect_set_response_header() + .returning(|key, value| { + assert_eq!(key, "X-Bar"); + assert_eq!(value, b"foo"); + return true; + }); + assert_eq!( + filter.on_request_headers(&mut envoy_filter, false), + abi::envoy_dynamic_module_type_on_http_filter_request_headers_status::StopIteration + ); + assert_eq!( + filter.on_request_headers(&mut envoy_filter, true), + abi::envoy_dynamic_module_type_on_http_filter_request_headers_status::Continue + ); + assert_eq!( + filter.on_response_headers(&mut envoy_filter, true), + abi::envoy_dynamic_module_type_on_http_filter_response_headers_status::Continue + ); + } +} diff --git a/internal/envoyinit/rustformations/src/lib.rs b/internal/envoyinit/rustformations/src/lib.rs new file mode 100644 index 00000000000..7566c40fd9c --- /dev/null +++ b/internal/envoyinit/rustformations/src/lib.rs @@ -0,0 +1,41 @@ +use envoy_proxy_dynamic_modules_rust_sdk::*; + +// ALL FILTERS HERE +mod http_simple_mutations; + +declare_init_functions!(init, new_http_filter_config_fn); + +/// This implements the [`envoy_proxy_dynamic_modules_rust_sdk::ProgramInitFunction`]. +/// +/// This is called exactly once when the module is loaded. It can be used to +/// initialize global state as well as check the runtime environment to ensure that +/// the module is running in a supported environment. +/// +/// Returning `false` will cause Envoy to reject the config hence the +/// filter will not be loaded. +fn init() -> bool { + true +} + +/// This implements the [`envoy_proxy_dynamic_modules_rust_sdk::NewHttpFilterConfigFunction`]. +/// +/// This is the entrypoint every time a new HTTP filter is created via the DynamicModuleFilter config. +/// +/// Each argument matches the corresponding argument in the Envoy config here: +/// https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/dynamic_modules/v3/dynamic_modules.proto#envoy-v3-api-msg-extensions-dynamic-modules-v3-dynamicmoduleconfig +/// +/// Returns None if the filter name or config is determined to be invalid by each filter's `new` function. +fn new_http_filter_config_fn( + _envoy_filter_config: &mut EC, + filter_name: &str, + filter_config: &str, +) -> Option>> { + match filter_name { + "http_simple_mutations" => http_simple_mutations::FilterConfig::new(filter_config) + .map(|config| Box::new(config) as Box>), + _ => panic!( + "Unknown filter name: {}, known filters are {}", + filter_name, "http_simple_mutations" + ), + } +} diff --git a/internal/kgateway/extensions2/plugins/routepolicy/route_policy_plugin.go b/internal/kgateway/extensions2/plugins/routepolicy/route_policy_plugin.go index cec5bd1e498..69b7f36302e 100644 --- a/internal/kgateway/extensions2/plugins/routepolicy/route_policy_plugin.go +++ b/internal/kgateway/extensions2/plugins/routepolicy/route_policy_plugin.go @@ -2,17 +2,24 @@ package routepolicy import ( "context" + "encoding/json" + "fmt" + "strconv" "time" - envoy_config_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - envoy_config_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" - envoy_ext_proc_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3" + exteniondynamicmodulev3 "github.com/envoyproxy/go-control-plane/envoy/extensions/dynamic_modules/v3" + dynamicmodulesv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_modules/v3" envoyhttp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" - "github.com/solo-io/go-utils/contextutils" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/durationpb" "istio.io/istio/pkg/kube/krt" "k8s.io/apimachinery/pkg/runtime/schema" + envoy_config_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + envoy_config_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + envoy_ext_proc_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3" + "github.com/kgateway-dev/kgateway/v2/api/v1alpha1" "github.com/kgateway-dev/kgateway/v2/internal/kgateway/extensions2/common" extensionplug "github.com/kgateway-dev/kgateway/v2/internal/kgateway/extensions2/plugin" @@ -21,16 +28,33 @@ import ( "github.com/kgateway-dev/kgateway/v2/internal/kgateway/ir" "github.com/kgateway-dev/kgateway/v2/internal/kgateway/krtcollections" "github.com/kgateway-dev/kgateway/v2/internal/kgateway/plugins" + "github.com/kgateway-dev/kgateway/v2/internal/kgateway/utils" "github.com/kgateway-dev/kgateway/v2/internal/kgateway/utils/krtutil" "github.com/kgateway-dev/kgateway/v2/internal/kgateway/wellknown" + + // TODO(nfuden): remove once rustformations are able to be used in a production environment + transformationpb "github.com/solo-io/envoy-gloo/go/config/filter/http/transformation/v2" + "github.com/solo-io/go-utils/contextutils" ) +const transformationFilterNamePrefix = "transformation" +const rustformationFilterNamePrefix = "dynamic_modules/simple_mutations" + type routePolicy struct { ct time.Time - spec v1alpha1.RoutePolicySpec + spec routeSpecIr AISecret *ir.Secret } +type routeSpecIr struct { + timeout *durationpb.Duration + AI *v1alpha1.AIRoutePolicy + transform *anypb.Any + rustformation *anypb.Any + rustformationStringToStash string + errors []error +} + func (d *routePolicy) CreationTime() time.Time { return d.ct } @@ -40,10 +64,24 @@ func (d *routePolicy) Equals(in any) bool { if !ok { return false } - return d.spec == d2.spec + + if !proto.Equal(d.spec.timeout, d2.spec.timeout) { + return false + } + if !proto.Equal(d.spec.transform, d2.spec.transform) { + return false + } + if !proto.Equal(d.spec.rustformation, d2.spec.rustformation) { + return false + } + + return true } type routePolicyPluginGwPass struct { + setTransformationInChain bool // TODO(nfuden): mae this multi stage + // TODO(nfuden): dont abuse httplevel filter in favor of route level + rustformationStash map[string]string ir.UnimplementedProxyTranslationPass setAIFilter bool } @@ -53,7 +91,11 @@ func (p *routePolicyPluginGwPass) ApplyHCM(ctx context.Context, pCtx *ir.HcmCont return nil } +var useRustformations bool + func NewPlugin(ctx context.Context, commoncol *common.CommonCollections) extensionplug.Plugin { + useRustformations = commoncol.Settings.UseRustFormations // stash the state of the env setup for rustformation usage + col := krtutil.SetupCollectionDynamic[v1alpha1.RoutePolicy]( ctx, commoncol.Client, @@ -106,6 +148,7 @@ func (p *routePolicy) Name() string { // called 1 time for each listener func (p *routePolicyPluginGwPass) ApplyListenerPlugin(ctx context.Context, pCtx *ir.ListenerContext, out *envoy_config_listener_v3.Listener) { + } func (p *routePolicyPluginGwPass) ApplyVhostPlugin(ctx context.Context, pCtx *ir.VirtualHostContext, out *envoy_config_route_v3.VirtualHost) { @@ -117,9 +160,71 @@ func (p *routePolicyPluginGwPass) ApplyForRoute(ctx context.Context, pCtx *ir.Ro if !ok { return nil } + if policy.spec.timeout != nil && outputRoute.GetRoute() != nil { + outputRoute.GetRoute().Timeout = policy.spec.timeout + } + + if policy.spec.transform != nil { + if outputRoute.GetTypedPerFilterConfig() == nil { + outputRoute.TypedPerFilterConfig = make(map[string]*anypb.Any) + } + if policy.spec.transform != nil { + outputRoute.GetTypedPerFilterConfig()[transformationFilterNamePrefix] = policy.spec.transform + } + p.setTransformationInChain = true + } + if policy.spec.rustformation != nil { + if outputRoute.GetTypedPerFilterConfig() == nil { + outputRoute.TypedPerFilterConfig = make(map[string]*anypb.Any) + } + // TODO(nfuden): get back to this path once we have valid perroute + // outputRoute.GetTypedPerFilterConfig()[rustformationFilterNamePrefix] = policy.spec.rustformation + + // Hack around not having route level. + // Note this is really really bad and rather fragile due to listener draining behaviors + routeHash := strconv.Itoa(int(utils.HashProto(outputRoute))) + if p.rustformationStash == nil { + p.rustformationStash = make(map[string]string) + } + // encode the configuration that would be route level and stash the serialized version in a map + p.rustformationStash[routeHash] = string(policy.spec.rustformationStringToStash) + + // augment the dynamic metadata so that we can do our route hack + // set_dynamic_metadata filter DOES NOT have a route level configuration + // set_filter_state can be used but the dynamic modules cannot access it on the current version of envoy + // therefore use the old transformation just for rustformation + reqm := &transformationpb.RouteTransformations_RouteTransformation_RequestMatch{ + RequestTransformation: &transformationpb.Transformation{ + TransformationType: &transformationpb.Transformation_TransformationTemplate{ + TransformationTemplate: &transformationpb.TransformationTemplate{ + ParseBodyBehavior: transformationpb.TransformationTemplate_DontParse, // Default is to try for JSON... Its kinda nice but failure is bad... + DynamicMetadataValues: []*transformationpb.TransformationTemplate_DynamicMetadataValue{ + &transformationpb.TransformationTemplate_DynamicMetadataValue{ + MetadataNamespace: "kgateway", + Key: "route", + Value: &transformationpb.InjaTemplate{ + Text: routeHash, + }, + }, + }, + }, + }, + }, + } + + setmetaTransform := &transformationpb.RouteTransformations{ + Transformations: []*transformationpb.RouteTransformations_RouteTransformation{ + { + + Match: &transformationpb.RouteTransformations_RouteTransformation_RequestMatch_{ + RequestMatch: reqm, + }, + }, + }, + } + outputRoute.GetTypedPerFilterConfig()["helper/perroute/transform"], _ = utils.MessageToAny(setmetaTransform) - if policy.spec.Timeout > 0 && outputRoute.GetRoute() != nil { - outputRoute.GetRoute().Timeout = durationpb.New(time.Second * time.Duration(policy.spec.Timeout)) + p.setTransformationInChain = true } // TODO: err/warn/ignore if targetRef is set on non-AI Backend @@ -170,7 +275,51 @@ func (p *routePolicyPluginGwPass) ApplyForRouteBackend( // if a plugin emits new filters, they must be with a plugin unique name. // any filter returned from route config must be disabled, so it doesnt impact other routes. func (p *routePolicyPluginGwPass) HttpFilters(ctx context.Context, fcc ir.FilterChainCommon) ([]plugins.StagedHttpFilter, error) { - return nil, nil + filters := []plugins.StagedHttpFilter{} + if p.setTransformationInChain { + // TODO(nfuden): support stages such as early + // first register classic + filters = append(filters, plugins.MustNewStagedFilter(transformationFilterNamePrefix, + &transformationpb.FilterTransformations{}, + plugins.BeforeStage(plugins.AcceptedStage))) + + // --------------- + // | END CLASSIC | + // --------------- + + // TODO(nfuden/yuvalk): how to do route level correctly probably contribute to dynamic module upstream + // smash together configuration + filterRouteHashConfig := map[string]string{} + + for k, v := range p.rustformationStash { + fmt.Println("k", k, "v", v) + filterRouteHashConfig[k] = v + } + + filterConfig, _ := json.Marshal(filterRouteHashConfig) + + rustCfg := dynamicmodulesv3.DynamicModuleFilter{ + DynamicModuleConfig: &exteniondynamicmodulev3.DynamicModuleConfig{ + Name: "rust_module", + }, + FilterName: "http_simple_mutations", + FilterConfig: fmt.Sprintf(`{"route_specific": %s}`, string(filterConfig)), + } + + filters = append(filters, plugins.MustNewStagedFilter(rustformationFilterNamePrefix, + &rustCfg, + plugins.BeforeStage(plugins.AcceptedStage))) + + // filters = append(filters, plugins.MustNewStagedFilter(setFilterStateFilterName, + // &set_filter_statev3.Config{}, plugins.AfterStage(plugins.FaultStage))) + filters = append(filters, plugins.MustNewStagedFilter("helper/perroute/transform", + &transformationpb.FilterTransformations{}, + plugins.AfterStage(plugins.FaultStage))) + } + if len(filters) == 0 { + return nil, nil + } + return filters, nil } func (p *routePolicyPluginGwPass) NetworkFilters(ctx context.Context) ([]plugins.StagedNetworkFilter, error) { @@ -184,30 +333,73 @@ func (p *routePolicyPluginGwPass) ResourcesToAdd(ctx context.Context) ir.Resourc func buildTranslateFunc(ctx context.Context, secrets *krtcollections.SecretIndex) func(krtctx krt.HandlerContext, i *v1alpha1.RoutePolicy) *routePolicy { return func(krtctx krt.HandlerContext, policyCR *v1alpha1.RoutePolicy) *routePolicy { - policyIr := routePolicy{ct: policyCR.CreationTimestamp.Time, spec: policyCR.Spec} - - // Check for the presence of the OpenAI Moderation which may require a secret reference - if policyCR.Spec.AI == nil || - policyCR.Spec.AI.PromptGuard == nil || - policyCR.Spec.AI.PromptGuard.Request == nil || - policyCR.Spec.AI.PromptGuard.Request.Moderation == nil { - return &policyIr + policyIr := routePolicy{ct: policyCR.CreationTimestamp.Time} + + outSpec := routeSpecIr{} + + if policyCR.Spec.Timeout > 0 { + outSpec.timeout = durationpb.New(time.Second * time.Duration(policyCR.Spec.Timeout)) } - secretRef := policyCR.Spec.AI.PromptGuard.Request.Moderation.OpenAIModeration.AuthToken.SecretRef - if secretRef == nil { - // no secret ref is set - return &policyIr + // Pass along the AI spec as is + outSpec.AI = policyCR.Spec.AI + // Augment with AI secrets as needed + policyIr.AISecret = aiSecretForSpec(ctx, secrets, krtctx, policyCR) + + // Apply transformation specific translation + transformationForSpec(policyCR.Spec, outSpec) + + for _, err := range outSpec.errors { + contextutils.LoggerFrom(ctx).Error(policyCR.GetNamespace(), policyCR.GetName(), err) } + policyIr.spec = outSpec - // Retrieve and assign the secret - secret, err := pluginutils.GetSecretIr(secrets, krtctx, secretRef.Name, policyCR.GetNamespace()) + return &policyIr + } +} + +// aiSecret checks for the presence of the OpenAI Moderation which may require a secret reference +// will log an error if the secret is needed but not found +func aiSecretForSpec( + ctx context.Context, secrets *krtcollections.SecretIndex, + krtctx krt.HandlerContext, policyCR *v1alpha1.RoutePolicy) *ir.Secret { + if policyCR.Spec.AI == nil || + policyCR.Spec.AI.PromptGuard == nil || + policyCR.Spec.AI.PromptGuard.Request == nil || + policyCR.Spec.AI.PromptGuard.Request.Moderation == nil { + return nil + } + + secretRef := policyCR.Spec.AI.PromptGuard.Request.Moderation.OpenAIModeration.AuthToken.SecretRef + if secretRef == nil { + // no secret ref is set + return nil + } + + // Retrieve and assign the secret + secret, err := pluginutils.GetSecretIr(secrets, krtctx, secretRef.Name, policyCR.GetNamespace()) + if err != nil { + contextutils.LoggerFrom(ctx).Error(err) + return nil + } + return secret +} + +// transformationForSpec translates the transformation spec into and onto the IR policy +func transformationForSpec(spec v1alpha1.RoutePolicySpec, out routeSpecIr) { + var err error + if !useRustformations { + out.transform, err = toTransformFilterConfig(&spec.Transformation) if err != nil { - contextutils.LoggerFrom(ctx).Error(err) - return &policyIr + out.errors = append(out.errors, err) } + return + } - policyIr.AISecret = secret - return &policyIr + rustformation, toStash, err := torustformFilterConfig(&spec.Transformation) + if err != nil { + out.errors = append(out.errors, err) } + out.rustformation = rustformation + out.rustformationStringToStash = string(toStash) } diff --git a/internal/kgateway/extensions2/plugins/routepolicy/transformation_plugin.go b/internal/kgateway/extensions2/plugins/routepolicy/transformation_plugin.go new file mode 100644 index 00000000000..e84746578bc --- /dev/null +++ b/internal/kgateway/extensions2/plugins/routepolicy/transformation_plugin.go @@ -0,0 +1,203 @@ +package routepolicy + +import ( + "encoding/json" + + // cncfcorev3 "github.com/cncf/xds/go/xds/core/v3" + // cncftypev3 "github.com/cncf/xds/go/xds/type/matcher/v3" + // v31 "github.com/cncf/xds/go/xds/type/matcher/v3" + // corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + // extensionmatcherv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/common/matching/v3" + exteniondynamicmodulev3 "github.com/envoyproxy/go-control-plane/envoy/extensions/dynamic_modules/v3" + dynamicmodulesv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_modules/v3" + transformationpb "github.com/solo-io/envoy-gloo/go/config/filter/http/transformation/v2" + "google.golang.org/protobuf/types/known/anypb" + + "github.com/kgateway-dev/kgateway/v2/api/v1alpha1" + "github.com/kgateway-dev/kgateway/v2/internal/kgateway/utils" +) + +func toTraditionalTransform(t *v1alpha1.Transform) *transformationpb.Transformation_TransformationTemplate { + if t == nil { + return nil + } + hasTransform := false + tt := &transformationpb.Transformation_TransformationTemplate{ + TransformationTemplate: &transformationpb.TransformationTemplate{ + Headers: map[string]*transformationpb.InjaTemplate{}, + }, + } + for _, h := range t.Set { + tt.TransformationTemplate.GetHeaders()[string(h.Name)] = &transformationpb.InjaTemplate{ + Text: string(h.Value), + } + tt.TransformationTemplate.ParseBodyBehavior = transformationpb.TransformationTemplate_DontParse + hasTransform = true + } + + for _, h := range t.Add { + tt.TransformationTemplate.HeadersToAppend = append(tt.TransformationTemplate.GetHeadersToAppend(), &transformationpb.TransformationTemplate_HeaderToAppend{ + Key: string(h.Name), + Value: &transformationpb.InjaTemplate{ + Text: string(h.Value), + }, + }) + tt.TransformationTemplate.ParseBodyBehavior = transformationpb.TransformationTemplate_DontParse + hasTransform = true + } + + tt.TransformationTemplate.HeadersToRemove = make([]string, 0, len(t.Remove)) + for _, h := range t.Remove { + tt.TransformationTemplate.HeadersToRemove = append(tt.TransformationTemplate.GetHeadersToRemove(), string(h)) + hasTransform = true + } + + //BODY + if t.Body == nil { + tt.TransformationTemplate.BodyTransformation = &transformationpb.TransformationTemplate_Passthrough{ + Passthrough: &transformationpb.Passthrough{}, + } + } else { + if t.Body.ParseAs == v1alpha1.BodyParseBehaviorAsString { + tt.TransformationTemplate.ParseBodyBehavior = transformationpb.TransformationTemplate_DontParse + } + if value := t.Body.Value; value != nil { + hasTransform = true + tt.TransformationTemplate.BodyTransformation = &transformationpb.TransformationTemplate_Body{ + Body: &transformationpb.InjaTemplate{ + Text: string(*value), + }, + } + } + } + + if !hasTransform { + return nil + } + return tt +} + +func toTransformFilterConfig(t *v1alpha1.TransformationPolicy) (*anypb.Any, error) { + if t == nil || *t == (v1alpha1.TransformationPolicy{}) { + return nil, nil + } + + var reqt *transformationpb.Transformation + var respt *transformationpb.Transformation + + if rtt := toTraditionalTransform(t.Request); rtt != nil { + reqt = &transformationpb.Transformation{ + TransformationType: rtt, + } + } + if rtt := toTraditionalTransform(t.Response); rtt != nil { + respt = &transformationpb.Transformation{ + TransformationType: rtt, + } + } + if reqt == nil && respt == nil { + return nil, nil + } + + reqm := &transformationpb.RouteTransformations_RouteTransformation_RequestMatch{ + RequestTransformation: reqt, + ResponseTransformation: respt, + } + + envoyT := &transformationpb.RouteTransformations{ + Transformations: []*transformationpb.RouteTransformations_RouteTransformation{ + { + + Match: &transformationpb.RouteTransformations_RouteTransformation_RequestMatch_{ + RequestMatch: reqm, + }, + }, + }, + } + return utils.MessageToAny(envoyT) +} + +func toRustFormationPerRouteConfig(t *v1alpha1.Transform) (map[string]interface{}, bool) { + // if there is no transformations present then return a + hasTransform := false + rustformationConfigMap := map[string]interface{}{} + if t == nil { + return rustformationConfigMap, hasTransform + } + + // we dont currently have strongly typed objects in rustformation + setter := make([][2]string, 0, len(t.Set)/2) + for _, h := range t.Set { + setter = append(setter, [2]string{string(h.Name), string(h.Value)}) + } + + rustformationConfigMap["headers_setter"] = setter + if len(setter) > 0 { + hasTransform = true + } + + //BODY + // if t.Body == nil { + // tt.TransformationTemplate.BodyTransformation = &transformationpb.TransformationTemplate_Passthrough{ + // Passthrough: &transformationpb.Passthrough{}, + // } + // } else { + // if t.Body.ParseAs == v1alpha1.BodyParseBehaviorAsString { + // tt.TransformationTemplate.ParseBodyBehavior = transformationpb.TransformationTemplate_DontParse + // } + // if value := t.Body.Value; value != nil { + // hasTransform = true + // tt.TransformationTemplate.BodyTransformation = &transformationpb.TransformationTemplate_Body{ + // Body: &transformationpb.InjaTemplate{ + // Text: string(*value), + // }, + // } + // } + // } + return rustformationConfigMap, hasTransform +} + +// torustformFilterConfig converts a TransformationPolicy to a RustFormation filter config. +// The sheape of this function currently resembles that of the traditional API +// Feel free to change the shape and flow of this function as needed provided there are sufficient unit tests on the configuration output. +// The most dangerous updates here will be any switch over env variables that we are working on.s +func torustformFilterConfig(t *v1alpha1.TransformationPolicy) (*anypb.Any, string, error) { + if t == nil || *t == (v1alpha1.TransformationPolicy{}) { + return nil, "", nil + } + hasTransform := false + rustformCfgMap := map[string]interface{}{} + + requestMap, hasRequestTransform := toRustFormationPerRouteConfig(t.Request) + hasTransform = hasTransform || hasRequestTransform + for k, v := range requestMap { + rustformCfgMap["request_"+k] = v + } + + requestMap, hasResponseTransform := toRustFormationPerRouteConfig(t.Response) + hasTransform = hasTransform || hasResponseTransform + for k, v := range requestMap { + rustformCfgMap["response_"+k] = v + } + + if !hasTransform { + return nil, "", nil + } + + rustformationJson, err := json.Marshal(rustformCfgMap) + if err != nil { + return nil, "", err + } + + stringConf := string(rustformationJson) + rustCfg := dynamicmodulesv3.DynamicModuleFilter{ + DynamicModuleConfig: &exteniondynamicmodulev3.DynamicModuleConfig{ + Name: "rust_module", + }, + FilterName: "http_simple_mutations", + FilterConfig: stringConf, + } + rustCfgAny, _ := utils.MessageToAny(&rustCfg) + + return rustCfgAny, stringConf, nil +} diff --git a/internal/kgateway/extensions2/settings/settings.go b/internal/kgateway/extensions2/settings/settings.go index d638586a30f..07d56ae5e44 100644 --- a/internal/kgateway/extensions2/settings/settings.go +++ b/internal/kgateway/extensions2/settings/settings.go @@ -25,6 +25,8 @@ type Settings struct { // XdsServicePort is the port of the Kubernetes Service that serves xDS config. // This corresponds to the value of the `grpc-xds` port in the service. XdsServicePort uint32 `split_words:"true" default:"9977"` + + UseRustFormations bool `split_words:"true" default:"false"` } // BuildSettings returns a zero-valued Settings obj if error is encountered when parsing env diff --git a/internal/kgateway/extensions2/settings/settings_test.go b/internal/kgateway/extensions2/settings/settings_test.go index c3c1fba67e9..b4744b6afcf 100644 --- a/internal/kgateway/extensions2/settings/settings_test.go +++ b/internal/kgateway/extensions2/settings/settings_test.go @@ -37,6 +37,7 @@ func TestSettings(t *testing.T) { StsUri: "", XdsServiceName: wellknown.DefaultXdsService, XdsServicePort: wellknown.DefaultXdsPort, + UseRustFormations: false, }, }, { @@ -49,6 +50,7 @@ func TestSettings(t *testing.T) { "KGW_STS_URI": "my.sts.uri", "KGW_XDS_SERVICE_NAME": "custom-svc", "KGW_XDS_SERVICE_PORT": "1234", + "KGW_USE_RUST_FORMATIONS": "true", }, expectedSettings: &settings.Settings{ DnsLookupFamily: "V4_ONLY", @@ -58,6 +60,7 @@ func TestSettings(t *testing.T) { StsUri: "my.sts.uri", XdsServiceName: "custom-svc", XdsServicePort: 1234, + UseRustFormations: true, }, }, { diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 16f904e8df3..b66864e4ea1 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -34,6 +34,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.BackendList": schema_kgateway_v2_api_v1alpha1_BackendList(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.BackendSpec": schema_kgateway_v2_api_v1alpha1_BackendSpec(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.BackendStatus": schema_kgateway_v2_api_v1alpha1_BackendStatus(ref), + "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.BodyTransformation": schema_kgateway_v2_api_v1alpha1_BodyTransformation(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.CELFilter": schema_kgateway_v2_api_v1alpha1_CELFilter(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.ComparisonFilter": schema_kgateway_v2_api_v1alpha1_ComparisonFilter(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.CustomLabel": schema_kgateway_v2_api_v1alpha1_CustomLabel(ref), @@ -60,6 +61,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.HTTPListenerPolicyList": schema_kgateway_v2_api_v1alpha1_HTTPListenerPolicyList(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.HTTPListenerPolicySpec": schema_kgateway_v2_api_v1alpha1_HTTPListenerPolicySpec(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.HeaderFilter": schema_kgateway_v2_api_v1alpha1_HeaderFilter(ref), + "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.HeaderTransformation": schema_kgateway_v2_api_v1alpha1_HeaderTransformation(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.Host": schema_kgateway_v2_api_v1alpha1_Host(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.Image": schema_kgateway_v2_api_v1alpha1_Image(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.IstioContainer": schema_kgateway_v2_api_v1alpha1_IstioContainer(ref), @@ -97,6 +99,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.StatsConfig": schema_kgateway_v2_api_v1alpha1_StatsConfig(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.StatusCodeFilter": schema_kgateway_v2_api_v1alpha1_StatusCodeFilter(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.SupportedLLMProvider": schema_kgateway_v2_api_v1alpha1_SupportedLLMProvider(ref), + "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.Transform": schema_kgateway_v2_api_v1alpha1_Transform(ref), + "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.TransformationPolicy": schema_kgateway_v2_api_v1alpha1_TransformationPolicy(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.VertexAIConfig": schema_kgateway_v2_api_v1alpha1_VertexAIConfig(ref), "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.Webhook": schema_kgateway_v2_api_v1alpha1_Webhook(ref), "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource": schema_k8sio_api_core_v1_AWSElasticBlockStoreVolumeSource(ref), @@ -1244,6 +1248,35 @@ func schema_kgateway_v2_api_v1alpha1_BackendStatus(ref common.ReferenceCallback) } } +func schema_kgateway_v2_api_v1alpha1_BodyTransformation(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BodyTransformation controls how the body should be parsed and transformed.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "parseAs": { + SchemaProps: spec.SchemaProps{ + Description: "ParseAs defines what auto formatting should be applied to the body. This can make interacting with keys within a json body much easier if AsJson is selected.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value is the template to apply to generate the output value for the body.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"parseAs"}, + }, + }, + } +} + func schema_kgateway_v2_api_v1alpha1_CELFilter(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -2193,6 +2226,33 @@ func schema_kgateway_v2_api_v1alpha1_HeaderFilter(ref common.ReferenceCallback) } } +func schema_kgateway_v2_api_v1alpha1_HeaderTransformation(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of the header to interact with.", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value is the template to apply to generate the output value for the header.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + } +} + func schema_kgateway_v2_api_v1alpha1_Host(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -3307,11 +3367,17 @@ func schema_kgateway_v2_api_v1alpha1_RoutePolicySpec(ref common.ReferenceCallbac Ref: ref("github.com/kgateway-dev/kgateway/v2/api/v1alpha1.AIRoutePolicy"), }, }, + "transformation": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/kgateway-dev/kgateway/v2/api/v1alpha1.TransformationPolicy"), + }, + }, }, }, }, Dependencies: []string{ - "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.AIRoutePolicy", "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.LocalPolicyTargetReference"}, + "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.AIRoutePolicy", "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.LocalPolicyTargetReference", "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.TransformationPolicy"}, } } @@ -3661,6 +3727,116 @@ func schema_kgateway_v2_api_v1alpha1_SupportedLLMProvider(ref common.ReferenceCa } } +func schema_kgateway_v2_api_v1alpha1_Transform(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Transform defines the operations to be performed by the transformation. These operations may include changing the actual request/response but may also cause side effects. Side effects may include setting info that can be used in future steps (e.g. dynamic metadata) and can cause envoy to buffer.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "set": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Set is a list of headers and the value they should be set to.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/kgateway-dev/kgateway/v2/api/v1alpha1.HeaderTransformation"), + }, + }, + }, + }, + }, + "add": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "name", + }, + "x-kubernetes-list-type": "map", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Add is a list of headers to add to the request and what that value should be set to. If there is already a header with these values then append the value as an extra entry.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/kgateway-dev/kgateway/v2/api/v1alpha1.HeaderTransformation"), + }, + }, + }, + }, + }, + "remove": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "set", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Remove is a list of header names to remove from the request/response.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "body": { + SchemaProps: spec.SchemaProps{ + Description: "Body controls both how to parse the body and if needed how to set.\n\nIf empty, body will not be buffered.", + Ref: ref("github.com/kgateway-dev/kgateway/v2/api/v1alpha1.BodyTransformation"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.BodyTransformation", "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.HeaderTransformation"}, + } +} + +func schema_kgateway_v2_api_v1alpha1_TransformationPolicy(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TransformationPolicy config is used to modify envoy behavior at a route level. These modifications can be performed on the request and response paths.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "request": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/kgateway-dev/kgateway/v2/api/v1alpha1.Transform"), + }, + }, + "response": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/kgateway-dev/kgateway/v2/api/v1alpha1.Transform"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/kgateway-dev/kgateway/v2/api/v1alpha1.Transform"}, + } +} + func schema_kgateway_v2_api_v1alpha1_VertexAIConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/utils/kubeutils/portforward/options.go b/pkg/utils/kubeutils/portforward/options.go index ef7032d95c4..c4b78fd5e7b 100644 --- a/pkg/utils/kubeutils/portforward/options.go +++ b/pkg/utils/kubeutils/portforward/options.go @@ -68,11 +68,16 @@ func WithResource(name, namespace, resourceType string) Option { } } +// WithRemotePort sets the remote port for the port-forwarding +// This overrides the local port and makes it set as random. +// Retrieve the allocated port with the Address() method on the PortForwarder. func WithRemotePort(remotePort int) Option { // 0 is special value for the local port, it will result in a port being chosen at random return WithPorts(0, remotePort) } +// WithPorts sets the local and remote ports for the port-forwarding +// Prefer WithRemotePort for local tests to prevent collisions. func WithPorts(localPort, remotePort int) Option { return func(config *properties) { config.localPort = localPort diff --git a/test/kubernetes/e2e/features/transformation/suite.go b/test/kubernetes/e2e/features/transformation/suite.go new file mode 100644 index 00000000000..0bdf426e14b --- /dev/null +++ b/test/kubernetes/e2e/features/transformation/suite.go @@ -0,0 +1,248 @@ +package transformation + +import ( + "context" + "fmt" + "net/http" + "time" + + "strings" + + "github.com/onsi/gomega" + "github.com/stretchr/testify/suite" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/kgateway-dev/kgateway/v2/pkg/utils/kubeutils" + "github.com/kgateway-dev/kgateway/v2/pkg/utils/requestutils/curl" + testmatchers "github.com/kgateway-dev/kgateway/v2/test/gomega/matchers" + "github.com/kgateway-dev/kgateway/v2/test/kubernetes/e2e" + testdefaults "github.com/kgateway-dev/kgateway/v2/test/kubernetes/e2e/defaults" + + envoyadmincli "github.com/kgateway-dev/kgateway/v2/pkg/utils/envoyutils/admincli" +) + +var _ e2e.NewSuiteFunc = NewTestingSuite + +// testingSuite is a suite of basic routing / "happy path" tests +type testingSuite struct { + suite.Suite + + ctx context.Context + + // testInstallation contains all the metadata/utilities necessary to execute a series of tests + // against an installation of kgateway + testInstallation *e2e.TestInstallation +} + +func NewTestingSuite(ctx context.Context, testInst *e2e.TestInstallation) suite.TestingSuite { + return &testingSuite{ + ctx: ctx, + testInstallation: testInst, + } +} + +func (s *testingSuite) TestGatewayWithTransformedRoute() { + manifests := []string{ + testdefaults.CurlPodManifest, + simpleServiceManifest, + gatewayWithRouteManifest, + } + manifestObjects := []client.Object{ + testdefaults.CurlPod, // curl + simpleSvc, // echo service + proxyService, proxyServiceAccount, proxyDeployment, // proxy + } + + s.T().Cleanup(func() { + for _, manifest := range manifests { + err := s.testInstallation.Actions.Kubectl().DeleteFileSafe(s.ctx, manifest) + s.Require().NoError(err) + } + s.testInstallation.Assertions.EventuallyObjectsNotExist(s.ctx, manifestObjects...) + }) + + for _, manifest := range manifests { + err := s.testInstallation.Actions.Kubectl().ApplyFile(s.ctx, manifest) + s.Require().NoError(err) + } + s.testInstallation.Assertions.EventuallyObjectsExist(s.ctx, manifestObjects...) + + // make sure pods are running + s.testInstallation.Assertions.EventuallyPodsRunning(s.ctx, testdefaults.CurlPod.GetNamespace(), metav1.ListOptions{ + LabelSelector: "app.kubernetes.io/name=curl", + }) + + s.testInstallation.Assertions.EventuallyPodsRunning(s.ctx, proxyObjectMeta.GetNamespace(), metav1.ListOptions{ + LabelSelector: "app.kubernetes.io/name=gw", + }) + + testCasess := []struct { + name string + opts []curl.Option + resp *testmatchers.HttpResponse + }{ + { + name: "basic", + opts: []curl.Option{ + curl.WithBody("hello"), + }, + resp: &testmatchers.HttpResponse{ + StatusCode: http.StatusOK, + Headers: map[string]interface{}{ + "x-foo-response": "notsuper", + }, + }, + }, + { + name: "conditional set by request header", // inja and the request_header function in use + opts: []curl.Option{ + curl.WithBody("hello"), + curl.WithHeader("x-add-bar", "super"), + }, + resp: &testmatchers.HttpResponse{ + StatusCode: http.StatusOK, + Headers: map[string]interface{}{ + "x-foo-response": "supersupersuper", + }, + }, + }, + } + for _, tc := range testCasess { + s.testInstallation.Assertions.AssertEventualCurlResponse( + s.ctx, + testdefaults.CurlPodExecOpt, + append(tc.opts, + curl.WithHost(kubeutils.ServiceFQDN(proxyObjectMeta)), + curl.WithHostHeader("example.com"), + curl.WithPort(8080), + ), + tc.resp) + } +} + +func (s *testingSuite) TestGatewayRustformationsWithTransformedRoute() { + manifests := []string{ + testdefaults.CurlPodManifest, + simpleServiceManifest, + gatewayWithRouteManifest, + } + manifestObjects := []client.Object{ + testdefaults.CurlPod, // curl + simpleSvc, // echo service + proxyService, proxyServiceAccount, proxyDeployment, // proxy + } + + controllerDeploymentOriginal := &appsv1.Deployment{} + err := s.testInstallation.ClusterContext.Client.Get(s.ctx, client.ObjectKey{ + Namespace: s.testInstallation.Metadata.InstallNamespace, + Name: "kgateway", + }, controllerDeploymentOriginal) + s.Assert().NoError(err, "has controller deploymnet") + + controllerDeploy := controllerDeploymentOriginal.DeepCopy() + // add the environment variable RUSTFORMATIONS to the controller deployment + + env := append(controllerDeploy.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{ + Name: "KGW_USE_RUST_FORMATIONS", + Value: "true", + }) + containers := controllerDeploy.Spec.Template.Spec.Containers + containers[0].Env = env + controllerDeploy.Spec.Template.Spec.Containers = containers + + // patch the actual deployment with the new environment variable + err = s.testInstallation.ClusterContext.Client.Patch(s.ctx, controllerDeploy, client.MergeFrom(controllerDeploymentOriginal)) + s.Assert().NoError(err, "patching controller deployment") + + s.T().Cleanup(func() { + for _, manifest := range manifests { + err := s.testInstallation.Actions.Kubectl().DeleteFileSafe(s.ctx, manifest) + s.Require().NoError(err) + } + s.testInstallation.Assertions.EventuallyObjectsNotExist(s.ctx, manifestObjects...) + err = s.testInstallation.ClusterContext.Client.Patch(s.ctx, controllerDeploy, client.MergeFrom(controllerDeploy)) + s.Require().NoError(err) + }) + + for _, manifest := range manifests { + err := s.testInstallation.Actions.Kubectl().ApplyFile(s.ctx, manifest) + s.Require().NoError(err) + } + s.testInstallation.Assertions.EventuallyObjectsExist(s.ctx, manifestObjects...) + + // make sure pods are running + s.testInstallation.Assertions.EventuallyPodsRunning(s.ctx, testdefaults.CurlPod.GetNamespace(), metav1.ListOptions{ + LabelSelector: "app.kubernetes.io/name=curl", + }) + + s.testInstallation.Assertions.EventuallyPodsRunning(s.ctx, proxyObjectMeta.GetNamespace(), metav1.ListOptions{ + LabelSelector: "app.kubernetes.io/name=gw", + }) + + s.testInstallation.Assertions.EventuallyPodsRunning(s.ctx, s.testInstallation.Metadata.InstallNamespace, metav1.ListOptions{ + LabelSelector: "app.kubernetes.io/name=kgateway", + }) + + adminClient, closeFwd, err := envoyadmincli.NewPortForwardedClient(s.ctx, "deploy/"+proxyObjectMeta.Name, proxyObjectMeta.Namespace) + s.Assert().NoError(err, "get admin cli for envoy") + + s.testInstallation.Assertions.Gomega.Eventually(func(g gomega.Gomega) { + listener, err := adminClient.GetSingleListenerFromDynamicListeners(context.Background(), "http") + g.Expect(err).ToNot(gomega.HaveOccurred(), "failed to get listener") + + // use a weak filter name check for cyclic imports + // also we dont intend for this to be long term so dont worry about pulling it out to wellknown or something like that for now + dynamicModuleLoaded := strings.Contains(listener.String(), "dynamic_modules/") + g.Expect(dynamicModuleLoaded).To(gomega.BeTrue(), fmt.Sprintf("dynamic module not loaded: %v", listener.String())) + }). + WithTimeout(time.Second*20). + WithPolling(time.Second).Should(gomega.Succeed(), "failed to load in dynamic modules") + + closeFwd() + + testCasess := []struct { + name string + opts []curl.Option + resp *testmatchers.HttpResponse + }{ + { + name: "basic", + opts: []curl.Option{ + curl.WithBody("hello"), + }, + resp: &testmatchers.HttpResponse{ + StatusCode: http.StatusOK, + Headers: map[string]interface{}{ + "x-foo-response": "notsuper", + }, + }, + }, + { + name: "conditional set by request header", // inja and the request_header function in use + opts: []curl.Option{ + curl.WithBody("hello"), + curl.WithHeader("x-add-bar", "super"), + }, + resp: &testmatchers.HttpResponse{ + StatusCode: http.StatusOK, + Headers: map[string]interface{}{ + "x-foo-response": "supersupersuper", + }, + }, + }, + } + for _, tc := range testCasess { + s.testInstallation.Assertions.AssertEventualCurlResponse( + s.ctx, + testdefaults.CurlPodExecOpt, + append(tc.opts, + curl.WithHost(kubeutils.ServiceFQDN(proxyObjectMeta)), + curl.WithHostHeader("example.com"), + curl.WithPort(8080), + ), + tc.resp) + } +} diff --git a/test/kubernetes/e2e/features/transformation/testdata/gateway-with-transformed-route.yaml b/test/kubernetes/e2e/features/transformation/testdata/gateway-with-transformed-route.yaml new file mode 100644 index 00000000000..6c69ffa4d57 --- /dev/null +++ b/test/kubernetes/e2e/features/transformation/testdata/gateway-with-transformed-route.yaml @@ -0,0 +1,47 @@ +kind: Gateway +apiVersion: gateway.networking.k8s.io/v1 +metadata: + name: gw +spec: + gatewayClassName: kgateway + listeners: + - protocol: HTTP + port: 8080 + name: http + allowedRoutes: + namespaces: + from: Same +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: example-route +spec: + parentRefs: + - name: gw + hostnames: + - "example.com" + rules: + - backendRefs: + - name: simple-svc + port: 8080 +--- +apiVersion: gateway.kgateway.dev/v1alpha1 +kind: RoutePolicy +metadata: + name: requestresponse-transformer +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: example-route + timeout: 1 + transformation: + request: + set: + - name: x-foo-bar + value: "foolen_{{header(\"content-length\")}}" + response: + set: + - name: x-foo-response + value: "{%- if request_header(\"x-add-bar\") != \"\" -%}supersuper{{request_header(\"x-add-bar\")}}{% else %}notsuper{% endif %}" \ No newline at end of file diff --git a/test/kubernetes/e2e/features/transformation/testdata/service.yaml b/test/kubernetes/e2e/features/transformation/testdata/service.yaml new file mode 100644 index 00000000000..aa217820cc0 --- /dev/null +++ b/test/kubernetes/e2e/features/transformation/testdata/service.yaml @@ -0,0 +1,47 @@ +apiVersion: v1 +kind: Service +metadata: + name: simple-svc + labels: + app: simple-svc +spec: + ports: + - name: http + port: 8080 + targetPort: 3000 + selector: + app: backend-0 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backend-0 +spec: + replicas: 1 + selector: + matchLabels: + app: backend-0 + version: v1 + template: + metadata: + labels: + app: backend-0 + version: v1 + spec: + containers: + - image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e + imagePullPolicy: IfNotPresent + name: backend-0 + ports: + - containerPort: 3000 + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SERVICE_NAME + value: simple-svc diff --git a/test/kubernetes/e2e/features/transformation/types.go b/test/kubernetes/e2e/features/transformation/types.go new file mode 100644 index 00000000000..9bd059e1c8b --- /dev/null +++ b/test/kubernetes/e2e/features/transformation/types.go @@ -0,0 +1,33 @@ +package transformation + +import ( + "path/filepath" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/kgateway-dev/kgateway/v2/pkg/utils/fsutils" +) + +var ( + // manifests + simpleServiceManifest = filepath.Join(fsutils.MustGetThisDir(), "testdata", "service.yaml") + gatewayWithRouteManifest = filepath.Join(fsutils.MustGetThisDir(), "testdata", "gateway-with-transformed-route.yaml") + + // objects + proxyObjectMeta = metav1.ObjectMeta{ + Name: "gw", + Namespace: "default", + } + proxyDeployment = &appsv1.Deployment{ObjectMeta: proxyObjectMeta} + proxyService = &corev1.Service{ObjectMeta: proxyObjectMeta} + proxyServiceAccount = &corev1.ServiceAccount{ObjectMeta: proxyObjectMeta} + + simpleSvc = &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "simple-svc", + Namespace: "default", + }, + } +) diff --git a/test/kubernetes/e2e/tests/kgateway_tests.go b/test/kubernetes/e2e/tests/kgateway_tests.go index 5b4ac4dc64c..1544d5c7a57 100644 --- a/test/kubernetes/e2e/tests/kgateway_tests.go +++ b/test/kubernetes/e2e/tests/kgateway_tests.go @@ -3,6 +3,7 @@ package tests import ( "github.com/kgateway-dev/kgateway/v2/test/kubernetes/e2e" "github.com/kgateway-dev/kgateway/v2/test/kubernetes/e2e/features/basicrouting" + "github.com/kgateway-dev/kgateway/v2/test/kubernetes/e2e/features/transformation" // "github.com/kgateway-dev/kgateway/v2/test/kubernetes/e2e/features/admin_server" // "github.com/kgateway-dev/kgateway/v2/test/kubernetes/e2e/features/crd_categories" @@ -27,11 +28,13 @@ func KubeGatewaySuiteRunner() e2e.SuiteRunner { kubeGatewaySuiteRunner.Register("BasicRouting", basicrouting.NewTestingSuite) kubeGatewaySuiteRunner.Register("Deployer", deployer.NewTestingSuite) + kubeGatewaySuiteRunner.Register("Transforms", transformation.NewTestingSuite) // kubeGatewaySuiteRunner.Register("HttpListenerOptions", http_listener_options.NewTestingSuite) // kubeGatewaySuiteRunner.Register("ListenerOptions", listener_options.NewTestingSuite) // kubeGatewaySuiteRunner.Register("RouteOptions", route_options.NewTestingSuite) // kubeGatewaySuiteRunner.Register("VirtualHostOptions", virtualhost_options.NewTestingSuite) + kubeGatewaySuiteRunner.Register("Backends", backends.NewTestingSuite) kubeGatewaySuiteRunner.Register("HTTPRouteServices", httproute.NewTestingSuite) kubeGatewaySuiteRunner.Register("TCPRouteServices", tcproute.NewTestingSuite) diff --git a/test/kubernetes/testutils/assertions/envoy.go b/test/kubernetes/testutils/assertions/envoy.go index 3fb34a29d4f..430fc34e05a 100644 --- a/test/kubernetes/testutils/assertions/envoy.go +++ b/test/kubernetes/testutils/assertions/envoy.go @@ -23,7 +23,7 @@ func (p *Provider) AssertEnvoyAdminApi( portForwarder, err := p.clusterContext.Cli.StartPortForward(ctx, portforward.WithDeployment(envoyDeployment.GetName(), envoyDeployment.GetNamespace()), - portforward.WithPorts(int(wellknown.EnvoyAdminPort), int(wellknown.EnvoyAdminPort)), + portforward.WithRemotePort(int(wellknown.EnvoyAdminPort)), ) p.Require.NoError(err, "can open port-forward") defer func() { @@ -35,7 +35,7 @@ func (p *Provider) AssertEnvoyAdminApi( WithReceiver(io.Discard). // adminAssertion can overwrite this WithCurlOptions( curl.WithRetries(3, 0, 10), - curl.WithPort(int(wellknown.EnvoyAdminPort)), + curl.WithHostPort(portForwarder.Address()), ) for _, adminAssertion := range adminAssertions { From 07c0dfc3a5469557a674a38bf83c0ad3e5635ac9 Mon Sep 17 00:00:00 2001 From: Jenny Shu <28537278+jenshu@users.noreply.github.com> Date: Fri, 7 Mar 2025 11:22:22 -0500 Subject: [PATCH 7/7] Clear disk space before release (#10775) Signed-off-by: Jenny Shu --- .github/actions/prep-go-runner/action.yaml | 42 +++++++++++++++++----- .github/workflows/release.yaml | 17 ++++----- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/.github/actions/prep-go-runner/action.yaml b/.github/actions/prep-go-runner/action.yaml index 434db0e8649..e7e02cff57a 100644 --- a/.github/actions/prep-go-runner/action.yaml +++ b/.github/actions/prep-go-runner/action.yaml @@ -20,19 +20,45 @@ runs: run: | echo "Before clearing disk space:" df -h + docker system df -v - # https://github.com/actions/virtual-environments/issues/709 - sudo apt-get clean - + # https://github.com/actions/runner-images/discussions/3242 github runners are bad at cleanup + echo "Removing large packages" + sudo apt-get remove -y '^dotnet-.*' || true + sudo apt-get remove -y '^llvm-.*' || true + sudo apt-get remove -y 'php.*' || true + sudo apt-get remove -y '^mongodb-.*' || true + sudo apt-get remove -y '^mysql-.*' || true + sudo apt-get remove -y azure-cli google-chrome-stable firefox powershell mono-devel libgl1-mesa-dri || true + sudo apt-get autoremove -y || true + sudo apt-get clean -y || true + echo "Done removing large packages" + # Clean up pre-installed tools - # https://github.com/actions/virtual-environments/issues/1918 - sudo rm -rf /usr/share/dotnet - sudo rm -rf /opt/ghc - sudo rm -rf /usr/local/share/boost - sudo rm -rf $AGENT_TOOLSDIRECTORY + sudo rm -rf /usr/local/lib/android || true + sudo rm -rf /usr/share/dotnet || true + sudo rm -rf /usr/local/graalvm || true + sudo rm -rf /opt/ghc || true + sudo rm -rf /usr/local/.ghcup || true + sudo rm -rf /usr/local/share/boost || true + sudo rm -rf /usr/local/share/powershell || true + sudo rm -rf /usr/local/share/chromium || true + sudo rm -rf $AGENT_TOOLSDIRECTORY || true + + # Clean up images + docker image rm node:16 || true + docker image rm node:16-alpine || true + docker image rm node:18 || true + docker image rm node:18-alpine || true + docker image rm node:20 || true + docker image rm node:20-alpine || true + # remove the dangling images and containers + docker images | tail -n +2 | awk '$1 == "" {print $3}' | xargs --no-run-if-empty docker rmi + docker ps -a | tail -n +2 | awk '$2 ~ "^[0-9a-f]+$" {print $1}' | xargs --no-run-if-empty docker rm --volumes=true echo "After clearing disk space:" df -h + docker system df -v - name: Set up Go id: setup-go uses: actions/setup-go@v5 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 3c919e06c36..976d3756a33 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -39,7 +39,6 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Set the release related variables id: set_vars run: | @@ -79,6 +78,9 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Prep Go Runner + uses: ./.github/actions/prep-go-runner + - name: Helm login to ${{ env.IMAGE_REGISTRY }} if: ${{ github.event_name != 'pull_request' }} run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ${{ env.IMAGE_REGISTRY }} -u ${{ github.repository_owner }} --password-stdin @@ -105,11 +107,8 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - - uses: actions/setup-go@v5 - with: - go-version-file: "go.mod" - cache: true + - name: Prep Go Runner + uses: ./.github/actions/prep-go-runner # We publish a rolling main release for every commit to main. Deleting the release # ensures that the tagged commit is not stale. Goreleaser will create a new tag @@ -151,10 +150,8 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - - uses: actions/setup-go@v5 - with: - go-version-file: "go.mod" + - name: Prep Go Runner + uses: ./.github/actions/prep-go-runner - name: Login to ghcr.io if: ${{ github.event_name != 'pull_request' }}