Skip to content

Commit

Permalink
Merge pull request canonical#340 from masnax/api-remove
Browse files Browse the repository at this point in the history
Use microcluster API to remove cluster members
  • Loading branch information
sabaini authored Jul 8, 2024
2 parents 2cbe158 + 9266ea6 commit b17a96f
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 126 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package main
package ceph

import (
"testing"

"github.com/canonical/microceph/microceph/api/types"
"github.com/canonical/microceph/microceph/client"
"github.com/canonical/microceph/microceph/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"testing"

"github.com/stretchr/testify/suite"
)
Expand Down Expand Up @@ -46,7 +47,6 @@ func (s *clusterRemoveSuite) TestRemoveNode() {
nil,
)
m.On("DeleteService", mock.Anything, "foonode", "mon").Return(nil).Once()
m.On("DeleteClusterMember", mock.Anything, "foonode", false).Return(nil).Once()

err := removeNode(nil, "foonode", false)

Expand All @@ -68,9 +68,6 @@ func (s *clusterRemoveSuite) TestRemoveNodeWithDisks() {
err := removeNode(nil, "foonode", false)

assert.Error(s.T(), err)

// assert that we didn't try to delete the node
m.AssertNotCalled(s.T(), "DeleteClusterMember", mock.Anything, "foonode", false)
}

// TestRemoveNodeLastMon tests that we don't try to delete a node that has the last mon
Expand All @@ -93,9 +90,6 @@ func (s *clusterRemoveSuite) TestRemoveNodeLastMon() {
err := removeNode(nil, "foonode", false)

assert.Error(s.T(), err)

// assert that we didn't try to delete the node
m.AssertNotCalled(s.T(), "DeleteClusterMember", mock.Anything, "foonode", false)
}

// TestRemoveNodeForce tests that we don't check prerequisites and delete a node if forced
Expand All @@ -114,7 +108,6 @@ func (s *clusterRemoveSuite) TestRemoveNodeForce() {
nil,
)
m.On("DeleteService", mock.Anything, "foonode", "mon").Return(nil).Once()
m.On("DeleteClusterMember", mock.Anything, "foonode", true).Return(nil).Once()

err := removeNode(nil, "foonode", true)

Expand Down
127 changes: 127 additions & 0 deletions microceph/ceph/remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package ceph

import (
"fmt"

"github.com/canonical/lxd/shared/logger"
microCli "github.com/canonical/microcluster/client"
"github.com/canonical/microcluster/microcluster"
"github.com/canonical/microcluster/state"

"github.com/canonical/microceph/microceph/client"
)

// PreRemove cleans up the underlying ceph services before the node is removed from the dqlite cluster.
func PreRemove(m *microcluster.MicroCluster) func(s *state.State, force bool) error {
return func(s *state.State, force bool) error {
cli, err := m.LocalClient()
if err != nil {
return err
}

return removeNode(cli, s.Name(), force)
}
}

func removeNode(cli *microCli.Client, node string, force bool) error {

logger.Debugf("Removing cluster member %v, force: %v", node, force)

// check prerquisites unless we're forcing
if !force {
err := checkPrerequisites(cli, node)
if err != nil {
return err
}
}

// delete from ceph
err := deleteNodeServices(cli, node)
if err != nil {
// forcing makes errs non-fatal
if !force {
return err
}
logger.Warnf("Error deleting services from node %v: %v", node, err)
}

return nil
}

func checkPrerequisites(cli *microCli.Client, name string) error {
// check if member exists
clusterMembers, err := client.MClient.GetClusterMembers(cli)
if err != nil {
return fmt.Errorf("Error getting cluster members: %v", err)
}
found := false
for _, member := range clusterMembers {
if member == name {
found = true
}
}
if !found {
return fmt.Errorf("Node %v not found", name)
}

// check if any OSDs present
disks, err := client.MClient.GetDisks(cli)
if err != nil {
return fmt.Errorf("Error getting disks: %v", err)
}
found = false
for _, disk := range disks {
if disk.Location == name {
found = true
}
}
logger.Debugf("Disks: %v, found: %v", disks, found)
if found {
return fmt.Errorf("Node %v still has disks configured, remove before proceeding", name)
}

// check if this node has the last mon
services, err := client.MClient.GetServices(cli)
if err != nil {
return fmt.Errorf("Error getting services: %v", err)
}
// create a map of service names counters
// init with false
foundMap := map[string]int{
"mon": 0,
"mgr": 0,
"mds": 0,
}
// loop through services and check service counts
for _, service := range services {
if service.Location == name {
continue
}
foundMap[service.Service]++
}
logger.Debugf("Services: %v, foundMap: %v", services, foundMap)
if foundMap["mon"] < 3 || foundMap["mgr"] < 1 || foundMap["mds"] < 1 {
return fmt.Errorf("Need at least 3 mon, 1 mds, and 1 mgr besides %v", name)
}

return nil
}

func deleteNodeServices(cli *microCli.Client, name string) error {
services, err := client.MClient.GetServices(cli)
if err != nil {
return err
}
for _, service := range services {
logger.Debugf("Check for deletion: %s", service)
if service.Location == name {
logger.Debugf("Deleting service %s", service)
err = client.MClient.DeleteService(cli, service.Location, service.Service)
if err != nil {
logger.Warnf("Fault deleting service %v on node %v: %v", service.Service, service.Location, err)
}
}
}
return nil

}
118 changes: 2 additions & 116 deletions microceph/cmd/microceph/cluster_remove.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package main

import (
"fmt"
"context"

"github.com/canonical/lxd/shared/logger"
microCli "github.com/canonical/microcluster/client"
"github.com/canonical/microcluster/microcluster"
"github.com/spf13/cobra"

"github.com/canonical/microceph/microceph/client"
)

type cmdClusterRemove struct {
Expand Down Expand Up @@ -45,115 +41,5 @@ func (c *cmdClusterRemove) Run(cmd *cobra.Command, args []string) error {
return err
}

return removeNode(cli, args[0], c.flagForce)
}

func removeNode(cli *microCli.Client, node string, force bool) error {

logger.Debugf("Removing cluster member %v, force: %v", node, force)

// check prerquisites unless we're forcing
if !force {
err := checkPrerequisites(cli, node)
if err != nil {
return err
}
}

// delete from ceph
err := deleteNodeServices(cli, node)
if err != nil {
// forcing makes errs non-fatal
if !force {
return err
}
logger.Warnf("Error deleting services from node %v: %v", node, err)
}

// delete from cluster db
err = client.MClient.DeleteClusterMember(cli, node, force)
logger.Debugf("DeleteClusterMember %v: %v", node, err)
if err != nil {
return err
}

return nil
}

func checkPrerequisites(cli *microCli.Client, name string) error {
// check if member exists
clusterMembers, err := client.MClient.GetClusterMembers(cli)
if err != nil {
return fmt.Errorf("Error getting cluster members: %v", err)
}
found := false
for _, member := range clusterMembers {
if member == name {
found = true
}
}
if !found {
return fmt.Errorf("Node %v not found", name)
}

// check if any OSDs present
disks, err := client.MClient.GetDisks(cli)
if err != nil {
return fmt.Errorf("Error getting disks: %v", err)
}
found = false
for _, disk := range disks {
if disk.Location == name {
found = true
}
}
logger.Debugf("Disks: %v, found: %v", disks, found)
if found {
return fmt.Errorf("Node %v still has disks configured, remove before proceeding", name)
}

// check if this node has the last mon
services, err := client.MClient.GetServices(cli)
if err != nil {
return fmt.Errorf("Error getting services: %v", err)
}
// create a map of service names counters
// init with false
foundMap := map[string]int{
"mon": 0,
"mgr": 0,
"mds": 0,
}
// loop through services and check service counts
for _, service := range services {
if service.Location == name {
continue
}
foundMap[service.Service]++
}
logger.Debugf("Services: %v, foundMap: %v", services, foundMap)
if foundMap["mon"] < 3 || foundMap["mgr"] < 1 || foundMap["mds"] < 1 {
return fmt.Errorf("Need at least 3 mon, 1 mds, and 1 mgr besides %v", name)
}

return nil
}

func deleteNodeServices(cli *microCli.Client, name string) error {
services, err := client.MClient.GetServices(cli)
if err != nil {
return err
}
for _, service := range services {
logger.Debugf("Check for deletion: %s", service)
if service.Location == name {
logger.Debugf("Deleting service %s", service)
err = client.MClient.DeleteService(cli, service.Location, service.Service)
if err != nil {
logger.Warnf("Fault deleting service %v on node %v: %v", service.Service, service.Location, err)
}
}
}
return nil

return cli.DeleteClusterMember(context.Background(), args[0], c.flagForce)
}
2 changes: 2 additions & 0 deletions microceph/cmd/microcephd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ func (c *cmdDaemon) Run(cmd *cobra.Command, args []string) error {
return ceph.Start(interf)
}

h.PreRemove = ceph.PreRemove(m)

m.AddServers(api.Servers)
return m.Start(context.Background(), database.SchemaExtensions, nil, h)
}
Expand Down

0 comments on commit b17a96f

Please sign in to comment.