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
42 changes: 15 additions & 27 deletions cmd/gluetun/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ func main() {
tun := tun.New()
netLinkDebugLogger := logger.New(log.SetComponent("netlink"))
netLinker := netlink.New(netLinkDebugLogger)
cli := cli.New()
cmder := command.New()

reader := reader.New(reader.Settings{
Expand All @@ -98,13 +97,24 @@ func main() {
},
})

clier, err := cli.New(
cli.NewUpdateCommand("./internal/storage/servers.json", args, logger),
cli.NewHealthCheckCommand(reader),
cli.NewOpenVPNConfigCommand(logger, reader, netLinker),
cli.NewFormatServersCommand(args),
cli.NewGenKeyCommand(args),
)
if err != nil {
logger.Error(err.Error())
os.Exit(1)
}

errorCh := make(chan error)
go func() {
errorCh <- _main(ctx, buildInfo, args, logger, reader, tun, netLinker, cmder, cli)
errorCh <- _main(ctx, buildInfo, args, logger, reader, tun, netLinker, cmder, clier)
}()

// Wait for OS signal or run error
var err error
select {
case receivedSignal := <-signalCh:
signal.Stop(signalCh)
Expand Down Expand Up @@ -142,31 +152,14 @@ func main() {
}
}

var errCommandUnknown = errors.New("command is unknown")

//nolint:gocognit,gocyclo,maintidx
func _main(ctx context.Context, buildInfo models.BuildInformation,
args []string, logger log.LoggerInterface, reader *reader.Reader,
tun Tun, netLinker netLinker, cmder RunStarter,
cli clier,
) error {
if len(args) > 1 { // cli operation
switch args[1] {
case "healthcheck":
return cli.HealthCheck(ctx, reader, logger)
case "clientkey":
return cli.ClientKey(args[2:])
case "openvpnconfig":
return cli.OpenvpnConfig(logger, reader, netLinker)
case "update":
return cli.Update(ctx, args[2:], logger)
case "format-servers":
return cli.FormatServers(args[2:])
case "genkey":
return cli.GenKey(args[2:])
default:
return fmt.Errorf("%w: %s", errCommandUnknown, args[1])
}
return cli.RunCommand(ctx, args[1])
}

defer fmt.Println(gluetunLogo)
Expand Down Expand Up @@ -614,12 +607,7 @@ type Linker interface {
}

type clier interface {
ClientKey(args []string) error
FormatServers(args []string) error
OpenvpnConfig(logger cli.OpenvpnConfigLogger, reader *reader.Reader, ipv6Checker cli.IPv6Checker) error
HealthCheck(ctx context.Context, reader *reader.Reader, warner cli.Warner) error
Update(ctx context.Context, args []string, logger cli.UpdaterLogger) error
GenKey(args []string) error
RunCommand(context.Context, string) error
}

type Tun interface {
Expand Down
47 changes: 43 additions & 4 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,50 @@
package cli

import (
"context"
"errors"
"fmt"
)

var (
ErrCommandAlreadyRegistered = errors.New("command already registered")
errCommandUnknown = errors.New("command is unknown")
)

type CLI struct {
repoServersPath string
subCommands map[string]SubCommand
}

func New(subCommands ...SubCommand) (*CLI, error) {
cli := &CLI{
subCommands: make(map[string]SubCommand),
}
for _, command := range subCommands {
if _, exists := cli.subCommands[command.Name()]; exists {
return nil, fmt.Errorf("%w: %s", ErrCommandAlreadyRegistered, command.Name())
}
cli.subCommands[command.Name()] = command
}
return cli, nil
}

func New() *CLI {
return &CLI{
repoServersPath: "./internal/storage/servers.json",
func (c *CLI) RunCommand(ctx context.Context, command string) error {
if subCommand, exists := c.subCommands[command]; exists {
return subCommand.Run(ctx)
}
c.help()
if command == "help" {
return nil
}
return fmt.Errorf("%w: %s", errCommandUnknown, command)
}

func (c *CLI) help() {
fmt.Printf("Usage: gluetun [COMMAND [OPTIONS]]\n\n" +
"Lightweight swiss-army-knife-like VPN client to multiple VPN service providers.\n\n" +
"Commands:\n")

for _, command := range c.subCommands {
fmt.Printf(" %-20s\t%s\n", command.Name(), command.Description())
}
}
23 changes: 21 additions & 2 deletions internal/cli/formatservers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"context"
"errors"
"flag"
"fmt"
Expand Down Expand Up @@ -31,7 +32,25 @@ func addProviderFlag(flagSet *flag.FlagSet, providerToFormat map[string]*bool,
flagSet.BoolVar(boolPtr, provider, false, "Format "+titleCaser.String(provider)+" servers")
}

func (c *CLI) FormatServers(args []string) error {
type FormatServersCommand struct {
args []string
}

func NewFormatServersCommand(args []string) *FormatServersCommand {
return &FormatServersCommand{
args: args,
}
}

func (c *FormatServersCommand) Name() string {
return "format-servers"
}

func (c *FormatServersCommand) Description() string {
return "Format the servers data into a Markdown table"
}

func (c *FormatServersCommand) Run(_ context.Context) error {
var format, output string
allProviders := providers.All()
allProviderFlags := make([]string, len(allProviders))
Expand All @@ -50,7 +69,7 @@ func (c *CLI) FormatServers(args []string) error {
for _, provider := range allProviderFlags {
addProviderFlag(flagSet, providersToFormat, provider, titleCaser)
}
if err := flagSet.Parse(args); err != nil {
if err := flagSet.Parse(c.args[2:]); err != nil {
return err
}

Expand Down
23 changes: 21 additions & 2 deletions internal/cli/genkey.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
package cli

import (
"context"
"crypto/rand"
"flag"
"fmt"
)

func (c *CLI) GenKey(args []string) (err error) {
type GenKeyCommand struct {
args []string
}

func NewGenKeyCommand(args []string) *GenKeyCommand {
return &GenKeyCommand{
args: args,
}
}

func (c *GenKeyCommand) Name() string {
return "genkey"
}

func (c *GenKeyCommand) Description() string {
return "Generate a new 32 bytes Wireguard key (base58 encoded)"
}

func (c *GenKeyCommand) Run(_ context.Context) (err error) {
flagSet := flag.NewFlagSet("genkey", flag.ExitOnError)
err = flagSet.Parse(args)
err = flagSet.Parse(c.args[2:])
if err != nil {
return fmt.Errorf("parsing flags: %w", err)
}
Expand Down
22 changes: 20 additions & 2 deletions internal/cli/healthcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,28 @@ import (
"github.com/qdm12/gosettings/reader"
)

func (c *CLI) HealthCheck(ctx context.Context, reader *reader.Reader, _ Warner) (err error) {
type HealthCheckCommand struct {
reader *reader.Reader
}

func NewHealthCheckCommand(reader *reader.Reader) *HealthCheckCommand {
return &HealthCheckCommand{
reader: reader,
}
}

func (c *HealthCheckCommand) Name() string {
return "healthcheck"
}

func (c *HealthCheckCommand) Description() string {
return "Check the health of the VPN connection of another Gluetun instance"
}

func (c *HealthCheckCommand) Run(ctx context.Context) (err error) {
// Extract the health server port from the configuration.
var config settings.Health
err = config.Read(reader)
err = config.Read(c.reader)
if err != nil {
return err
}
Expand Down
12 changes: 11 additions & 1 deletion internal/cli/interfaces.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
package cli

import "github.com/qdm12/gluetun/internal/configuration/settings"
import (
"context"

"github.com/qdm12/gluetun/internal/configuration/settings"
)

type Source interface {
Read() (settings settings.Settings, err error)
ReadHealth() (health settings.Health, err error)
String() string
}

type SubCommand interface {
Run(ctx context.Context) (err error)
Name() string
Description() string
}
34 changes: 28 additions & 6 deletions internal/cli/openvpnconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,27 +43,49 @@ type IPv6Checker interface {
IsIPv6Supported() (supported bool, err error)
}

func (c *CLI) OpenvpnConfig(logger OpenvpnConfigLogger, reader *reader.Reader,
type OpenVPNConfigCommand struct {
logger OpenvpnConfigLogger
reader *reader.Reader
ipv6Checker IPv6Checker
}

func NewOpenVPNConfigCommand(logger OpenvpnConfigLogger, reader *reader.Reader,
ipv6Checker IPv6Checker,
) error {
storage, err := storage.New(logger, constants.ServersData)
) *OpenVPNConfigCommand {
return &OpenVPNConfigCommand{
logger: logger,
reader: reader,
ipv6Checker: ipv6Checker,
}
}

func (c *OpenVPNConfigCommand) Name() string {
return "openvpnconfig"
}

func (c *OpenVPNConfigCommand) Description() string {
return "OPrint the OpenVPN configuration (for debugging)"
}

func (c *OpenVPNConfigCommand) Run(_ context.Context) error {
storage, err := storage.New(c.logger, constants.ServersData)
if err != nil {
return err
}

var allSettings settings.Settings
err = allSettings.Read(reader, logger)
err = allSettings.Read(c.reader, c.logger)
if err != nil {
return err
}
allSettings.SetDefaults()

ipv6Supported, err := ipv6Checker.IsIPv6Supported()
ipv6Supported, err := c.ipv6Checker.IsIPv6Supported()
if err != nil {
return fmt.Errorf("checking for IPv6 support: %w", err)
}

if err = allSettings.Validate(storage, ipv6Supported, logger); err != nil {
if err = allSettings.Validate(storage, ipv6Supported, c.logger); err != nil {
return fmt.Errorf("validating settings: %w", err)
}

Expand Down
Loading