Skip to content
Open
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
1 change: 1 addition & 0 deletions cni/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ RUN GOOS=$OS CGO_ENABLED=0 go build -a -o /go/bin/azure-vnet -trimpath -ldflags
RUN GOOS=$OS CGO_ENABLED=0 go build -a -o /go/bin/azure-vnet-telemetry -trimpath -ldflags "-s -w -X main.version="$VERSION" -X "$CNI_AI_PATH"="$CNI_AI_ID"" -gcflags="-dwarflocationlists=true" cni/telemetry/service/telemetrymain.go
RUN GOOS=$OS CGO_ENABLED=0 go build -a -o /go/bin/azure-vnet-ipam -trimpath -ldflags "-s -w -X main.version="$VERSION"" -gcflags="-dwarflocationlists=true" cni/ipam/plugin/main.go
RUN GOOS=$OS CGO_ENABLED=0 go build -a -o /go/bin/azure-vnet-stateless -trimpath -ldflags "-s -w -X main.version="$VERSION"" -gcflags="-dwarflocationlists=true" cni/network/stateless/main.go
RUN GOOS=$OS CGO_ENABLED=0 go build -a -o /go/bin/azure-cni-telemetry-sidecar -trimpath -ldflags "-X main.version=$VERSION" -gcflags="-dwarflocationlists=true" ./cns/cni-telemetry-sidecar

FROM mariner-core AS compressor
ARG OS
Expand Down
183 changes: 183 additions & 0 deletions cns/cni-telemetry-sidecar/configmanager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package main

import (
"encoding/json"
"fmt"
"os"

"github.com/Azure/azure-container-networking/cns/configuration"
"github.com/Azure/azure-container-networking/telemetry"
"go.uber.org/zap"
)

// ConfigManager handles CNS configuration loading
type ConfigManager struct {
configPath string
logger *zap.Logger
}

// NewConfigManager creates a new config manager
func NewConfigManager(configPath string) *ConfigManager {
return &ConfigManager{
configPath: configPath,
}
}

// SetLogger sets the zap logger for the config manager
func (cm *ConfigManager) SetLogger(logger *zap.Logger) {
cm.logger = logger
}

// LoadConfig loads the CNS configuration from file
func (cm *ConfigManager) LoadConfig() (*configuration.CNSConfig, error) {
if cm.logger != nil {
cm.logger.Debug("Loading CNS configuration", zap.String("path", cm.configPath))
}

// Check if config file exists
if _, err := os.Stat(cm.configPath); os.IsNotExist(err) {
if cm.logger != nil {
cm.logger.Info("CNS config file not found, using default configuration",
zap.String("path", cm.configPath))
}
return cm.createDefaultConfig(), nil
}

// Read the config file
data, err := os.ReadFile(cm.configPath)
if err != nil {
if cm.logger != nil {
cm.logger.Error("Failed to read CNS config file",
zap.String("path", cm.configPath),
zap.Error(err))
}
return nil, fmt.Errorf("failed to read config file %s: %w", cm.configPath, err)
}

// Parse the JSON configuration
var config configuration.CNSConfig
if err := json.Unmarshal(data, &config); err != nil {
if cm.logger != nil {
cm.logger.Error("Failed to parse CNS config file",
zap.String("path", cm.configPath),
zap.Error(err))
}
return nil, fmt.Errorf("failed to parse config file: %w", err)
}

// Apply defaults and environment variable overrides
cm.setConfigDefaults(&config)

// Check for AppInsights key from all sources (build-time, config, env)
hasAppInsightsKey := cm.hasEffectiveAppInsightsKey(&config.TelemetrySettings)

if cm.logger != nil {
cm.logger.Info("Successfully loaded CNS configuration",
zap.String("path", cm.configPath),
zap.Bool("telemetryDisabled", config.TelemetrySettings.DisableAll),
zap.Bool("cniTelemetryEnabled", config.TelemetrySettings.EnableCNITelemetry),
zap.String("socketPath", config.TelemetrySettings.CNITelemetrySocketPath),
zap.Bool("hasAppInsightsKey", hasAppInsightsKey))
}

return &config, nil
}

// createDefaultConfig creates a default configuration
func (cm *ConfigManager) createDefaultConfig() *configuration.CNSConfig {
config := &configuration.CNSConfig{
TelemetrySettings: configuration.TelemetrySettings{
DisableAll: false,
TelemetryBatchSizeBytes: defaultBatchSizeInBytes,
TelemetryBatchIntervalInSecs: defaultBatchIntervalInSecs,
RefreshIntervalInSecs: defaultRefreshTimeoutInSecs,
DisableMetadataRefreshThread: false,
DebugMode: false,
DisableTrace: false,
DisableMetric: false,
DisableEvent: false,
EnableCNITelemetry: false, // Default to false
CNITelemetrySocketPath: "/var/run/azure-vnet-telemetry.sock",
},
}

// Set AppInsights key from environment variables (if any)
cm.setAppInsightsKeyFromEnv(&config.TelemetrySettings)

return config
}

// setConfigDefaults applies default values and environment variable overrides
func (cm *ConfigManager) setConfigDefaults(config *configuration.CNSConfig) {
// Set default values for telemetry settings if not specified
if config.TelemetrySettings.TelemetryBatchSizeBytes == 0 {
config.TelemetrySettings.TelemetryBatchSizeBytes = defaultBatchSizeInBytes
}
if config.TelemetrySettings.TelemetryBatchIntervalInSecs == 0 {
config.TelemetrySettings.TelemetryBatchIntervalInSecs = defaultBatchIntervalInSecs
}
if config.TelemetrySettings.RefreshIntervalInSecs == 0 {
config.TelemetrySettings.RefreshIntervalInSecs = defaultRefreshTimeoutInSecs
}

// Set default CNI telemetry socket path
if config.TelemetrySettings.CNITelemetrySocketPath == "" {
config.TelemetrySettings.CNITelemetrySocketPath = "/var/run/azure-vnet-telemetry.sock"
}

// Handle AppInsights instrumentation key from environment variables
cm.setAppInsightsKeyFromEnv(&config.TelemetrySettings)
}

// setAppInsightsKeyFromEnv sets the AppInsights instrumentation key from environment variables
func (cm *ConfigManager) setAppInsightsKeyFromEnv(ts *configuration.TelemetrySettings) {
// Try multiple environment variable names
envKeys := []string{
"APPINSIGHTS_INSTRUMENTATIONKEY",
"APPLICATIONINSIGHTS_CONNECTION_STRING",
"AI_INSTRUMENTATION_KEY",
}

// If no key is set in config, try environment variables
if ts.AppInsightsInstrumentationKey == "" {
for _, envKey := range envKeys {
if key := os.Getenv(envKey); key != "" {
ts.AppInsightsInstrumentationKey = key
if cm.logger != nil {
cm.logger.Debug("Found AppInsights key in environment variable",
zap.String("envVar", envKey))
}
break
}
}
}
}

// hasEffectiveAppInsightsKey checks if AppInsights key is available from any source
// (build-time aiMetadata, config file, or environment variables)
func (cm *ConfigManager) hasEffectiveAppInsightsKey(ts *configuration.TelemetrySettings) bool {
// Priority 1: Build-time embedded key via telemetry.aiMetadata
if buildTimeKey := telemetry.GetAIMetadata(); buildTimeKey != "" {
return true
}

// Priority 2: Config file
if ts.AppInsightsInstrumentationKey != "" {
return true
}

// Priority 3: Environment variables
envKeys := []string{
"APPINSIGHTS_INSTRUMENTATIONKEY",
"APPLICATIONINSIGHTS_CONNECTION_STRING",
"AI_INSTRUMENTATION_KEY",
}

for _, envKey := range envKeys {
if key := os.Getenv(envKey); key != "" {
return true
}
}

return false
}
100 changes: 100 additions & 0 deletions cns/cni-telemetry-sidecar/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package main

import (
"context"
"flag"
"os"
"os/signal"
"syscall"

"github.com/Azure/azure-container-networking/telemetry"
"go.uber.org/zap"
)

var (
configPath = flag.String("config", "/etc/azure-cns/cns_config.json", "Path to CNS configuration file")
logLevel = flag.String("log-level", "info", "Log level (debug, info, warn, error)")

// This variable is set at build time via ldflags from Makefile
version = "1.0.0" // -X main.version=$(CNI_TELEMETRY_SIDECAR_VERSION)
)

func main() {
flag.Parse()

// Initialize logger
logger, err := initializeLogger(*logLevel)
if err != nil {
panic(err)
}
defer logger.Sync()

Check failure on line 30 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.23.x, ubuntu-latest)

Error return value of `logger.Sync` is not checked (errcheck)

Check failure on line 30 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.22.x, ubuntu-latest)

Error return value of `logger.Sync` is not checked (errcheck)

Check failure on line 30 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.23.x, windows-latest)

Error return value of `logger.Sync` is not checked (errcheck)

Check failure on line 30 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.22.x, windows-latest)

Error return value of `logger.Sync` is not checked (errcheck)

Check failure on line 30 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.22.x, ubuntu-latest)

Error return value of `logger.Sync` is not checked (errcheck)

Check failure on line 30 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.23.x, ubuntu-latest)

Error return value of `logger.Sync` is not checked (errcheck)

Check failure on line 30 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.23.x, windows-latest)

Error return value of `logger.Sync` is not checked (errcheck)

Check failure on line 30 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.22.x, windows-latest)

Error return value of `logger.Sync` is not checked (errcheck)

// DEBUG: Check if aiMetadata was set at build time via ldflags
currentAIMetadata := telemetry.GetAIMetadata()
logger.Info("Starting Azure CNI Telemetry Sidecar",
zap.String("version", version),
zap.String("configPath", *configPath),
zap.String("logLevel", *logLevel),
zap.Bool("hasBuiltInAIKey", currentAIMetadata != ""),
zap.String("aiKeyPrefix", MaskAIKey(currentAIMetadata)))

// Create and configure telemetry sidecar
// Pass the configPath to NewTelemetrySidecar (it expects a string parameter)
sidecar := NewTelemetrySidecar(*configPath)
if err := sidecar.SetLogger(logger); err != nil {
logger.Error("Failed to set logger", zap.Error(err))
os.Exit(1)

Check failure on line 46 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.23.x, ubuntu-latest)

exitAfterDefer: os.Exit will exit, and `defer logger.Sync()` will not run (gocritic)

Check failure on line 46 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.22.x, ubuntu-latest)

exitAfterDefer: os.Exit will exit, and `defer logger.Sync()` will not run (gocritic)

Check failure on line 46 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.23.x, windows-latest)

exitAfterDefer: os.Exit will exit, and `defer logger.Sync()` will not run (gocritic)

Check failure on line 46 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.22.x, windows-latest)

exitAfterDefer: os.Exit will exit, and `defer logger.Sync()` will not run (gocritic)

Check failure on line 46 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.22.x, ubuntu-latest)

exitAfterDefer: os.Exit will exit, and `defer logger.Sync()` will not run (gocritic)

Check failure on line 46 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.23.x, ubuntu-latest)

exitAfterDefer: os.Exit will exit, and `defer logger.Sync()` will not run (gocritic)

Check failure on line 46 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.23.x, windows-latest)

exitAfterDefer: os.Exit will exit, and `defer logger.Sync()` will not run (gocritic)

Check failure on line 46 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.22.x, windows-latest)

exitAfterDefer: os.Exit will exit, and `defer logger.Sync()` will not run (gocritic)
}

// Log which AI key source we're using
if currentAIMetadata != "" {
logger.Info("Using build-time embedded AppInsights key (from Makefile)")
} else {
logger.Info("No build-time AppInsights key found - will check config/environment")
}

// Create context with cancellation for graceful shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Handle shutdown signals
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sigChan
logger.Info("Received shutdown signal", zap.String("signal", sig.String()))
cancel()
}()

// Run the sidecar
if err := sidecar.Run(ctx); err != nil && err != context.Canceled {

Check failure on line 70 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.23.x, ubuntu-latest)

do not compare errors directly "err != context.Canceled", use "!errors.Is(err, context.Canceled)" instead (err113)

Check failure on line 70 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.22.x, ubuntu-latest)

do not compare errors directly "err != context.Canceled", use "!errors.Is(err, context.Canceled)" instead (err113)

Check failure on line 70 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.23.x, windows-latest)

do not compare errors directly "err != context.Canceled", use "!errors.Is(err, context.Canceled)" instead (err113)

Check failure on line 70 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.22.x, windows-latest)

do not compare errors directly "err != context.Canceled", use "!errors.Is(err, context.Canceled)" instead (err113)

Check failure on line 70 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.22.x, ubuntu-latest)

do not compare errors directly "err != context.Canceled", use "!errors.Is(err, context.Canceled)" instead (err113)

Check failure on line 70 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.23.x, ubuntu-latest)

do not compare errors directly "err != context.Canceled", use "!errors.Is(err, context.Canceled)" instead (err113)

Check failure on line 70 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.23.x, windows-latest)

do not compare errors directly "err != context.Canceled", use "!errors.Is(err, context.Canceled)" instead (err113)

Check failure on line 70 in cns/cni-telemetry-sidecar/main.go

View workflow job for this annotation

GitHub Actions / Lint (1.22.x, windows-latest)

do not compare errors directly "err != context.Canceled", use "!errors.Is(err, context.Canceled)" instead (err113)
logger.Error("Sidecar execution failed", zap.Error(err))
os.Exit(1)
}

logger.Info("Azure CNI Telemetry Sidecar shutdown complete")
}

// initializeLogger creates a zap logger with the specified level
func initializeLogger(level string) (*zap.Logger, error) {
var zapLevel zap.AtomicLevel
switch level {
case "debug":
zapLevel = zap.NewAtomicLevelAt(zap.DebugLevel)
case "info":
zapLevel = zap.NewAtomicLevelAt(zap.InfoLevel)
case "warn":
zapLevel = zap.NewAtomicLevelAt(zap.WarnLevel)
case "error":
zapLevel = zap.NewAtomicLevelAt(zap.ErrorLevel)
default:
zapLevel = zap.NewAtomicLevelAt(zap.InfoLevel)
}

config := zap.NewProductionConfig()
config.Level = zapLevel
config.DisableStacktrace = true
config.DisableCaller = false

return config.Build()
}
Loading
Loading