Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add logger #18

Merged
merged 1 commit into from
Nov 11, 2024
Merged
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
57 changes: 28 additions & 29 deletions cmd/woodpecker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,35 @@ package main

import (
"fmt"
"github.com/robfig/cron/v3"
"os"

"woodpecker/internal/config"
"woodpecker/internal/providers/namecheap"
"woodpecker/internal/providers/porkbun"
"woodpecker/internal/services"
"woodpecker/internal/utils"

"github.com/robfig/cron/v3"
)

func main() {
utils.InitLogger()

configDir, err := utils.GetAppPath()
if err != nil {
fmt.Println("failed to get config path:", err)
utils.Log.Error().Err(err).Msg("an error occurred")
os.Exit(1)
}

loadConfig, err := config.LoadConfig()
if err != nil {
fmt.Println("error loading environment file:", err)
utils.Log.Error().Err(err).Msg("an error occurred")
os.Exit(1)
}

err = updateDNS(loadConfig, configDir)
if err != nil {
fmt.Printf("error during initial DNS update: %v\n", err)
utils.Log.Error().Err(err).Msg("an error occurred")
os.Exit(1)
}

Expand All @@ -42,12 +46,12 @@ func setupCron(config *config.Config, configPath string) {
_, err := c.AddFunc(checkInterval, func() {
err := updateDNS(config, configPath)
if err != nil {
fmt.Printf("error during DNS update: %v\n", err)
utils.Log.Error().Err(err).Msg("error during DNS update: " + err.Error())
}
})

if err != nil {
fmt.Println("failed to schedule DNS update:", err)
utils.Log.Fatal().Err(err).Msg("failed to schedule DNS update")
os.Exit(1)
}

Expand All @@ -57,83 +61,78 @@ func setupCron(config *config.Config, configPath string) {
func updateDNS(config *config.Config, configPath string) error {
ip, err := services.GetPublicIP(config)
if err != nil {
return fmt.Errorf("failed to retrieve public IP: %v", err)
return err
}
fmt.Println("current public IP address:", ip)

storedIP, err := utils.ReadIPFromFile(configPath)
if err != nil {
return fmt.Errorf("failed to read stored IP: %v", err)
return err
}
fmt.Println("current stored public IP address:", storedIP)

if storedIP == ip {
fmt.Println("IP address unchanged, skipping DNS updates")
utils.Log.Info().Msgf("public and stored IP address equal (%s), sleeping for %d minute(s)", ip, config.CheckInterval)
return nil
}

fmt.Println("IP address has changed, proceeding to update DNS records...")
utils.Log.Info().Msg("public and stored IP address not equal, proceeding to update DNS records...")

if config.PorkbunAPIKey != "" && config.PorkbunSecretKey != "" {
err = updatePorkbunDNS(config, ip)
if err != nil {
return err
}
} else {
fmt.Println("skipping Porkbun DNS update as required config not provided")
}

if config.NamecheapPassword != "" {
err = updateNamecheapDNS(config, ip)
if err != nil {
return err
}
} else {
fmt.Println("skipping Namecheap DNS update as required config not provided")
}

err = utils.WriteIPToFile(ip, configPath)
if err != nil {
return fmt.Errorf("failed to store new updated public IP address: %v", err)
return err
}

fmt.Println("DNS updates completed for both providers.")
utils.Log.Info().Str("level", "update").Msgf("update complete, sleeping for %d minute(s)", config.CheckInterval)
return nil
}

func updatePorkbunDNS(config *config.Config, ip string) error {
fmt.Println("checking Porkbun DNS records...")
utils.Log.Info().Msg("checking Porkbun DNS records...")
porkbunProvider := porkbun.New(config)

dnsIP, err := porkbunProvider.GetCurrentARecord()
if err != nil {
return fmt.Errorf("failed to retrieve Porkbun DNS A record: %v", err)
utils.Log.Error().Err(err).Msg("failed to retrieve Porkbun DNS A record")
return err
}
fmt.Println("current Porkbun DNS A record IP address:", dnsIP)
utils.Log.Info().Msg("current Porkbun DNS A record IP address")

if dnsIP != ip {
fmt.Printf("Porkbun DNS record is outdated- current: %s, expected: %s\n", dnsIP, ip)
utils.Log.Info().Msg("Porkbun DNS record is outdated")
err := porkbunProvider.UpdateARecord(ip)
if err != nil {
return fmt.Errorf("failed to update Porkbun DNS A record: %v", err)
return fmt.Errorf("failed to update Porkbun DNS A record: %w", err)
}
fmt.Println("Porkbun DNS A record updated successfully.")
} else {
fmt.Println("Porkbun DNS A record already up to date.")

utils.Log.Info().Str("level", "update").Msgf("PorkBun (%s.%s) | DNS A record updated successfully.", config.PorkbunSubdomain, config.PorkbunDomain)
}

return nil
}

func updateNamecheapDNS(config *config.Config, ip string) error {
fmt.Println("updating Namecheap DNS records...")
utils.Log.Info().Str("level", "update").Msg("updating Namecheap DNS records...")
namecheapProvider := namecheap.New(config)

err := namecheapProvider.UpdateARecord(ip)
if err != nil {
return fmt.Errorf("failed to update Namecheap DNS A record: %v", err)
return fmt.Errorf("failed to update Namecheap DNS A record: %w", err)
}
fmt.Println("Namecheap DNS A record updated successfully.")

utils.Log.Info().Str("level", "update").Msgf("Namecheap (%s.%s) | DNS A record updated successfully.", config.NamecheapSubdomain, config.NamecheapDomain)

return nil
}
11 changes: 10 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,13 @@ module woodpecker

go 1.22

require github.com/robfig/cron/v3 v3.0.1
require (
github.com/robfig/cron/v3 v3.0.1
github.com/rs/zerolog v1.33.0
)

require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
golang.org/x/sys v0.12.0 // indirect
)
15 changes: 15 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
9 changes: 8 additions & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"strconv"

"woodpecker/internal/constants"
)

Expand Down Expand Up @@ -45,7 +46,13 @@ func LoadConfig() (*Config, error) {
}

if config.IPService == "" {
return nil, fmt.Errorf("missing required IP Service field in %s", constants.ConfigFilename)
err = fmt.Errorf("missing required IP Service field in %s", constants.ConfigFilename)
return nil, err
}

if config.PorkbunSecretKey == "" && config.NamecheapPassword == "" {
err = fmt.Errorf("no domain variables specified in %s, exiting", constants.ConfigFilename)
return nil, err
}

return config, nil
Expand Down
7 changes: 4 additions & 3 deletions internal/providers/namecheap/namecheap.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io"
"net/http"
"strings"

"woodpecker/internal/config"
"woodpecker/internal/providers"
)
Expand All @@ -18,7 +19,7 @@ func New(config *config.Config) providers.DNSProvider {
}

func (n *Namecheap) GetCurrentARecord() (string, error) {
return "", fmt.Errorf("GetCurrentARecord not supported in namecheap via API access")
return "", fmt.Errorf("GetCurrentARecord not supported in namecheap without API access requirements: https://www.namecheap.com/support/knowledgebase/article.aspx/9739/63/api-faq/#c")
}

func (n *Namecheap) UpdateARecord(ip string) error {
Expand All @@ -32,7 +33,7 @@ func (n *Namecheap) UpdateARecord(ip string) error {

resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("failed to send update request to Namecheap: %v", err)
return fmt.Errorf("failed to send update request to Namecheap: %w", err)
}
defer resp.Body.Close()

Expand All @@ -42,7 +43,7 @@ func (n *Namecheap) UpdateARecord(ip string) error {

body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response from Namecheap: %v", err)
return fmt.Errorf("failed to read response from Namecheap: %w", err)
}

if strings.Contains(string(body), "<ErrCount>1</ErrCount>") {
Expand Down
13 changes: 7 additions & 6 deletions internal/providers/porkbun/porkbun.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"net/http"

"woodpecker/internal/config"
"woodpecker/internal/providers"
)
Expand Down Expand Up @@ -45,7 +46,7 @@ func (p *Porkbun) GetCurrentARecord() (string, error) {

resp, err := http.Post(url, "application/json", bytes.NewBuffer(bodyData))
if err != nil {
return "", fmt.Errorf("failed to send request: %v", err)
return "", fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()

Expand All @@ -56,7 +57,7 @@ func (p *Porkbun) GetCurrentARecord() (string, error) {
var dnsResponse DNSResponse
err = json.NewDecoder(resp.Body).Decode(&dnsResponse)
if err != nil {
return "", fmt.Errorf("failed to parse DNS response: %v", err)
return "", fmt.Errorf("failed to parse DNS response: %w", err)
}

if len(dnsResponse.Records) == 0 {
Expand All @@ -77,12 +78,12 @@ func (p *Porkbun) UpdateARecord(ip string) error {

bodyData, err := json.Marshal(body)
if err != nil {
return fmt.Errorf("failed to parse request body: %v", err)
return fmt.Errorf("failed to parse request body: %w", err)
}

resp, err := http.Post(url, "application/json", bytes.NewBuffer(bodyData))
if err != nil {
return fmt.Errorf("failed to send update request: %v", err)
return fmt.Errorf("failed to send update request: %w", err)
}
defer resp.Body.Close()

Expand All @@ -93,11 +94,11 @@ func (p *Porkbun) UpdateARecord(ip string) error {
var response map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
return fmt.Errorf("failed to parse DNS update response: %v", err)
return fmt.Errorf("failed to parse DNS update response: %w", err)
}

if response["status"] != "SUCCESS" {
return fmt.Errorf("failed to update DNS record, response: %v", response)
return fmt.Errorf("failed to update DNS record, response: %s", response)
}

return nil
Expand Down
5 changes: 3 additions & 2 deletions internal/services/ipservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@ import (
"io"
"net/http"
"strings"

"woodpecker/internal/config"
)

func GetPublicIP(config *config.Config) (string, error) {
resp, err := http.Get(config.IPService)
if err != nil {
return "", fmt.Errorf("failed to retrieve IP from %s: %v", config.IPService, err)
return "", fmt.Errorf("failed to retrieve IP from "+config.IPService, err)
}
defer resp.Body.Close()

ip, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read response from %s: %v", config.IPService, err)
return "", fmt.Errorf("failed to read response from %s", err)
}

return strings.TrimSpace(string(ip)), nil
Expand Down
10 changes: 6 additions & 4 deletions internal/utils/utils.go → internal/utils/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"

"woodpecker/internal/constants"
)

Expand All @@ -12,7 +13,7 @@ func GetAppPath() (string, error) {

err := os.MkdirAll(dir, os.ModePerm)
if err != nil {
return "", fmt.Errorf("failed to create directory: %v", err)
return "", fmt.Errorf("failed to create directory: %w", err)
}

return dir, nil
Expand All @@ -22,12 +23,12 @@ func ReadIPFromFile(configPath string) (string, error) {
ipFile := filepath.Join(configPath, constants.CurrentIpFilename)
data, err := os.ReadFile(ipFile)
if os.IsNotExist(err) {
fmt.Println("IP file does not exist yet, assuming first run")
Log.Info().Msg("IP file does not exist yet, assuming first run")
return "", nil
}

if err != nil {
return "", fmt.Errorf("failed to read IP from file: %v", err)
return "", fmt.Errorf("failed to read IP from file: %w", err)
}

return string(data), nil
Expand All @@ -37,8 +38,9 @@ func WriteIPToFile(ip, configPath string) error {
ipFile := filepath.Join(configPath, constants.CurrentIpFilename)
err := os.WriteFile(ipFile, []byte(ip), 0644)
if err != nil {
return fmt.Errorf("failed to write IP to file: %v", err)
return fmt.Errorf("failed to write IP to file: %w", err)
}

Log.Info().Str("level", "update").Msgf("stored IP address (%s) locally", ip)
return nil
}
Loading