Skip to content
Open
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
9 changes: 7 additions & 2 deletions cmd/zk/delete.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package zk

import "github.com/spf13/cobra"
import (
"github.com/jam2in/arcusctl/internal/zk"
"github.com/spf13/cobra"
)

var deleteCmd = &cobra.Command{
Use: "delete <ensemble-name>",
Short: "Delete a ZooKeeper ensemble",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
// TODO: delete ๊ตฌํ˜„
if err := zk.Delete(args[0]); err != nil {
panic(err)
}
},
}
9 changes: 7 additions & 2 deletions cmd/zk/list.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package zk

import "github.com/spf13/cobra"
import (
"github.com/jam2in/arcusctl/internal/zk"
"github.com/spf13/cobra"
)

var listCmd = &cobra.Command{
Use: "list",
Short: "List ZooKeeper ensembles",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
// TODO: list ๊ตฌํ˜„
if err := zk.List(); err != nil {
panic(err)
}
},
}
12 changes: 10 additions & 2 deletions cmd/zk/start.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package zk

import "github.com/spf13/cobra"
import (
"github.com/jam2in/arcusctl/internal/zk"
"github.com/spf13/cobra"
)

var startCmd = &cobra.Command{
Use: "start <ensemble-name> [--node <myid>]",
Short: "Start a ZooKeeper ensemble",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
// TODO: start ๊ตฌํ˜„
ensembleName := args[0]
myID, _ := cmd.Flags().GetInt("node")

if err := zk.Start(ensembleName, myID); err != nil {
panic(err)
}
},
}

Expand Down
9 changes: 7 additions & 2 deletions cmd/zk/status.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package zk

import "github.com/spf13/cobra"
import (
"github.com/jam2in/arcusctl/internal/zk"
"github.com/spf13/cobra"
)

var statusCmd = &cobra.Command{
Use: "status <ensemble-name>",
Short: "Show status of a ZooKeeper ensemble",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
// TODO: status ๊ตฌํ˜„
if err := zk.Status(args[0]); err != nil {
panic(err)
}
},
}
12 changes: 10 additions & 2 deletions cmd/zk/stop.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package zk

import "github.com/spf13/cobra"
import (
"github.com/jam2in/arcusctl/internal/zk"
"github.com/spf13/cobra"
)

var stopCmd = &cobra.Command{
Use: "stop <ensemble-name> [--node <myid>]",
Short: "Stop a ZooKeeper ensemble",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
// TODO: stop ๊ตฌํ˜„
ensembleName := args[0]
myID, _ := cmd.Flags().GetInt("node")

if err := zk.Stop(ensembleName, myID); err != nil {
panic(err)
}
},
}

Expand Down
19 changes: 19 additions & 0 deletions internal/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/jam2in/arcusctl/internal"
"github.com/jam2in/arcusctl/internal/topology"
"go.yaml.in/yaml/v3"
)

Expand Down Expand Up @@ -63,11 +64,29 @@ func LoadZKMeta(ensembleName string) (*ZKMeta, error) {
return &meta, nil
}

func LoadZKTopology(ensembleName string) (*topology.ZKTopology, error) {
data, err := os.ReadFile(filepath.Join(zkDir(ensembleName), topologyYML))
if err != nil {
return nil, err
}

var topo topology.ZKTopology
if err := yaml.Unmarshal(data, &topo); err != nil {
return nil, err
}

return &topo, nil
}

func ZKExists(ensembleName string) bool {
_, err := os.Stat(filepath.Join(zkDir(ensembleName), metaYML))
return err == nil
}

func DeleteZK(ensembleName string) error {
return os.RemoveAll(zkDir(ensembleName))
}

func ListZK() ([]string, error) {
dir := zkBaseDir()
entries, err := os.ReadDir(dir)
Expand Down
15 changes: 15 additions & 0 deletions internal/util.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package internal

import (
"bufio"
"fmt"
"log"
"os"
"strings"
"syscall"
"time"
Expand Down Expand Up @@ -90,3 +92,16 @@ func ReadStdin(msg string, isPassword bool) string {
return input
}
}

// FIXME: ReadStdin์™€ ํ†ตํ•ฉ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ๊ฒ€ํ†  ํ•„์š”
func Confirm(prompt string) bool {
fmt.Print(prompt)
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
if err != nil {
return false
}

input = strings.TrimSpace(strings.ToLower(input))
return input == "y" || input == "yes"
}
87 changes: 87 additions & 0 deletions internal/zk/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package zk

import (
"fmt"
"strings"

"github.com/jam2in/arcusctl/internal"
"github.com/jam2in/arcusctl/internal/ssh"
"github.com/jam2in/arcusctl/internal/store"
"github.com/jam2in/arcusctl/internal/topology"
)

func Delete(ensembleName string) error {
_, topo, err := loadEnsemble(ensembleName)
if err != nil {
return err
}

if err := verifyTopology(topo); err != nil {
return err
}

if err := verifyAllStopped(topo); err != nil {
return err
}

fmt.Printf("This will remove ZooKeeper ensemble %q from all servers.\n", ensembleName)
if !internal.Confirm("Are you sure you want to proceed? (y/N): ") {
fmt.Println("Aborted.")
return nil
}

hostsMap := groupServersByHost(topo.Servers)
for host, servers := range hostsMap {
fmt.Printf("Removing files on %s...\n", host)
if err := removeHostFiles(host, servers, topo); err != nil {
return fmt.Errorf("remove files on %s: %w", host, err)
}
}

if err := store.DeleteZK(ensembleName); err != nil {
return fmt.Errorf("delete metadata: %w", err)
}

fmt.Printf("ZooKeeper ensemble %q deleted.\n", ensembleName)
return nil
}

func verifyTopology(topo *topology.ZKTopology) error {
for _, server := range topo.Servers {
confDir := fmt.Sprintf("%s/conf_myid_%d", topo.Path, server.MyID)
if err := ssh.Run(server.Host(), fmt.Sprintf("test -d %s", confDir)); err != nil {
return fmt.Errorf("topology mismatch: %s not found on %s", confDir, server.Host())
}
}
return nil
}

func verifyAllStopped(topo *topology.ZKTopology) error {
for _, server := range topo.Servers {
confPath := zkConfigPath(topo.Path, server.MyID)
cmd := fmt.Sprintf("pgrep -f 'QuorumPeerMain.*%s' > /dev/null 2>&1", confPath)
if err := ssh.Run(server.Host(), cmd); err == nil {
return fmt.Errorf("server %s (myid=%d) is still running. stop the ensemble before delete",
server.Host(), server.MyID)
}
}
return nil
}

func groupServersByHost(servers []topology.ZKServer) map[string][]topology.ZKServer {
hosts := map[string][]topology.ZKServer{}
for _, server := range servers {
hosts[server.Host()] = append(hosts[server.Host()], server)
}
return hosts
}

func removeHostFiles(host string, servers []topology.ZKServer, topo *topology.ZKTopology) error {
paths := []string{topo.Path}
for _, server := range servers {
paths = append(paths, fmt.Sprintf("%s/zk%d", server.Config.DataDir, server.MyID))
}

cmd := "rm -rf " + strings.Join(paths, " ")
return ssh.Run(host, cmd)
}
23 changes: 3 additions & 20 deletions internal/zk/deploy.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package zk

import (
"bufio"
"fmt"
"os"
"strings"
"text/tabwriter"

"github.com/jam2in/arcusctl/internal"
"github.com/jam2in/arcusctl/internal/store"
"github.com/jam2in/arcusctl/internal/topology"
)
Expand All @@ -18,7 +17,7 @@ func Deploy(version string, topologyPath string) error {
}

printPlan(topo, version)
if !confirm() {
if !internal.Confirm("Proceed with deployment? (y/N): ") {
fmt.Println("Aborted.")
return nil
}
Expand Down Expand Up @@ -49,10 +48,7 @@ func prepareTopology(topologyPath string) (*topology.ZKTopology, []byte, error)
return nil, nil, err
}

for i := range topo.Servers {
merged := mergeConfig(topo.GlobalConfig, topo.Servers[i].Config)
topo.Servers[i].Config = &merged
}
mergeServerConfigs(topo)

if err := topo.Validate(); err != nil {
return nil, nil, err
Expand Down Expand Up @@ -97,16 +93,3 @@ func printRecoveryGuide(deployed []topology.ZKServer, topo *topology.ZKTopology)
fmt.Println("\nTo clean up, manually run on each server:")
fmt.Printf(" rm -rf %s/conf_myid_<myid>\n", topo.Path)
}

func confirm() bool {
// FIXME: internal.ReadStdin()์œผ๋กœ ๋ณ€๊ฒฝ ํ•„์š”
fmt.Print("\nProceed? [y/N]: ")
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
if err != nil {
return false
}

input = strings.TrimSpace(strings.ToLower(input))
return input == "y" || input == "yes"
}
39 changes: 39 additions & 0 deletions internal/zk/ensemble.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package zk

import (
"fmt"

"github.com/jam2in/arcusctl/internal/store"
"github.com/jam2in/arcusctl/internal/topology"
)

func loadEnsemble(name string) (*store.ZKMeta, *topology.ZKTopology, error) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

์ด ํ•จ์ˆ˜๋Š” ํŒŒ์ผ ์ฝ์–ด์™€์„œ ๊ฐ์ฒดํ™”ํ•˜๋Š” ๊ฒƒ์ด๋ผ store ํŒจํ‚ค์ง€์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฒŒ ๋‚˜์„ ๊ฒƒ ๊ฐ™์€๋ฐ ์–ด๋–ค๊ฐ€์š”?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

ํŒŒ์ผ์„ ์ฝ์–ด์™€์„œ ๊ฐ์ฒดํ™”ํ•˜๋Š” ํ•จ์ˆ˜(store.LoadZkMeta, store.LoadZkTopology)์„ ํ˜ธ์ถœํ•˜๊ณ  ์žˆ๋Š” ํ˜•ํƒœ์ด๊ธด ํ•˜์ง€๋งŒ, ZooKeeper์™€ ๊ด€๋ จ๋œ ์„ค์ • ๋ณ‘ํ•ฉ(์ „์—ญ ์„ค์ • + ๊ฐœ๋ณ„ ์„ค์ •)๊ณผ ๊ฒ€์ฆ ๊ฐ™์€ zk ๋„๋ฉ”์ธ ๊ทœ์น™๋„ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๋„๋ฉ”์ธ ๋กœ์ง์€ ์ €์ˆ˜์ค€ store ๋ณด๋‹ค zk ์— ๋‘๋Š” ํ˜„์žฌ ๊ตฌ์กฐ๊ฐ€ ๋” ๊ดœ์ฐฎ๋‹ค๊ณ  ๋ณด์—ฌ์ง‘๋‹ˆ๋‹ค.

meta, err := store.LoadZKMeta(name)
if err != nil {
return nil, nil, fmt.Errorf("load ZooKeeper metadata %q: %w", name, err)
}

topo, err := store.LoadZKTopology(name)
if err != nil {
return nil, nil, fmt.Errorf("load ZooKeeper topology %q: %w", name, err)
}

if topo.Name != name {
return nil, nil, fmt.Errorf("ZooKeeper topology name mismatch: got %q, want %q", topo.Name, name)
}

mergeServerConfigs(topo)

if err := topo.Validate(); err != nil {
return nil, nil, fmt.Errorf("invalid ZooKeeper topology %q: %w", name, err)
}

return meta, topo, nil
}

func mergeServerConfigs(topo *topology.ZKTopology) {
for i := range topo.Servers {
merged := mergeConfig(topo.GlobalConfig, topo.Servers[i].Config)
topo.Servers[i].Config = &merged
}
}
33 changes: 33 additions & 0 deletions internal/zk/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package zk

import (
"fmt"
"os"
"text/tabwriter"

"github.com/jam2in/arcusctl/internal/store"
)

func List() error {
names, err := store.ListZK()
if err != nil {
return fmt.Errorf("list ZooKeeper ensembles: %w", err)
}

if len(names) == 0 {
fmt.Println("No ZooKeeper ensemble found.")
return nil
}

w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintln(w, "NAME\tVERSION\tDEPLOYED AT")
for _, name := range names {
meta, err := store.LoadZKMeta(name)
if err != nil {
fmt.Fprintf(w, "%s\t<error>\t%v\n", name, err)
continue
}
fmt.Fprintf(w, "%s\t%s\t%s\n", meta.Name, meta.Version, meta.DeployedAt.Format("2006-01-02 15:04:05"))
}
return w.Flush()
}
Loading
Loading