Skip to content

Commit d49ad42

Browse files
porridgeclaude
andauthored
feat(roxctl): migrate-to-operator subcommands (#20155)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ab4ef42 commit d49ad42

23 files changed

Lines changed: 2115 additions & 0 deletions

‎.github/CODEOWNERS‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ sensor/admission-control/**/* @stackrox/core-workflows
4444
tests/ocp_dynamic_plugin_test.go @stackrox/sensor-ecosystem
4545
tests/performance/**/* @stackrox/sensor-ecosystem
4646
tests/roxctl/**/* @stackrox/sensor-ecosystem
47+
roxctl/central/migratetooperator/**/* @stackrox/sensor-ecosystem @stackrox/install
48+
roxctl/common/migratetooperator/**/* @stackrox/sensor-ecosystem @stackrox/install
49+
roxctl/sensor/migratetooperator/**/* @stackrox/sensor-ecosystem @stackrox/install
4750

4851
qa-tests-backend/**/* @janisz
4952

@@ -55,9 +58,13 @@ image/templates/** @stackrox/install
5558
operator/** @stackrox/install
5659
pkg/crs/** @stackrox/install
5760
pkg/helm/** @stackrox/install
61+
pkg/migratetooperator/** @stackrox/install
5862
sensor/kubernetes/certrefresh/** @stackrox/install @stackrox/sensor-ecosystem
5963
sensor/kubernetes/crs/** @stackrox/install @stackrox/sensor-ecosystem
6064
roxctl/central/crs/** @stackrox/install @stackrox/sensor-ecosystem
65+
# Also the e2e tests for legacy-to-operator migration commands in roxctl:
66+
tests/roxctl/bats-tests/cluster/sensor-migrate-to-operator.bats @stackrox/install
67+
tests/roxctl/bats-tests/local/central-migrate-to-operator.bats @stackrox/install
6168

6269
# Scanner team's responsibilities include anything related to the scanner itself and scanning utilities
6370
# such as vulnerability uploading and image integrations.

‎pkg/migratetooperator/central.go‎

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package migratetooperator
2+
3+
import (
4+
"github.com/pkg/errors"
5+
platform "github.com/stackrox/rox/operator/api/v1alpha1"
6+
"github.com/stackrox/rox/pkg/pointers"
7+
appsv1 "k8s.io/api/apps/v1"
8+
corev1 "k8s.io/api/core/v1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
)
11+
12+
// TransformToCentral detects the configuration from the given source and generates
13+
// a Central custom resource. It returns the CR and a list of warnings for the caller
14+
// to emit.
15+
func TransformToCentral(src Source) (*platform.Central, []string, error) {
16+
var warnings []string
17+
18+
cr := &platform.Central{
19+
TypeMeta: metav1.TypeMeta{
20+
APIVersion: "platform.stackrox.io/v1alpha1",
21+
Kind: "Central",
22+
},
23+
ObjectMeta: metav1.ObjectMeta{
24+
Name: "stackrox-central-services",
25+
},
26+
}
27+
28+
if err := setCentralDBSpec(src, cr); err != nil {
29+
return nil, nil, err
30+
}
31+
32+
centralDep, err := src.Deployment("central")
33+
if err != nil {
34+
return nil, nil, errors.Wrap(err, "retrieving central Deployment")
35+
}
36+
if centralDep == nil {
37+
return nil, nil, errors.New("central Deployment not found")
38+
}
39+
40+
setCentralMonitoring(centralDep, cr)
41+
if err := setCentralExposure(src, cr); err != nil {
42+
return nil, nil, err
43+
}
44+
if err := setCentralDefaultTLS(src, cr); err != nil {
45+
return nil, nil, err
46+
}
47+
setCentralDeclarativeConfig(centralDep, cr)
48+
setCentralTelemetry(centralDep, cr)
49+
setCentralPlaintextEndpoints(centralDep, cr)
50+
setCentralOfflineMode(centralDep, cr)
51+
52+
if detectCustomImages(centralDep) {
53+
warnings = append(warnings, "Detected non-default container images. "+
54+
"The operator does not support image overrides in the Central CR. "+
55+
"Configure RELATED_IMAGE_* environment variables on the operator Deployment instead.")
56+
}
57+
58+
return cr, warnings, nil
59+
}
60+
61+
func setCentralDBSpec(src Source, cr *platform.Central) error {
62+
dep, err := src.Deployment("central-db")
63+
if err != nil {
64+
return errors.Wrap(err, "retrieving central-db Deployment")
65+
}
66+
if dep == nil {
67+
return errors.New("central-db Deployment not found")
68+
}
69+
70+
db := &platform.CentralDBSpec{}
71+
diskVolume := findVolume(dep, "disk")
72+
if diskVolume == nil {
73+
return errors.New("central-db Deployment has no volume named \"disk\"")
74+
}
75+
switch {
76+
case diskVolume.PersistentVolumeClaim != nil:
77+
// Only claimName is set. Size and storageClassName are intentionally
78+
// omitted: the PVC already exists on the cluster, and the operator
79+
// rejects these fields for pre-existing ("BYO") PVCs.
80+
db.Persistence = &platform.DBPersistence{
81+
PersistentVolumeClaim: &platform.DBPersistentVolumeClaim{
82+
ClaimName: pointers.String(diskVolume.PersistentVolumeClaim.ClaimName),
83+
},
84+
}
85+
case diskVolume.HostPath != nil:
86+
db.Persistence = &platform.DBPersistence{
87+
HostPath: &platform.HostPathSpec{
88+
Path: pointers.String(diskVolume.HostPath.Path),
89+
},
90+
}
91+
default:
92+
return errors.New("central-db Deployment \"disk\" volume is neither a PVC nor a hostPath")
93+
}
94+
if ns := dep.Spec.Template.Spec.NodeSelector; len(ns) > 0 {
95+
db.NodeSelector = ns
96+
}
97+
cr.Spec.Central = &platform.CentralComponentSpec{DB: db}
98+
return nil
99+
}
100+
101+
func setCentralMonitoring(centralDep *appsv1.Deployment, cr *platform.Central) {
102+
isOpenShift := envVarIsTrue(centralDep, "ROX_ENABLE_OPENSHIFT_AUTH")
103+
if isOpenShift && !envVarIsTrue(centralDep, "ROX_ENABLE_SECURE_METRICS") {
104+
cr.Spec.Monitoring = &platform.GlobalMonitoring{
105+
OpenShiftMonitoring: &platform.OpenShiftMonitoring{
106+
Enabled: pointers.Bool(false),
107+
},
108+
}
109+
}
110+
}
111+
112+
func setCentralExposure(src Source, cr *platform.Central) error {
113+
svc, err := src.Service("central-loadbalancer")
114+
if err != nil {
115+
return errors.Wrap(err, "checking for central-loadbalancer Service")
116+
}
117+
route, err := src.Route("central")
118+
if err != nil {
119+
return errors.Wrap(err, "checking for central Route")
120+
}
121+
if svc != nil || route != nil {
122+
exposure := &platform.Exposure{}
123+
if svc != nil {
124+
switch svc.Spec.Type {
125+
case corev1.ServiceTypeLoadBalancer:
126+
exposure.LoadBalancer = &platform.ExposureLoadBalancer{Enabled: pointers.Bool(true)}
127+
case corev1.ServiceTypeNodePort:
128+
exposure.NodePort = &platform.ExposureNodePort{Enabled: pointers.Bool(true)}
129+
}
130+
}
131+
if route != nil {
132+
exposure.Route = &platform.ExposureRoute{Enabled: pointers.Bool(true)}
133+
}
134+
cr.Spec.Central.Exposure = exposure
135+
}
136+
return nil
137+
}
138+
139+
func setCentralDefaultTLS(src Source, cr *platform.Central) error {
140+
tlsSecret, err := src.Secret("central-default-tls-cert")
141+
if err != nil {
142+
return errors.Wrap(err, "checking for central-default-tls-cert Secret")
143+
}
144+
if tlsSecret != nil {
145+
cr.Spec.Central.DefaultTLSSecret = &platform.LocalSecretReference{
146+
Name: "central-default-tls-cert",
147+
}
148+
}
149+
return nil
150+
}
151+
152+
func setCentralDeclarativeConfig(centralDep *appsv1.Deployment, cr *platform.Central) {
153+
declConfigMaps, declSecrets := detectDeclarativeConfig(centralDep)
154+
if len(declConfigMaps) > 0 || len(declSecrets) > 0 {
155+
dc := &platform.DeclarativeConfiguration{}
156+
for _, name := range declConfigMaps {
157+
dc.ConfigMaps = append(dc.ConfigMaps, platform.LocalConfigMapReference{Name: name})
158+
}
159+
for _, name := range declSecrets {
160+
dc.Secrets = append(dc.Secrets, platform.LocalSecretReference{Name: name})
161+
}
162+
cr.Spec.Central.DeclarativeConfiguration = dc
163+
}
164+
}
165+
166+
func setCentralTelemetry(centralDep *appsv1.Deployment, cr *platform.Central) {
167+
if envVarValue(centralDep, "ROX_TELEMETRY_STORAGE_KEY_V1") == "DISABLED" {
168+
cr.Spec.Central.Telemetry = &platform.Telemetry{
169+
Enabled: pointers.Bool(false),
170+
}
171+
}
172+
}
173+
174+
func setCentralPlaintextEndpoints(centralDep *appsv1.Deployment, cr *platform.Central) {
175+
if pe := envVarValue(centralDep, "ROX_PLAINTEXT_ENDPOINTS"); pe != "" {
176+
if cr.Spec.Customize == nil {
177+
cr.Spec.Customize = &platform.CustomizeSpec{}
178+
}
179+
cr.Spec.Customize.EnvVars = append(cr.Spec.Customize.EnvVars, corev1.EnvVar{
180+
Name: "ROX_PLAINTEXT_ENDPOINTS",
181+
Value: pe,
182+
})
183+
}
184+
}
185+
186+
func setCentralOfflineMode(centralDep *appsv1.Deployment, cr *platform.Central) {
187+
if envVarIsTrue(centralDep, "ROX_OFFLINE_MODE") {
188+
cr.Spec.Egress = &platform.Egress{
189+
ConnectivityPolicy: platform.ConnectivityOffline.Pointer(),
190+
}
191+
}
192+
}

0 commit comments

Comments
 (0)