Skip to content

Commit

Permalink
Merge pull request lima-vm#2667 from jandubois/refactor-network-yaml
Browse files Browse the repository at this point in the history
Refactor: network.YAML → network.Config
  • Loading branch information
AkihiroSuda authored Sep 29, 2024
2 parents de460fd + 7bb8702 commit 9877e14
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 171 deletions.
20 changes: 10 additions & 10 deletions cmd/limactl/sudoers.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,30 +36,30 @@ See %s for the usage.`, networksMD),
RunE: sudoersAction,
GroupID: advancedCommand,
}
configFile, _ := networks.ConfigFile()
cfgFile, _ := networks.ConfigFile()
sudoersCommand.Flags().Bool("check", false,
fmt.Sprintf("check that the sudoers file is up-to-date with %q", configFile))
fmt.Sprintf("check that the sudoers file is up-to-date with %q", cfgFile))
return sudoersCommand
}

func sudoersAction(cmd *cobra.Command, args []string) error {
if runtime.GOOS != "darwin" {
return errors.New("sudoers command is only supported on macOS right now")
}
config, err := networks.Config()
nwCfg, err := networks.LoadConfig()
if err != nil {
return err
}
// Make sure the current network configuration is secure
if err := config.Validate(); err != nil {
if err := nwCfg.Validate(); err != nil {
return err
}
check, err := cmd.Flags().GetBool("check")
if err != nil {
return err
}
if check {
return verifySudoAccess(config, args)
return verifySudoAccess(nwCfg, args)
}
switch len(args) {
case 0:
Expand All @@ -77,21 +77,21 @@ func sudoersAction(cmd *cobra.Command, args []string) error {
return nil
}

func verifySudoAccess(config networks.YAML, args []string) error {
func verifySudoAccess(nwCfg networks.Config, args []string) error {
var file string
switch len(args) {
case 0:
file = config.Paths.Sudoers
file = nwCfg.Paths.Sudoers
if file == "" {
configFile, _ := networks.ConfigFile()
return fmt.Errorf("no sudoers file defined in %q", configFile)
cfgFile, _ := networks.ConfigFile()
return fmt.Errorf("no sudoers file defined in %q", cfgFile)
}
case 1:
file = args[0]
default:
return errors.New("can check only a single sudoers file")
}
if err := config.VerifySudoAccess(file); err != nil {
if err := nwCfg.VerifySudoAccess(file); err != nil {
return err
}
fmt.Printf("%q is up-to-date (or sudo doesn't require a password)\n", file)
Expand Down
6 changes: 3 additions & 3 deletions pkg/limayaml/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,14 +364,14 @@ func validateNetwork(y *LimaYAML) error {
field := fmt.Sprintf("networks[%d]", i)
switch {
case nw.Lima != "":
config, err := networks.Config()
nwCfg, err := networks.LoadConfig()
if err != nil {
return err
}
if config.Check(nw.Lima) != nil {
if nwCfg.Check(nw.Lima) != nil {
return fmt.Errorf("field `%s.lima` references network %q which is not defined in networks.yaml", field, nw.Lima)
}
usernet, err := config.Usernet(nw.Lima)
usernet, err := nwCfg.Usernet(nw.Lima)
if err != nil {
return err
}
Expand Down
56 changes: 28 additions & 28 deletions pkg/networks/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,36 @@ const (
)

// Commands in `sudoers` cannot use quotes, so all arguments are printed via "%s"
// and not "%q". config.Paths.* entries must not include any whitespace!
// and not "%q". cfg.Paths.* entries must not include any whitespace!

func (config *YAML) Check(name string) error {
if _, ok := config.Networks[name]; ok {
func (c *Config) Check(name string) error {
if _, ok := c.Networks[name]; ok {
return nil
}
return fmt.Errorf("network %q is not defined", name)
}

// Usernet returns true if the mode of given network is ModeUserV2.
func (config *YAML) Usernet(name string) (bool, error) {
if nw, ok := config.Networks[name]; ok {
func (c *Config) Usernet(name string) (bool, error) {
if nw, ok := c.Networks[name]; ok {
return nw.Mode == ModeUserV2, nil
}
return false, fmt.Errorf("network %q is not defined", name)
}

// DaemonPath returns the daemon path.
func (config *YAML) DaemonPath(daemon string) (string, error) {
func (c *Config) DaemonPath(daemon string) (string, error) {
switch daemon {
case SocketVMNet:
return config.Paths.SocketVMNet, nil
return c.Paths.SocketVMNet, nil
default:
return "", fmt.Errorf("unknown daemon type %q", daemon)
}
}

// IsDaemonInstalled checks whether the daemon is installed.
func (config *YAML) IsDaemonInstalled(daemon string) (bool, error) {
p, err := config.DaemonPath(daemon)
func (c *Config) IsDaemonInstalled(daemon string) (bool, error) {
p, err := c.DaemonPath(daemon)
if err != nil {
return false, err
}
Expand All @@ -62,22 +62,22 @@ func (config *YAML) IsDaemonInstalled(daemon string) (bool, error) {
}

// Sock returns a socket_vmnet socket.
func (config *YAML) Sock(name string) string {
return filepath.Join(config.Paths.VarRun, fmt.Sprintf("socket_vmnet.%s", name))
func (c *Config) Sock(name string) string {
return filepath.Join(c.Paths.VarRun, fmt.Sprintf("socket_vmnet.%s", name))
}

func (config *YAML) PIDFile(name, daemon string) string {
return filepath.Join(config.Paths.VarRun, fmt.Sprintf("%s_%s.pid", name, daemon))
func (c *Config) PIDFile(name, daemon string) string {
return filepath.Join(c.Paths.VarRun, fmt.Sprintf("%s_%s.pid", name, daemon))
}

func (config *YAML) LogFile(name, daemon, stream string) string {
func (c *Config) LogFile(name, daemon, stream string) string {
networksDir, _ := dirnames.LimaNetworksDir()
return filepath.Join(networksDir, fmt.Sprintf("%s_%s.%s.log", name, daemon, stream))
}

func (config *YAML) User(daemon string) (osutil.User, error) {
if ok, _ := config.IsDaemonInstalled(daemon); !ok {
daemonPath, _ := config.DaemonPath(daemon)
func (c *Config) User(daemon string) (osutil.User, error) {
if ok, _ := c.IsDaemonInstalled(daemon); !ok {
daemonPath, _ := c.DaemonPath(daemon)
return osutil.User{}, fmt.Errorf("daemon %q (path=%q) is not available", daemon, daemonPath)
}
//nolint:gocritic // singleCaseSwitch: should rewrite switch statement to if statement
Expand All @@ -88,37 +88,37 @@ func (config *YAML) User(daemon string) (osutil.User, error) {
return osutil.User{}, fmt.Errorf("daemon %q not defined", daemon)
}

func (config *YAML) MkdirCmd() string {
return fmt.Sprintf("/bin/mkdir -m 775 -p %s", config.Paths.VarRun)
func (c *Config) MkdirCmd() string {
return fmt.Sprintf("/bin/mkdir -m 775 -p %s", c.Paths.VarRun)
}

func (config *YAML) StartCmd(name, daemon string) string {
if ok, _ := config.IsDaemonInstalled(daemon); !ok {
func (c *Config) StartCmd(name, daemon string) string {
if ok, _ := c.IsDaemonInstalled(daemon); !ok {
panic(fmt.Errorf("daemon %q is not available", daemon))
}
var cmd string
switch daemon {
case SocketVMNet:
nw := config.Networks[name]
if config.Paths.SocketVMNet == "" {
panic("config.Paths.SocketVMNet is empty")
nw := c.Networks[name]
if c.Paths.SocketVMNet == "" {
panic("c.Paths.SocketVMNet is empty")
}
cmd = fmt.Sprintf("%s --pidfile=%s --socket-group=%s --vmnet-mode=%s",
config.Paths.SocketVMNet, config.PIDFile(name, SocketVMNet), config.Group, nw.Mode)
c.Paths.SocketVMNet, c.PIDFile(name, SocketVMNet), c.Group, nw.Mode)
switch nw.Mode {
case ModeBridged:
cmd += fmt.Sprintf(" --vmnet-interface=%s", nw.Interface)
case ModeHost, ModeShared:
cmd += fmt.Sprintf(" --vmnet-gateway=%s --vmnet-dhcp-end=%s --vmnet-mask=%s",
nw.Gateway, nw.DHCPEnd, nw.NetMask)
}
cmd += " " + config.Sock(name)
cmd += " " + c.Sock(name)
default:
panic(fmt.Errorf("unexpected daemon %q", daemon))
}
return cmd
}

func (config *YAML) StopCmd(name, daemon string) string {
return fmt.Sprintf("/usr/bin/pkill -F %s", config.PIDFile(name, daemon))
func (c *Config) StopCmd(name, daemon string) string {
return fmt.Sprintf("/usr/bin/pkill -F %s", c.PIDFile(name, daemon))
}
90 changes: 45 additions & 45 deletions pkg/networks/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,109 +51,109 @@ func defaultConfigBytes() ([]byte, error) {
return textutil.ExecuteTemplate(defaultConfigTemplate, args)
}

func fillDefaults(nwYaml YAML) (YAML, error) {
func fillDefaults(cfg Config) (Config, error) {
usernetFound := false
if nwYaml.Networks == nil {
nwYaml.Networks = make(map[string]Network)
if cfg.Networks == nil {
cfg.Networks = make(map[string]Network)
}
for nw := range nwYaml.Networks {
if nwYaml.Networks[nw].Mode == ModeUserV2 && nwYaml.Networks[nw].Gateway != nil {
for nw := range cfg.Networks {
if cfg.Networks[nw].Mode == ModeUserV2 && cfg.Networks[nw].Gateway != nil {
usernetFound = true
}
}
if !usernetFound {
defaultConfig, err := DefaultConfig()
defaultCfg, err := DefaultConfig()
if err != nil {
return nwYaml, err
return cfg, err
}
nwYaml.Networks[ModeUserV2] = defaultConfig.Networks[ModeUserV2]
cfg.Networks[ModeUserV2] = defaultCfg.Networks[ModeUserV2]
}
return nwYaml, nil
return cfg, nil
}

func DefaultConfig() (YAML, error) {
var config YAML
defaultConfig, err := defaultConfigBytes()
func DefaultConfig() (Config, error) {
var cfg Config
b, err := defaultConfigBytes()
if err != nil {
return config, err
return cfg, err
}
err = yaml.UnmarshalWithOptions(defaultConfig, &config, yaml.Strict())
err = yaml.UnmarshalWithOptions(b, &cfg, yaml.Strict())
if err != nil {
return config, err
return cfg, err
}
return config, nil
return cfg, nil
}

var cache struct {
sync.Once
config YAML
err error
cfg Config
err error
}

func ConfigFile() (string, error) {
configDir, err := dirnames.LimaConfigDir()
cfgDir, err := dirnames.LimaConfigDir()
if err != nil {
return "", err
}
return filepath.Join(configDir, filenames.NetworksConfig), nil
return filepath.Join(cfgDir, filenames.NetworksConfig), nil
}

// loadCache loads the _config/networks.yaml file into the cache.
func loadCache() {
cache.Do(func() {
var configFile string
configFile, cache.err = ConfigFile()
var cfgFile string
cfgFile, cache.err = ConfigFile()
if cache.err != nil {
return
}
_, cache.err = os.Stat(configFile)
_, cache.err = os.Stat(cfgFile)
if cache.err != nil {
if !errors.Is(cache.err, os.ErrNotExist) {
return
}
configDir := filepath.Dir(configFile)
cache.err = os.MkdirAll(configDir, 0o755)
cfgDir := filepath.Dir(cfgFile)
cache.err = os.MkdirAll(cfgDir, 0o755)
if cache.err != nil {
cache.err = fmt.Errorf("could not create %q directory: %w", configDir, cache.err)
cache.err = fmt.Errorf("could not create %q directory: %w", cfgDir, cache.err)
return
}
var defaultConfig []byte
defaultConfig, cache.err = defaultConfigBytes()
var b []byte
b, cache.err = defaultConfigBytes()
if cache.err != nil {
return
}
cache.err = os.WriteFile(configFile, defaultConfig, 0o644)
cache.err = os.WriteFile(cfgFile, b, 0o644)
if cache.err != nil {
return
}
}
var b []byte
b, cache.err = os.ReadFile(configFile)
b, cache.err = os.ReadFile(cfgFile)
if cache.err != nil {
return
}
cache.err = yaml.UnmarshalWithOptions(b, &cache.config, yaml.DisallowDuplicateKey())
cache.err = yaml.UnmarshalWithOptions(b, &cache.cfg, yaml.DisallowDuplicateKey())
if cache.err != nil {
cache.err = fmt.Errorf("cannot parse %q: %w", configFile, cache.err)
cache.err = fmt.Errorf("cannot parse %q: %w", cfgFile, cache.err)
return
}
var strictConfig YAML
if strictErr := yaml.UnmarshalWithOptions(b, &strictConfig, yaml.Strict()); strictErr != nil {
// Allow non-existing YAML fields, as a config created with Lima < v0.22 contains `vdeSwitch` and `vdeVMNet`.
var strictCfg Config
if strictErr := yaml.UnmarshalWithOptions(b, &strictCfg, yaml.Strict()); strictErr != nil {
// Allow non-existing YAML fields, as a cfg created with Lima < v0.22 contains `vdeSwitch` and `vdeVMNet`.
// These fields were removed in Lima v0.22.
logrus.WithError(strictErr).Warn("Non-strict YAML is deprecated and will be unsupported in a future version of Lima: " + configFile)
logrus.WithError(strictErr).Warn("Non-strict YAML is deprecated and will be unsupported in a future version of Lima: " + cfgFile)
}
cache.config, cache.err = fillDefaults(cache.config)
cache.cfg, cache.err = fillDefaults(cache.cfg)
if cache.err != nil {
cache.err = fmt.Errorf("cannot fill default %q: %w", configFile, cache.err)
cache.err = fmt.Errorf("cannot fill default %q: %w", cfgFile, cache.err)
}
})
}

// Config returns the network config from the _config/networks.yaml file.
func Config() (YAML, error) {
// LoadConfig returns the network cfg from the _config/networks.yaml file.
func LoadConfig() (Config, error) {
loadCache()
return cache.config, cache.err
return cache.cfg, cache.err
}

// Sock returns a socket_vmnet socket.
Expand All @@ -162,13 +162,13 @@ func Sock(name string) (string, error) {
if cache.err != nil {
return "", cache.err
}
if err := cache.config.Check(name); err != nil {
if err := cache.cfg.Check(name); err != nil {
return "", err
}
if cache.config.Paths.SocketVMNet == "" {
if cache.cfg.Paths.SocketVMNet == "" {
return "", errors.New("socketVMNet is not set")
}
return cache.config.Sock(name), nil
return cache.cfg.Sock(name), nil
}

// IsUsernet returns true if the given network name is a usernet network.
Expand All @@ -178,7 +178,7 @@ func IsUsernet(name string) bool {
if cache.err != nil {
return false
}
isUsernet, err := cache.config.Usernet(name)
isUsernet, err := cache.cfg.Usernet(name)
if err != nil {
return false
}
Expand Down
Loading

0 comments on commit 9877e14

Please sign in to comment.