diff --git a/.gitignore b/.gitignore index 6f6f5e6a..4e318b07 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ # Go workspace file go.work go.work.sum + +.idea/ diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 00000000..c914115b --- /dev/null +++ b/.mise.toml @@ -0,0 +1,2 @@ +[tools] +go = "1.22" diff --git a/cmd/machined/install/root.go b/cmd/machined/install/root.go new file mode 100644 index 00000000..90208b3d --- /dev/null +++ b/cmd/machined/install/root.go @@ -0,0 +1,41 @@ +package install + +import ( + "github.com/spf13/cobra" + "uncloud/internal/machine" + "uncloud/internal/machine/daemon" +) + +type Options struct { + uncloudID string + uncloudSecret string + network string +} + +func NewCommand(dataDir *string) *cobra.Command { + opts := Options{} + cmd := &cobra.Command{ + Use: "install", + Short: "Install OS dependencies and configure Uncloud machine.", + RunE: func(cmd *cobra.Command, args []string) error { + return install(*dataDir, opts) + }, + } + cmd.Flags().StringVar(&opts.uncloudID, "id", "", "Globally unique identifier for the uncloud this machine belongs to") + _ = cmd.MarkFlagRequired("id") + // TODO: read secret from file for security reasons (bash history). + cmd.Flags().StringVar(&opts.uncloudSecret, "secret", "", "Shared secret for the uncloud this machine belongs to") + _ = cmd.MarkFlagRequired("secret") + cmd.Flags().StringVar(&opts.network, "network", "", "IPv4 network in CIDR format to use for the machine network") + _ = cmd.MarkFlagRequired("network") + return cmd +} + +func install(dataDir string, opts Options) error { + cfg := daemon.Config{ + UncloudID: opts.uncloudID, + UncloudSecret: opts.uncloudSecret, + Network: opts.network, + } + return machine.Install(dataDir, cfg) +} diff --git a/cmd/machined/main.go b/cmd/machined/main.go new file mode 100644 index 00000000..be6b4c41 --- /dev/null +++ b/cmd/machined/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "github.com/spf13/cobra" + "uncloud/cmd/machined/install" + "uncloud/internal/machine/daemon" +) + +func main() { + var dataDir string + cmd := &cobra.Command{ + Use: "machined", + Short: "Uncloud machine daemon.", + SilenceUsage: true, + SilenceErrors: true, + RunE: func(cmd *cobra.Command, args []string) error { + return daemon.Run(dataDir) + }, + } + cmd.PersistentFlags().StringVarP(&dataDir, "data-dir", "d", daemon.DefaultDataDir, "Directory to store machine state") + _ = cmd.MarkFlagDirname("data-dir") + cmd.AddCommand( + install.NewCommand(&dataDir), + ) + cobra.CheckErr(cmd.Execute()) +} diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..91b0a6cd --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module uncloud + +go 1.22 + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..912390a7 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/machine/daemon/config.go b/internal/machine/daemon/config.go new file mode 100644 index 00000000..9a740f1f --- /dev/null +++ b/internal/machine/daemon/config.go @@ -0,0 +1,45 @@ +package daemon + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" +) + +const ( + DefaultDataDir = "/var/lib/uncloud" + MachineConfigPath = "machine.json" +) + +type Config struct { + UncloudID string + UncloudSecret string + // IPv4 network in CIDR format to use for the machine network. + Network string +} + +func ReadConfig(path string) (Config, error) { + data, err := os.ReadFile(path) + if err != nil { + return Config{}, fmt.Errorf("read config file %q: %w", path, err) + } + var config Config + if err = json.Unmarshal(data, &config); err != nil { + return Config{}, fmt.Errorf("parse config file %q: %w", path, err) + } + return config, nil +} + +func (c *Config) Write(path string) error { + dir, _ := filepath.Split(path) + if err := os.MkdirAll(dir, 0700); err != nil { + return fmt.Errorf("create config directory %q: %w", dir, err) + } + + data, err := json.Marshal(c) + if err != nil { + return fmt.Errorf("marshal config: %w", err) + } + return os.WriteFile(path, data, 0600) +} diff --git a/internal/machine/daemon/run.go b/internal/machine/daemon/run.go new file mode 100644 index 00000000..c678132e --- /dev/null +++ b/internal/machine/daemon/run.go @@ -0,0 +1,17 @@ +package daemon + +import ( + "errors" + "fmt" + "path/filepath" +) + +func Run(dataDir string) error { + cfg, err := ReadConfig(filepath.Join(dataDir, MachineConfigPath)) + if err != nil { + return err + } + fmt.Println("Config", cfg) + + return errors.New("Running daemon...") +} diff --git a/internal/machine/install.go b/internal/machine/install.go new file mode 100644 index 00000000..8cc176d5 --- /dev/null +++ b/internal/machine/install.go @@ -0,0 +1,15 @@ +package machine + +import ( + "fmt" + "path/filepath" + "uncloud/internal/machine/daemon" +) + +func Install(dataDir string, cfg daemon.Config) error { + err := cfg.Write(filepath.Join(dataDir, daemon.MachineConfigPath)) + if err == nil { + fmt.Println("Machine config created.") + } + return err +}