-
Notifications
You must be signed in to change notification settings - Fork 247
Move over Service controller - handle CCM LoadBalancerClass #895
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
k8s-ci-robot
merged 2 commits into
kubernetes:master
from
FelipeYepez:l4-move-service-controller
Sep 24, 2025
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
|
|
||
| utilfeature "k8s.io/apiserver/pkg/util/feature" | ||
| cloudprovider "k8s.io/cloud-provider" | ||
| gkeservicecontroller "k8s.io/cloud-provider-gcp/pkg/controller/service" | ||
| "k8s.io/cloud-provider/app" | ||
| cloudcontrollerconfig "k8s.io/cloud-provider/app/config" | ||
| controllermanagerapp "k8s.io/controller-manager/app" | ||
| "k8s.io/controller-manager/controller" | ||
| "k8s.io/klog/v2" | ||
| ) | ||
|
|
||
| // startGkeServiceControllerWrapper is used to take cloud config as input and start the GKE service controller | ||
| func startGkeServiceControllerWrapper(initContext app.ControllerInitContext, completedConfig *cloudcontrollerconfig.CompletedConfig, cloud cloudprovider.Interface) app.InitFunc { | ||
| return func(ctx context.Context, controllerContext controllermanagerapp.ControllerContext) (controller.Interface, bool, error) { | ||
| return startGkeServiceController(ctx, initContext, controllerContext, completedConfig, cloud) | ||
| } | ||
| } | ||
|
|
||
| func startGkeServiceController(ctx context.Context, initContext app.ControllerInitContext, controlexContext controllermanagerapp.ControllerContext, completedConfig *cloudcontrollerconfig.CompletedConfig, cloud cloudprovider.Interface) (controller.Interface, bool, error) { | ||
| // Start the service controller | ||
| serviceController, err := gkeservicecontroller.New( | ||
| cloud, | ||
| completedConfig.ClientBuilder.ClientOrDie(initContext.ClientName), | ||
| completedConfig.SharedInformers.Core().V1().Services(), | ||
| completedConfig.SharedInformers.Core().V1().Nodes(), | ||
| completedConfig.ComponentConfig.KubeCloudShared.ClusterName, | ||
| utilfeature.DefaultFeatureGate, | ||
| ) | ||
| if err != nil { | ||
| // This error shouldn't fail. It lives like this as a legacy. | ||
| klog.Errorf("Failed to start service controller: %v", err) | ||
| return nil, false, nil | ||
| } | ||
|
|
||
| go serviceController.Run(ctx, int(completedConfig.ComponentConfig.ServiceController.ConcurrentServiceSyncs), controlexContext.ControllerManagerMetrics) | ||
|
|
||
| return nil, true, nil | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| v1 "k8s.io/api/core/v1" | ||
| gkeservicecontroller "k8s.io/cloud-provider-gcp/pkg/controller/service" | ||
| ) | ||
|
|
||
| // TestWantsLoadBalancer verifies that the forked and modified WantsLoadBalancer | ||
| // function in k8s.io/cloud-provider-gcp/pkg/controller/service has the correct | ||
| // logic. This test acts as a safeguard to prevent future updates from | ||
| // overwriting the custom logic. It ensures that the controller correctly | ||
| // processes services with GKE-specific legacy LoadBalancerClasses and, | ||
| // just as importantly, ignores services that have no LoadBalancerClass set | ||
| // or have a class that is not managed by this controller. | ||
| func TestWantsLoadBalancer(t *testing.T) { | ||
| gkeLegacyExternalClass := "networking.gke.io/l4-regional-external-legacy" | ||
| gkeLegacyInternalClass := "networking.gke.io/l4-regional-internal-legacy" | ||
| otherClass := "some-other-class" | ||
|
|
||
| testCases := []struct { | ||
| name string | ||
| service *v1.Service | ||
| want bool | ||
| }{ | ||
| { | ||
| name: "service is not of type LoadBalancer", | ||
| service: &v1.Service{ | ||
| Spec: v1.ServiceSpec{ | ||
| Type: v1.ServiceTypeClusterIP, | ||
| }, | ||
| }, | ||
| want: false, | ||
| }, | ||
| { | ||
| name: "service is of type LoadBalancer with no loadBalancerClass", | ||
| service: &v1.Service{ | ||
| Spec: v1.ServiceSpec{ | ||
| Type: v1.ServiceTypeLoadBalancer, | ||
| }, | ||
| }, | ||
| want: false, | ||
| }, | ||
| { | ||
| name: "service is of type LoadBalancer with GKE legacy external loadBalancerClass", | ||
| service: &v1.Service{ | ||
| Spec: v1.ServiceSpec{ | ||
| Type: v1.ServiceTypeLoadBalancer, | ||
| LoadBalancerClass: &gkeLegacyExternalClass, | ||
| }, | ||
| }, | ||
| want: true, | ||
| }, | ||
| { | ||
| name: "service is of type LoadBalancer with GKE legacy internal loadBalancerClass", | ||
| service: &v1.Service{ | ||
| Spec: v1.ServiceSpec{ | ||
| Type: v1.ServiceTypeLoadBalancer, | ||
| LoadBalancerClass: &gkeLegacyInternalClass, | ||
| }, | ||
| }, | ||
| want: true, | ||
| }, | ||
| { | ||
| name: "service is of type LoadBalancer with other loadBalancerClass", | ||
| service: &v1.Service{ | ||
| Spec: v1.ServiceSpec{ | ||
| Type: v1.ServiceTypeLoadBalancer, | ||
| LoadBalancerClass: &otherClass, | ||
| }, | ||
| }, | ||
| want: false, | ||
| }, | ||
| } | ||
|
|
||
| for _, tc := range testCases { | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| if got := gkeservicecontroller.WantsLoadBalancer(tc.service); got != tc.want { | ||
| t.Errorf("WantsLoadBalancer() = %v, want %v", got, tc.want) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| load("@io_bazel_rules_go//go:def.bzl", "go_library") | ||
|
|
||
| go_library( | ||
| name = "service", | ||
| srcs = [ | ||
| "controller.go", | ||
| "controller_gke.go", | ||
| "doc.go", | ||
| "metrics.go", | ||
| ], | ||
| importpath = "k8s.io/cloud-provider-gcp/pkg/controller/service", | ||
| visibility = ["//visibility:public"], | ||
| deps = [ | ||
| "//vendor/k8s.io/api/core/v1:core", | ||
| "//vendor/k8s.io/apimachinery/pkg/api/errors", | ||
| "//vendor/k8s.io/apimachinery/pkg/labels", | ||
| "//vendor/k8s.io/apimachinery/pkg/util/runtime", | ||
| "//vendor/k8s.io/apimachinery/pkg/util/sets", | ||
| "//vendor/k8s.io/apimachinery/pkg/util/wait", | ||
| "//vendor/k8s.io/client-go/informers/core/v1:core", | ||
| "//vendor/k8s.io/client-go/kubernetes", | ||
| "//vendor/k8s.io/client-go/kubernetes/scheme", | ||
| "//vendor/k8s.io/client-go/kubernetes/typed/core/v1:core", | ||
| "//vendor/k8s.io/client-go/listers/core/v1:core", | ||
| "//vendor/k8s.io/client-go/tools/cache", | ||
| "//vendor/k8s.io/client-go/tools/record", | ||
| "//vendor/k8s.io/client-go/util/workqueue", | ||
| "//vendor/k8s.io/cloud-provider", | ||
| "//vendor/k8s.io/cloud-provider/api", | ||
| "//vendor/k8s.io/cloud-provider/service/helpers", | ||
| "//vendor/k8s.io/component-base/featuregate", | ||
| "//vendor/k8s.io/component-base/metrics", | ||
| "//vendor/k8s.io/component-base/metrics/legacyregistry", | ||
| "//vendor/k8s.io/component-base/metrics/prometheus/controllers", | ||
| "//vendor/k8s.io/klog/v2:klog", | ||
| ], | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| approvers: | ||
| - aojea | ||
| - bowei | ||
| - mmamczur | ||
| - thockin | ||
| - felipeyepez | ||
| labels: | ||
| - sig/network |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # Service Controller | ||
|
|
||
| This service controller is a fork of the upstream service controller from `k8s.io/cloud-provider/controllers/service`. | ||
|
|
||
| ## Modifications | ||
|
|
||
| The primary modification in this fork is to the `WantsLoadBalancer` function in `controller.go`. This change allows the controller to manage services that have a `loadBalancerClass` set to one of the following GKE-specific values: | ||
|
|
||
| - `networking.gke.io/l4-regional-external-legacy` | ||
| - `networking.gke.io/l4-regional-internal-legacy` | ||
|
|
||
| The upstream controller ignores any service with a non-nil `loadBalancerClass`. This fork extends that logic to claim these specific classes for the GKE cloud provider. This ensures that this controller will only process services with a valid `loadBalancerClass`, while the default service controller handles all other services without a `LoadBalancerClass`. | ||
|
|
||
| When updating this forked controller from upstream, it is critical that the custom logic in the `WantsLoadBalancer` function is preserved. The purpose of this modification is to allow the controller to manage services that have a `loadBalancerClass` set to one of the GKE-specific values mentioned above. | ||
|
|
||
| ## Controller Startup | ||
|
|
||
| This controller is started using a wrapper function, `startGkeServiceControllerWrapper`, located in `@cmd/cloud-controller-manager/gkeservicecontroller.go`. This wrapper initializes and runs the GKE-specific service controller when the `--controllers` flag includes `gke-service`. | ||
|
|
||
| ## Configuration | ||
|
|
||
| The configuration for this controller, defined in `@vendor/k8s.io/cloud-provider/controllers/service/config/**`, is intentionally not forked. This approach allows the controller to reuse the existing command-line flags and configuration parameters from the upstream service controller, ensuring backward compatibility and simplifying maintenance. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.