Change:
Unit test that will ensure that Cluster Autoscaler objects that currently implement interfaces from stay that way. It would suffice to do add compile-time checks for the following types: Interface changes could still be introduced in a PR against k/k repo, but they would require Cluster Autoscaler changes first to be merged before the interface change itself is merged. To avoid circular dependency issues, the unit test itself would have to live in a separate module inside k/k repo, importing both scheduler interfaces and CA implementations. Scheduler framework is an API used for communication between Cluster Autoscaler and Scheduler code. Since it is not a versioned API, it is easy to introduce changes that are backwards incompatible, which is exactly what happened recently in Ensuring interface compatibility in core k8s repo will prevent accidentally breaking Cluster Autoscaler code.
This issue is currently awaiting triage. If a SIG or subproject determines this is a relevant issue, they will accept it by applying the label and provide further guidance. The label can be by org members by writing in a comment. <detailsInstructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/sig scheduling
/sig autoscaling
I'm +1 and based on the discussion on last sig-autoscaling meeting the idea has a wide support in the sig-autoscaling. One question: shouldn't we also cover ?
Good point, CA is not only implementing scheduler interfaces, but also calling them, I think it makes sense to extend the scope to include in a compile-time check.
+1 I think it's not only for CA. But also for the plugins (not in k/k) implemented by users themselves. Ensuring interface compatibility is needed.
I understand the intention to prevent CA's code to be broken, however, the "CA's change first" strategy doesn't sound that practical as (1) that would require k/k to vendor CA's code, and (2) the "preventive changes" in CA doesn't really get verified at runtime/compilation time as the upstream changes are not merged yet. Given no matter what kind of changes go first, the scheduler framework changes need to get CA team involved. I'd suggest we continue to follow this rule, which works well all the time and is just violated once in . In addition to the early involvement in the k/k PR's review, we can setup additional CI check in CA's side, so when the deps in CA, the CI should have (which isn't today).
The scheduler framework changes are somewhat unavoidable to evolve over time, aimed to behave performant as well as fitting more scenarios. The changes vary from minor changes like PodInfo-QueuedPodInfo, to major changes like interface addition/deletion/update, and hence those would require the consumers (like plugins) to adjust when bumping versions. I don't think we can promise strict compatibility at this moment.
Good point that we wouldn't be able to verify the code on CA side does indeed implement the updated interface. That might be sufficient, I'm just worried that this is not an automated process and hence can be skipped again. Is there anything we could do to limit the possibility of human error? My best idea here so far is to add sig-autoscaling as reviewers for framework changes. Or perhaps a subset of sig-autoscaling members that are cluster-autoscaler maintainers (as opposed to, for example, VPA). Actually, tests are already run for CA PRs, so it should already be detectable that way. The problem is that by the point it happens, the interface changes are already merged and there is no way to influence them to be more compatible with CA use case.
I think this is where unittests could help. The unittest wouldn't mean the interface cannot be changed, it would just provide a signal that sig-autoscaling / Cluster Autoscaler reviewer should be involved. It seems much more reliable than the current system based on scheduler contributors' intuitions on which changes may affect CA.
+1 on unit tests owned by sig-autoscaling
Unit test will block submission, which is more than just letting sig-autoscaling know they should take a look. In that case an integration test would probably make more sense: use GitHub API to pull latest version of kubernetes/autoscaler repo, then use Go compiler libs to try to compile selected files. On breaking interface changes, SIG autoscaling approver would be required to temporarily disable the test and drive adjustments on Cluster Autoscaler side (asynchronously, not blocking development on k/k repo). Thoughts?
I think we just need to have tests in k/k that hardcode the CA expectations rather than trying to think how to import CA.
Do you mean a slim fake library to simulate CA and other consumers? If so, it may still have gaps with CA. While if we import CA in k/k, again, that may yield a cyclic dependency. That sounds fair to me as it simulates how a consumer consumes the framework. Just not sure how complicated it can be (supposing we don't have similar tests to leverage).
I am hoping that we don't need to "simulate" CA; just their expectations of the framework itself.
I like suggestion: Simple test that validates CA expectations of the framework interface, without taking on all the extra complexity stemming from dependencies. This should be sufficient for now and may be replaced by more sophisticated mechanism I described above if needed. At the very least, it should suffice to create an object and check if it satisfies the framework interface. We can iterate on that later, if needed.
+1 with the suggestion. What about verify the framework at startup like , and make CA team part of the folder reviewer.
Yup, exactly something like this. I think putting it inside a unit test makes more sense though - there's no reason for this code to end up in scheduler binary.
would you like to finish that PR?
Sure, quite busy in the past few days. I'll complete this ASAP. Thanks for reminding.
Thanks, I left a minor comment on the PR, otherwise LGTM.

Discussion:
<ins> # See the OWNERS docs at https://go.k8s.io/owners approvers: - sig-scheduling-maintainers reviewers: - sig-autoscaling-maintainers - x13n </ins>

<ins> /* Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // This file helps to detect whether we changed the interface of Framework. // This is important for projects under `github.com/kubernetes*` orgs // who import the Framework to not breaking their codes, e.g. cluster-autoscaler. package contract import ( "context" "testing" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" "k8s.io/kubernetes/pkg/scheduler/framework" "k8s.io/kubernetes/pkg/scheduler/framework/runtime" ) type frameworkContract interface { RunPreFilterPlugins(ctx context.Context, state *framework.CycleState, pod *v1.Pod) (*framework.PreFilterResult, *framework.Status) RunFilterPlugins(context.Context, *framework.CycleState, *v1.Pod, *framework.NodeInfo) framework.PluginToStatus } func TestFrameworkContract(t *testing.T) { var f framework.Framework var c frameworkContract = f assert.Nil(t, c) } func TestNewFramework(t *testing.T) { var f interface{} if f, _ = runtime.NewFramework(nil, nil, nil); f != nil { _, ok := f.(framework.Framework) assert.True(t, ok) } } func TestNewCycleState(t *testing.T) { var state interface{} = framework.NewCycleState() _, ok := state.(*framework.CycleState) assert.True(t, ok) } </ins>

<ins> /* Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // This file helps to detect whether we changed the interface of Listers. // This is important for downstream projects who import the Listers to not // breaking their codes. package contract import ( "k8s.io/kubernetes/pkg/scheduler/framework" ) var _ framework.NodeInfoLister = &nodeInfoListerContract{} var _ framework.StorageInfoLister = &storageInfoListerContract{} var _ framework.SharedLister = &shareListerContract{} type nodeInfoListerContract struct{} func (c *nodeInfoListerContract) List() ([]*framework.NodeInfo, error) { return nil, nil } func (c *nodeInfoListerContract) HavePodsWithAffinityList() ([]*framework.NodeInfo, error) { return nil, nil } func (c *nodeInfoListerContract) HavePodsWithRequiredAntiAffinityList() ([]*framework.NodeInfo, error) { return nil, nil } func (c *nodeInfoListerContract) Get(_ string) (*framework.NodeInfo, error) { return nil, nil } type storageInfoListerContract struct{} func (c *storageInfoListerContract) IsPVCUsedByPods(_ string) bool { return false } type shareListerContract struct{} func (c *shareListerContract) NodeInfos() framework.NodeInfoLister { return nil } func (c *shareListerContract) StorageInfos() framework.StorageInfoLister { return nil } </ins>