Skip to content

Commit

Permalink
Introduced a logging framework with context-aware capabilities for im…
Browse files Browse the repository at this point in the history
…proved observability (#35)
  • Loading branch information
brettcurtis authored Aug 30, 2024
1 parent a773314 commit acef7cc
Show file tree
Hide file tree
Showing 13 changed files with 462 additions and 368 deletions.
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ updates:
schedule:
interval: daily

- package-ecosystem: gomod
directories:
- "**/*"
schedule:
interval: daily

- package-ecosystem: terraform
directories:
- "**/*"
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repos:


- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.92.1
rev: v1.92.3
hooks:
- id: terraform_fmt

Expand All @@ -30,7 +30,7 @@ repos:
- --hook-config=--create-file-if-not-exist=false

- repo: https://github.com/bridgecrewio/checkov.git
rev: 3.2.219
rev: 3.2.238
hooks:
- id: checkov
files: Dockerfile
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
FROM golang:1.23
FROM golang:1.22-alpine

# Ensure that HEALTHCHECK instructions have been added to container images
# checkov:skip=CKV_DOCKER_2: Since Kubernetes 1.8, the Docker HEALTHCHECK has been disabled explicitly

# Create a non-root user for security purposes

RUN useradd -m gke-info
RUN addgroup -S gke-info && adduser -S gke-info -G gke-info
USER gke-info

ARG DD_GIT_REPOSITORY_URL
Expand Down
106 changes: 57 additions & 49 deletions cmd/http/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ package main

import (
"context"
"log"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"

"gke-info/internal/metadata"
"gke-info/internal/observability"

"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
"gopkg.in/DataDog/dd-trace-go.v1/profiler"
Expand All @@ -19,52 +20,59 @@ import (

// main initializes the HTTP server and sets up the routes.
func main() {
tracer.Start()
defer tracer.Stop()

err := profiler.Start(
profiler.WithProfileTypes(
profiler.CPUProfile,
profiler.HeapProfile,
),
)
if err != nil {
log.Printf("Warning: Failed to start profiler: %v", err)
}
defer profiler.Stop()

mux := httptrace.NewServeMux()
mux.HandleFunc("/gke-info-go/metadata/", metadata.MetadataHandler)
mux.HandleFunc("/gke-info-go/health", metadata.HealthCheckHandler)

port := "8080"
if envPort := os.Getenv("PORT"); envPort != "" {
port = envPort
}

server := &http.Server{
Addr: ":" + port,
Handler: mux,
}

go func() {
log.Printf("Starting server on port %s...\n", port)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Failed to start server: %v", err)
}
}()

// Graceful shutdown
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatalf("Server forced to shutdown: %v", err)
}

log.Println("Server exiting")
ctx := context.Background()

// Initialize logger
observability.Init()

observability.InfoWithContext(ctx, "Application is starting")

tracer.Start()
defer tracer.Stop()

err := profiler.Start(
profiler.WithProfileTypes(
profiler.CPUProfile,
profiler.HeapProfile,
),
)
if err != nil {
observability.ErrorWithContext(ctx, fmt.Sprintf("Warning: Failed to start profiler: %v", err))
}
defer profiler.Stop()

mux := httptrace.NewServeMux()
mux.HandleFunc("/gke-info-go/metadata/", metadata.MetadataHandler(metadata.FetchMetadata))
mux.HandleFunc("/gke-info-go/health", metadata.HealthCheckHandler)

port := "8080"
if envPort := os.Getenv("PORT"); envPort != "" {
port = envPort
}

server := &http.Server{
Addr: ":" + port,
Handler: mux,
}

go func() {
observability.InfoWithContext(ctx, fmt.Sprintf("Starting server on port %s...", port))
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
observability.ErrorWithContext(ctx, fmt.Sprintf("Failed to start server: %v", err))
}
}()

// Graceful shutdown
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
observability.InfoWithContext(ctx, "Shutting down server...")

ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
observability.ErrorWithContext(ctx, fmt.Sprintf("Server forced to shutdown: %v", err))
}

observability.InfoWithContext(ctx, "Server exiting")
}
96 changes: 47 additions & 49 deletions cmd/http/main_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"
"io"
"net/http"
Expand All @@ -13,60 +14,57 @@ import (
)

func TestMain(t *testing.T) {
// Set up a test server
mux := http.NewServeMux()
mux.HandleFunc("/gke-info-go/metadata/", metadata.MetadataHandler)
// Set up a test server
mux := http.NewServeMux()
mux.HandleFunc("/gke-info-go/metadata/", metadata.MetadataHandler(mockFetchMetadata))

ts := httptest.NewServer(mux)
defer ts.Close()
ts := httptest.NewServer(mux)
defer ts.Close()

// Set the PORT environment variable to the test server's port
os.Setenv("PORT", ts.Listener.Addr().String())
defer os.Unsetenv("PORT")
// Set the PORT environment variable to the test server's port
os.Setenv("PORT", ts.Listener.Addr().String())
defer os.Unsetenv("PORT")

// Test cases
tests := []struct {
url string
expectedCode int
expectedBody string
isJSON bool
}{
{ts.URL + "/gke-info-go/metadata/cluster-name", http.StatusOK, `{"cluster-name":"test-cluster-name"}`, true},
{ts.URL + "/gke-info-go/metadata/cluster-location", http.StatusOK, `{"cluster-location":"test-cluster-location"}`, true},
{ts.URL + "/gke-info-go/metadata/instance-zone", http.StatusOK, `{"instance-zone":"us-central1-a"}`, true},
{ts.URL + "/gke-info-go/metadata/unknown", http.StatusBadRequest, "Unknown metadata type\n", false},
}
// Test cases
tests := []struct {
url string
expectedCode int
expectedBody string
isJSON bool
}{
{ts.URL + "/gke-info-go/metadata/cluster-name", http.StatusOK, `{"cluster-name":"test-cluster-name"}`, true},
{ts.URL + "/gke-info-go/metadata/cluster-location", http.StatusOK, `{"cluster-location":"test-cluster-location"}`, true},
{ts.URL + "/gke-info-go/metadata/instance-zone", http.StatusOK, `{"instance-zone":"us-central1-a"}`, true},
{ts.URL + "/gke-info-go/metadata/unknown", http.StatusBadRequest, "Unknown metadata type\n", false},
}

// Mock FetchMetadata for the tests
originalFetchMetadata := metadata.FetchMetadata
defer func() { metadata.FetchMetadata = originalFetchMetadata }()
metadata.FetchMetadata = func(url string) (string, error) {
switch url {
case metadata.ClusterNameURL:
return "test-cluster-name", nil
case metadata.ClusterLocationURL:
return "test-cluster-location", nil
case metadata.InstanceZoneURL:
return "projects/1234567890/zones/us-central1-a", nil
default:
return "", fmt.Errorf("unknown URL: %s", url)
}
}
// Run the test cases
for _, test := range tests {
resp, err := http.Get(test.url)
assert.NoError(t, err)
assert.Equal(t, test.expectedCode, resp.StatusCode)

// Run the test cases
for _, test := range tests {
resp, err := http.Get(test.url)
assert.NoError(t, err)
assert.Equal(t, test.expectedCode, resp.StatusCode)
body, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
assert.NoError(t, err)
defer resp.Body.Close()
if test.isJSON {
assert.JSONEq(t, test.expectedBody, string(body))
} else {
assert.Equal(t, test.expectedBody, string(body))
}
}
}

if test.isJSON {
assert.JSONEq(t, test.expectedBody, string(body))
} else {
assert.Equal(t, test.expectedBody, string(body))
}
}
func mockFetchMetadata(ctx context.Context, url string) (string, error) {
switch url {
case metadata.ClusterNameURL:
return "test-cluster-name", nil
case metadata.ClusterLocationURL:
return "test-cluster-location", nil
case metadata.InstanceZoneURL:
return "projects/1234567890/zones/us-central1-a", nil
default:
return "", fmt.Errorf("unknown URL: %s", url)
}
}
6 changes: 3 additions & 3 deletions deployments/regional/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ No requirements.

| Name | Version |
|------|---------|
| <a name="provider_datadog"></a> [datadog](#provider\_datadog) | 3.42.0 |
| <a name="provider_google"></a> [google](#provider\_google) | 5.40.0 |
| <a name="provider_kubernetes"></a> [kubernetes](#provider\_kubernetes) | 2.31.0 |
| <a name="provider_datadog"></a> [datadog](#provider\_datadog) | 3.43.1 |
| <a name="provider_google"></a> [google](#provider\_google) | 6.0.1 |
| <a name="provider_kubernetes"></a> [kubernetes](#provider\_kubernetes) | 2.32.0 |

## Modules

Expand Down
2 changes: 1 addition & 1 deletion deployments/regional/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# https://www.terraform.io/docs/language/values/locals.html

locals {
datadog_mci_synthetic_url = var.environment == "production" ? "https://gcp.osinfra.io/${local.datadog_synthetic_service}" : "https://${local.env}.gcp.osinfra.io/${local.datadog_synthetic_service}"
datadog_mci_synthetic_url = var.environment == "production" ? "https://gcp.osinfra.io/${local.datadog_synthetic_service}/metadata/cluster-name" : "https://${local.env}.gcp.osinfra.io/${local.datadog_synthetic_service}/metadata/cluster-name"
datadog_synthetic_message_critical = var.environment == "production" ? "@hangouts-Platform-CriticalHighPriority" : ""
datadog_synthetic_message_medium = var.environment == "production" ? "@hangouts-Platform-MediumLowInfoPriority" : ""
datadog_synthetic_name = "GKE Info"
Expand Down
55 changes: 30 additions & 25 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,53 @@ module gke-info

go 1.22.4

require github.com/stretchr/testify v1.8.4
require (
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.9.0
)

require (
github.com/DataDog/appsec-internal-go v1.6.0 // indirect
github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 // indirect
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1 // indirect
github.com/DataDog/datadog-go/v5 v5.3.0 // indirect
github.com/DataDog/go-libddwaf/v3 v3.2.1 // indirect
github.com/DataDog/go-tuf v1.0.2-0.5.2 // indirect
github.com/DataDog/appsec-internal-go v1.7.0 // indirect
github.com/DataDog/datadog-agent/pkg/obfuscate v0.56.0 // indirect
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.56.0 // indirect
github.com/DataDog/datadog-go/v5 v5.5.0 // indirect
github.com/DataDog/go-libddwaf/v3 v3.3.0 // indirect
github.com/DataDog/go-sqllexer v0.0.14 // indirect
github.com/DataDog/go-tuf v1.1.0-0.5.2 // indirect
github.com/DataDog/gostackparse v0.7.0 // indirect
github.com/DataDog/sketches-go v1.4.5 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/DataDog/sketches-go v1.4.6 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 // indirect
github.com/ebitengine/purego v0.6.0-alpha.5 // indirect
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect
github.com/ebitengine/purego v0.7.1 // indirect
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 // indirect
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
github.com/hashicorp/go-sockaddr v1.0.6 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/outcaste-io/ristretto v0.2.3 // indirect
github.com/philhofer/fwd v1.1.2 // indirect
github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/tinylib/msgp v1.1.8 // indirect
github.com/tinylib/msgp v1.2.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.16.1 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/protobuf v1.33.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/time v0.6.0 // indirect
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
google.golang.org/protobuf v1.34.2 // indirect
)

require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
gopkg.in/DataDog/dd-trace-go.v1 v1.66.0
gopkg.in/DataDog/dd-trace-go.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit acef7cc

Please sign in to comment.