diff --git a/CHANGELOG.md b/CHANGELOG.md index 46525ba2..d1267ae3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # CHANGELOG -## 6.0.0 +## 6.0.0 (2023-07-12) * `geoipupdate` now supports configuration via environment variables. Any configuration set this way will override any value from the config file, @@ -27,6 +27,8 @@ a positional config file path argument, which can now be passed in using the option from `WithConfigFile` along with the other optional parameters. * `geoipupdate` and `NewConfig` no longer require a config file to exist. +* The `--stack-trace` flag has been removed. This flag has been broken since + 4.11.0. ## 5.1.1 (2023-05-08) diff --git a/README.md b/README.md index e7dd744c..ecf4f785 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ website](https://golang.org). The easiest way is via `go install`: - $ go install github.com/maxmind/geoipupdate/v5/cmd/geoipupdate@latest + $ go install github.com/maxmind/geoipupdate/v6/cmd/geoipupdate@latest This installs `geoipupdate` to `$GOPATH/bin/geoipupdate`. diff --git a/cmd/geoipupdate/args.go b/cmd/geoipupdate/args.go index bfcb0243..aa197136 100644 --- a/cmd/geoipupdate/args.go +++ b/cmd/geoipupdate/args.go @@ -4,7 +4,7 @@ import ( "log" "os" - "github.com/maxmind/geoipupdate/v5/pkg/geoipupdate/vars" + "github.com/maxmind/geoipupdate/v6/pkg/geoipupdate/vars" flag "github.com/spf13/pflag" ) @@ -12,7 +12,6 @@ import ( type Args struct { ConfigFile string DatabaseDirectory string - StackTrace bool Verbose bool Output bool Parallelism int @@ -37,7 +36,6 @@ func getArgs() *Args { "Store databases in this directory (uses config if not specified)", ) help := flag.BoolP("help", "h", false, "Display help and exit") - stackTrace := flag.Bool("stack-trace", false, "Show a stack trace along with any error message") verbose := flag.BoolP("verbose", "v", false, "Use verbose output") output := flag.BoolP("output", "o", false, "Output download/update results in JSON format") displayVersion := flag.BoolP("version", "V", false, "Display the version and exit") @@ -62,7 +60,6 @@ func getArgs() *Args { return &Args{ ConfigFile: *configFile, DatabaseDirectory: *databaseDirectory, - StackTrace: *stackTrace, Verbose: *verbose, Output: *output, Parallelism: *parallelism, diff --git a/cmd/geoipupdate/end_to_end_test.go b/cmd/geoipupdate/end_to_end_test.go index d58a2c1b..670db7c4 100644 --- a/cmd/geoipupdate/end_to_end_test.go +++ b/cmd/geoipupdate/end_to_end_test.go @@ -16,7 +16,7 @@ import ( "testing" "time" - "github.com/maxmind/geoipupdate/v5/pkg/geoipupdate" + "github.com/maxmind/geoipupdate/v6/pkg/geoipupdate" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/cmd/geoipupdate/main.go b/cmd/geoipupdate/main.go index 0a9bf0a5..f8e4a81a 100644 --- a/cmd/geoipupdate/main.go +++ b/cmd/geoipupdate/main.go @@ -4,16 +4,17 @@ package main import ( "context" "log" - "os" - "github.com/maxmind/geoipupdate/v5/pkg/geoipupdate" - "github.com/maxmind/geoipupdate/v5/pkg/geoipupdate/vars" + "github.com/maxmind/geoipupdate/v6/pkg/geoipupdate" + "github.com/maxmind/geoipupdate/v6/pkg/geoipupdate/vars" ) +const unknownVersion = "unknown" + // These values are set by build scripts. Changing the names of // the variables should be considered a breaking change. var ( - version = "unknown" + version = unknownVersion defaultConfigFile string defaultDatabaseDirectory string ) @@ -30,14 +31,6 @@ func main() { } args := getArgs() - fatalLogger := func(message string, err error) { - if args.StackTrace { - log.Printf("%s: %+v", message, err) - } else { - log.Printf("%s: %s", message, err) - } - os.Exit(1) - } config, err := geoipupdate.NewConfig( geoipupdate.WithConfigFile(args.ConfigFile), @@ -47,7 +40,7 @@ func main() { geoipupdate.WithOutput(args.Output), ) if err != nil { - fatalLogger("error loading configuration", err) + log.Fatalf("Error loading configuration: %s", err) } if config.Verbose { @@ -58,6 +51,6 @@ func main() { client := geoipupdate.NewClient(config) if err = client.Run(context.Background()); err != nil { - fatalLogger("error retrieving updates", err) + log.Fatalf("Error retrieving updates: %s", err) } } diff --git a/cmd/geoipupdate/version.go b/cmd/geoipupdate/version.go index 50f8526f..b26cb147 100644 --- a/cmd/geoipupdate/version.go +++ b/cmd/geoipupdate/version.go @@ -6,7 +6,45 @@ package main import "runtime/debug" func init() { - if info, ok := debug.ReadBuildInfo(); ok && info.Main.Version != "(devel)" { + info, ok := debug.ReadBuildInfo() + if !ok { + // Getting the build info failed, e.g., it was disabled on build. + return + } + if version == unknownVersion { + // This will set the version on go install ... version = info.Main.Version } + + var rev, time, arch, os string + dirty := false + for _, kv := range info.Settings { + switch kv.Key { + case "vcs.revision": + rev = kv.Value + case "vcs.time": + time = kv.Value + case "vcs.modified": + dirty = kv.Value == "true" + case "GOARCH": + arch = kv.Value + case "GOOS": + os = kv.Value + } + } + + bi := "" + + if len(rev) >= 8 { + bi += rev[:8] + if dirty { + bi += "-modified" + } + bi += ", " + } + if time != "" { + bi += time + ", " + } + bi += os + "-" + arch + version += " (" + bi + ")" } diff --git a/docker/Dockerfile b/docker/Dockerfile index 5e5bf03f..959fae63 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,8 +1,6 @@ FROM alpine:3 -RUN apk update && \ - apk add jq -RUN adduser -D -h /var/lib/geoipupdate -u 1000 geoipupdate +RUN apk update && apk add jq COPY geoipupdate /usr/bin/geoipupdate COPY docker/entry.sh /usr/bin/entry.sh diff --git a/docker/entry.sh b/docker/entry.sh index c2b5ebca..7100f3e7 100755 --- a/docker/entry.sh +++ b/docker/entry.sh @@ -15,13 +15,14 @@ trap 'kill ${!}; term_handler' SIGTERM pid=0 database_dir=/usr/share/GeoIP -log_dir="/var/lib/geoipupdate" +log_dir="/tmp/geoipupdate" log_file="$log_dir/.healthcheck" flags="--output" frequency=$((GEOIPUPDATE_FREQUENCY * 60 * 60)) +export GEOIPUPDATE_CONF_FILE="" if [ -z "$GEOIPUPDATE_DB_DIR" ]; then - GEOIPUPDATE_DB_DIR="$database_dir" + export GEOIPUPDATE_DB_DIR="$database_dir" fi if [ -z "$GEOIPUPDATE_ACCOUNT_ID" ] && [ -z "$GEOIPUPDATE_ACCOUNT_ID_FILE" ]; then diff --git a/docker/healthcheck.sh b/docker/healthcheck.sh index 6d0b05d2..eb97360b 100755 --- a/docker/healthcheck.sh +++ b/docker/healthcheck.sh @@ -7,7 +7,7 @@ cutoff_duration=$(($GEOIPUPDATE_FREQUENCY * 60 * 60 + 120)) current_time=$(date +%s) cutoff_date=$(($current_time - $cutoff_duration)) -log_file="/var/lib/geoipupdate/.healthcheck" +log_file="/tmp/geoipupdate/.healthcheck" editions=$(cat "$log_file" | jq -r '.[] | select(.checked_at > '$cutoff_date') | .edition_id') checked_editions=$(echo "$editions" | wc -l) desired_editions=$(echo "$GEOIPUPDATE_EDITION_IDS" | awk -F' ' '{print NF}') diff --git a/go.mod b/go.mod index 1959928b..32f78589 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/maxmind/geoipupdate/v5 +module github.com/maxmind/geoipupdate/v6 go 1.19 diff --git a/pkg/geoipupdate/config.go b/pkg/geoipupdate/config.go index 1cb18505..546ad8d2 100644 --- a/pkg/geoipupdate/config.go +++ b/pkg/geoipupdate/config.go @@ -12,7 +12,7 @@ import ( "strings" "time" - "github.com/maxmind/geoipupdate/v5/pkg/geoipupdate/vars" + "github.com/maxmind/geoipupdate/v6/pkg/geoipupdate/vars" ) // Config is a parsed configuration file. @@ -274,7 +274,7 @@ func setConfigFromFile(config *Config, path string) error { } if err := scanner.Err(); err != nil { - return fmt.Errorf("error reading file: %w", err) + return fmt.Errorf("reading file: %w", err) } return nil @@ -436,7 +436,7 @@ func parseProxy( // Now that we have a scheme, we should be able to parse. u, err := url.Parse(proxyURL) if err != nil { - return nil, fmt.Errorf("error parsing proxy URL: %w", err) + return nil, fmt.Errorf("parsing proxy URL: %w", err) } if !strings.Contains(u.Host, ":") { diff --git a/pkg/geoipupdate/config_test.go b/pkg/geoipupdate/config_test.go index 6cdb182d..0c4b1227 100644 --- a/pkg/geoipupdate/config_test.go +++ b/pkg/geoipupdate/config_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/maxmind/geoipupdate/v5/pkg/geoipupdate/vars" + "github.com/maxmind/geoipupdate/v6/pkg/geoipupdate/vars" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/geoipupdate/database/http_reader.go b/pkg/geoipupdate/database/http_reader.go index 56fbccb7..9aa6a411 100644 --- a/pkg/geoipupdate/database/http_reader.go +++ b/pkg/geoipupdate/database/http_reader.go @@ -15,8 +15,8 @@ import ( "github.com/cenkalti/backoff/v4" - "github.com/maxmind/geoipupdate/v5/pkg/geoipupdate/internal" - "github.com/maxmind/geoipupdate/v5/pkg/geoipupdate/vars" + "github.com/maxmind/geoipupdate/v6/pkg/geoipupdate/internal" + "github.com/maxmind/geoipupdate/v6/pkg/geoipupdate/vars" ) const urlFormat = "%s/geoip/databases/%s/update?db_md5=%s" @@ -104,7 +104,7 @@ func (r *HTTPReader) Read(ctx context.Context, editionID, hash string) (*ReadRes }, ) if err != nil { - return nil, fmt.Errorf("error getting update for %s: %w", editionID, err) + return nil, fmt.Errorf("getting update for %s: %w", editionID, err) } return result, nil @@ -129,14 +129,14 @@ func (r *HTTPReader) get( req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil) if err != nil { - return nil, fmt.Errorf("error creating request: %w", err) + return nil, fmt.Errorf("creating request: %w", err) } req.Header.Add("User-Agent", "geoipupdate/"+vars.Version) req.SetBasicAuth(fmt.Sprintf("%d", r.accountID), r.licenseKey) response, err := r.client.Do(req) if err != nil { - return nil, fmt.Errorf("error performing HTTP request: %w", err) + return nil, fmt.Errorf("performing HTTP request: %w", err) } // It is safe to close the response body reader as it wouldn't be // consumed in case this function returns an error. @@ -160,7 +160,7 @@ func (r *HTTPReader) get( Body: string(buf), StatusCode: response.StatusCode, } - return nil, fmt.Errorf("unexpcted HTTP status code: %w", httpErr) + return nil, fmt.Errorf("unexpected HTTP status code: %w", httpErr) } newHash := response.Header.Get("X-Database-MD5") @@ -170,7 +170,7 @@ func (r *HTTPReader) get( modifiedAt, err := parseTime(response.Header.Get("Last-Modified")) if err != nil { - return nil, fmt.Errorf("error reading Last-Modified header: %w", err) + return nil, fmt.Errorf("reading Last-Modified header: %w", err) } gzReader, err := gzip.NewReader(response.Body) @@ -196,7 +196,7 @@ func (r *HTTPReader) get( func parseTime(s string) (time.Time, error) { t, err := time.ParseInLocation(time.RFC1123, s, time.UTC) if err != nil { - return time.Time{}, fmt.Errorf("error parsing time: %w", err) + return time.Time{}, fmt.Errorf("parsing time: %w", err) } return t, nil diff --git a/pkg/geoipupdate/database/local_file_writer.go b/pkg/geoipupdate/database/local_file_writer.go index a22a1e89..ef5bb42b 100644 --- a/pkg/geoipupdate/database/local_file_writer.go +++ b/pkg/geoipupdate/database/local_file_writer.go @@ -34,7 +34,7 @@ func NewLocalFileWriter( ) (*LocalFileWriter, error) { err := os.MkdirAll(filepath.Dir(databaseDir), 0o750) if err != nil { - return nil, fmt.Errorf("error creating database directory: %w", err) + return nil, fmt.Errorf("creating database directory: %w", err) } return &LocalFileWriter{ @@ -56,7 +56,7 @@ func (w *LocalFileWriter) Write(result *ReadResult) error { defer func() { if err := result.reader.Close(); err != nil { - log.Printf("error closing reader for %s: %+v", result.EditionID, err) + log.Printf("closing reader for %s: %+v", result.EditionID, err) } }() @@ -65,32 +65,32 @@ func (w *LocalFileWriter) Write(result *ReadResult) error { // write the Reader's result into a temporary file. fw, err := newFileWriter(databaseFilePath + tempExtension) if err != nil { - return fmt.Errorf("error setting up database writer for %s: %w", result.EditionID, err) + return fmt.Errorf("setting up database writer for %s: %w", result.EditionID, err) } defer func() { if err := fw.close(); err != nil { - log.Printf("error closing file writer: %+v", err) + log.Printf("closing file writer: %+v", err) } }() if err := fw.write(result.reader); err != nil { - return fmt.Errorf("error writing to the temp file for %s: %w", result.EditionID, err) + return fmt.Errorf("writing to the temp file for %s: %w", result.EditionID, err) } // make sure the hash of the temp file matches the expected hash. if err := fw.validateHash(result.NewHash); err != nil { - return fmt.Errorf("error validating hash for %s: %w", result.EditionID, err) + return fmt.Errorf("validating hash for %s: %w", result.EditionID, err) } // move the temoporary database file into it's final location and // sync the directory. if err := fw.syncAndRename(databaseFilePath); err != nil { - return fmt.Errorf("error renaming temp file: %w", err) + return fmt.Errorf("renaming temp file: %w", err) } // sync database directory. if err := syncDir(filepath.Dir(databaseFilePath)); err != nil { - return fmt.Errorf("error syncing database directory: %w", err) + return fmt.Errorf("syncing database directory: %w", err) } // check if we need to set the file's modified at time @@ -119,18 +119,18 @@ func (w *LocalFileWriter) GetHash(editionID string) (string, error) { } return ZeroMD5, nil } - return "", fmt.Errorf("error opening database: %w", err) + return "", fmt.Errorf("opening database: %w", err) } defer func() { if err := database.Close(); err != nil { - log.Println(fmt.Errorf("error closing database: %w", err)) + log.Println(fmt.Errorf("closing database: %w", err)) } }() md5Hash := md5.New() if _, err := io.Copy(md5Hash, database); err != nil { - return "", fmt.Errorf("error calculating database hash: %w", err) + return "", fmt.Errorf("calculating database hash: %w", err) } result := byteToString(md5Hash.Sum(nil)) @@ -160,7 +160,7 @@ func newFileWriter(path string) (*fileWriter, error) { //nolint:gosec // we really need to read this file. file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644) if err != nil { - return nil, fmt.Errorf("error creating temporary file at %s: %w", path, err) + return nil, fmt.Errorf("creating temporary file at %s: %w", path, err) } return &fileWriter{ @@ -174,12 +174,12 @@ func (w *fileWriter) close() error { if err := w.file.Close(); err != nil { var perr *os.PathError if !errors.As(err, &perr) || !errors.Is(perr.Err, os.ErrClosed) { - return fmt.Errorf("error closing temporary file: %w", err) + return fmt.Errorf("closing temporary file: %w", err) } } if err := os.Remove(w.file.Name()); err != nil && !os.IsNotExist(err) { - return fmt.Errorf("error removing temporary file: %w", err) + return fmt.Errorf("removing temporary file: %w", err) } return nil @@ -189,7 +189,7 @@ func (w *fileWriter) close() error { func (w *fileWriter) write(r io.Reader) error { writer := io.MultiWriter(w.md5Writer, w.file) if _, err := io.Copy(writer, r); err != nil { - return fmt.Errorf("error writing database: %w", err) + return fmt.Errorf("writing database: %w", err) } return nil } @@ -206,13 +206,13 @@ func (w *fileWriter) validateHash(h string) error { // syncAndRename syncs the content of the file to storage and renames it. func (w *fileWriter) syncAndRename(name string) error { if err := w.file.Sync(); err != nil { - return fmt.Errorf("error syncing temporary file: %w", err) + return fmt.Errorf("syncing temporary file: %w", err) } if err := w.file.Close(); err != nil { - return fmt.Errorf("error closing temporary file: %w", err) + return fmt.Errorf("closing temporary file: %w", err) } if err := os.Rename(w.file.Name(), name); err != nil { - return fmt.Errorf("error moving database into place: %w", err) + return fmt.Errorf("moving database into place: %w", err) } return nil } @@ -223,7 +223,7 @@ func syncDir(path string) error { //nolint:gosec // we really need to read this file. d, err := os.Open(path) if err != nil { - return fmt.Errorf("error opening database directory %s: %w", path, err) + return fmt.Errorf("opening database directory %s: %w", path, err) } defer func() { if err := d.Close(); err != nil { @@ -241,7 +241,7 @@ func syncDir(path string) error { // setModifiedAtTime sets the times for a database file to a certain value. func setModifiedAtTime(path string, t time.Time) error { if err := os.Chtimes(path, t, t); err != nil { - return fmt.Errorf("error setting times on file %s: %w", path, err) + return fmt.Errorf("setting times on file %s: %w", path, err) } return nil } diff --git a/pkg/geoipupdate/database/reader.go b/pkg/geoipupdate/database/reader.go index f0e1010a..17f49353 100644 --- a/pkg/geoipupdate/database/reader.go +++ b/pkg/geoipupdate/database/reader.go @@ -47,7 +47,7 @@ func (r ReadResult) MarshalJSON() ([]byte, error) { res, err := json.Marshal(s) if err != nil { - return nil, fmt.Errorf("error marshaling ReadResult: %w", err) + return nil, fmt.Errorf("marshaling ReadResult: %w", err) } return res, nil } @@ -64,7 +64,7 @@ func (r *ReadResult) UnmarshalJSON(data []byte) error { err := json.Unmarshal(data, &s) if err != nil { - return fmt.Errorf("error unmarshaling json into ReadResult: %w", err) + return fmt.Errorf("unmarshaling json into ReadResult: %w", err) } result := ReadResult(s.partialResult) diff --git a/pkg/geoipupdate/geoip_updater.go b/pkg/geoipupdate/geoip_updater.go index b709654e..24847709 100644 --- a/pkg/geoipupdate/geoip_updater.go +++ b/pkg/geoipupdate/geoip_updater.go @@ -11,8 +11,8 @@ import ( "sync" "time" - "github.com/maxmind/geoipupdate/v5/pkg/geoipupdate/database" - "github.com/maxmind/geoipupdate/v5/pkg/geoipupdate/internal" + "github.com/maxmind/geoipupdate/v6/pkg/geoipupdate/database" + "github.com/maxmind/geoipupdate/v6/pkg/geoipupdate/internal" ) // Client uses config data to initiate a download or update @@ -57,14 +57,14 @@ func NewClient(config *Config) *Client { func (c *Client) Run(ctx context.Context) error { fileLock, err := internal.NewFileLock(c.config.LockFile, c.config.Verbose) if err != nil { - return fmt.Errorf("error initializing file lock: %w", err) + return fmt.Errorf("initializing file lock: %w", err) } if err := fileLock.Acquire(); err != nil { - return fmt.Errorf("error acquiring file lock: %w", err) + return fmt.Errorf("acquiring file lock: %w", err) } defer func() { if err := fileLock.Release(); err != nil { - log.Printf("error releasing file lock: %s", err) + log.Printf("releasing file lock: %s", err) } }() @@ -72,12 +72,12 @@ func (c *Client) Run(ctx context.Context) error { reader, err := c.getReader() if err != nil { - return fmt.Errorf("error initializing database reader: %w", err) + return fmt.Errorf("initializing database reader: %w", err) } writer, err := c.getWriter() if err != nil { - return fmt.Errorf("error initializing database writer: %w", err) + return fmt.Errorf("initializing database writer: %w", err) } var editions []database.ReadResult @@ -113,13 +113,13 @@ func (c *Client) Run(ctx context.Context) error { // Run blocks until all jobs are processed or exits early after // the first encountered error. if err := jobProcessor.Run(ctx); err != nil { - return fmt.Errorf("error running the job processor: %w", err) + return fmt.Errorf("running the job processor: %w", err) } if c.config.Output { result, err := json.Marshal(editions) if err != nil { - return fmt.Errorf("error marshaling result log: %w", err) + return fmt.Errorf("marshaling result log: %w", err) } c.output.Print(string(result)) } diff --git a/pkg/geoipupdate/geoip_updater_test.go b/pkg/geoipupdate/geoip_updater_test.go index 2fd60f1b..cf648ab9 100644 --- a/pkg/geoipupdate/geoip_updater_test.go +++ b/pkg/geoipupdate/geoip_updater_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/maxmind/geoipupdate/v5/pkg/geoipupdate/database" + "github.com/maxmind/geoipupdate/v6/pkg/geoipupdate/database" "github.com/stretchr/testify/require" ) diff --git a/pkg/geoipupdate/internal/file_lock.go b/pkg/geoipupdate/internal/file_lock.go index 66b0d622..598d9696 100644 --- a/pkg/geoipupdate/internal/file_lock.go +++ b/pkg/geoipupdate/internal/file_lock.go @@ -19,7 +19,7 @@ type FileLock struct { func NewFileLock(path string, verbose bool) (*FileLock, error) { err := os.MkdirAll(filepath.Dir(path), 0o750) if err != nil { - return nil, fmt.Errorf("error creating lock file directory: %w", err) + return nil, fmt.Errorf("creating lock file directory: %w", err) } if verbose { @@ -35,7 +35,7 @@ func NewFileLock(path string, verbose bool) (*FileLock, error) { // Release unlocks the file lock. func (f *FileLock) Release() error { if err := f.lock.Unlock(); err != nil { - return fmt.Errorf("error releasing file lock at %s: %w", f.lock.Path(), err) + return fmt.Errorf("releasing file lock at %s: %w", f.lock.Path(), err) } if f.verbose { log.Printf("Lock file %s successfully released", f.lock.Path()) @@ -50,7 +50,7 @@ func (f *FileLock) Release() error { func (f *FileLock) Acquire() error { ok, err := f.lock.TryLock() if err != nil { - return fmt.Errorf("error acquiring file lock at %s: %w", f.lock.Path(), err) + return fmt.Errorf("acquiring file lock at %s: %w", f.lock.Path(), err) } if !ok { return fmt.Errorf("lock %s already acquired by another process", f.lock.Path()) diff --git a/pkg/geoipupdate/internal/job_processor.go b/pkg/geoipupdate/internal/job_processor.go index 2bfdc8c3..8ae8b3a1 100644 --- a/pkg/geoipupdate/internal/job_processor.go +++ b/pkg/geoipupdate/internal/job_processor.go @@ -63,7 +63,7 @@ func (j *JobProcessor) Run(ctx context.Context) error { // error encountered, if any. func (j *JobProcessor) Wait() error { if err := j.processor.Wait(); err != nil { - return fmt.Errorf("error running job: %w", err) + return fmt.Errorf("running job: %w", err) } return nil }