Skip to content

Commit 891fd13

Browse files
authored
Extend textual configuration support with the Datalayer's configuration (#1914)
* Added datalayer config to text config definition Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Added datalayer config to EPP config Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Load datalayer config from text to EPP config Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Updated tests for datalayer config Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Updated configuration documentation Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Refactored configuration DataLayer Extractor elements Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Test data updates due to configuration refactoring Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Documentation updates due to comments from the review Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Removed V2 from errors and descriptions WRT the Datalayer Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Ensure there is no Datalayer config if it isn't enabled Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Debug build failure Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Removed debugging statement Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Add verbose to failing command Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Remove verbose from formerly failing command Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> * Update from review comment Signed-off-by: Shmuel Kallner <kallner@il.ibm.com> --------- Signed-off-by: Shmuel Kallner <kallner@il.ibm.com>
1 parent 68a5612 commit 891fd13

File tree

8 files changed

+345
-21
lines changed

8 files changed

+345
-21
lines changed

apix/config/v1alpha1/endpointpickerconfig_types.go

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ import (
3030
type EndpointPickerConfig struct {
3131
metav1.TypeMeta `json:",inline"`
3232

33+
// +optional
34+
// FeatureGates is a set of flags that enable various experimental features with the EPP.
35+
// If omitted non of these experimental features will be enabled.
36+
FeatureGates FeatureGates `json:"featureGates,omitempty"`
37+
3338
// +required
3439
// +kubebuilder:validation:Required
3540
// Plugins is the list of plugins that will be instantiated.
@@ -41,23 +46,23 @@ type EndpointPickerConfig struct {
4146
// that will be created.
4247
SchedulingProfiles []SchedulingProfile `json:"schedulingProfiles"`
4348

44-
// +optional
45-
// FeatureGates is a set of flags that enable various experimental features with the EPP.
46-
// If omitted non of these experimental features will be enabled.
47-
FeatureGates FeatureGates `json:"featureGates,omitempty"`
48-
4949
// +optional
5050
// SaturationDetector when present specifies the configuration of the
5151
// Saturation detector. If not present, default values are used.
5252
SaturationDetector *SaturationDetector `json:"saturationDetector,omitempty"`
53+
54+
// +optional
55+
// Data configures the DataLayer. It is required if the new DataLayer is enabled.
56+
Data *DataLayerConfig `json:"data"`
5357
}
5458

5559
func (cfg EndpointPickerConfig) String() string {
5660
return fmt.Sprintf(
57-
"{Plugins: %v, SchedulingProfiles: %v, FeatureGates: %v, SaturationDetector: %v}",
61+
"{FeatureGates: %v, Plugins: %v, SchedulingProfiles: %v, Data: %v, SaturationDetector: %v}",
62+
cfg.FeatureGates,
5863
cfg.Plugins,
5964
cfg.SchedulingProfiles,
60-
cfg.FeatureGates,
65+
cfg.Data,
6166
cfg.SaturationDetector,
6267
)
6368
}
@@ -193,3 +198,50 @@ func (sd *SaturationDetector) String() string {
193198
}
194199
return "{" + result + "}"
195200
}
201+
202+
// DataLayerConfig contains the configuration of the DataLayer feature
203+
type DataLayerConfig struct {
204+
// +required
205+
// +kubebuilder:validation:Required
206+
// Sources is the list of sources to define to the DataLayer
207+
Sources []DataLayerSource `json:"sources"`
208+
}
209+
210+
func (dlc DataLayerConfig) String() string {
211+
return fmt.Sprintf("{Sources: %v}", dlc.Sources)
212+
}
213+
214+
// DataLayerSource contains the configuration of a DataSource of the DataLayer feature
215+
type DataLayerSource struct {
216+
// +required
217+
// +kubebuilder:validation:Required
218+
// PluginRef specifies a partiular Plugin instance to be associated with
219+
// this Source. The reference is to the name of an entry of the Plugins
220+
// defined in the configuration's Plugins section
221+
PluginRef string `json:"pluginRef"`
222+
223+
// +required
224+
// +kubebuilder:validation:Required
225+
// Extractors specifies the list of Plugin instances to be associated with
226+
// this Source. The entries are references to the names of entries of the Plugins
227+
// defined in the configuration's Plugins section
228+
Extractors []DataLayerExtractor `json:"extractors"`
229+
}
230+
231+
func (dls DataLayerSource) String() string {
232+
return fmt.Sprintf("{PluginRef: %s, Extractors: %v}", dls.PluginRef, dls.Extractors)
233+
}
234+
235+
// DataLayerExtractor contains the configuration of an Extractor of the DataLayer feature
236+
type DataLayerExtractor struct {
237+
// +required
238+
// +kubebuilder:validation:Required
239+
// PluginRef specifies a partiular Plugin instance to be associated with
240+
// this Extractor. The reference is to the name of an entry of the Plugins
241+
// defined in the configuration's Plugins section
242+
PluginRef string `json:"pluginRef"`
243+
}
244+
245+
func (dle DataLayerExtractor) String() string {
246+
return "{PluginRef: " + dle.PluginRef + "}"
247+
}

apix/config/v1alpha1/zz_generated.deepcopy.go

Lines changed: 67 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/epp/config/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package config
1818

1919
import (
20+
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/datalayer"
2021
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/saturationdetector"
2122
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling"
2223
)
@@ -25,4 +26,5 @@ import (
2526
type Config struct {
2627
SchedulerConfig *scheduling.SchedulerConfig
2728
SaturationDetectorConfig *saturationdetector.Config
29+
DataConfig *datalayer.Config
2830
}

pkg/epp/config/loader/configloader.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828

2929
configapi "sigs.k8s.io/gateway-api-inference-extension/apix/config/v1alpha1"
3030
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/config"
31+
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/datalayer"
3132
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/plugins"
3233
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/saturationdetector"
3334
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling"
@@ -95,9 +96,16 @@ func InstantiateAndConfigure(
9596
return nil, fmt.Errorf("scheduler config build failed: %w", err)
9697
}
9798

99+
featureGates := loadFeatureConfig(rawConfig.FeatureGates)
100+
dataConfig, err := buildDataLayerConfig(rawConfig.Data, featureGates[datalayer.FeatureGate], handle)
101+
if err != nil {
102+
return nil, fmt.Errorf("data layer config build failed: %w", err)
103+
}
104+
98105
return &config.Config{
99106
SchedulerConfig: schedulerConfig,
100107
SaturationDetectorConfig: buildSaturationConfig(rawConfig.SaturationDetector),
108+
DataConfig: dataConfig,
101109
}, nil
102110
}
103111

@@ -224,3 +232,40 @@ func buildSaturationConfig(apiConfig *configapi.SaturationDetector) *saturationd
224232

225233
return cfg
226234
}
235+
236+
func buildDataLayerConfig(rawDataConfig *configapi.DataLayerConfig, dataLayerEnabled bool, handle plugins.Handle) (*datalayer.Config, error) {
237+
if !dataLayerEnabled {
238+
if rawDataConfig != nil {
239+
return nil, errors.New("the Datalayer has not been enabled, but you specified a configuration for it")
240+
}
241+
return nil, nil
242+
}
243+
244+
if rawDataConfig == nil {
245+
return nil, errors.New("the Datalayer has been enabled. You must specify the Data section in the configuration")
246+
}
247+
248+
cfg := datalayer.Config{
249+
Sources: []datalayer.DataSourceConfig{},
250+
}
251+
for _, source := range rawDataConfig.Sources {
252+
if sourcePlugin, ok := handle.Plugin(source.PluginRef).(datalayer.DataSource); ok {
253+
sourceConfig := datalayer.DataSourceConfig{
254+
Plugin: sourcePlugin,
255+
Extractors: []datalayer.Extractor{},
256+
}
257+
for _, extractor := range source.Extractors {
258+
if extractorPlugin, ok := handle.Plugin(extractor.PluginRef).(datalayer.Extractor); ok {
259+
sourceConfig.Extractors = append(sourceConfig.Extractors, extractorPlugin)
260+
} else {
261+
return nil, fmt.Errorf("the plugin %s is not a datalayer.Extractor", source.PluginRef)
262+
}
263+
}
264+
cfg.Sources = append(cfg.Sources, sourceConfig)
265+
} else {
266+
return nil, fmt.Errorf("the plugin %s is not a datalayer.Source", source.PluginRef)
267+
}
268+
}
269+
270+
return &cfg, nil
271+
}

pkg/epp/config/loader/configloader_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package loader
1919
import (
2020
"context"
2121
"encoding/json"
22+
"reflect"
2223
"testing"
2324
"time"
2425

@@ -47,6 +48,8 @@ const (
4748
testPickerType = "test-picker"
4849
testScorerType = "test-scorer"
4950
testProfileHandler = "test-profile-handler"
51+
testSourceType = "test-source"
52+
testExtractorType = "test-extractor"
5053
)
5154

5255
// --- Test: Phase 1 (Raw Loading & Static Defaults) ---
@@ -281,6 +284,21 @@ func TestInstantiateAndConfigure(t *testing.T) {
281284
configText: errorMultiProfilesUseSingleProfileHandlerText,
282285
wantErr: true,
283286
},
287+
{
288+
name: "Error - Missing Data Config",
289+
configText: errorMissingDataConfigText,
290+
wantErr: true,
291+
},
292+
{
293+
name: "Error - Bad Source Reference",
294+
configText: errorBadSourceReferenceText,
295+
wantErr: true,
296+
},
297+
{
298+
name: "Error - Bad Extractor Reference",
299+
configText: errorBadExtractorReferenceText,
300+
wantErr: true,
301+
},
284302
}
285303

286304
for _, tc := range tests {
@@ -422,6 +440,32 @@ func (m *mockHandler) ProcessResults(
422440
return nil, nil
423441
}
424442

443+
// Mock Source
444+
type mockSource struct{ mockPlugin }
445+
446+
func (m *mockSource) AddExtractor(_ datalayer.Extractor) error {
447+
return nil
448+
}
449+
450+
func (m *mockSource) Collect(ctx context.Context, ep datalayer.Endpoint) error {
451+
return nil
452+
}
453+
454+
func (m *mockSource) Extractors() []string {
455+
return []string{}
456+
}
457+
458+
// Mock Extractor
459+
type mockExtractor struct{ mockPlugin }
460+
461+
func (m *mockExtractor) ExpectedInputType() reflect.Type {
462+
return reflect.TypeOf("")
463+
}
464+
465+
func (m *mockExtractor) Extract(ctx context.Context, data any, ep datalayer.Endpoint) error {
466+
return nil
467+
}
468+
425469
func registerTestPlugins(t *testing.T) {
426470
t.Helper()
427471

@@ -460,6 +504,14 @@ func registerTestPlugins(t *testing.T) {
460504
return &mockHandler{mockPlugin{t: plugins.TypedName{Name: name, Type: testProfileHandler}}}, nil
461505
})
462506

507+
plugins.Register(testSourceType, func(name string, _ json.RawMessage, _ plugins.Handle) (plugins.Plugin, error) {
508+
return &mockSource{mockPlugin{t: plugins.TypedName{Name: name, Type: testSourceType}}}, nil
509+
})
510+
511+
plugins.Register(testExtractorType, func(name string, _ json.RawMessage, _ plugins.Handle) (plugins.Plugin, error) {
512+
return &mockExtractor{mockPlugin{t: plugins.TypedName{Name: name, Type: testExtractorType}}}, nil
513+
})
514+
463515
// Ensure system defaults are registered too.
464516
plugins.Register(picker.MaxScorePickerType, picker.MaxScorePickerFactory)
465517
plugins.Register(profile.SingleProfileHandlerType, profile.SingleProfileHandlerFactory)

0 commit comments

Comments
 (0)