Skip to content
Merged
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
4 changes: 4 additions & 0 deletions .changes/unreleased/operator-Changed-20250721-184422.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
project: operator
kind: Changed
body: Redpanda operator, by default will now reconcile Redpanda custom resource accross namespaces. If user would like to scope reconciler to particular namespace please set `--namespace` flag into operator deployment.
time: 2025-07-21T18:44:22.117328+02:00
2 changes: 1 addition & 1 deletion acceptance/features/metrics.feature
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Feature: Metrics endpoint has authentication and authorization
metadata:
name: testing
"""
And "testing" service account has bounded "redpanda-operator-metrics-reader" cluster role
And "testing" service account has bounded "redpanda-operator-.*-metrics-reader" regexp cluster role name
Then its metrics endpoint should accept https request with "testing" service account token


40 changes: 38 additions & 2 deletions acceptance/features/operator-upgrades.feature
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
@operator:none
@operator:none @vcluster
Feature: Upgrading the operator
@skip:gke @skip:aks @skip:eks
Scenario: Operator upgrade from 2.4.5
Given I install redpanda helm chart version "v2.4.5" with the values:
Given I install local CRDs from "../operator/config/crd/bases"
And I install redpanda helm chart version "v2.4.5" with the values:
"""

"""
Expand All @@ -26,6 +27,41 @@ Feature: Upgrading the operator
image:
tag: dev
repository: localhost/redpanda-operator
crds:
enabled: true
"""
# use the new status as this will eventually get set
And cluster "operator-upgrade" should be stable with 1 nodes

@skip:gke @skip:aks @skip:eks
Scenario: Operator upgrade from 25.1.3
And I install redpanda helm chart version "v25.1.3" with the values:
"""
crds:
enabled: true
"""
And I apply Kubernetes manifest:
"""
---
apiVersion: cluster.redpanda.com/v1alpha2
kind: Redpanda
metadata:
name: operator-upgrade
spec:
clusterSpec:
statefulset:
replicas: 1
"""
# use just a Ready status check here since that's all the
# old operator supports
And cluster "operator-upgrade" is available
Then I can upgrade to the latest operator with the values:
"""
image:
tag: dev
repository: localhost/redpanda-operator
crds:
enabled: true
"""
# use the new status as this will eventually get set
And cluster "operator-upgrade" should be stable with 1 nodes
4 changes: 4 additions & 0 deletions acceptance/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,8 @@ k8s.io/client-go v0.33.2 h1:z8CIcc0P581x/J1ZYf4CNzRKxRvQAwoAolYPbtQes+E=
k8s.io/client-go v0.33.2/go.mod h1:9mCgT4wROvL948w6f6ArJNb7yQd7QsvqavDeZHvNmHo=
k8s.io/component-base v0.33.2 h1:sCCsn9s/dG3ZrQTX/Us0/Sx2R0G5kwa0wbZFYoVp/+0=
k8s.io/component-base v0.33.2/go.mod h1:/41uw9wKzuelhN+u+/C59ixxf4tYQKW7p32ddkYNe2k=
k8s.io/component-helpers v0.33.2 h1:AjCtYzst11NV8ensxV/2LEEXRwctqS7Bs44bje9Qcnw=
k8s.io/component-helpers v0.33.2/go.mod h1:PsPpiCk74n8pGWp1d6kjK/iSKBTyQfIacv02BNkMenU=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
Expand All @@ -1157,6 +1159,8 @@ oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc=
oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o=
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU=
sigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY=
sigs.k8s.io/gateway-api v1.1.0 h1:DsLDXCi6jR+Xz8/xd0Z1PYl2Pn0TyaFMOPPZIj4inDM=
Expand Down
20 changes: 12 additions & 8 deletions acceptance/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ import (
"testing"

"github.com/stretchr/testify/require"
"k8s.io/utils/ptr"

_ "github.com/redpanda-data/redpanda-operator/acceptance/steps"
framework "github.com/redpanda-data/redpanda-operator/harpoon"
"github.com/redpanda-data/redpanda-operator/harpoon/providers"
redpandav1alpha1 "github.com/redpanda-data/redpanda-operator/operator/api/redpanda/v1alpha1"
redpandav1alpha2 "github.com/redpanda-data/redpanda-operator/operator/api/redpanda/v1alpha2"
operatorchart "github.com/redpanda-data/redpanda-operator/operator/chart"
"github.com/redpanda-data/redpanda-operator/pkg/helm"
"github.com/redpanda-data/redpanda-operator/pkg/otelutil"
"github.com/redpanda-data/redpanda-operator/pkg/testutil"
Expand Down Expand Up @@ -67,7 +69,6 @@ var setupSuite = sync.OnceValues(func() (*framework.Suite, error) {
"installCRDs": true,
},
}).
WithCRDDirectory("../operator/config/crd/bases").
OnFeature(func(ctx context.Context, t framework.TestingT, tags ...framework.ParsedTag) {
// this actually switches namespaces, run it first
namespace := t.IsolateNamespace(ctx)
Expand All @@ -79,15 +80,18 @@ var setupSuite = sync.OnceValues(func() (*framework.Suite, error) {
t.InstallLocalHelmChart(ctx, "../operator/chart", helm.InstallOptions{
Name: "redpanda-operator",
Namespace: namespace,
Values: map[string]any{
"logLevel": "trace",
"image": map[string]any{
"tag": imageTag,
"repository": imageRepo,
Values: operatorchart.PartialValues{
LogLevel: ptr.To("trace"),
Image: &operatorchart.PartialImage{
Tag: ptr.To(imageTag),
Repository: ptr.To(imageRepo),
},
"additionalCmdFlags": []string{
CRDs: &operatorchart.PartialCRDs{
Enabled: ptr.To(true),
},
AdditionalCmdFlags: []string{
// These are needed for running decommissioning tests.
"--additional-controllers=all",
"--additional-controllers=nodeWatcher,decommission",
"--unbind-pvcs-after=5s",
// This is set to a lower timeout due to the way that our internal
// admin client handles retries to brokers that are gone but still
Expand Down
12 changes: 7 additions & 5 deletions acceptance/steps/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,10 +481,12 @@ func removeAllFinalizers(ctx context.Context, t framework.TestingT, gvk schema.G
list := &unstructured.UnstructuredList{}
list.SetGroupVersionKind(gvk)

require.NoError(t, t.List(ctx, list))
for i := range list.Items {
item := list.Items[i]
item.SetFinalizers(nil)
require.NoError(t, t.Update(ctx, &item))
// swallow errors for non-existent crds
if err := t.List(ctx, list); err == nil {
for i := range list.Items {
item := list.Items[i]
item.SetFinalizers(nil)
require.NoError(t, t.Update(ctx, &item))
}
}
}
5 changes: 5 additions & 0 deletions acceptance/steps/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ import (
redpandav1alpha2 "github.com/redpanda-data/redpanda-operator/operator/api/redpanda/v1alpha2"
)

// this is a nasty hack due to the fact that we can't disable the linter for typecheck
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😬 hadn't seen that issue before

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's gross, it happens because the linter freaks out when we're only doing type assertions/casting with an imported package (the places where we're upcasting runtime.Object to client.Object via o := obj.(client.Object)). Output looks like this as a linter failure and is unable to be suppressed:

acceptance/steps/k8s.go:13:2: "sigs.k8s.io/controller-runtime/pkg/client" imported and not used (typecheck)
--
  | "sigs.k8s.io/controller-runtime/pkg/client"

this is the only way of getting around it since I can't mute it. We can probably upgrade golangci-lint to see if any newer versions don't have this issue, but it's pretty 🤮 as is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upgrading golangci-lint is generally pretty painful punt that to another day / week / month 😛

// that reports sigs.k8s.io/controller-runtime/pkg/client as unused when it's solely used
// for type assertions
var _ client.Object = (client.Object)(nil)

func kubernetesObjectHasClusterOwner(ctx context.Context, t framework.TestingT, groupVersionKind, resourceName, clusterName string) {
var cluster redpandav1alpha2.Redpanda

Expand Down
4 changes: 4 additions & 0 deletions acceptance/steps/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ func iApplyKubernetesManifest(ctx context.Context, t framework.TestingT, manifes

t.ApplyManifest(ctx, file.Name())
}

func iInstallLocalCRDs(ctx context.Context, t framework.TestingT, directory string) {
t.ApplyManifest(ctx, directory)
}
25 changes: 21 additions & 4 deletions acceptance/steps/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import (
"context"
"fmt"
"os"
"regexp"
"strconv"
"strings"

"github.com/cucumber/godog"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -50,9 +53,18 @@ func acceptServiceAccountMetricsRequest(ctx context.Context, serviceAccountName
clientsForOperator(ctx, true, serviceAccountName, "").ExpectCorrectMetricsResponse(ctx)
}

func createClusterRoleBinding(ctx context.Context, serviceAccountName, clusterRoleName string) {
func createClusterRoleBinding(ctx context.Context, serviceAccountName, clusterRoleRegexp string) {
t := framework.T(ctx)

crs := &rbacv1.ClusterRoleList{}
require.NoError(t, t.List(ctx, crs))
clusterRoleName := ""
for _, cr := range crs.Items {
if regexp.MustCompile(clusterRoleRegexp).Match([]byte(cr.Name)) {
clusterRoleName = cr.Name
}
}

require.NoError(t, t.Create(ctx, &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: serviceAccountName,
Expand Down Expand Up @@ -112,9 +124,14 @@ func iInstallRedpandaHelmChartVersionWithTheValues(ctx context.Context, t framew
require.NoError(t, os.RemoveAll(file.Name()))
})

// these are needed for old versions of the operator
t.ApplyManifest(ctx, fmt.Sprintf("https://raw.githubusercontent.com/redpanda-data/redpanda-operator/refs/tags/%s/operator/config/crd/bases/toolkit.fluxcd.io/helm-controller.yaml", version))
t.ApplyManifest(ctx, fmt.Sprintf("https://raw.githubusercontent.com/redpanda-data/redpanda-operator/refs/tags/%s/operator/config/crd/bases/toolkit.fluxcd.io/source-controller.yaml", version))
major, err := strconv.Atoi(strings.Split(strings.TrimPrefix(version, "v"), ".")[0])
require.NoError(t, err)

if major < 25 {
// these are needed for old versions of the operator
t.ApplyManifest(ctx, fmt.Sprintf("https://raw.githubusercontent.com/redpanda-data/redpanda-operator/refs/tags/%s/operator/config/crd/bases/toolkit.fluxcd.io/helm-controller.yaml", version))
t.ApplyManifest(ctx, fmt.Sprintf("https://raw.githubusercontent.com/redpanda-data/redpanda-operator/refs/tags/%s/operator/config/crd/bases/toolkit.fluxcd.io/source-controller.yaml", version))
}

t.Cleanup(func(ctx context.Context) {
// make sure we remove all finalizers for these or the CRD cleanup will get wedged
Expand Down
3 changes: 2 additions & 1 deletion acceptance/steps/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func init() {
framework.RegisterStep(`^the operator is running$`, operatorIsRunning)
framework.RegisterStep(`^its metrics endpoint should reject http request with status code "([^"]*)"$`, requestMetricsEndpointPlainHTTP)
framework.RegisterStep(`^its metrics endpoint should reject authorization random token request with status code "([^"]*)"$`, requestMetricsEndpointWithTLSAndRandomToken)
framework.RegisterStep(`^"([^"]*)" service account has bounded "([^"]*)" cluster role$`, createClusterRoleBinding)
framework.RegisterStep(`^"([^"]*)" service account has bounded "([^"]*)" regexp cluster role name$`, createClusterRoleBinding)
framework.RegisterStep(`^its metrics endpoint should accept https request with "([^"]*)" service account token$`, acceptServiceAccountMetricsRequest)

// Helm migration scenario steps
Expand Down Expand Up @@ -79,4 +79,5 @@ func init() {
// Operator upgrade scenario steps
framework.RegisterStep(`^I can upgrade to the latest operator with the values:$`, iCanUpgradeToTheLatestOperatorWithTheValues)
framework.RegisterStep(`^I install redpanda helm chart version "([^"]*)" with the values:$`, iInstallRedpandaHelmChartVersionWithTheValues)
framework.RegisterStep(`^I install local CRDs from "([^"]*)"`, iInstallLocalCRDs)
}
2 changes: 1 addition & 1 deletion charts/redpanda/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ Annotations to add to the `rbac` resources.

### [rbac.enabled](https://artifacthub.io/packages/helm/redpanda-data/redpanda?modal=values&path=rbac.enabled)

Controls whether or not Roles, ClusterRoles, and bindings thereof will be generated. Disabling this very likely result in a non-functional deployment. If you use the Redpanda Operator, you must deploy it with the `--set rbac.createRPKBundleCRs=true` flag to give it the required ClusterRoles.
Controls whether or not Roles, ClusterRoles, and bindings thereof will be generated. Disabling this very likely result in a non-functional deployment.

**Default:** `true`

Expand Down
2 changes: 0 additions & 2 deletions charts/redpanda/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -731,8 +731,6 @@ rbac:
# -- Controls whether or not Roles, ClusterRoles, and bindings thereof will
# be generated. Disabling this very likely result in a non-functional
# deployment.
# If you use the Redpanda Operator, you must deploy it with the `--set
# rbac.createRPKBundleCRs=true` flag to give it the required ClusterRoles.
enabled: true

# -- Controls whether or not a Role and RoleBinding will be generated for the
Expand Down
10 changes: 0 additions & 10 deletions operator/api/redpanda/v1alpha1/redpanda_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,3 @@ type RedpandaList redpandav1alpha2.RedpandaList
func init() {
SchemeBuilder.Register(&Redpanda{}, &RedpandaList{})
}

// RedpandaReady registers a successful reconciliation of the given HelmRelease.
func RedpandaReady(rp *Redpanda) *Redpanda {
return (*Redpanda)(redpandav1alpha2.RedpandaReady((*redpandav1alpha2.Redpanda)(rp)))
}

// RedpandaNotReady registers a failed reconciliation of the given Redpanda.
func RedpandaNotReady(rp *Redpanda, reason, message string) *Redpanda {
return (*Redpanda)(redpandav1alpha2.RedpandaNotReady((*redpandav1alpha2.Redpanda)(rp), reason, message))
}
8 changes: 0 additions & 8 deletions operator/api/redpanda/v1alpha2/redpanda_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,9 @@
package v1alpha2

import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/conversion"
)

var _ conversion.Hub = &Redpanda{}

func (*Redpanda) Hub() {}

// SetupWebhookWithManager will setup the manager to manage the webhooks
func (in *Redpanda) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(in).
Complete()
}
79 changes: 45 additions & 34 deletions operator/api/redpanda/v1alpha2/redpanda_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (

"github.com/cockroachdb/errors"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
Expand Down Expand Up @@ -273,14 +272,6 @@ func (in *Redpanda) GetHelmReleaseName() string {
return in.Name
}

func (in *Redpanda) GetHelmRepositoryName() string {
helmRepository := in.Spec.ChartRef.HelmRepositoryName
if helmRepository == "" {
helmRepository = "redpanda-repository"
}
return helmRepository
}

func (in *Redpanda) ValuesJSON() (*apiextensionsv1.JSON, error) {
vyaml, err := json.Marshal(in.Spec.ClusterSpec)
if err != nil {
Expand All @@ -295,31 +286,6 @@ func (in *Redpanda) GenerationObserved() bool {
return in.Generation != 0 && in.Generation == in.Status.ObservedGeneration
}

// RedpandaReady registers a successful reconciliation of the given HelmRelease.
func RedpandaReady(rp *Redpanda) *Redpanda {
newCondition := metav1.Condition{
Type: ReadyCondition,
Status: metav1.ConditionTrue,
Reason: "RedpandaClusterDeployed",
Message: "Redpanda reconciliation succeeded",
}
apimeta.SetStatusCondition(rp.GetConditions(), newCondition)
rp.Status.LastAppliedRevision = rp.Status.LastAttemptedRevision
return rp
}

// RedpandaNotReady registers a failed reconciliation of the given Redpanda.
func RedpandaNotReady(rp *Redpanda, reason, message string) *Redpanda {
newCondition := metav1.Condition{
Type: ReadyCondition,
Status: metav1.ConditionFalse,
Reason: reason,
Message: message,
}
apimeta.SetStatusCondition(rp.GetConditions(), newCondition)
return rp
}

// GetConditions returns the status conditions of the object.
func (in *Redpanda) GetConditions() *[]metav1.Condition {
return &in.Status.Conditions
Expand Down Expand Up @@ -355,3 +321,48 @@ func (in *Redpanda) GetDot(restConfig *rest.Config) (*helmette.Dot, error) {
IsUpgrade: true,
}, in.Spec.ClusterSpec.DeepCopy())
}

// MinimalRedpandaSpec returns a [RedpandaSpec] with the smallest resource
// footprint possible for use in integration and E2E tests.
func MinimalRedpandaSpec() RedpandaSpec {
return RedpandaSpec{
// Any empty structs are to make setting them more ergonomic
// without having to worry about nil pointers.
ChartRef: ChartRef{},
ClusterSpec: &RedpandaClusterSpec{
Config: &Config{},
External: &External{
// Disable NodePort creation to stop broken tests from blocking others due to port conflicts.
Enabled: ptr.To(false),
},
Image: &RedpandaImage{
Repository: ptr.To("redpandadata/redpanda"), // Use docker.io to make caching easier and to not inflate our own metrics.
},
Console: &RedpandaConsole{
Enabled: ptr.To(false), // Speed up most cases by not enabling console to start.
},
Statefulset: &Statefulset{
Replicas: ptr.To(1), // Speed up tests ever so slightly.
PodAntiAffinity: &PodAntiAffinity{
// Disable the default "hard" affinity so we can
// schedule multiple redpanda Pods on a single
// kubernetes node. Useful for tests that require > 3
// brokers.
Type: ptr.To("soft"),
},
// Speeds up managed decommission tests. Decommissioned
// nodes will take the entirety of
// TerminationGracePeriodSeconds as the pre-stop hook
// doesn't account for decommissioned nodes.
TerminationGracePeriodSeconds: ptr.To(10),
},
Resources: &Resources{
CPU: &CPU{
// Inform redpanda/seastar that it's not going to get
// all the resources it's promised.
Overprovisioned: ptr.To(true),
},
},
},
}
}
Loading
Loading