Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Single node support #319

Merged
merged 7 commits into from
Aug 29, 2024
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
1 change: 1 addition & 0 deletions cmd/microcloud/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func (c *cmdAdd) Run(cmd *cobra.Command, args []string) error {

cfg := initConfig{
bootstrap: false,
setupMany: true,
autoSetup: c.flagAutoSetup,
wipeAllDisks: c.flagWipe,
common: c.common,
Expand Down
6 changes: 5 additions & 1 deletion cmd/microcloud/ask.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func (c *initConfig) askAddress() error {
return fmt.Errorf("Cloud not find valid subnet for address %q", listenAddr)
}

if !c.autoSetup {
if !c.autoSetup && c.setupMany {
filter, err := c.asker.AskBool(fmt.Sprintf("Limit search for other MicroCloud servers to %s? (yes/no) [default=yes]: ", subnet.String()), "yes")
if err != nil {
return err
Expand Down Expand Up @@ -1296,6 +1296,10 @@ func (c *initConfig) askCephNetwork(sh *service.Handler) error {
// In auto setup, we will expect no initialized services so that we can be opinionated about how we configure the cluster without user input.
// This works by deleting the record for the service from the `service.Handler`, thus ignoring it for the remainder of the setup.
func (c *initConfig) askClustered(s *service.Handler, expectedServices []types.ServiceType) error {
if !c.setupMany {
return nil
}

for _, serviceType := range expectedServices {
for name, info := range c.state {
_, newSystem := c.systems[name]
Expand Down
15 changes: 15 additions & 0 deletions cmd/microcloud/main_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ type initConfig struct {
// autoSetup indicates whether questions should automatically choose defaults.
autoSetup bool

// setupMany indicates whether we are setting up remote nodes concurrently, or just a single cluster member.
setupMany bool

// lookupTimeout is the duration to wait for mDNS records to appear during system lookup.
lookupTimeout time.Duration

Expand Down Expand Up @@ -139,6 +142,7 @@ func (c *cmdInit) Run(cmd *cobra.Command, args []string) error {

cfg := initConfig{
bootstrap: true,
setupMany: true,
address: c.flagAddress,
autoSetup: c.flagAutoSetup,
wipeAllDisks: c.flagWipeAllDisks,
Expand Down Expand Up @@ -176,6 +180,13 @@ func (c *initConfig) RunInteractive(cmd *cobra.Command, args []string) error {
return err
}

if !c.autoSetup {
c.setupMany, err = c.common.asker.AskBool("Do you want to set up more than one cluster member? (yes/no) [default=yes]: ", "yes")
if err != nil {
return err
}
}

err = c.askAddress()
if err != nil {
return err
Expand Down Expand Up @@ -283,6 +294,10 @@ func (c *initConfig) RunInteractive(cmd *cobra.Command, args []string) error {
// - `expectedSystems` is a list of expected hostnames. If given, the behaviour is similar to `autoSetup`,
// except it will wait up to a minute for exclusively these systems to be recorded.
func (c *initConfig) lookupPeers(s *service.Handler, expectedSystems []string) error {
if !c.setupMany {
return nil
}

header := []string{"NAME", "IFACE", "ADDR"}
var table *SelectableTable
var answers []string
Expand Down
56 changes: 20 additions & 36 deletions cmd/microcloud/main_init_preseed.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,6 @@ func (p *Preseed) validate(name string, bootstrap bool) error {
return fmt.Errorf("No systems given")
}

if bootstrap && len(p.Systems) < 2 {
return fmt.Errorf("At least 2 systems are required to set up MicroCloud")
}

for _, system := range p.Systems {
if system.Name == "" {
return fmt.Errorf("Missing system name")
Expand Down Expand Up @@ -446,9 +442,11 @@ func (p *Preseed) Parse(s *service.Handler, c *initConfig) (map[string]InitSyste
return nil, fmt.Errorf("Failed to find lookup interface %q", p.LookupInterface)
}

err = c.lookupPeers(s, expectedSystems)
if err != nil {
return nil, err
if len(expectedSystems) > 0 {
err = c.lookupPeers(s, expectedSystems)
if err != nil {
return nil, err
}
}

expectedServices := make(map[types.ServiceType]service.Service, len(s.Services))
Expand Down Expand Up @@ -492,6 +490,11 @@ func (p *Preseed) Parse(s *service.Handler, c *initConfig) (map[string]InitSyste
}
}

localInfo, err := s.CollectSystemInformation(context.Background(), mdns.ServerInfo{Name: c.name, Address: c.address})
if err != nil {
return nil, err
}

// If an uplink interface was explicitly chosen, we will try to set up an OVN network.
explicitOVN := len(ifaceByPeer) > 0

Expand All @@ -503,10 +506,12 @@ func (p *Preseed) Parse(s *service.Handler, c *initConfig) (map[string]InitSyste
}

// Take the first alphabetical interface for each system's uplink network.
for k := range uplinkIfaces {
currentIface := ifaceByPeer[system.ServerInfo.Name]
if k < currentIface || currentIface == "" {
ifaceByPeer[system.ServerInfo.Name] = k
if !explicitOVN {
for k := range uplinkIfaces {
currentIface := ifaceByPeer[system.ServerInfo.Name]
if k < currentIface || currentIface == "" {
roosterfish marked this conversation as resolved.
Show resolved Hide resolved
ifaceByPeer[system.ServerInfo.Name] = k
}
}
}

Expand All @@ -520,30 +525,8 @@ func (p *Preseed) Parse(s *service.Handler, c *initConfig) (map[string]InitSyste
}

// If we have specified any part of OVN config, implicitly assume we want to set it up.
usingOVN := p.OVN.IPv4Gateway != "" || p.OVN.IPv6Gateway != "" || explicitOVN
if usingOVN {
// Only select systems not explicitly picked above.
infos := make([]mdns.ServerInfo, 0, len(c.systems))
for peer, system := range c.systems {
if ifaceByPeer[peer] == "" {
infos = append(infos, system.ServerInfo)
}
}

for _, info := range infos {
ifaces, _, _, err := lxd.GetNetworkInterfaces(context.Background(), info.Name, info.Address, info.AuthSecret)
if err != nil {
return nil, err
}

for k := range ifaces {
ifaceByPeer[info.Name] = k
break
}
}
}

// Setup FAN network if OVN not available.
hasOVN, _ := localInfo.SupportsOVNNetwork()
usingOVN := p.OVN.IPv4Gateway != "" || p.OVN.IPv6Gateway != "" || explicitOVN || hasOVN
if usingOVN {
for peer, iface := range ifaceByPeer {
system := c.systems[peer]
Expand Down Expand Up @@ -863,7 +846,8 @@ func (p *Preseed) Parse(s *service.Handler, c *initConfig) (map[string]InitSyste
return nil, fmt.Errorf("Failed to find at least 1 disk on each machine for local storage pool configuration")
}

if len(cephMatches)+len(directCephMatches) > 0 && p.Storage.CephFS {
hasCephFS, _ := localInfo.SupportsRemoteFSPool()
if (len(cephMatches)+len(directCephMatches) > 0 && p.Storage.CephFS) || hasCephFS {
for name, system := range c.systems {
if c.bootstrap {
system.TargetStoragePools = append(system.TargetStoragePools, lxd.DefaultPendingCephFSStoragePool())
Expand Down
4 changes: 2 additions & 2 deletions cmd/microcloud/preseed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (s *preseedSuite) Test_preseedValidateInvalid() {
err: errors.New("No systems given"),
},
{
desc: "Not enough systems",
desc: "Single node preseed",
subnet: "10.0.0.1/24",
iface: "enp5s0",
systems: []System{{Name: "n1", UplinkInterface: "eth0", Storage: InitStorage{}}},
Expand All @@ -55,7 +55,7 @@ func (s *preseedSuite) Test_preseedValidateInvalid() {
},

addErr: false,
err: errors.New("At least 2 systems are required to set up MicroCloud"),
err: nil,
},
{
desc: "Missing lookup subnet",
Expand Down
1 change: 1 addition & 0 deletions cmd/microcloud/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ func (c *cmdServiceAdd) Run(cmd *cobra.Command, args []string) error {
cfg := initConfig{
// Set bootstrap to true because we are setting up a new cluster for new services.
bootstrap: true,
setupMany: true,
common: c.common,
asker: &c.common.asker,
systems: map[string]InitSystem{},
Expand Down
12 changes: 10 additions & 2 deletions test/includes/microcloud.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ unset_interactive_vars() {
SETUP_ZFS ZFS_FILTER ZFS_WIPE \
SETUP_CEPH CEPH_MISSING_DISKS CEPH_FILTER CEPH_WIPE CEPH_ENCRYPT SETUP_CEPHFS CEPH_CLUSTER_NETWORK \
PROCEED_WITH_NO_OVERLAY_NETWORKING SETUP_OVN OVN_WARNING OVN_FILTER IPV4_SUBNET IPV4_START IPV4_END DNS_ADDRESSES IPV6_SUBNET \
REPLACE_PROFILE CEPH_RETRY_HA
REPLACE_PROFILE CEPH_RETRY_HA MULTI_NODE
}

# microcloud_interactive: outputs text that can be passed to `TEST_CONSOLE=1 microcloud init`
# to simulate terminal input to the interactive CLI.
# The lines that are output are based on the values passed to the listed environment variables.
# Any unset variables will be omitted.
microcloud_interactive() {
MULTI_NODE=${MULTI_NODE:-} # (yes/no) whether to set up multiple nodes
SKIP_LOOKUP=${SKIP_LOOKUP:-} # whether or not to skip the whole lookup block in the interactive command list.
LOOKUP_IFACE=${LOOKUP_IFACE:-} # filter string for the lookup interface table.
LIMIT_SUBNET=${LIMIT_SUBNET:-} # (yes/no) input for limiting lookup of systems to the above subnet.
Expand Down Expand Up @@ -44,11 +45,18 @@ microcloud_interactive() {
REPLACE_PROFILE="${REPLACE_PROFILE:-}" # Replace default profile config and devices.

setup=""
if ! [ "${SKIP_LOOKUP}" = 1 ]; then
if [ -n "${MULTI_NODE}" ]; then
setup="
${MULTI_NODE} # lookup multiple nodes
${LOOKUP_IFACE} # filter the lookup interface
$([ -n "${LOOKUP_IFACE}" ] && printf "select") # select the interface
$([ -n "${LOOKUP_IFACE}" ] && printf -- "---")
$(true)
"
fi

if ! [ "${SKIP_LOOKUP}" = 1 ]; then
setup="${setup}
${LIMIT_SUBNET} # limit lookup subnet (yes/no)
$([ "${SKIP_SERVICE}" = "yes" ] && printf "%s" "${SKIP_SERVICE}") # skip MicroOVN/MicroCeph (yes/no)
expect ${EXPECT_PEERS} # wait until the systems show up
Expand Down
1 change: 1 addition & 0 deletions test/main.sh
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ run_instances_tests() {

run_basic_tests() {
run_test test_reuse_cluster "reuse_cluster"
run_test test_add_services "add_services"
run_test test_auto "auto"
run_test test_remove_cluster_member "remove_cluster_member"
run_test test_non_ha "non_ha"
Expand Down
2 changes: 2 additions & 0 deletions test/suites/add.sh
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ test_add_interactive() {

echo "Test growing a MicroCloud with all services and devices set up"
unset_interactive_vars
export MULTI_NODE="yes"
export LOOKUP_IFACE="enp5s0"
export LIMIT_SUBNET="yes"
export EXPECT_PEERS=2
Expand Down Expand Up @@ -200,6 +201,7 @@ test_add_interactive() {
reset_systems 4 2 1
echo "Test growing a MicroCloud when storage & networks were not already set up"
unset_interactive_vars
export MULTI_NODE="yes"
export LOOKUP_IFACE="enp5s0"
export LIMIT_SUBNET="yes"
export EXPECT_PEERS=2
Expand Down
Loading
Loading