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
2 changes: 2 additions & 0 deletions cmd/operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package main

import (
_ "knative.dev/operator/pkg/reconciler/common" // init() registers flags and caching scheme.
"knative.dev/operator/pkg/reconciler/knativeeventing"
"knative.dev/operator/pkg/reconciler/knativeserving"
kubefilteredfactory "knative.dev/pkg/client/injection/kube/informers/factory/filtered"
Expand All @@ -25,6 +26,7 @@ import (
)

func main() {
// Do not call flag.Parse() here: sharedmain registers its flags late and parses them itself.
ctx := signals.NewContext()
ctx = kubefilteredfactory.WithSelectors(ctx,
knativeserving.Selector,
Expand Down
67 changes: 67 additions & 0 deletions cmd/operator/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright 2026 The Knative 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.
*/

package main

import (
"os/exec"
"path/filepath"
"strings"
"testing"
"time"
)

// TestFlagsAcceptedBySharedmain guards against an early flag.Parse() regression
// by building the binary and invoking each late-registered sharedmain flag.
func TestFlagsAcceptedBySharedmain(t *testing.T) {
bin := filepath.Join(t.TempDir(), "knative-operator")
build := exec.Command("go", "build", "-o", bin, ".")
if out, err := build.CombinedOutput(); err != nil {
t.Fatalf("go build failed: %v\n%s", err, out)
}

flags := []string{
"--kubeconfig=/nonexistent",
"--disable-ha",
"--server=https://example.invalid",
"--cluster=remote",
"--kube-api-burst=200",
"--kube-api-qps=100",
}
for _, fl := range flags {
t.Run(strings.TrimPrefix(strings.SplitN(fl, "=", 2)[0], "--"), func(t *testing.T) {
cmd := exec.Command(bin, fl)
cmd.Env = append(cmd.Environ(), "KUBECONFIG=/dev/null")
done := make(chan struct{})
var out []byte
var runErr error
go func() {
out, runErr = cmd.CombinedOutput()
close(done)
}()
select {
case <-done:
case <-time.After(15 * time.Second):
_ = cmd.Process.Kill()
<-done
}
_ = runErr
if strings.Contains(string(out), "flag provided but not defined") {
t.Fatalf("%s rejected as undefined — early flag.Parse() regression?\n%s", fl, out)
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright 2025 The Knative 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
#
# https://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.

{{- $mc := .Values.knative_operator.multicluster | default dict }}
{{- if $mc.enabled }}
{{- if not $mc.accessProvidersConfig }}
{{- fail "knative_operator.multicluster.enabled is true but knative_operator.multicluster.accessProvidersConfig is empty — either provide an access-providers config or disable multi-cluster support" }}
{{- end }}
{{- $mountPaths := list }}
{{- range ($mc.plugins | default (list)) }}
{{- $mountPaths = append $mountPaths .mountPath }}
{{- end }}
{{- $cfg := $mc.accessProvidersConfig | default dict }}
{{- range ($cfg.providers | default (list)) }}
{{- $cmd := (.execConfig | default dict).command | default "" }}
{{- if $cmd }}
{{- $cmdDir := dir $cmd }}
{{- if not (has $cmdDir $mountPaths) }}
{{- fail (printf "multicluster validation error: provider %q command %q has parent dir %q which does not match any plugins[].mountPath (have %v); execConfig.command parent directory must equal a plugin mountPath" .name $cmd $cmdDir $mountPaths) }}
{{- end }}
{{- end }}
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
name: clusterprofile-provider-file
namespace: "{{ .Release.Namespace }}"
labels:
app.kubernetes.io/name: knative-operator
app.kubernetes.io/version: "{{ .Chart.Version }}"
data:
config.json: {{ $mc.accessProvidersConfig | default dict | mustToJson | quote }}
{{- end }}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ spec:
singular: knativeeventing
scope: Namespaced
versions:
- name: v1beta1
- additionalPrinterColumns:
- jsonPath: .spec.clusterProfileRef.name
name: Target Cluster
type: string
name: v1beta1
schema:
openAPIV3Schema:
description: KnativeEventing is the Schema for the eventings API
Expand Down Expand Up @@ -69,6 +73,25 @@ spec:
- URL
type: object
type: array
clusterProfileRef:
description: |-
ClusterProfileRef is an optional reference to a ClusterProfile resource
(multicluster.x-k8s.io/v1alpha1). When set, the operator reconciles
the component on the remote cluster described by the referenced
ClusterProfile instead of the local cluster.
properties:
name:
description: Name is the name of the ClusterProfile resource.
minLength: 1
type: string
namespace:
description: Namespace is the namespace of the ClusterProfile resource.
minLength: 1
type: string
required:
- name
- namespace
type: object
config:
additionalProperties:
additionalProperties:
Expand Down Expand Up @@ -3503,6 +3526,9 @@ spec:
type: object
type: array
type: object
x-kubernetes-validations:
- message: spec.clusterProfileRef is immutable
rule: (!has(self.clusterProfileRef) && !has(oldSelf.clusterProfileRef)) || (has(self.clusterProfileRef) && has(oldSelf.clusterProfileRef) && self.clusterProfileRef == oldSelf.clusterProfileRef)
status:
description: KnativeEventingStatus defines the observed state of KnativeEventing
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ spec:
singular: knativeserving
scope: Namespaced
versions:
- name: v1beta1
- additionalPrinterColumns:
- jsonPath: .spec.clusterProfileRef.name
name: Target Cluster
type: string
name: v1beta1
schema:
openAPIV3Schema:
description: KnativeServing is the Schema for the knativeservings API
Expand Down Expand Up @@ -69,6 +73,25 @@ spec:
- URL
type: object
type: array
clusterProfileRef:
description: |-
ClusterProfileRef is an optional reference to a ClusterProfile resource
(multicluster.x-k8s.io/v1alpha1). When set, the operator reconciles
the component on the remote cluster described by the referenced
ClusterProfile instead of the local cluster.
properties:
name:
description: Name is the name of the ClusterProfile resource.
minLength: 1
type: string
namespace:
description: Namespace is the namespace of the ClusterProfile resource.
minLength: 1
type: string
required:
- name
- namespace
type: object
config:
additionalProperties:
additionalProperties:
Expand Down Expand Up @@ -3774,6 +3797,9 @@ spec:
type: object
type: array
type: object
x-kubernetes-validations:
- message: spec.clusterProfileRef is immutable
rule: (!has(self.clusterProfileRef) && !has(oldSelf.clusterProfileRef)) || (has(self.clusterProfileRef) && has(oldSelf.clusterProfileRef) && self.clusterProfileRef == oldSelf.clusterProfileRef)
status:
description: KnativeServingStatus defines the observed state of KnativeServing
properties:
Expand Down
25 changes: 25 additions & 0 deletions config/charts/knative-operator/templates/operator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -855,5 +855,30 @@ spec:
ports:
- name: metrics
containerPort: 9090
{{- $mc := .Values.knative_operator.multicluster | default dict }}
{{- if $mc.enabled }}
args:
- --clusterprofile-provider-file=/etc/cluster-inventory/config.json
volumeMounts:
- name: access-config
mountPath: /etc/cluster-inventory
readOnly: true
{{- range ($mc.plugins | default list) }}
- name: {{ .name }}
mountPath: {{ .mountPath }}
readOnly: true
{{- end }}
{{- end }}
{{- if $mc.enabled }}
volumes:
- name: access-config
configMap:
name: clusterprofile-provider-file
{{- range ($mc.plugins | default list) }}
- name: {{ .name }}
image:
reference: {{ .image }}
{{- end }}
{{- end }}

---
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,12 @@ rules:
- list
- get
- watch
# for multicluster support
- apiGroups:
- multicluster.x-k8s.io
resources:
- clusterprofiles
verbs:
- get
- list
- watch
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,15 @@ rules:
- pods
verbs:
- get
# for multicluster support
- apiGroups:
- multicluster.x-k8s.io
resources:
- clusterprofiles
verbs:
- get
- list
- watch
# Copyright 2020 The Knative Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
20 changes: 20 additions & 0 deletions config/charts/knative-operator/values.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
knative_operator:
# Multi-cluster (Cluster Inventory API): when enabled, the chart mounts
# access provider config and optional plugin images, and sets
# --clusterprofile-provider-file on the operator. ClusterProfile.status
# accessProviders are not managed by this chart.
multicluster:
enabled: false
accessProvidersConfig: {}
# plugins[] uses the Kubernetes "image" volume type
plugins: []
# accessProvidersConfig:
# providers:
# - name: token-secretreader
# execConfig:
# apiVersion: client.authentication.k8s.io/v1
# command: /access-plugins/token-secretreader/kubeconfig-secretreader-plugin
# provideClusterInfo: true
# plugins:
# - name: token-secretreader
# image: ghcr.io/example/plugin:v1.0.0
# mountPath: /access-plugins/token-secretreader
knative_operator:
image: gcr.io/knative-releases/knative.dev/operator/cmd/operator
tag: {{ tag }}
Expand Down
31 changes: 30 additions & 1 deletion config/crd/bases/operator.knative.dev_knativeeventings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ spec:
singular: knativeeventing
scope: Namespaced
versions:
- name: v1beta1
- additionalPrinterColumns:
- jsonPath: .spec.clusterProfileRef.name
name: Target Cluster
type: string
name: v1beta1
schema:
openAPIV3Schema:
description: KnativeEventing is the Schema for the eventings API
Expand Down Expand Up @@ -66,6 +70,26 @@ spec:
- URL
type: object
type: array
clusterProfileRef:
description: |-
ClusterProfileRef is an optional reference to a ClusterProfile resource
(multicluster.x-k8s.io/v1alpha1). When set, the operator reconciles
the component on the remote cluster described by the referenced
ClusterProfile instead of the local cluster.
properties:
name:
description: Name is the name of the ClusterProfile resource.
minLength: 1
type: string
namespace:
description: Namespace is the namespace of the ClusterProfile
resource.
minLength: 1
type: string
required:
- name
- namespace
type: object
config:
additionalProperties:
additionalProperties:
Expand Down Expand Up @@ -3666,6 +3690,11 @@ spec:
type: object
type: array
type: object
x-kubernetes-validations:
- message: spec.clusterProfileRef is immutable
rule: (!has(self.clusterProfileRef) && !has(oldSelf.clusterProfileRef))
|| (has(self.clusterProfileRef) && has(oldSelf.clusterProfileRef)
&& self.clusterProfileRef == oldSelf.clusterProfileRef)
status:
description: KnativeEventingStatus defines the observed state of KnativeEventing
properties:
Expand Down
Loading
Loading