Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 4 additions & 25 deletions cmd/glbc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,11 @@ import (

firewallcrclient "github.com/GoogleCloudPlatform/gke-networking-api/client/gcpfirewall/clientset/versioned"
networkclient "github.com/GoogleCloudPlatform/gke-networking-api/client/network/clientset/versioned"
informernetwork "github.com/GoogleCloudPlatform/gke-networking-api/client/network/informers/externalversions"
nodetopologyclient "github.com/GoogleCloudPlatform/gke-networking-api/client/nodetopology/clientset/versioned"
informernodetopology "github.com/GoogleCloudPlatform/gke-networking-api/client/nodetopology/informers/externalversions"
k8scp "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud"
flag "github.com/spf13/pflag"
crdclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
informers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/leaderelection"
Expand All @@ -54,7 +51,6 @@ import (
serviceattachmentclient "k8s.io/ingress-gce/pkg/serviceattachment/client/clientset/versioned"
"k8s.io/ingress-gce/pkg/svcneg"
svcnegclient "k8s.io/ingress-gce/pkg/svcneg/client/clientset/versioned"
informersvcneg "k8s.io/ingress-gce/pkg/svcneg/client/informers/externalversions"
"k8s.io/ingress-gce/pkg/systemhealth"
"k8s.io/ingress-gce/pkg/utils"
"k8s.io/klog/v2"
Expand Down Expand Up @@ -259,19 +255,6 @@ func main() {
if err != nil {
klog.Fatalf("Failed to create ProviderConfig client: %v", err)
}
informersFactory := informers.NewSharedInformerFactory(kubeClient, flags.F.ResyncPeriod)
var svcNegFactory informersvcneg.SharedInformerFactory
if svcNegClient != nil {
svcNegFactory = informersvcneg.NewSharedInformerFactory(svcNegClient, flags.F.ResyncPeriod)
}
var networkFactory informernetwork.SharedInformerFactory
if networkClient != nil {
networkFactory = informernetwork.NewSharedInformerFactory(networkClient, flags.F.ResyncPeriod)
}
var nodeTopologyFactory informernodetopology.SharedInformerFactory
if nodeTopologyClient != nil {
nodeTopologyFactory = informernodetopology.NewSharedInformerFactory(nodeTopologyClient, flags.F.ResyncPeriod)
}
ctx := context.Background()
syncerMetrics := syncMetrics.NewNegMetricsCollector(flags.F.NegMetricsExportInterval, rootLogger)
go syncerMetrics.Run(stopCh)
Expand All @@ -284,13 +267,11 @@ func main() {
rootLogger,
kubeClient,
svcNegClient,
networkClient,
nodeTopologyClient,
kubeSystemUID,
eventRecorderKubeClient,
providerConfigClient,
informersFactory,
svcNegFactory,
networkFactory,
nodeTopologyFactory,
gceCreator,
namer,
stopCh,
Expand All @@ -305,13 +286,11 @@ func main() {
rootLogger,
kubeClient,
svcNegClient,
networkClient,
nodeTopologyClient,
kubeSystemUID,
eventRecorderKubeClient,
providerConfigClient,
informersFactory,
svcNegFactory,
networkFactory,
nodeTopologyFactory,
gceCreator,
namer,
stopCh,
Expand Down
82 changes: 82 additions & 0 deletions pkg/multiproject/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Multi-Project Controller

Enables ingress-gce to manage GCP resources across multiple projects through ProviderConfig CRs.

## Architecture

```
┌─────────────────────────────────────────────────────────────┐
│ Main Controller │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Shared Kubernetes Informers │ │
│ │ (Services, Ingresses, EndpointSlices) │ │
│ └─────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────▼──────────────────────────────────┐ │
│ │ ProviderConfig Controller │ │
│ │ Watches ProviderConfig resources │ │
│ │ Manages per-project controllers │ │
│ └─────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ┌────────────────┼────────────────┐ │
│ │ │ │ │
│ ┌────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ │
│ │Project A │ │ Project B │ │ Project C │ ... │
│ │Controller│ │Controller │ │Controller │ │
│ └──────────┘ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────────┘
```

## Key Concepts

- **ProviderConfig**: CR defining a GCP project configuration
- **Resource Filtering**: Resources are associated via labels; each controller sees only its labeled resources
- **Shared Informers**: Base informers are created once and shared; controllers get filtered views
- **Dynamic Lifecycle**: Controllers start/stop with ProviderConfig create/delete

## Usage

### Create ProviderConfig

```yaml
apiVersion: networking.gke.io/v1
kind: ProviderConfig
metadata:
name: team-a-project
spec:
projectID: team-a-gcp-project
network: team-a-network
```

### Associate Resources

```yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
labels:
${PROVIDER_CONFIG_LABEL_KEY}: provider-config-a
spec:
# Service spec...
```

## Operations

### Adding a Project
1. Create ProviderConfig
2. Label services/ingresses with PC name
3. NEGs created in target project

### Removing a Project
1. Remove/relabel services using the PC
2. Wait for NEG cleanup
3. Delete ProviderConfig

## Guarantees

- Controllers only manage explicitly labeled resources
- One controller per ProviderConfig
- Base infrastructure survives individual controller failures
- PC deletion doesn't affect other projects
9 changes: 1 addition & 8 deletions pkg/multiproject/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
package controller

import (
"context"
"fmt"
"math/rand"
"runtime/debug"
Expand Down Expand Up @@ -72,15 +71,9 @@ func (pcc *ProviderConfigController) Run() {
defer pcc.shutdown()

pcc.logger.Info("Starting ProviderConfig controller")
ctx, cancel := context.WithCancel(context.Background())
go func() {
<-pcc.stopCh
pcc.logger.Info("Stop channel closed, cancelling context")
cancel()
}()

pcc.logger.Info("Waiting for initial cache sync before starting ProviderConfig Controller")
ok := cache.WaitForCacheSync(ctx.Done(), pcc.hasSynced)
ok := cache.WaitForCacheSync(pcc.stopCh, pcc.hasSynced)
if !ok {
pcc.logger.Error(nil, "Failed to wait for initial cache sync before starting ProviderConfig Controller")
}
Expand Down
Loading