main
Raw Download raw file
 1package cmd
 2
 3import (
 4	"errors"
 5	"fmt"
 6	"log/slog"
 7	"os"
 8	"strconv"
 9
10	"github.com/bryfry/mm/internal/aod"
11	"github.com/bryfry/mm/internal/cf"
12	"github.com/bryfry/mm/internal/iplookup"
13	"github.com/spf13/cobra"
14)
15
16const (
17	EnvDie = "MM_POWEROFF_ON_IDLE"
18)
19
20var (
21	_errFmt        = "required env variable %s not found or empty"
22	ErrDieRequired = errors.New(fmt.Sprintf(_errFmt, EnvDie))
23)
24
25// TODO:
26// - move cfdns to a goroutine in cf package
27// - move watch into a goroutine
28// - setup context for canx and signals
29func service(cmd *cobra.Command, args []string) error {
30
31	zoneName, zoneNameExists := os.LookupEnv(cf.EnvZoneName)
32	if !zoneNameExists || len(zoneName) == 0 {
33		return cf.ErrZoneNameRequired
34	}
35	subdomainName, subdomainNameExists := os.LookupEnv(cf.EnvSubdomainName)
36	if !subdomainNameExists || len(subdomainName) == 0 {
37		return cf.ErrSubdomainNameRequired
38	}
39	apiToken, apiTokenExists := os.LookupEnv(cf.EnvAPIToken)
40	if !apiTokenExists || len(apiToken) == 0 {
41		return cf.ErrAPITokenRequired
42	}
43	dieRaw, dieExists := os.LookupEnv(EnvDie)
44	if !dieExists || len(dieRaw) == 0 {
45		return ErrDieRequired
46	}
47	die, err := strconv.ParseBool(dieRaw)
48	if err != nil {
49		return fmt.Errorf("failed to parse %s bool=%q: %w", EnvDie, dieRaw, err)
50	}
51
52	ipDetails, err := iplookup.Discover()
53	if err != nil {
54		return err
55	}
56
57	slog.Info("cfdns: ip discovery",
58		slog.String("ip", ipDetails.Address))
59
60	cfdns, err := cf.New(zoneName, subdomainName, apiToken)
61	if err != nil {
62		return err
63	}
64
65	slog.Info("cfdns: dns discovery",
66		slog.String("domain", subdomainName),
67		slog.String("ip", cfdns.IP()))
68
69	if ipDetails.Address != cfdns.IP() {
70		slog.Info("cfdns: update required")
71
72		err = cfdns.SetIP(ipDetails.Address)
73		if err != nil {
74			return err
75		}
76
77		slog.Info("cfdns: update complete",
78			slog.String("domain", subdomainName),
79			slog.String("ip", cfdns.IP()))
80	}
81
82	return aod.Watch(subdomainName, die)
83}