Merge pull request #12198 from rook/mergify/bp/release-1.11/pr-11845

mgr: use mgr_role dynamic label to tag the active ceph manager (backport #11845)
This commit is contained in:
Travis Nielsen authored and GitHub committed 2023-05-05 14:50:58 -06:00
commit f741d5cb79
15 files changed
+231 -206

No files matched your search

@@ -59,9 +59,10 @@ If this value is empty, each pod will get an ephemeral directory to store their
For more details on the mons and when to choose a number other than `3`, see the [mon health doc](../../Storage-Configuration/Advanced/ceph-mon-health.md).
* `mgr`: manager top level section
* `count`: set number of ceph managers between `1` to `2`. The default value is 2.
If there are two managers, it is important for all mgr services point to the active mgr and not the passive mgr. Therefore, Rook will
automatically update all services (in the cluster namespace) that have a label `app=rook-ceph-mgr` with a selector pointing to the
active mgr. This commonly applies to services for the dashboard or the prometheus metrics collector.
If there are two managers, it is important for all mgr services point to the active mgr and not the standby mgr. Rook automatically
updates the label `mgr_role` on the mgr pods to be either `active` or `standby`. Therefore, services need just to add the label
`mgr_role=active` to their selector to point to the active mgr. This applies to all services that rely on the ceph mgr such as
the dashboard or the prometheus metrics collector.
* `modules`: is the list of Ceph manager modules to enable
* `crashCollector`: The settings for crash collector daemon(s).
* `disable`: is set to `true`, the crash collector will not run on any node where a Ceph daemon runs
+3 -2
View File
@@ -96,12 +96,13 @@ func runMgrSidecar(cmd *cobra.Command, args []string) error {
clusterInfo.CephVersion = *version
m := mgr.New(context, &clusterInfo, clusterSpec, "")
prevActiveMgr := "unknown"
for {
err := m.ReconcileActiveMgrServices(daemonName)
prevActiveMgr, err = m.UpdateActiveMgrLabel(daemonName, prevActiveMgr)
if err != nil {
logger.Errorf("failed to reconcile services. %v", err)
} else {
logger.Infof("successfully reconciled services. checking again in %ds", (int)(interval.Seconds()))
logger.Infof("successfully checked mgr_role label. checking again in %ds", (int)(interval.Seconds()))
}
time.Sleep(interval)
}
@@ -99,9 +99,12 @@ rules:
resources:
- deployments/scale
- deployments
- pods
verbs:
- patch
- delete
- get
- update
- apiGroups:
- ''
resources:
+3
View File
@@ -793,9 +793,12 @@ rules:
resources:
- deployments/scale
- deployments
- pods
verbs:
- patch
- delete
- get
- update
- apiGroups:
- ''
resources:
@@ -14,6 +14,7 @@ spec:
targetPort: 7000
selector:
app: rook-ceph-mgr
mgr_role: active
rook_cluster: rook-ceph
sessionAffinity: None
type: NodePort
@@ -14,6 +14,7 @@ spec:
targetPort: 8443
selector:
app: rook-ceph-mgr
mgr_role: active
rook_cluster: rook-ceph
sessionAffinity: None
type: NodePort
@@ -14,6 +14,7 @@ spec:
targetPort: 8443
selector:
app: rook-ceph-mgr
mgr_role: active
rook_cluster: rook-ceph
sessionAffinity: None
type: LoadBalancer
@@ -11,9 +11,9 @@ spec:
- rook-ceph
selector:
matchLabels:
mgr_role: active
app: rook-ceph-mgr
rook_cluster: rook-ceph
ceph_daemon_id: a
endpoints:
- port: http-metrics
path: /metrics
@@ -211,7 +211,7 @@ func (c *ClusterController) configureExternalClusterMonitoring(context *clusterd
)
// Create external monitoring Service
service, err := manager.MakeMetricsService(opcontroller.ExternalMgrAppName, "", opcontroller.ServiceExternalMetricName)
service, err := manager.MakeMetricsService(opcontroller.ExternalMgrAppName, opcontroller.ServiceExternalMetricName)
if err != nil {
return err
}
@@ -231,7 +231,7 @@ func (c *ClusterController) configureExternalClusterMonitoring(context *clusterd
// Deploy external ServiceMonitor
logger.Info("creating external service monitor")
// servicemonitor takes some metadata from the service for easy mapping
err = manager.EnableServiceMonitor("")
err = manager.EnableServiceMonitor()
if err != nil {
logger.Errorf("failed to enable external service monitor. %v", err)
} else {
+2 -2
View File
@@ -54,8 +54,8 @@ var (
dashboardInitWaitTime = 5 * time.Second
)
func (c *Cluster) configureDashboardService(activeDaemon string) error {
dashboardService, err := c.makeDashboardService(AppName, activeDaemon)
func (c *Cluster) configureDashboardService() error {
dashboardService, err := c.makeDashboardService(AppName)
if err != nil {
return err
}
@@ -131,7 +131,7 @@ func TestStartSecureDashboard(t *testing.T) {
}
dashboardInitWaitTime = 0
err := c.configureDashboardService("a")
err := c.configureDashboardService()
assert.NoError(t, err)
err = c.configureDashboardModules()
assert.NoError(t, err)
@@ -148,7 +148,7 @@ func TestStartSecureDashboard(t *testing.T) {
// disable the dashboard
c.spec.Dashboard.Enabled = false
err = c.configureDashboardService("a")
err = c.configureDashboardService()
assert.Nil(t, err)
err = c.configureDashboardModules()
assert.NoError(t, err)
@@ -163,7 +163,7 @@ func TestStartSecureDashboard(t *testing.T) {
// Set the port to something over 1024 and confirm the port and targetPort are the same
c.spec.Dashboard.Enabled = true
c.spec.Dashboard.Port = 1025
err = c.configureDashboardService("a")
err = c.configureDashboardService()
assert.Nil(t, err)
svc, err = c.context.Clientset.CoreV1().Services(clusterInfo.Namespace).Get(ctx, "rook-ceph-mgr-dashboard", metav1.GetOptions{})
@@ -175,7 +175,7 @@ func TestStartSecureDashboard(t *testing.T) {
// Fall back to the default port
c.spec.Dashboard.Enabled = true
c.spec.Dashboard.Port = 0
err = c.configureDashboardService("a")
err = c.configureDashboardService()
assert.Nil(t, err)
svc, err = c.context.Clientset.CoreV1().Services(clusterInfo.Namespace).Get(ctx, "rook-ceph-mgr-dashboard", metav1.GetOptions{})
+85 -66
View File
@@ -51,6 +51,7 @@ const (
PgautoscalerModuleName = "pg_autoscaler"
balancerModuleName = "balancer"
balancerModuleMode = "upmap"
mgrRoleLabelName = "mgr_role"
monitoringPath = "/etc/ceph-monitoring/"
serviceMonitorFile = "service-monitor.yaml"
// minimum amount of memory in MB to run the pod
@@ -178,18 +179,7 @@ func (c *Cluster) Start() error {
// check if any extra mgrs need to be removed
c.removeExtraMgrs(daemonIDs)
activeMgr := daemonIDs[0]
if len(daemonIDs) > 1 {
// When multiple mgrs are running, the mgr sidecar for the active mgr
// will create the services. However, the sidecar will only reconcile all
// the services when the active mgr changes. Here as part of the regular reconcile
// we trigger reconciling all the services to ensure they are current.
activeMgr, err = c.getActiveMgr()
if err != nil || activeMgr == "" {
activeMgr = ""
logger.Infof("cannot reconcile mgr services, no active mgr found. err=%v", err)
}
// reconcile mgr PDB
if err := c.reconcileMgrPDB(); err != nil {
return errors.Wrap(err, "failed to reconcile mgr PDB")
@@ -198,10 +188,9 @@ func (c *Cluster) Start() error {
// delete MGR PDB as the count is less than 2
c.deleteMgrPDB()
}
if activeMgr != "" {
if err := c.reconcileServices(activeMgr); err != nil {
return errors.Wrap(err, "failed to enable mgr services")
}
if err := c.reconcileServices(); err != nil {
return errors.Wrap(err, "failed to enable mgr services")
}
// configure the mgr modules
@@ -222,35 +211,52 @@ func (c *Cluster) removeExtraMgrs(daemonIDs []string) {
}
}
// ReconcileActiveMgrServices reconciles the services if the active mgr is the one running
// in the sidecar
func (c *Cluster) ReconcileActiveMgrServices(daemonNameToUpdate string) error {
// If the services are already set to this daemon, no need to attempt to update
svc, err := c.context.Clientset.CoreV1().Services(c.clusterInfo.Namespace).Get(c.clusterInfo.Context, AppName, metav1.GetOptions{})
// UpdateActiveMgrLabel updates the mgr_role label value to either
// active or standby depending on the status of the ceph mgr running
// in the pod
func (c *Cluster) UpdateActiveMgrLabel(daemonNameToUpdate string, prevActiveMgr string) (string, error) {
logger.Infof("Checking mgr_role label value of daemon %s (prev active mgr was %s)", daemonNameToUpdate, prevActiveMgr)
currActiveMgr, err := c.getActiveMgr()
if err != nil || currActiveMgr == "" {
logger.Infof("cannot update active mgr, no active mgr found. err=%v", err)
return "", err
} else if prevActiveMgr == currActiveMgr {
logger.Infof("active mgr is still the same (%s). No need to update mgr_role label on daemon %s.", currActiveMgr, daemonNameToUpdate)
return currActiveMgr, err
}
pods, err := c.context.Clientset.CoreV1().Pods(c.clusterInfo.Namespace).List(c.clusterInfo.Context, metav1.ListOptions{
LabelSelector: fmt.Sprintf("mgr=%s", daemonNameToUpdate),
})
if err != nil {
logger.Errorf("failed to check current mgr service, proceeding to update. %v", err)
} else {
currentDaemon := svc.Spec.Selector[controller.DaemonIDLabel]
if currentDaemon == daemonNameToUpdate {
logger.Infof("mgr services already set to daemon %q, no need to update", daemonNameToUpdate)
return nil
logger.Infof("cannot get pod for mgr daemon %s", daemonNameToUpdate)
return "", err // force mrg_role update in the next call
}
for i, pod := range pods.Items {
labels := pod.GetLabels()
cephDaemonId := labels[controller.DaemonIDLabel]
newMgrRole := "standby"
if currActiveMgr == cephDaemonId {
newMgrRole = "active"
}
currMgrRole, mgrHasLabel := labels[mgrRoleLabelName]
if !mgrHasLabel || currMgrRole != newMgrRole {
logger.Infof("updating mgr_role label value of daemon %s to '%s'. New active mgr is %s.", daemonNameToUpdate, newMgrRole, currActiveMgr)
labels[mgrRoleLabelName] = newMgrRole
pod.SetLabels(labels)
_, err = c.context.Clientset.CoreV1().Pods(c.clusterInfo.Namespace).Update(c.clusterInfo.Context, &pods.Items[i], metav1.UpdateOptions{})
if err != nil {
logger.Infof("cannot update the active mgr pod %q. err=%v", pods.Items[i].Name, err)
}
}
logger.Infof("mgr service currently set to %q, checking if need to update to %q", currentDaemon, daemonNameToUpdate)
}
activeName, err := c.getActiveMgr()
if err != nil {
return err
}
if activeName == "" {
return errors.New("active mgr not found")
}
if daemonNameToUpdate != activeName {
logger.Infof("no need for the mgr update since the active mgr is %q, rather than the local mgr %q", activeName, daemonNameToUpdate)
return nil
}
return c.reconcileServices(activeName)
return currActiveMgr, c.reconcileServices()
}
func (c *Cluster) getActiveMgr() (string, error) {
@@ -261,16 +267,15 @@ func (c *Cluster) getActiveMgr() (string, error) {
return mgrStat.ActiveName, nil
}
// reconcile the services, if the active mgr is not detected, use the default mgr
func (c *Cluster) reconcileServices(activeDaemon string) error {
logger.Infof("setting services to point to mgr %q", activeDaemon)
// reconcile the services,
func (c *Cluster) reconcileServices() error {
if err := c.configureDashboardService(activeDaemon); err != nil {
if err := c.configureDashboardService(); err != nil {
return errors.Wrap(err, "failed to configure dashboard svc")
}
// create the metrics service
service, err := c.MakeMetricsService(AppName, activeDaemon, serviceMetricName)
service, err := c.MakeMetricsService(AppName, serviceMetricName)
if err != nil {
return err
}
@@ -280,41 +285,56 @@ func (c *Cluster) reconcileServices(activeDaemon string) error {
// enable monitoring if `monitoring: enabled: true`
if c.spec.Monitoring.Enabled {
if err := c.EnableServiceMonitor(activeDaemon); err != nil {
if err := c.EnableServiceMonitor(); err != nil {
return errors.Wrap(err, "failed to enable service monitor")
}
}
return c.updateServiceSelectors(activeDaemon)
c.updateServiceSelectors()
return nil
}
// Make a best effort to update the services that have been labeled for being updated
// when the mgr has changed. They might be services for node ports, ingress, etc
func (c *Cluster) updateServiceSelectors(activeDaemon string) error {
// For the upgrade scenario: we remove any selector DaemonIDLabel from the all
// the services since new mgr HA doesn't rely on this label anymore and we add
// the new "mgr_role=active" instead.
func (c *Cluster) updateServiceSelectors() {
selector := metav1.ListOptions{LabelSelector: "app=rook-ceph-mgr"}
services, err := c.context.Clientset.CoreV1().Services(c.clusterInfo.Namespace).List(c.clusterInfo.Context, selector)
if err != nil {
return errors.Wrap(err, "failed to query mgr services to update")
logger.Errorf("failed to query mgr services to update labels: %v", err)
return
}
for i, service := range services.Items {
if service.Spec.Selector == nil {
service.Spec.Selector = map[string]string{}
}
// Update the selector on the service to point to the active mgr
if service.Spec.Selector[controller.DaemonIDLabel] == activeDaemon {
logger.Infof("no need to update service %q", service.Name)
continue
}
// Update the service to point to the new active mgr
service.Spec.Selector[controller.DaemonIDLabel] = activeDaemon
logger.Infof("updating selector on mgr service %q to active mgr %q", service.Name, activeDaemon)
if _, err := c.context.Clientset.CoreV1().Services(c.clusterInfo.Namespace).Update(c.clusterInfo.Context, &services.Items[i], metav1.UpdateOptions{}); err != nil {
logger.Errorf("failed to update service %q. %v", service.Name, err)
} else {
logger.Infof("service %q successfully updated to active mgr %q", service.Name, activeDaemon)
updateService := false
// Check if the service has a DaemonIDLabel (legacy mgr HA implementation) and remove it
_, hasDaemonLabel := service.Spec.Selector[controller.DaemonIDLabel]
if hasDaemonLabel {
logger.Infof("removing %s selector label on mgr service %q", controller.DaemonIDLabel, service.Name)
delete(service.Spec.Selector, controller.DaemonIDLabel)
updateService = true
}
// In case the service doesn't have the new mgr_role DaemonIDLabel we add it
_, hasMgrRoleLabel := service.Spec.Selector[mgrRoleLabelName]
if !hasMgrRoleLabel {
logger.Infof("adding %s selector label on mgr service %q", mgrRoleLabelName, service.Name)
service.Spec.Selector[mgrRoleLabelName] = "active"
updateService = true
}
if updateService {
if _, err := c.context.Clientset.CoreV1().Services(c.clusterInfo.Namespace).Update(c.clusterInfo.Context, &services.Items[i], metav1.UpdateOptions{}); err != nil {
logger.Errorf("failed to update service %q. %v", service.Name, err)
} else {
logger.Infof("service %q successfully updated", service.Name)
}
}
}
return nil
}
func (c *Cluster) configureModules(daemonIDs []string) {
@@ -480,7 +500,7 @@ func wellKnownModule(name string) bool {
}
// EnableServiceMonitor add a servicemonitor that allows prometheus to scrape from the monitoring endpoint of the cluster
func (c *Cluster) EnableServiceMonitor(activeDaemon string) error {
func (c *Cluster) EnableServiceMonitor() error {
serviceMonitor, err := k8sutil.GetServiceMonitor(path.Join(monitoringPath, serviceMonitorFile))
if err != nil {
return errors.Wrap(err, "service monitor could not be enabled")
@@ -497,7 +517,6 @@ func (c *Cluster) EnableServiceMonitor(activeDaemon string) error {
return errors.Wrapf(err, "failed to set owner reference to service monitor %q", serviceMonitor.Name)
}
serviceMonitor.Spec.NamespaceSelector.MatchNames = []string{c.clusterInfo.Namespace}
serviceMonitor.Spec.Selector.MatchLabels = c.selectorLabels(activeDaemon)
applyMonitoringLabels(c, serviceMonitor)
+99 -111
View File
@@ -31,6 +31,7 @@ import (
cephclient "github.com/rook/rook/pkg/daemon/ceph/client"
"github.com/rook/rook/pkg/operator/ceph/controller"
cephver "github.com/rook/rook/pkg/operator/ceph/version"
"github.com/rook/rook/pkg/operator/k8sutil"
testopk8s "github.com/rook/rook/pkg/operator/k8sutil/test"
testop "github.com/rook/rook/pkg/operator/test"
exectest "github.com/rook/rook/pkg/util/exec/test"
@@ -45,10 +46,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)
func TestStartMgr(t *testing.T) {
var deploymentsUpdated *[]*apps.Deployment
updateDeploymentAndWait, deploymentsUpdated = testopk8s.UpdateDeploymentAndWaitStub()
func createNewCluster(t *testing.T) *Cluster {
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
logger.Infof("Execute: %s %v", command, args)
@@ -92,8 +90,17 @@ func TestStartMgr(t *testing.T) {
c := New(ctx, clusterInfo, clusterSpec, "myversion")
defer os.RemoveAll(c.spec.DataDirHostPath)
return c
}
func TestStartMgr(t *testing.T) {
var deploymentsUpdated *[]*apps.Deployment
updateDeploymentAndWait, deploymentsUpdated = testopk8s.UpdateDeploymentAndWaitStub()
c := createNewCluster(t)
// start a basic service
err = c.Start()
err := c.Start()
assert.NoError(t, err)
validateStart(t, c)
assert.ElementsMatch(t, []string{}, testopk8s.DeploymentNamesUpdated(deploymentsUpdated))
@@ -176,6 +183,49 @@ func validateServices(t *testing.T, c *Cluster) {
}
}
func TestActiveMgrLabels(t *testing.T) {
var deploymentsUpdated *[]*apps.Deployment
updateDeploymentAndWait, deploymentsUpdated = testopk8s.UpdateDeploymentAndWaitStub()
c := createNewCluster(t)
err := c.Start()
assert.NoError(t, err)
validateStart(t, c)
mgrNames := []string{"a", "b"}
for i := 0; i < c.spec.Mgr.Count; i++ {
// Simulate the Mgr pod having been created
daemonName := mgrNames[i]
mgrPod := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("rook-ceph-mgr-%s", daemonName),
Labels: map[string]string{k8sutil.AppAttr: "rook-ceph-mgr",
"instance": daemonName,
"ceph_daemon_id": daemonName,
"mgr": daemonName,
"rook_cluster": "ns"}}}
_, err = c.context.Clientset.CoreV1().Pods(c.clusterInfo.Namespace).Create(c.clusterInfo.Context, mgrPod, metav1.CreateOptions{})
assert.NoError(t, err)
}
for i := 0; i < c.spec.Mgr.Count; i++ {
daemonName := mgrNames[i]
_, err := c.context.Clientset.AppsV1().Deployments(c.clusterInfo.Namespace).Get(context.TODO(), fmt.Sprintf("rook-ceph-mgr-%s", daemonName), metav1.GetOptions{})
assert.NoError(t, err)
_, err = c.UpdateActiveMgrLabel(daemonName, "")
assert.NoError(t, err)
pods, err := c.context.Clientset.CoreV1().Pods(c.clusterInfo.Namespace).List(c.clusterInfo.Context, metav1.ListOptions{LabelSelector: "app=rook-ceph-mgr"})
assert.NoError(t, err)
assert.Contains(t, pods.Items[i].Labels, "mgr_role")
if daemonName == "a" {
assert.Equal(t, pods.Items[i].Labels["mgr_role"], "active")
} else if daemonName == "b" {
assert.Equal(t, pods.Items[i].Labels["mgr_role"], "standby")
}
}
testopk8s.ClearDeploymentsUpdated(deploymentsUpdated)
}
func TestUpdateServiceSelectors(t *testing.T) {
clientset := testop.New(t, 3)
ctx := &clusterd.Context{Clientset: clientset}
@@ -188,116 +238,54 @@ func TestUpdateServiceSelectors(t *testing.T) {
}
c := &Cluster{spec: spec, context: ctx, clusterInfo: clusterInfo}
t.Run("initial active daemon", func(t *testing.T) {
activeDaemon := "a"
err := c.reconcileServices(activeDaemon)
assert.NoError(t, err)
validateServiceActiveDaemon(t, c, activeDaemon, 2, 0)
})
t.Run("update active daemon", func(t *testing.T) {
activeDaemon := "b"
err := c.updateServiceSelectors(activeDaemon)
assert.NoError(t, err)
validateServiceActiveDaemon(t, c, activeDaemon, 2, 0)
})
t.Run("skip non-mgr services", func(t *testing.T) {
svc := corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "mysvc"}}
_, err := c.context.Clientset.CoreV1().Services(c.clusterInfo.Namespace).Create(clusterInfo.Context, &svc, metav1.CreateOptions{})
assert.NoError(t, err)
activeDaemon := "c"
err = c.updateServiceSelectors(activeDaemon)
assert.NoError(t, err)
validateServiceActiveDaemon(t, c, activeDaemon, 2, 1)
})
}
func validateServiceActiveDaemon(t *testing.T, c *Cluster, activeDaemon string, expectedUpdated, expectedSkipped int) {
services, err := c.context.Clientset.CoreV1().Services(c.clusterInfo.Namespace).List(c.clusterInfo.Context, metav1.ListOptions{})
assert.NoError(t, err)
skipped := 0
updated := 0
for _, service := range services.Items {
if service.Labels["app"] == "rook-ceph-mgr" {
updated++
assert.Equal(t, activeDaemon, service.Spec.Selector[controller.DaemonIDLabel])
} else {
skipped++
assert.Equal(t, "", service.Spec.Selector[controller.DaemonIDLabel])
// Make sure we remove the daemon_id label from the selector
// of all services with a label "app=rook-ceph-mgr"
t.Run("remove daemon_id from mgr services", func(t *testing.T) {
svc1 := corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "svc1",
Labels: map[string]string{
"app": "rook-ceph-mgr",
"svc": "rook-ceph-mgr",
controller.DaemonIDLabel: "a",
},
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": "rook-ceph-mgr",
controller.DaemonIDLabel: "a",
}},
}
}
assert.Equal(t, expectedUpdated, updated)
assert.Equal(t, expectedSkipped, skipped)
}
svc2 := corev1.Service{
ObjectMeta: metav1.ObjectMeta{Name: "svc2"},
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": "rook-ceph-mgr",
controller.DaemonIDLabel: "a",
}},
}
_, err := c.context.Clientset.CoreV1().Services(c.clusterInfo.Namespace).Create(clusterInfo.Context, &svc1, metav1.CreateOptions{})
assert.NoError(t, err)
_, err2 := c.context.Clientset.CoreV1().Services(c.clusterInfo.Namespace).Create(clusterInfo.Context, &svc2, metav1.CreateOptions{})
assert.NoError(t, err2)
func TestMgrSidecarReconcile(t *testing.T) {
activeMgr := "a"
calledMgrStat := false
spec := cephv1.ClusterSpec{
Mgr: cephv1.MgrSpec{Count: 1},
Dashboard: cephv1.DashboardSpec{
Enabled: true,
Port: 7000,
},
}
executor := &exectest.MockExecutor{
MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) {
logger.Infof("Command: %s %v", command, args)
if args[1] == "stat" {
calledMgrStat = true
}
return fmt.Sprintf(`{"active_name":"%s"}`, activeMgr), nil
},
}
clientset := testop.New(t, 3)
configDir := t.TempDir()
ctx := &clusterd.Context{
Executor: executor,
ConfigDir: configDir,
Clientset: clientset,
}
clusterInfo := cephclient.AdminTestClusterInfo("mycluster")
clusterInfo.SetName("test")
c := &Cluster{spec: spec, context: ctx, clusterInfo: clusterInfo}
// Check the the label has been only removed from svc1
c.updateServiceSelectors()
// Update services according to the active mgr
calledMgrStat = false
clusterInfo.CephVersion = cephver.CephVersion{Major: 17, Minor: 2, Build: 0}
err := c.ReconcileActiveMgrServices(activeMgr)
assert.NoError(t, err)
assert.True(t, calledMgrStat)
validateServices(t, c)
validateServiceMatches(t, c, "a")
// Make sure we remove controller.DaemonIDLabel label from all services tagged as 'app: rook-ceph-mgr'
// and we add the selector label "mgr_role" set to "active"
updatedService1, err := c.context.Clientset.CoreV1().Services(c.clusterInfo.Namespace).Get(clusterInfo.Context, "svc1", metav1.GetOptions{})
assert.NoError(t, err)
assert.NotContains(t, updatedService1.Spec.Selector, controller.DaemonIDLabel)
assert.Contains(t, updatedService1.Spec.Selector, "mgr_role")
assert.Equal(t, updatedService1.Spec.Selector["mgr_role"], "active")
// nothing is created or updated when the requested mgr is not the active mgr
clusterInfo.CephVersion = cephver.CephVersion{Major: 16, Minor: 2, Build: 5}
err = c.ReconcileActiveMgrServices("b")
assert.NoError(t, err)
assert.True(t, calledMgrStat)
_, err = c.context.Clientset.CoreV1().Services(c.clusterInfo.Namespace).Get(context.TODO(), "rook-ceph-mgr", metav1.GetOptions{})
assert.True(t, kerrors.IsNotFound(err))
// nothing is updated when the requested mgr is not the active mgr
activeMgr = "b"
err = c.ReconcileActiveMgrServices("b")
assert.NoError(t, err)
validateServices(t, c)
validateServiceMatches(t, c, "b")
}
func validateServiceMatches(t *testing.T, c *Cluster, expectedActive string) {
// The service labels should match the active mgr
svc, err := c.context.Clientset.CoreV1().Services(c.clusterInfo.Namespace).Get(context.TODO(), "rook-ceph-mgr", metav1.GetOptions{})
assert.NoError(t, err)
matchDaemon, ok := svc.Spec.Selector["ceph_daemon_id"]
assert.True(t, ok)
assert.Equal(t, expectedActive, matchDaemon)
// clean up the service for the next test
err = c.context.Clientset.CoreV1().Services(c.clusterInfo.Namespace).Delete(context.TODO(), "rook-ceph-mgr", metav1.DeleteOptions{})
assert.NoError(t, err)
// Make sure services not tagged as 'app: rook-ceph-mgr' are not modified
updatedService2, err := c.context.Clientset.CoreV1().Services(c.clusterInfo.Namespace).Get(clusterInfo.Context, "svc2", metav1.GetOptions{})
assert.NoError(t, err)
assert.Contains(t, updatedService2.Spec.Selector, controller.DaemonIDLabel)
assert.NotContains(t, updatedService2.Spec.Selector, "mgr_role")
})
}
func TestConfigureModules(t *testing.T) {
+16 -11
View File
@@ -255,8 +255,10 @@ func (c *Cluster) makeCmdProxySidecarContainer(mgrConfig *mgrConfig) v1.Containe
}
// MakeMetricsService generates the Kubernetes service object for the monitoring service
func (c *Cluster) MakeMetricsService(name, activeDaemon, servicePortMetricName string) (*v1.Service, error) {
labels := c.selectorLabels(activeDaemon)
func (c *Cluster) MakeMetricsService(name, servicePortMetricName string) (*v1.Service, error) {
labels := controller.AppLabels(AppName, c.clusterInfo.Namespace)
selectorLabels := c.buildSelectorLabels(labels)
svc := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
@@ -278,7 +280,7 @@ func (c *Cluster) MakeMetricsService(name, activeDaemon, servicePortMetricName s
// If the cluster is external we don't need to add the selector
if name != controller.ExternalMgrAppName {
svc.Spec.Selector = labels
svc.Spec.Selector = selectorLabels
}
err := c.clusterInfo.OwnerInfo.SetControllerReference(svc)
@@ -288,8 +290,10 @@ func (c *Cluster) MakeMetricsService(name, activeDaemon, servicePortMetricName s
return svc, nil
}
func (c *Cluster) makeDashboardService(name, activeDaemon string) (*v1.Service, error) {
labels := c.selectorLabels(activeDaemon)
func (c *Cluster) makeDashboardService(name string) (*v1.Service, error) {
labels := controller.AppLabels(AppName, c.clusterInfo.Namespace)
selectorLabels := c.buildSelectorLabels(labels)
portName := "https-dashboard"
if !c.spec.Dashboard.SSL {
@@ -302,7 +306,7 @@ func (c *Cluster) makeDashboardService(name, activeDaemon string) (*v1.Service,
Labels: labels,
},
Spec: v1.ServiceSpec{
Selector: labels,
Selector: selectorLabels,
Type: v1.ServiceTypeClusterIP,
Ports: []v1.ServicePort{
{
@@ -352,10 +356,11 @@ func (c *Cluster) cephMgrOrchestratorModuleEnvs() []v1.EnvVar {
return envVars
}
func (c *Cluster) selectorLabels(activeDaemon string) map[string]string {
labels := controller.AppLabels(AppName, c.clusterInfo.Namespace)
if activeDaemon != "" {
labels[controller.DaemonIDLabel] = activeDaemon
func (c *Cluster) buildSelectorLabels(labels map[string]string) map[string]string {
selectorLabels := make(map[string]string)
for k, v := range labels {
selectorLabels[k] = v
}
return labels
selectorLabels["mgr_role"] = "active"
return selectorLabels
}
+6 -4
View File
@@ -23,7 +23,6 @@ import (
"github.com/rook/rook/pkg/clusterd"
cephclient "github.com/rook/rook/pkg/daemon/ceph/client"
"github.com/rook/rook/pkg/operator/ceph/config"
"github.com/rook/rook/pkg/operator/ceph/controller"
"github.com/rook/rook/pkg/operator/ceph/test"
optest "github.com/rook/rook/pkg/operator/test"
"github.com/stretchr/testify/assert"
@@ -113,14 +112,17 @@ func TestServiceSpec(t *testing.T) {
clusterSpec := cephv1.ClusterSpec{}
c := New(&clusterd.Context{Clientset: clientset}, clusterInfo, clusterSpec, "myversion")
s, err := c.MakeMetricsService("rook-mgr", "foo", serviceMetricName)
s, err := c.MakeMetricsService("rook-mgr", serviceMetricName)
assert.NoError(t, err)
assert.NotNil(t, s)
assert.Equal(t, "rook-mgr", s.Name)
assert.Equal(t, 1, len(s.Spec.Ports))
assert.Equal(t, 3, len(s.Labels))
assert.Equal(t, 2, len(s.Labels))
// Check service selector
assert.Equal(t, 3, len(s.Spec.Selector))
assert.Equal(t, "foo", s.Spec.Selector[controller.DaemonIDLabel])
assert.Equal(t, s.Spec.Selector["app"], "rook-ceph-mgr")
assert.Equal(t, s.Spec.Selector["mgr_role"], "active")
assert.Equal(t, s.Spec.Selector["rook_cluster"], "ns")
}
func TestHostNetwork(t *testing.T) {