Ask Bjørn Hansen c991335da7
All checks were successful
continuous-integration/drone/push Build is passing
Add comprehensive test suite and documentation
- Complete unit, integration, and E2E test coverage (189 test cases)
- Enhanced CI/CD pipeline with race detection and quality checks
- Comprehensive godoc documentation for all packages
- Updated README with API docs, examples, and deployment guides
2025-07-02 01:02:28 -07:00
2025-03-23 23:02:33 -07:00
2018-03-18 15:24:57 -08:00

GeoIP API

A high-performance HTTP service that provides MaxMind GeoIP data for IP geolocation lookups. Designed to run as a lightweight daemon within Kubernetes clusters to serve geolocation data to other services.

Features

  • Fast HTTP API for IP geolocation lookups
  • Multiple response formats: country codes and full JSON data
  • OpenTelemetry tracing with standard Traceparent headers
  • Automatic database discovery in standard system paths
  • Health check endpoint with actual database verification
  • Go client library for easy integration
  • Comprehensive test coverage with unit, integration, and E2E tests

API Endpoints

Country Lookup

GET /api/country?ip=192.0.2.1

Returns the lowercase ISO country code (e.g., us, gb)

Full JSON Data

GET /api/json?ip=192.0.2.1

Returns complete MaxMind GeoIP data in JSON format including:

  • Country, city, and region information
  • Latitude/longitude coordinates
  • ISP and organization data (if available)
  • Time zone information

Health Check

GET /healthz

Performs an actual GeoIP lookup to verify database connectivity and returns the country code for a test IP.

Installation

From Source

go build -o geoipapi
./geoipapi

Using Docker

docker build -t geoipapi .
docker run -p 8009:8009 geoipapi

Database Setup

The service automatically searches for MaxMind databases in standard locations:

  • /usr/share/GeoIP/ (Linux default)
  • /usr/local/share/GeoIP/ (FreeBSD)
  • /opt/local/share/GeoIP/ (MacPorts)
  • /opt/homebrew/var/GeoIP/ (Homebrew)

Supported Database Files

  • Country databases: GeoIP2-Country.mmdb, GeoLite2-Country.mmdb
  • City databases: GeoIP2-City.mmdb, GeoLite2-City.mmdb
  • ISP databases: GeoIP2-ISP.mmdb

Installing GeoLite2 Databases (Free)

  1. Create a free MaxMind account at https://www.maxmind.com/en/geolite2/signup
  2. Download the databases manually, or
  3. Use the built-in MaxMind package for automatic downloads:
import "go.ntppool.org/geoipapi/maxmind"

maxmind.LicenseKey = "your_license_key_here"
maxmind.EditionIDs = "GeoLite2-City,GeoLite2-Country"
maxmind.Path = "/usr/share/GeoIP/"

err := maxmind.DownloadGeoLite2DB()

Configuration

Environment Variables

  • OpenTelemetry: Standard OTel environment variables are supported
    • OTEL_EXPORTER_OTLP_ENDPOINT
    • OTEL_SERVICE_NAME
    • OTEL_RESOURCE_ATTRIBUTES

Server Configuration

The server runs on port 8009 by default with the following timeouts:

  • Read timeout: 1 second
  • Write timeout: 10 seconds

OpenTelemetry Support

The service includes comprehensive OpenTelemetry instrumentation:

  • HTTP requests are automatically traced
  • Database lookups are instrumented with spans
  • Health checks are filtered from tracing to reduce noise
  • Custom attributes include IP addresses and operation details

Tracing works seamlessly with the OpenTelemetry Collector and common observability platforms.

Go Client Library

Use the provided Go client for easy integration:

package main

import (
    "context"
    "fmt"
    "log"
    "net/netip"
    "os"

    "go.ntppool.org/geoipapi/client/geoipapi"
)

func main() {
    // Set the service endpoint
    os.Setenv("geoip_service", "geoip-service:8009")

    ctx := context.Background()
    ip := netip.MustParseAddr("8.8.8.8")
    
    city, err := geoipapi.GetCity(ctx, ip)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("IP: %s\n", ip)
    fmt.Printf("Country: %s\n", city.Country.IsoCode)
    fmt.Printf("City: %s\n", city.City.Names["en"])
    fmt.Printf("Location: %f, %f\n", city.Location.Latitude, city.Location.Longitude)
}

Client Configuration

Set the geoip_service environment variable to point to your GeoIP API service:

export geoip_service="geoip-service.default.svc.cluster.local:8009"

Command Line Usage

The service can also be used as a command-line tool for IP lookups:

./geoipapi 8.8.8.8 1.1.1.1 192.168.1.1

Output:

8.8.8.8: us
1.1.1.1: us
192.168.1.1: 

Development

Running Tests

The project includes comprehensive test coverage:

# Run all tests
go test ./...

# Run tests with race detection
go test -race ./...

# Run only fast tests
go test -short ./...

# Run with coverage
go test -cover ./...

Test Types

  • Unit tests: Test individual functions and components
  • Integration tests: Test HTTP API endpoints with a running server
  • End-to-end tests: Test complete client-server workflows
  • Race condition tests: Verify thread safety under concurrent load

Code Quality

# Format code
gofumpt -w .

# Lint code
go vet ./...

# Verify dependencies
go mod verify

Architecture

Core Components

  1. HTTP Server (geoipapi.go): Main API server with endpoint handlers
  2. MaxMind Package (maxmind/): Database download and management utilities
  3. Client Library (client/geoipapi/): Go client for consuming the HTTP API

Database Discovery

The service automatically discovers MaxMind databases by:

  1. Searching standard system paths
  2. Looking for supported database filenames
  3. Opening the first available database for each type
  4. Gracefully handling missing databases

Error Handling

  • Invalid IP addresses return HTTP 500 with "data error"
  • Missing databases are detected during health checks
  • Network errors in the client include proper context
  • Tracing errors are recorded in spans for debugging

Deployment

Kubernetes

Example Kubernetes deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: geoipapi
spec:
  replicas: 3
  selector:
    matchLabels:
      app: geoipapi
  template:
    metadata:
      labels:
        app: geoipapi
    spec:
      containers:
      - name: geoipapi
        image: your-registry/geoipapi:latest
        ports:
        - containerPort: 8009
        env:
        - name: OTEL_EXPORTER_OTLP_ENDPOINT
          value: "http://otel-collector:4317"
        - name: OTEL_SERVICE_NAME
          value: "geoipapi"
        volumeMounts:
        - name: geoip-data
          mountPath: /usr/share/GeoIP
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8009
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8009
          initialDelaySeconds: 15
          periodSeconds: 20
      volumes:
      - name: geoip-data
        configMap:
          name: geoip-databases
---
apiVersion: v1
kind: Service
metadata:
  name: geoipapi
spec:
  selector:
    app: geoipapi
  ports:
  - port: 8009
    targetPort: 8009

Performance Considerations

  • Database caching: MaxMind databases are loaded once on startup
  • Connection pooling: HTTP client uses connection pooling for better performance
  • Concurrent requests: Server handles multiple concurrent requests efficiently
  • Memory usage: Minimal memory footprint suitable for container environments

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Run the full test suite: go test ./...
  5. Format code: gofumpt -w .
  6. Submit a pull request

CI/CD

The project uses Drone CI with the following pipeline:

  1. Dependencies: Download Go modules
  2. Testing: Run unit, integration, and race tests
  3. Code Quality: Run go vet, gofumpt, and go mod verify
  4. Build: Compile the binary
  5. Docker: Build and push container image

License

This project is licensed under the terms specified in the LICENSE file.

Support

For issues and questions:

  • Bug reports: Create an issue in the GitHub repository
  • Feature requests: Submit a feature request with use case details
  • Documentation: Check the Go docs: go doc go.ntppool.org/geoipapi
Description
No description provided
Readme MIT 1.9 MiB
Languages
Go 99%
Dockerfile 0.6%
Shell 0.4%