Skip to content

Commit 3677a53

Browse files
committed
feat: validate stability degradation for pluginRef dependencies
Signed-off-by: Cong Liu <conliu@google.com>
1 parent b87c94c commit 3677a53

4 files changed

Lines changed: 72 additions & 11 deletions

File tree

pkg/epp/config/loader/configloader.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ func buildPluginDAG(configuredPlugins []configapi.PluginSpec, handle fwkplugin.H
259259
pluginMap := map[string]configapi.PluginSpec{}
260260
orderedPlugins := []configapi.PluginSpec{}
261261

262+
for _, spec := range configuredPlugins {
263+
pluginMap[spec.Name] = spec
264+
}
265+
262266
for _, spec := range configuredPlugins {
263267
if parserFunc, ok := fwkplugin.PluginsWithPluginDependencies[spec.Type]; !ok {
264268
// Add plugins that don't have dependencies to the graph
@@ -269,9 +273,20 @@ func buildPluginDAG(configuredPlugins []configapi.PluginSpec, handle fwkplugin.H
269273
if err != nil {
270274
return nil, fmt.Errorf("failed to parse plugin parameters for %s (type: %s): %w", spec.Name, spec.Type, err)
271275
}
272-
graph[spec.Name] = findPluginDependencies(configStruct)
276+
deps := findPluginDependencies(configStruct)
277+
graph[spec.Name] = deps
278+
279+
parentStability := fwkplugin.GetPluginStability(spec.Type)
280+
for _, depName := range deps {
281+
if depSpec, ok := pluginMap[depName]; ok {
282+
childStability := fwkplugin.GetPluginStability(depSpec.Type)
283+
if childStability.Rank() < parentStability.Rank() {
284+
return nil, fmt.Errorf("stability degradation detected: plugin %s (%s, %s) depends on plugin %s (%s, %s)",
285+
spec.Name, spec.Type, parentStability, depSpec.Name, depSpec.Type, childStability)
286+
}
287+
}
288+
}
273289
}
274-
pluginMap[spec.Name] = spec
275290
}
276291

277292
sortedGraph, err := util.TopologicalSort(graph)

pkg/epp/config/loader/configloader_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,3 +1316,43 @@ func TestExperimentalPluginsFeatureGate(t *testing.T) {
13161316
_, err = InstantiateAndConfigure(rawConfigWithGate, testutils.NewTestHandle(context.Background()), logger)
13171317
require.NoError(t, err)
13181318
}
1319+
1320+
func TestPluginRefStabilityDegradation(t *testing.T) {
1321+
parentType := "test-beta-parent-plugin"
1322+
childType := "test-alpha-child-plugin"
1323+
1324+
fwkplugin.RegisterWithPluginDependencies(parentType, fwkplugin.StabilityBeta,
1325+
func(name string, _ *json.Decoder, _ fwkplugin.Handle) (fwkplugin.Plugin, error) {
1326+
return &mockPlugin{t: fwkplugin.TypedName{Name: name, Type: parentType}}, nil
1327+
},
1328+
func(dec *json.Decoder, _ fwkplugin.Handle) (any, error) {
1329+
type config struct {
1330+
ChildRef string `json:"childRef" pluginRef:"true"`
1331+
}
1332+
var cfg config
1333+
if dec != nil {
1334+
if err := dec.Decode(&cfg); err != nil {
1335+
return nil, err
1336+
}
1337+
}
1338+
return cfg, nil
1339+
},
1340+
)
1341+
fwkplugin.Register(childType, fwkplugin.StabilityAlpha, func(name string, _ *json.Decoder, _ fwkplugin.Handle) (fwkplugin.Plugin, error) {
1342+
return &mockPlugin{t: fwkplugin.TypedName{Name: name, Type: childType}}, nil
1343+
})
1344+
1345+
rawConfig := &configapi.EndpointPickerConfig{
1346+
FeatureGates: configapi.FeatureGates{fwkplugin.ExperimentalPluginsFeatureGate},
1347+
Plugins: []configapi.PluginSpec{
1348+
{Name: "child", Type: childType},
1349+
{Name: "parent", Type: parentType, Parameters: json.RawMessage(`{"childRef":"child"}`)},
1350+
},
1351+
}
1352+
1353+
logger := logging.NewTestLogger()
1354+
handle := testutils.NewTestHandle(context.Background())
1355+
_, err := InstantiateAndConfigure(rawConfig, handle, logger)
1356+
require.Error(t, err)
1357+
require.Contains(t, err.Error(), "stability degradation detected")
1358+
}

pkg/epp/datalayer/data_graph.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,28 +155,20 @@ func ValidatePluginStabilityDependencies(plugins []plugin.Plugin, logger logr.Lo
155155
}
156156
}
157157

158-
stabilityRank := map[plugin.StabilityLevel]int{
159-
plugin.StabilityStable: 3,
160-
plugin.StabilityBeta: 2,
161-
plugin.StabilityAlpha: 1,
162-
}
163-
164158
for _, p := range plugins {
165159
consumer, ok := p.(plugin.ConsumerPlugin)
166160
if !ok {
167161
continue
168162
}
169163
consumerType := consumer.TypedName().Type
170164
consumerStability := plugin.GetPluginStability(consumerType)
171-
consumerRank := stabilityRank[consumerStability]
172165

173166
for key := range consumer.Consumes().Required {
174167
if producer, found := producerKeyMap[key.String()]; found {
175168
producerType := producer.TypedName().Type
176169
producerStability := plugin.GetPluginStability(producerType)
177-
producerRank := stabilityRank[producerStability]
178170

179-
if producerRank < consumerRank {
171+
if producerStability.Rank() < consumerStability.Rank() {
180172
return fmt.Errorf("stability degradation detected: consumer %s (%s) depends on producer %s (%s) for dataKey %s",
181173
consumer.TypedName().String(), consumerStability,
182174
producer.TypedName().String(), producerStability,

pkg/epp/framework/interface/plugin/stability.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,17 @@ type PluginMetadata struct {
4040
ScheduledRemovalIn string
4141
ReplacementType string
4242
}
43+
44+
// Rank returns an integer representing the stability level ordering (Stable: 3, Beta: 2, Alpha: 1).
45+
func (s StabilityLevel) Rank() int {
46+
switch s {
47+
case StabilityStable:
48+
return 3
49+
case StabilityBeta:
50+
return 2
51+
case StabilityAlpha:
52+
return 1
53+
default:
54+
return 0
55+
}
56+
}

0 commit comments

Comments
 (0)