diff --git a/.github/workflows/test-go-sdk.yml b/.github/workflows/test-go-sdk.yml new file mode 100644 index 0000000..bd0b13d --- /dev/null +++ b/.github/workflows/test-go-sdk.yml @@ -0,0 +1,14 @@ +# name: CI +# on: [push, pull_request] +# jobs: +# test: +# runs-on: ubuntu-latest +# steps: +# - name: Checkout code +# uses: actions/checkout@v4 +# - name: Set up Go +# uses: actions/setup-go@v5 +# with: +# go-version-file: go.mod +# - name: Test +# run: go test \ No newline at end of file diff --git a/.gitignore b/.gitignore index de5dfbd..3bddd47 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,25 @@ # MacOS Build Artifacts *.DS_Store -# Don't commit .env files with secret info, EVER + +# Secrets and keys ****DO NOT COMMIT SECRETS*** *.env -# JetBrains IDE files +*/secrets.json + +# Logs +*.log +*.gz + +# IDE files +## Visual Studio Code files +.vscode +## JetBrains IDE files *.idea -# Python virtual environment +## Python virtual environment */venv -# Node.js dependencies + +# Language specific output +## Node.js dependencies */node_modules -# Java build files -*/target \ No newline at end of file +## Java build files +*/target + diff --git a/config.json b/config.json index 10fdfb3..be899e6 100644 --- a/config.json +++ b/config.json @@ -1,67 +1,11 @@ [ { "notes": "Copy atlas-sdk-go project files to user-facing Atlas Architecture Center artifact repo root", - "source_directory" : "generated-usage-examples/go/atlas-sdk-go/copied-project/", + "source_directory" : "generated-usage-examples/go/atlas-sdk-go/project-copy/", "target_repo" : "atlas-architecture-go-sdk", "target_branch" : "main", "target_directory" : "." }, - { - "notes": "atlas-sdk-go/copied-project/cmd subdirectory", - "source_directory" : "generated-usage-examples/go/atlas-sdk-go/copied-project/cmd", - "target_repo" : "atlas-architecture-go-sdk", - "target_branch" : "main", - "target_directory" : "cmd" - }, - { - "notes": "atlas-sdk-go/copied-project/cmd/get_logs subdirectory", - "source_directory" : "generated-usage-examples/go/atlas-sdk-go/copied-project/cmd/get_logs", - "target_repo" : "atlas-architecture-go-sdk", - "target_branch" : "main", - "target_directory" : "cmd/get_logs" - }, - { - "notes": "atlas-sdk-go/copied-project/cmd/get_metrics_process subdirectory", - "source_directory" : "generated-usage-examples/go/atlas-sdk-go/copied-project/cmd/get_metrics_process", - "target_repo" : "atlas-architecture-go-sdk", - "target_branch" : "main", - "target_directory" : "cmd/get_metrics_process" - }, - { - "notes": "atlas-sdk-go/copied-project/cmd/get_metrics_disk subdirectory", - "source_directory" : "generated-usage-examples/go/atlas-sdk-go/copied-project/cmd/get_metrics_disk", - "target_repo" : "atlas-architecture-go-sdk", - "target_branch" : "main", - "target_directory" : "cmd/get_metrics_disk" - }, - { - "notes": "atlas-sdk-go/copied-project/configs", - "source_directory" : "generated-usage-examples/go/atlas-sdk-go/copied-project/configs", - "target_repo" : "atlas-architecture-go-sdk", - "target_branch" : "main", - "target_directory" : "configs" - }, - { - "notes": "atlas-sdk-go/copied-project/internal subdirectory", - "source_directory" : "generated-usage-examples/go/atlas-sdk-go/copied-project/internal", - "target_repo" : "atlas-architecture-go-sdk", - "target_branch" : "main", - "target_directory" : "internal" - }, - { - "notes": "atlas-sdk-go/copied-project/internal/auth subdirectory", - "source_directory" : "generated-usage-examples/go/atlas-sdk-go/copied-project/internal/auth", - "target_repo" : "atlas-architecture-go-sdk", - "target_branch" : "main", - "target_directory" : "internal/auth" - }, - { - "notes": "Copy atlas-sdk-go project files to user-facing Atlas Architecture Center artifact repo root", - "source_directory" : "generated-usage-examples/go/atlas-sdk-go/copied-project/", - "target_repo" : "atlas-architecture-go-sdk", - "target_branch" : "copy-test-2", - "target_directory" : "." - }, { "notes": "Example config for copying files from a directory to another repo", "source_directory" : "generated-examples", diff --git a/generated-usage-examples/go/atlas-sdk-go/copied-project/cmd/get_logs/main.go b/generated-usage-examples/go/atlas-sdk-go/copied-project/cmd/get_logs/main.go deleted file mode 100644 index 82a5234..0000000 --- a/generated-usage-examples/go/atlas-sdk-go/copied-project/cmd/get_logs/main.go +++ /dev/null @@ -1,110 +0,0 @@ -package main - -import ( - "atlas-sdk-go/internal/auth" - "compress/gzip" - "context" - "fmt" - "go.mongodb.org/atlas-sdk/v20250219001/admin" - "io" - "log" - "os" - "strings" -) - -// getHostLogs downloads a compressed .gz file that contains the MongoDB logs for -// the specified host in your project. -func getHostLogs(ctx context.Context, atlasClient admin.APIClient, params *admin.GetHostLogsApiParams) (string, error) { - logFileName := fmt.Sprintf("logs_%s_%s.gz", params.GroupId, params.HostName) - fmt.Printf("Fetching %s log for host %s in project %s\n", params.LogName, params.HostName, params.GroupId) - - if err := downloadLogs(ctx, atlasClient, params, logFileName); err != nil { - return "", err - } - - fmt.Printf("Logs saved to %s\n", logFileName) - return logFileName, nil -} - -func SafeClose(c io.Closer) { - if c != nil { - if err := c.Close(); err != nil { - log.Printf("Warning: failed to close resource: %v", err) - } - } -} - -func downloadLogs(ctx context.Context, atlasClient admin.APIClient, params *admin.GetHostLogsApiParams, filePath string) error { - resp, _, err := atlasClient.MonitoringAndLogsApi.GetHostLogsWithParams(ctx, params).Execute() - if err != nil { - return fmt.Errorf("fetch logs: %w", err) - } - defer SafeClose(resp) - - file, err := os.Create(filePath) - if err != nil { - return fmt.Errorf("create %q: %w", filePath, err) - } - defer SafeClose(file) - - if _, err := io.Copy(file, resp); err != nil { - return fmt.Errorf("write to %q: %w", filePath, err) - } - - return nil -} - -func unzipGzFile(srcPath, destPath string) error { - srcFile, err := os.Open(srcPath) - if err != nil { - return fmt.Errorf("open gz file: %w", err) - } - defer SafeClose(srcFile) - - gzReader, err := gzip.NewReader(srcFile) - if err != nil { - return fmt.Errorf("create gzip reader: %w", err) - } - defer SafeClose(gzReader) - - destFile, err := os.Create(destPath) - if err != nil { - return fmt.Errorf("create destination file: %w", err) - } - defer SafeClose(destFile) - - if _, err := io.Copy(destFile, gzReader); err != nil { - return fmt.Errorf("unzip copy error: %w", err) - } - - fmt.Printf("Unzipped logs to %s\n", destPath) - return nil -} - -func main() { - ctx := context.Background() - - // Create an Atlas client authenticated using OAuth2 with service account credentials - client, _, config, err := auth.CreateAtlasClient() - if err != nil { - log.Fatalf("Failed to create Atlas client: %v", err) - } - - params := &admin.GetHostLogsApiParams{ - GroupId: config.ProjectID, - HostName: config.HostName, - LogName: "mongodb", // Type of log ("mongodb" or "mongos") - } - - logFileName, err := getHostLogs(ctx, *client, params) - if err != nil { - log.Fatalf("Failed to download logs: %v", err) - } - - plainTextLog := strings.TrimSuffix(logFileName, ".gz") + ".log" - if err := unzipGzFile(logFileName, plainTextLog); err != nil { - log.Fatalf("Failed to unzip log file: %v", err) - } - -} - diff --git a/generated-usage-examples/go/atlas-sdk-go/copied-project/cmd/get_metrics_disk/main.go b/generated-usage-examples/go/atlas-sdk-go/copied-project/cmd/get_metrics_disk/main.go deleted file mode 100644 index fe61acf..0000000 --- a/generated-usage-examples/go/atlas-sdk-go/copied-project/cmd/get_metrics_disk/main.go +++ /dev/null @@ -1,63 +0,0 @@ -package main - -import ( - "atlas-sdk-go/internal/auth" - "context" - "encoding/json" - "fmt" - "go.mongodb.org/atlas-sdk/v20250219001/admin" - "log" -) - -// getDiskMetrics fetches metrics for a specified disk partition in a project and prints results to the console -func getDiskMetrics(ctx context.Context, atlasClient admin.APIClient, params *admin.GetDiskMeasurementsApiParams) (*admin.ApiMeasurementsGeneralViewAtlas, error) { - - resp, _, err := atlasClient.MonitoringAndLogsApi.GetDiskMeasurementsWithParams(ctx, params).Execute() - if err != nil { - if apiError, ok := admin.AsError(err); ok { - return nil, fmt.Errorf("failed to get metrics for partition: %s (API error: %v)", err, apiError.GetDetail()) - } - return nil, fmt.Errorf("failed to get metrics: %w", err) - } - if resp == nil || resp.HasMeasurements() == false { - return nil, fmt.Errorf("no metrics found for partition %s in project %s", params.PartitionName, params.GroupId) - } - jsonData, err := json.MarshalIndent(resp, "", " ") - if err != nil { - return nil, fmt.Errorf("failed to marshal response: %w", err) - } - fmt.Println(string(jsonData)) - return resp, nil -} - -func main() { - ctx := context.Background() - - // Create an Atlas client authenticated using OAuth2 with service account credentials - atlasClient, _, config, err := auth.CreateAtlasClient() - if err != nil { - log.Fatalf("Failed to create Atlas client: %v", err) - } - - // Fetch disk metrics using the following parameters: - partitionName := "data" - diskMetricsGranularity := admin.PtrString("P1D") - diskMetricsPeriod := admin.PtrString("P1D") - diskMetrics := []string{ - "DISK_PARTITION_SPACE_FREE", "DISK_PARTITION_SPACE_USED", - } - - diskMeasurementsParams := &admin.GetDiskMeasurementsApiParams{ - GroupId: config.ProjectID, - ProcessId: config.ProcessID, - PartitionName: partitionName, - M: &diskMetrics, - Granularity: diskMetricsGranularity, - Period: diskMetricsPeriod, - } - _, err = getDiskMetrics(ctx, *atlasClient, diskMeasurementsParams) - if err != nil { - fmt.Printf("Error fetching disk metrics: %v", err) - } -} - diff --git a/generated-usage-examples/go/atlas-sdk-go/copied-project/cmd/get_metrics_process/main.go b/generated-usage-examples/go/atlas-sdk-go/copied-project/cmd/get_metrics_process/main.go deleted file mode 100644 index e957167..0000000 --- a/generated-usage-examples/go/atlas-sdk-go/copied-project/cmd/get_metrics_process/main.go +++ /dev/null @@ -1,66 +0,0 @@ -package main - -import ( - "atlas-sdk-go/internal/auth" - "context" - "encoding/json" - "fmt" - "go.mongodb.org/atlas-sdk/v20250219001/admin" - "log" -) - -// getProcessMetrics fetches metrics for a specified host process in a project and prints results to the console -func getProcessMetrics(ctx context.Context, atlasClient admin.APIClient, params *admin.GetHostMeasurementsApiParams) (*admin.ApiMeasurementsGeneralViewAtlas, error) { - fmt.Printf("Fetching metrics for host process %s in project %s", params.ProcessId, params.GroupId) - - resp, _, err := atlasClient.MonitoringAndLogsApi.GetHostMeasurementsWithParams(ctx, params).Execute() - if err != nil { - if apiError, ok := admin.AsError(err); ok { - return nil, fmt.Errorf("failed to get metrics for process in host: %s (API error: %v)", err, apiError.GetDetail()) - } - return nil, fmt.Errorf("failed to get metrics: %w", err) - } - - if resp == nil || resp.HasMeasurements() == false { - return nil, fmt.Errorf("no metrics found for host process %s in project %s", params.ProcessId, params.GroupId) - } - jsonData, err := json.MarshalIndent(resp, "", " ") - if err != nil { - return nil, fmt.Errorf("failed to marshal response: %w", err) - } - fmt.Println(string(jsonData)) - return resp, nil -} - -func main() { - ctx := context.Background() - - // Create an Atlas client authenticated using OAuth2 with service account credentials - atlasClient, _, config, err := auth.CreateAtlasClient() - if err != nil { - log.Fatalf("Failed to create Atlas client: %v", err) - } - - // Fetch process metrics using the following parameters: - processMetricGranularity := admin.PtrString("PT1H") - processMetricPeriod := admin.PtrString("P7D") - processMetrics := []string{ - "OPCOUNTER_INSERT", "OPCOUNTER_QUERY", "OPCOUNTER_UPDATE", "TICKETS_AVAILABLE_READS", - "TICKETS_AVAILABLE_WRITE", "CONNECTIONS", "QUERY_TARGETING_SCANNED_OBJECTS_PER_RETURNED", - "QUERY_TARGETING_SCANNED_PER_RETURNED", "SYSTEM_CPU_GUEST", "SYSTEM_CPU_IOWAIT", - "SYSTEM_CPU_IRQ", "SYSTEM_CPU_KERNEL", "SYSTEM_CPU_NICE", "SYSTEM_CPU_SOFTIRQ", - "SYSTEM_CPU_STEAL", "SYSTEM_CPU_USER", - } - hostMeasurementsParams := &admin.GetHostMeasurementsApiParams{ - GroupId: config.ProjectID, - ProcessId: config.ProcessID, - M: &processMetrics, - Granularity: processMetricGranularity, - Period: processMetricPeriod, - } - _, err = getProcessMetrics(ctx, *atlasClient, hostMeasurementsParams) - if err != nil { - fmt.Printf("Error fetching host process metrics: %v", err) - } -} - diff --git a/generated-usage-examples/go/atlas-sdk-go/copied-project/internal/auth/auth.go b/generated-usage-examples/go/atlas-sdk-go/copied-project/internal/auth/auth.go deleted file mode 100644 index 2580744..0000000 --- a/generated-usage-examples/go/atlas-sdk-go/copied-project/internal/auth/auth.go +++ /dev/null @@ -1,43 +0,0 @@ -package auth - -import ( - "atlas-sdk-go/internal" - "context" - "fmt" - "go.mongodb.org/atlas-sdk/v20250219001/admin" -) - -const filePath = "./configs/config.json" - -// CreateAtlasClient initializes and returns an authenticated Atlas API client -// using OAuth2 with service account credentials. -func CreateAtlasClient() (*admin.APIClient, *internal.Secrets, *internal.Config, error) { - - var secrets, err = internal.LoadSecrets() - if err != nil { - return nil, nil, nil, fmt.Errorf("failed to load secrets: %w", err) - } - if err := secrets.CheckRequiredEnv(); err != nil { - return nil, nil, nil, fmt.Errorf("invalid .env: %w", err) - } - - config, err := internal.LoadConfig(filePath) - if err != nil { - return nil, nil, nil, fmt.Errorf("failed to load config file: %w", err) - } - config.SetDefaults() - if err := config.CheckRequiredFields(); err != nil { - return nil, nil, nil, fmt.Errorf("invalid config: %w", err) - } - - ctx := context.Background() - atlasClient, err := admin.NewClient( - admin.UseBaseURL(config.BaseURL), - admin.UseOAuthAuth(ctx, secrets.ServiceAccountID, secrets.ServiceAccountSecret), - ) - if err != nil { - return nil, nil, nil, fmt.Errorf("error creating SDK client: %w", err) - } - - return atlasClient, secrets, config, nil -} diff --git a/generated-usage-examples/go/atlas-sdk-go/copied-project/internal/config_loader.go b/generated-usage-examples/go/atlas-sdk-go/copied-project/internal/config_loader.go deleted file mode 100644 index d75df81..0000000 --- a/generated-usage-examples/go/atlas-sdk-go/copied-project/internal/config_loader.go +++ /dev/null @@ -1,56 +0,0 @@ -package internal - -import ( - "encoding/json" - "fmt" - "os" - "strings" -) - -type Config struct { - BaseURL string `json:"MONGODB_ATLAS_BASE_URL"` - OrgID string `json:"ATLAS_ORG_ID"` - ProjectID string `json:"ATLAS_PROJECT_ID"` - ClusterName string `json:"ATLAS_CLUSTER_NAME"` - HostName string - ProcessID string `json:"ATLAS_PROCESS_ID"` -} - -// LoadConfig loads a JSON config file to make it globally available -func LoadConfig(filePath string) (*Config, error) { - file, err := os.Open(filePath) - if err != nil { - return nil, fmt.Errorf("error opening config file: %w", err) - } - defer func(file *os.File) { - err := file.Close() - if err != nil { - fmt.Println("Error closing file") - } - }(file) - - var config Config - decoder := json.NewDecoder(file) - if err := decoder.Decode(&config); err != nil { - return nil, fmt.Errorf("error decoding config file: %w", err) - } - return &config, nil -} - -// SetDefaults sets default values if specified config variables are empty -func (c *Config) SetDefaults() { - if c.BaseURL == "" { - c.BaseURL = "https://cloud.mongodb.com" - } - if c.HostName == "" { - c.HostName = strings.Split(c.ProcessID, ":")[0] - } -} - -// CheckRequiredFields verifies that required Atlas fields are set in the config file -func (c *Config) CheckRequiredFields() error { - if c.OrgID == "" || c.ProjectID == "" { - return fmt.Errorf("missing required Atlas fields in config file") - } - return nil -} diff --git a/generated-usage-examples/go/atlas-sdk-go/copied-project/internal/secrets_loader.go b/generated-usage-examples/go/atlas-sdk-go/copied-project/internal/secrets_loader.go deleted file mode 100644 index 19f9d49..0000000 --- a/generated-usage-examples/go/atlas-sdk-go/copied-project/internal/secrets_loader.go +++ /dev/null @@ -1,33 +0,0 @@ -package internal - -import ( - "fmt" - "github.com/joho/godotenv" - "log" - "os" -) - -type Secrets struct { - ServiceAccountID string `json:"MONGODB_ATLAS_SERVICE_ACCOUNT_ID"` - ServiceAccountSecret string `json:"MONGODB_ATLAS_SERVICE_ACCOUNT_SECRET"` -} - -// LoadSecrets loads .env file variables to use in the application -func LoadSecrets() (*Secrets, error) { - if err := godotenv.Load("./.env"); err != nil { - log.Println("No .env file found") - } - secrets := &Secrets{ - ServiceAccountID: os.Getenv("MONGODB_ATLAS_SERVICE_ACCOUNT_ID"), - ServiceAccountSecret: os.Getenv("MONGODB_ATLAS_SERVICE_ACCOUNT_SECRET"), - } - return secrets, nil -} - -// CheckRequiredEnv verifies that required environment variables are set -func (s *Secrets) CheckRequiredEnv() error { - if s.ServiceAccountID == "" || s.ServiceAccountSecret == "" { - return fmt.Errorf("service account client credentials must be set") - } - return nil -} diff --git a/generated-usage-examples/go/atlas-sdk-go/main.snippet.get-logs.go b/generated-usage-examples/go/atlas-sdk-go/main.snippet.get-logs.go new file mode 100644 index 0000000..6c9acd3 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/main.snippet.get-logs.go @@ -0,0 +1,62 @@ +// See entire project at https://github.com/mongodb/atlas-architecture-go-sdk +package main + +import ( + "context" + "fmt" + "log" + "os" + "path/filepath" + "time" + + "github.com/joho/godotenv" + "go.mongodb.org/atlas-sdk/v20250219001/admin" + + "atlas-sdk-go/internal" + "atlas-sdk-go/internal/auth" + "atlas-sdk-go/internal/config" + "atlas-sdk-go/internal/logs" +) + +func main() { + _ = godotenv.Load() + secrets, cfg, err := config.LoadAll("configs/config.json") + if err != nil { + log.Fatalf("config load: %v", err) + } + + sdk, err := auth.NewClient(cfg, secrets) + if err != nil { + log.Fatalf("client init: %v", err) + } + + ctx := context.Background() + p := &admin.GetHostLogsApiParams{ + GroupId: cfg.ProjectID, + HostName: cfg.HostName, + LogName: "mongodb", + } + ts := time.Now().Format("20060102_150405") + base := fmt.Sprintf("%s_%s_%s", p.HostName, p.LogName, ts) + outDir := "logs" + os.MkdirAll(outDir, 0o755) + gzPath := filepath.Join(outDir, base+".gz") + txtPath := filepath.Join(outDir, base+".txt") + + rc, err := logs.FetchHostLogs(ctx, sdk.MonitoringAndLogsApi, p) + if err != nil { + log.Fatalf("download logs: %v", err) + } + defer internal.SafeClose(rc) + + if err := logs.WriteToFile(rc, gzPath); err != nil { + log.Fatalf("save gz: %v", err) + } + fmt.Println("Saved compressed log to", gzPath) + + if err := logs.DecompressGzip(gzPath, txtPath); err != nil { + log.Fatalf("decompress: %v", err) + } + fmt.Println("Uncompressed log to", txtPath) +} + diff --git a/generated-usage-examples/go/atlas-sdk-go/main.snippet.get-metrics-dev.go b/generated-usage-examples/go/atlas-sdk-go/main.snippet.get-metrics-dev.go new file mode 100644 index 0000000..8f96696 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/main.snippet.get-metrics-dev.go @@ -0,0 +1,48 @@ +// See entire project at https://github.com/mongodb/atlas-architecture-go-sdk +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + + "github.com/joho/godotenv" + "go.mongodb.org/atlas-sdk/v20250219001/admin" + + "atlas-sdk-go/internal/auth" + "atlas-sdk-go/internal/config" + "atlas-sdk-go/internal/metrics" +) + +func main() { + _ = godotenv.Load() + secrets, cfg, err := config.LoadAll("configs/config.json") + if err != nil { + log.Fatalf("config load: %v", err) + } + + sdk, err := auth.NewClient(cfg, secrets) + if err != nil { + log.Fatalf("client init: %v", err) + } + + ctx := context.Background() + p := &admin.GetDiskMeasurementsApiParams{ + GroupId: cfg.ProjectID, + ProcessId: cfg.ProcessID, + PartitionName: "data", + M: &[]string{"DISK_PARTITION_SPACE_FREE", "DISK_PARTITION_SPACE_USED"}, + Granularity: admin.PtrString("P1D"), + Period: admin.PtrString("P1D"), + } + + view, err := metrics.FetchDiskMetrics(ctx, sdk.MonitoringAndLogsApi, p) + if err != nil { + log.Fatalf("disk metrics: %v", err) + } + + out, _ := json.MarshalIndent(view, "", " ") + fmt.Println(string(out)) +} + diff --git a/generated-usage-examples/go/atlas-sdk-go/main.snippet.get-metrics-prod.go b/generated-usage-examples/go/atlas-sdk-go/main.snippet.get-metrics-prod.go new file mode 100644 index 0000000..62c8dd4 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/main.snippet.get-metrics-prod.go @@ -0,0 +1,53 @@ +// See entire project at https://github.com/mongodb/atlas-architecture-go-sdk +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + + "github.com/joho/godotenv" + "go.mongodb.org/atlas-sdk/v20250219001/admin" + + "atlas-sdk-go/internal/auth" + "atlas-sdk-go/internal/config" + "atlas-sdk-go/internal/metrics" +) + +func main() { + _ = godotenv.Load() + secrets, cfg, err := config.LoadAll("configs/config.json") + if err != nil { + log.Fatalf("config load: %v", err) + } + + sdk, err := auth.NewClient(cfg, secrets) + if err != nil { + log.Fatalf("client init: %v", err) + } + + ctx := context.Background() + p := &admin.GetHostMeasurementsApiParams{ + GroupId: cfg.ProjectID, + ProcessId: cfg.ProcessID, + M: &[]string{ + "OPCOUNTER_INSERT", "OPCOUNTER_QUERY", "OPCOUNTER_UPDATE", "TICKETS_AVAILABLE_READS", + "TICKETS_AVAILABLE_WRITE", "CONNECTIONS", "QUERY_TARGETING_SCANNED_OBJECTS_PER_RETURNED", + "QUERY_TARGETING_SCANNED_PER_RETURNED", "SYSTEM_CPU_GUEST", "SYSTEM_CPU_IOWAIT", + "SYSTEM_CPU_IRQ", "SYSTEM_CPU_KERNEL", "SYSTEM_CPU_NICE", "SYSTEM_CPU_SOFTIRQ", + "SYSTEM_CPU_STEAL", "SYSTEM_CPU_USER", + }, + Granularity: admin.PtrString("PT1H"), + Period: admin.PtrString("P7D"), + } + + view, err := metrics.FetchProcessMetrics(ctx, sdk.MonitoringAndLogsApi, p) + if err != nil { + log.Fatalf("process metrics: %v", err) + } + + out, _ := json.MarshalIndent(view, "", " ") + fmt.Println(string(out)) +} + diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/.gitignore b/generated-usage-examples/go/atlas-sdk-go/project-copy/.gitignore new file mode 100644 index 0000000..22cadb7 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/.gitignore @@ -0,0 +1,6 @@ +# Secrets +.env + +# Logs +*.log +*.gz diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/README.md b/generated-usage-examples/go/atlas-sdk-go/project-copy/README.md new file mode 100644 index 0000000..ce2bbb0 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/README.md @@ -0,0 +1,24 @@ +# MongoDB Atlas Architecture Center Go SDK Code Examples + +This repository contains [Atlas Go SDK](https://www.mongodb.com/docs/atlas/sdk/) +code examples that follow recommendations in MongoDB's official +[Atlas Architecture Center documentation](https://www.mongodb.com/docs/atlas/architecture/current/). +You can run, download, and modify these code examples as starting points for +configuring your MongoDB Atlas architecture for your use case. + +## License + +This project is licensed under the [Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0). + +## Issues + +To report an issue with any of these code examples, please leave feedback +through the corresponding documentation page in the +[MongoDB Atlas Architecture Center](https://www.mongodb.com/docs/atlas/architecture/current/). +Using the `Rate This Page` button, you can add a comment about the issue after +leaving a star rating. + +## Contributing + +We are not currently accepting public contributions to this repository at this +time. \ No newline at end of file diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/cmd/get_logs/main.go b/generated-usage-examples/go/atlas-sdk-go/project-copy/cmd/get_logs/main.go new file mode 100644 index 0000000..99ec158 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/cmd/get_logs/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + "path/filepath" + "time" + + "github.com/joho/godotenv" + "go.mongodb.org/atlas-sdk/v20250219001/admin" + + "atlas-sdk-go/internal" + "atlas-sdk-go/internal/auth" + "atlas-sdk-go/internal/config" + "atlas-sdk-go/internal/logs" +) + +func main() { + _ = godotenv.Load() + secrets, cfg, err := config.LoadAll("configs/config.json") + if err != nil { + log.Fatalf("config load: %v", err) + } + + sdk, err := auth.NewClient(cfg, secrets) + if err != nil { + log.Fatalf("client init: %v", err) + } + + ctx := context.Background() + p := &admin.GetHostLogsApiParams{ + GroupId: cfg.ProjectID, + HostName: cfg.HostName, + LogName: "mongodb", + } + ts := time.Now().Format("20060102_150405") + base := fmt.Sprintf("%s_%s_%s", p.HostName, p.LogName, ts) + outDir := "logs" + os.MkdirAll(outDir, 0o755) + gzPath := filepath.Join(outDir, base+".gz") + txtPath := filepath.Join(outDir, base+".txt") + + rc, err := logs.FetchHostLogs(ctx, sdk.MonitoringAndLogsApi, p) + if err != nil { + log.Fatalf("download logs: %v", err) + } + defer internal.SafeClose(rc) + + if err := logs.WriteToFile(rc, gzPath); err != nil { + log.Fatalf("save gz: %v", err) + } + fmt.Println("Saved compressed log to", gzPath) + + if err := logs.DecompressGzip(gzPath, txtPath); err != nil { + log.Fatalf("decompress: %v", err) + } + fmt.Println("Uncompressed log to", txtPath) +} + diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/cmd/get_metrics_disk/main.go b/generated-usage-examples/go/atlas-sdk-go/project-copy/cmd/get_metrics_disk/main.go new file mode 100644 index 0000000..912cc75 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/cmd/get_metrics_disk/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + + "github.com/joho/godotenv" + "go.mongodb.org/atlas-sdk/v20250219001/admin" + + "atlas-sdk-go/internal/auth" + "atlas-sdk-go/internal/config" + "atlas-sdk-go/internal/metrics" +) + +func main() { + _ = godotenv.Load() + secrets, cfg, err := config.LoadAll("configs/config.json") + if err != nil { + log.Fatalf("config load: %v", err) + } + + sdk, err := auth.NewClient(cfg, secrets) + if err != nil { + log.Fatalf("client init: %v", err) + } + + ctx := context.Background() + p := &admin.GetDiskMeasurementsApiParams{ + GroupId: cfg.ProjectID, + ProcessId: cfg.ProcessID, + PartitionName: "data", + M: &[]string{"DISK_PARTITION_SPACE_FREE", "DISK_PARTITION_SPACE_USED"}, + Granularity: admin.PtrString("P1D"), + Period: admin.PtrString("P1D"), + } + + view, err := metrics.FetchDiskMetrics(ctx, sdk.MonitoringAndLogsApi, p) + if err != nil { + log.Fatalf("disk metrics: %v", err) + } + + out, _ := json.MarshalIndent(view, "", " ") + fmt.Println(string(out)) +} + diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/cmd/get_metrics_process/main.go b/generated-usage-examples/go/atlas-sdk-go/project-copy/cmd/get_metrics_process/main.go new file mode 100644 index 0000000..26fb4a3 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/cmd/get_metrics_process/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + + "github.com/joho/godotenv" + "go.mongodb.org/atlas-sdk/v20250219001/admin" + + "atlas-sdk-go/internal/auth" + "atlas-sdk-go/internal/config" + "atlas-sdk-go/internal/metrics" +) + +func main() { + _ = godotenv.Load() + secrets, cfg, err := config.LoadAll("configs/config.json") + if err != nil { + log.Fatalf("config load: %v", err) + } + + sdk, err := auth.NewClient(cfg, secrets) + if err != nil { + log.Fatalf("client init: %v", err) + } + + ctx := context.Background() + p := &admin.GetHostMeasurementsApiParams{ + GroupId: cfg.ProjectID, + ProcessId: cfg.ProcessID, + M: &[]string{ + "OPCOUNTER_INSERT", "OPCOUNTER_QUERY", "OPCOUNTER_UPDATE", "TICKETS_AVAILABLE_READS", + "TICKETS_AVAILABLE_WRITE", "CONNECTIONS", "QUERY_TARGETING_SCANNED_OBJECTS_PER_RETURNED", + "QUERY_TARGETING_SCANNED_PER_RETURNED", "SYSTEM_CPU_GUEST", "SYSTEM_CPU_IOWAIT", + "SYSTEM_CPU_IRQ", "SYSTEM_CPU_KERNEL", "SYSTEM_CPU_NICE", "SYSTEM_CPU_SOFTIRQ", + "SYSTEM_CPU_STEAL", "SYSTEM_CPU_USER", + }, + Granularity: admin.PtrString("PT1H"), + Period: admin.PtrString("P7D"), + } + + view, err := metrics.FetchProcessMetrics(ctx, sdk.MonitoringAndLogsApi, p) + if err != nil { + log.Fatalf("process metrics: %v", err) + } + + out, _ := json.MarshalIndent(view, "", " ") + fmt.Println(string(out)) +} + diff --git a/generated-usage-examples/go/atlas-sdk-go/copied-project/configs/config.json b/generated-usage-examples/go/atlas-sdk-go/project-copy/configs/config.json similarity index 100% rename from generated-usage-examples/go/atlas-sdk-go/copied-project/configs/config.json rename to generated-usage-examples/go/atlas-sdk-go/project-copy/configs/config.json diff --git a/generated-usage-examples/go/atlas-sdk-go/copied-project/go.mod b/generated-usage-examples/go/atlas-sdk-go/project-copy/go.mod similarity index 81% rename from generated-usage-examples/go/atlas-sdk-go/copied-project/go.mod rename to generated-usage-examples/go/atlas-sdk-go/project-copy/go.mod index 1a74333..29ef1b0 100644 --- a/generated-usage-examples/go/atlas-sdk-go/copied-project/go.mod +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/go.mod @@ -12,6 +12,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/mongodb-forks/digest v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/oauth2 v0.28.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + golang.org/x/oauth2 v0.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/generated-usage-examples/go/atlas-sdk-go/copied-project/go.sum b/generated-usage-examples/go/atlas-sdk-go/project-copy/go.sum similarity index 81% rename from generated-usage-examples/go/atlas-sdk-go/copied-project/go.sum rename to generated-usage-examples/go/atlas-sdk-go/project-copy/go.sum index f7ec5e3..20ec82d 100644 --- a/generated-usage-examples/go/atlas-sdk-go/copied-project/go.sum +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/go.sum @@ -1,19 +1,19 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/mongodb-forks/digest v1.1.0 h1:7eUdsR1BtqLv0mdNm4OXs6ddWvR4X2/OsLwdKksrOoc= github.com/mongodb-forks/digest v1.1.0/go.mod h1:rb+EX8zotClD5Dj4NdgxnJXG9nwrlx3NWKJ8xttz1Dg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.mongodb.org/atlas-sdk/v20250219001 v20250219001.1.0 h1:tm7d3xvbNFIpuvFcppXc1zdpM/dO7HwivpA+Y4np3uQ= go.mongodb.org/atlas-sdk/v20250219001 v20250219001.1.0/go.mod h1:huR1gWJhExa60NIRhsLDdc7RmmqKJJwnbdlA1UUh8V4= -golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= -golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/auth/client.go b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/auth/client.go new file mode 100644 index 0000000..7e8f192 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/auth/client.go @@ -0,0 +1,26 @@ +package auth + +import ( + "context" + "fmt" + + "atlas-sdk-go/internal/config" + + "go.mongodb.org/atlas-sdk/v20250219001/admin" +) + +// NewClient initializes and returns an authenticated Atlas API client +// using OAuth2 with service account credentials (recommended) +func NewClient(cfg *config.Config, secrets *config.Secrets) (*admin.APIClient, error) { + sdk, err := admin.NewClient( + admin.UseBaseURL(cfg.BaseURL), + admin.UseOAuthAuth(context.Background(), + secrets.ServiceAccountID, + secrets.ServiceAccountSecret, + ), + ) + if err != nil { + return nil, fmt.Errorf("create atlas client: %w", err) + } + return sdk, nil +} diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/config/loadall.go b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/config/loadall.go new file mode 100644 index 0000000..6cad472 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/config/loadall.go @@ -0,0 +1,20 @@ +package config + +import ( + "fmt" +) + +// LoadAll loads secrets and config from the specified paths +func LoadAll(configPath string) (*Secrets, *Config, error) { + s, err := LoadSecrets() + if err != nil { + return nil, nil, fmt.Errorf("loading secrets: %w", err) + } + + c, err := LoadConfig(configPath) + if err != nil { + return nil, nil, fmt.Errorf("loading config: %w", err) + } + + return s, c, nil +} diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/config/loadconfig.go b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/config/loadconfig.go new file mode 100644 index 0000000..e55cd57 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/config/loadconfig.go @@ -0,0 +1,48 @@ +package config + +import ( + "encoding/json" + "fmt" + "os" + "strings" + + "atlas-sdk-go/internal" +) + +type Config struct { + BaseURL string `json:"MONGODB_ATLAS_BASE_URL"` + OrgID string `json:"ATLAS_ORG_ID"` + ProjectID string `json:"ATLAS_PROJECT_ID"` + ClusterName string `json:"ATLAS_CLUSTER_NAME"` + HostName string + ProcessID string `json:"ATLAS_PROCESS_ID"` +} + +func LoadConfig(path string) (*Config, error) { + f, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("open config %s: %w", path, err) + } + defer internal.SafeClose(f) + + var c Config + if err := json.NewDecoder(f).Decode(&c); err != nil { + return nil, fmt.Errorf("decode %s: %w", path, err) + } + + if c.BaseURL == "" { + c.BaseURL = "https://cloud.mongodb.com" + } + if c.HostName == "" { + // Go 1.18+: + if host, _, ok := strings.Cut(c.ProcessID, ":"); ok { + c.HostName = host + } + } + + if c.OrgID == "" || c.ProjectID == "" { + return nil, fmt.Errorf("ATLAS_ORG_ID and ATLAS_PROJECT_ID are required") + } + + return &c, nil +} diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/config/loadenv.go b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/config/loadenv.go new file mode 100644 index 0000000..16dbeb2 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/config/loadenv.go @@ -0,0 +1,38 @@ +package config + +import ( + "fmt" + "os" + "strings" +) + +const ( + EnvSAClientID = "MONGODB_ATLAS_SERVICE_ACCOUNT_ID" + EnvSAClientSecret = "MONGODB_ATLAS_SERVICE_ACCOUNT_SECRET" +) + +type Secrets struct { + ServiceAccountID string + ServiceAccountSecret string +} + +func LoadSecrets() (*Secrets, error) { + s := &Secrets{} + var missing []string + + look := func(key string, dest *string) { + if v, ok := os.LookupEnv(key); ok && v != "" { + *dest = v + } else { + missing = append(missing, key) + } + } + + look(EnvSAClientID, &s.ServiceAccountID) + look(EnvSAClientSecret, &s.ServiceAccountSecret) + + if len(missing) > 0 { + return nil, fmt.Errorf("missing required env vars: %s", strings.Join(missing, ", ")) + } + return s, nil +} diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/logs/fetch.go b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/logs/fetch.go new file mode 100644 index 0000000..48ca8bc --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/logs/fetch.go @@ -0,0 +1,26 @@ +package logs + +import ( + "context" + "fmt" + "io" + + "go.mongodb.org/atlas-sdk/v20250219001/admin" +) + +// FetchHostLogs calls the Atlas SDK and returns the raw, compressed log stream. +func FetchHostLogs( + ctx context.Context, + sdk admin.MonitoringAndLogsApi, + p *admin.GetHostLogsApiParams, +) (io.ReadCloser, error) { + req := sdk.GetHostLogs(ctx, p.GroupId, p.HostName, p.LogName) + rc, _, err := req.Execute() + if err != nil { + if apiErr, ok := admin.AsError(err); ok { + return nil, fmt.Errorf("failed to fetch logs: %w – %s", err, apiErr.GetDetail()) + } + return nil, fmt.Errorf("failed to fetch logs: %w", err) + } + return rc, nil +} diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/logs/file.go b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/logs/file.go new file mode 100644 index 0000000..b68f17f --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/logs/file.go @@ -0,0 +1,24 @@ +package logs + +import ( + "fmt" + "io" + "os" + + "atlas-sdk-go/internal" +) + +// WriteToFile copies everything from r into a new file at path. +// It will create or truncate that file. +func WriteToFile(r io.Reader, path string) error { + f, err := os.Create(path) + if err != nil { + return fmt.Errorf("create %s: %w", path, err) + } + defer internal.SafeClose(f) + + if err := internal.SafeCopy(f, r); err != nil { + return fmt.Errorf("write %s: %w", path, err) + } + return nil +} diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/logs/gzip.go b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/logs/gzip.go new file mode 100644 index 0000000..13901f6 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/logs/gzip.go @@ -0,0 +1,35 @@ +package logs + +import ( + "compress/gzip" + "fmt" + "os" + + "atlas-sdk-go/internal" +) + +// DecompressGzip opens a .gz file and unpacks to specified destination. +func DecompressGzip(srcPath, destPath string) error { + srcFile, err := os.Open(srcPath) + if err != nil { + return fmt.Errorf("open %s: %w", srcPath, err) + } + defer internal.SafeClose(srcFile) + + gzReader, err := gzip.NewReader(srcFile) + if err != nil { + return fmt.Errorf("gzip reader %s: %w", srcPath, err) + } + defer internal.SafeClose(gzReader) + + destFile, err := os.Create(destPath) + if err != nil { + return fmt.Errorf("create %s: %w", destPath, err) + } + defer internal.SafeClose(destFile) + + if err := internal.SafeCopy(destFile, gzReader); err != nil { + return fmt.Errorf("decompress to %s: %w", destPath, err) + } + return nil +} diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/metrics/disk.go b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/metrics/disk.go new file mode 100644 index 0000000..e5b1636 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/metrics/disk.go @@ -0,0 +1,27 @@ +package metrics + +import ( + "context" + "fmt" + + "go.mongodb.org/atlas-sdk/v20250219001/admin" +) + +// FetchDiskMetrics returns measurements for a specified disk partition +func FetchDiskMetrics(ctx context.Context, sdk admin.MonitoringAndLogsApi, p *admin.GetDiskMeasurementsApiParams) (*admin.ApiMeasurementsGeneralViewAtlas, error) { + req := sdk.GetDiskMeasurements(ctx, p.GroupId, p.PartitionName, p.ProcessId) + req = req.Granularity(*p.Granularity).Period(*p.Period).M(*p.M) + + r, _, err := req.Execute() + if err != nil { + if apiErr, ok := admin.AsError(err); ok { + return nil, fmt.Errorf("fetch disk metrics: %w – %s", err, apiErr.GetDetail()) + } + return nil, fmt.Errorf("fetch disk metrics: %w", err) + } + if r == nil || !r.HasMeasurements() { + return nil, fmt.Errorf("no metrics for partition %q on process %q", + p.PartitionName, p.ProcessId) + } + return r, nil +} diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/metrics/process.go b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/metrics/process.go new file mode 100644 index 0000000..c250f72 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/metrics/process.go @@ -0,0 +1,26 @@ +package metrics + +import ( + "context" + "fmt" + + "go.mongodb.org/atlas-sdk/v20250219001/admin" +) + +// FetchProcessMetrics returns measurements for a specified host process +func FetchProcessMetrics(ctx context.Context, sdk admin.MonitoringAndLogsApi, p *admin.GetHostMeasurementsApiParams) (*admin.ApiMeasurementsGeneralViewAtlas, error) { + req := sdk.GetHostMeasurements(ctx, p.GroupId, p.ProcessId) + req = req.Granularity(*p.Granularity).Period(*p.Period).M(*p.M) + + r, _, err := req.Execute() + if err != nil { + if apiErr, ok := admin.AsError(err); ok { + return nil, fmt.Errorf("failed to fetch process metrics: %w – %s", err, apiErr.GetDetail()) + } + return nil, fmt.Errorf("failed to fetch process metrics: %w", err) + } + if r == nil || !r.HasMeasurements() { + return nil, fmt.Errorf("no metrics for process %q", p.ProcessId) + } + return r, nil +} diff --git a/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/utils.go b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/utils.go new file mode 100644 index 0000000..f105de6 --- /dev/null +++ b/generated-usage-examples/go/atlas-sdk-go/project-copy/internal/utils.go @@ -0,0 +1,25 @@ +package internal + +import ( + "io" + "log" +) + +// SafeClose closes c and logs a warning on error. +func SafeClose(c io.Closer) { + if c != nil { + if err := c.Close(); err != nil { + log.Printf("warning: close failed: %v", err) + } + } +} + +// SafeCopy copies src → dst and propagates any error (after logging). +func SafeCopy(dst io.Writer, src io.Reader) error { + if _, err := io.Copy(dst, src); err != nil { + log.Printf("warning: copy failed: %v", err) + return err + } + return nil +} + diff --git a/generated-examples/go/ann-basic-with-scenario.snippet.example.go b/generated-usage-examples/go/driver/ann-basic-with-scenario.snippet.example.go similarity index 100% rename from generated-examples/go/ann-basic-with-scenario.snippet.example.go rename to generated-usage-examples/go/driver/ann-basic-with-scenario.snippet.example.go diff --git a/generated-examples/go/ann-basic.snippet.example.go b/generated-usage-examples/go/driver/ann-basic.snippet.example.go similarity index 100% rename from generated-examples/go/ann-basic.snippet.example.go rename to generated-usage-examples/go/driver/ann-basic.snippet.example.go diff --git a/generated-examples/go/ann-filter-with-scenario.snippet.example.go b/generated-usage-examples/go/driver/ann-filter-with-scenario.snippet.example.go similarity index 100% rename from generated-examples/go/ann-filter-with-scenario.snippet.example.go rename to generated-usage-examples/go/driver/ann-filter-with-scenario.snippet.example.go diff --git a/generated-examples/go/ann-filter.snippet.example.go b/generated-usage-examples/go/driver/ann-filter.snippet.example.go similarity index 100% rename from generated-examples/go/ann-filter.snippet.example.go rename to generated-usage-examples/go/driver/ann-filter.snippet.example.go diff --git a/generated-examples/go/create-index-basic.snippet.example.go b/generated-usage-examples/go/driver/create-index-basic.snippet.example.go similarity index 100% rename from generated-examples/go/create-index-basic.snippet.example.go rename to generated-usage-examples/go/driver/create-index-basic.snippet.example.go diff --git a/generated-examples/go/create-index-filter.snippet.example.go b/generated-usage-examples/go/driver/create-index-filter.snippet.example.go similarity index 100% rename from generated-examples/go/create-index-filter.snippet.example.go rename to generated-usage-examples/go/driver/create-index-filter.snippet.example.go diff --git a/generated-examples/go/create-index-without-filter-using-scenarios.snippet.example.go b/generated-usage-examples/go/driver/create-index-without-filter-using-scenarios.snippet.example.go similarity index 100% rename from generated-examples/go/create-index-without-filter-using-scenarios.snippet.example.go rename to generated-usage-examples/go/driver/create-index-without-filter-using-scenarios.snippet.example.go diff --git a/generated-examples/go/drop-index.snippet.example.go b/generated-usage-examples/go/driver/drop-index.snippet.example.go similarity index 100% rename from generated-examples/go/drop-index.snippet.example.go rename to generated-usage-examples/go/driver/drop-index.snippet.example.go diff --git a/generated-examples/go/edit-index.snippet.example.go b/generated-usage-examples/go/driver/edit-index.snippet.example.go similarity index 100% rename from generated-examples/go/edit-index.snippet.example.go rename to generated-usage-examples/go/driver/edit-index.snippet.example.go diff --git a/generated-examples/go/enn.snippet.example.go b/generated-usage-examples/go/driver/enn.snippet.example.go similarity index 100% rename from generated-examples/go/enn.snippet.example.go rename to generated-usage-examples/go/driver/enn.snippet.example.go diff --git a/generated-examples/go/view-index.snippet.example.go b/generated-usage-examples/go/driver/view-index.snippet.example.go similarity index 100% rename from generated-examples/go/view-index.snippet.example.go rename to generated-usage-examples/go/driver/view-index.snippet.example.go diff --git a/generated-examples/java/AnnQueryBasic.snippet.example.java b/generated-usage-examples/java/sync/AnnQueryBasic.snippet.example.java similarity index 100% rename from generated-examples/java/AnnQueryBasic.snippet.example.java rename to generated-usage-examples/java/sync/AnnQueryBasic.snippet.example.java diff --git a/generated-examples/java/AnnQueryFilter.snippet.example.java b/generated-usage-examples/java/sync/AnnQueryFilter.snippet.example.java similarity index 100% rename from generated-examples/java/AnnQueryFilter.snippet.example.java rename to generated-usage-examples/java/sync/AnnQueryFilter.snippet.example.java diff --git a/generated-examples/java/CreateIndexBasic.snippet.example.java b/generated-usage-examples/java/sync/CreateIndexBasic.snippet.example.java similarity index 100% rename from generated-examples/java/CreateIndexBasic.snippet.example.java rename to generated-usage-examples/java/sync/CreateIndexBasic.snippet.example.java diff --git a/generated-examples/java/CreateIndexFilter.snippet.example.java b/generated-usage-examples/java/sync/CreateIndexFilter.snippet.example.java similarity index 100% rename from generated-examples/java/CreateIndexFilter.snippet.example.java rename to generated-usage-examples/java/sync/CreateIndexFilter.snippet.example.java diff --git a/generated-examples/java/DropIndex.snippet.example.java b/generated-usage-examples/java/sync/DropIndex.snippet.example.java similarity index 100% rename from generated-examples/java/DropIndex.snippet.example.java rename to generated-usage-examples/java/sync/DropIndex.snippet.example.java diff --git a/generated-examples/java/ViewIndex.snippet.example.java b/generated-usage-examples/java/sync/ViewIndex.snippet.example.java similarity index 100% rename from generated-examples/java/ViewIndex.snippet.example.java rename to generated-usage-examples/java/sync/ViewIndex.snippet.example.java diff --git a/generated-examples/javascript/ann-query-basic.snippet.example.js b/generated-usage-examples/javascript/ann-query-basic.snippet.example.js similarity index 100% rename from generated-examples/javascript/ann-query-basic.snippet.example.js rename to generated-usage-examples/javascript/ann-query-basic.snippet.example.js diff --git a/generated-examples/javascript/ann-query-filter.snippet.example.js b/generated-usage-examples/javascript/ann-query-filter.snippet.example.js similarity index 100% rename from generated-examples/javascript/ann-query-filter.snippet.example.js rename to generated-usage-examples/javascript/ann-query-filter.snippet.example.js diff --git a/generated-examples/javascript/create-index-basic.snippet.example.js b/generated-usage-examples/javascript/create-index-basic.snippet.example.js similarity index 100% rename from generated-examples/javascript/create-index-basic.snippet.example.js rename to generated-usage-examples/javascript/create-index-basic.snippet.example.js diff --git a/generated-examples/javascript/create-index-filter.snippet.example.js b/generated-usage-examples/javascript/create-index-filter.snippet.example.js similarity index 100% rename from generated-examples/javascript/create-index-filter.snippet.example.js rename to generated-usage-examples/javascript/create-index-filter.snippet.example.js diff --git a/generated-examples/javascript/drop-index.snippet.example.js b/generated-usage-examples/javascript/drop-index.snippet.example.js similarity index 100% rename from generated-examples/javascript/drop-index.snippet.example.js rename to generated-usage-examples/javascript/drop-index.snippet.example.js diff --git a/generated-examples/javascript/view-index.snippet.example.js b/generated-usage-examples/javascript/view-index.snippet.example.js similarity index 100% rename from generated-examples/javascript/view-index.snippet.example.js rename to generated-usage-examples/javascript/view-index.snippet.example.js diff --git a/generated-examples/python/ann_basic.snippet.example.py b/generated-usage-examples/python/ann_basic.snippet.example.py similarity index 100% rename from generated-examples/python/ann_basic.snippet.example.py rename to generated-usage-examples/python/ann_basic.snippet.example.py diff --git a/generated-examples/python/ann_filter.snippet.example.py b/generated-usage-examples/python/ann_filter.snippet.example.py similarity index 100% rename from generated-examples/python/ann_filter.snippet.example.py rename to generated-usage-examples/python/ann_filter.snippet.example.py diff --git a/generated-examples/python/create_basic.snippet.example.py b/generated-usage-examples/python/create_basic.snippet.example.py similarity index 100% rename from generated-examples/python/create_basic.snippet.example.py rename to generated-usage-examples/python/create_basic.snippet.example.py diff --git a/generated-examples/python/create_filter.snippet.example.py b/generated-usage-examples/python/create_filter.snippet.example.py similarity index 100% rename from generated-examples/python/create_filter.snippet.example.py rename to generated-usage-examples/python/create_filter.snippet.example.py diff --git a/generated-examples/python/drop.snippet.example.py b/generated-usage-examples/python/drop.snippet.example.py similarity index 100% rename from generated-examples/python/drop.snippet.example.py rename to generated-usage-examples/python/drop.snippet.example.py diff --git a/generated-examples/python/langchain.snippet.example.py b/generated-usage-examples/python/langchain.snippet.example.py similarity index 100% rename from generated-examples/python/langchain.snippet.example.py rename to generated-usage-examples/python/langchain.snippet.example.py diff --git a/generated-examples/python/view.snippet.example.py b/generated-usage-examples/python/view.snippet.example.py similarity index 100% rename from generated-examples/python/view.snippet.example.py rename to generated-usage-examples/python/view.snippet.example.py diff --git a/go/newgotestfile.go b/go/newgotestfile.go deleted file mode 100644 index 5879b07..0000000 --- a/go/newgotestfile.go +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "filename": "go/delete_me.go", - "repo": "", - "branch": "", - "deleted_on": "2025-03-07T21:53:21Z" - }, - { - "filename": "go/newgotestfile.go", - "repo": "docs-code-examples-test-target", - "branch": "v2.2", - "deleted_on": "2025-03-19T16:08:30-07:00" - } -] \ No newline at end of file diff --git a/go/ohhai_detete_me b/go/ohhai_detete_me deleted file mode 100644 index c0b264e..0000000 --- a/go/ohhai_detete_me +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "filename": "go/delete_me.go", - "repo": "", - "branch": "", - "deleted_on": "2025-03-07T21:53:21Z" - }, - { - "filename": "go/ohhai_detete_me", - "repo": "docs-code-examples-test-target", - "branch": "v2.2", - "deleted_on": "2025-03-19T16:08:31-07:00" - } -] \ No newline at end of file diff --git a/go/v22testfile2.go b/go/v22testfile2.go deleted file mode 100644 index 916f4ac..0000000 --- a/go/v22testfile2.go +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "filename": "go/delete_me.go", - "repo": "", - "branch": "", - "deleted_on": "2025-03-07T21:53:21Z" - }, - { - "filename": "go/v22testfile2.go", - "repo": "docs-code-examples-test-target", - "branch": "v2.2", - "deleted_on": "2025-03-19T16:08:33-07:00" - } -] \ No newline at end of file diff --git a/usage-examples/go/atlas-sdk-go/.gitignore b/usage-examples/go/atlas-sdk-go/.gitignore new file mode 100644 index 0000000..22cadb7 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/.gitignore @@ -0,0 +1,6 @@ +# Secrets +.env + +# Logs +*.log +*.gz diff --git a/usage-examples/go/atlas-sdk-go/INTERNAL_README.md b/usage-examples/go/atlas-sdk-go/INTERNAL_README.md new file mode 100644 index 0000000..be9cd5d --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/INTERNAL_README.md @@ -0,0 +1,90 @@ +# Atlas SDK for Go + +This project demonstrates how to script specific functionality using the Atlas +SDK for Go. Code examples are used in the Atlas Architecture Center docs, and +the project is made available in a user-facing repo. + +## Project Structure +```text +atlas-sdk-go/ +│── cmd/ # Self-contained, runnable scripts +│ ├── get_logs/ +│ ├── main.go +│ ├── get_metrics_disk/ +│ ├── main.go +│ ├── get_metrics_process/ +│ ├── main.go +│── config/ # Atlas configuration settings +│ ├── config.json +│── internal/ # Shared internal logic +│ ├── auth/ +| ├── client.go +│ ├── config/ +| ├── json.go +| ├── secrets.go +| ├── loader.go +│── .env # Secrets file (excluded from Git) +│── go.mod +│── go.sum +│── README.md # Internal-only README (do not copy with Copier Tool) +│── scripts/ # Internal-only Bluehawk scripts to snip and copy code examples +│ ├── bluehawk.sh +``` + +## Runnable Scripts +You can run individual scripts from the terminal. For example, to run `get_logs/main.go`: +```shell +go run cmd/get_logs/main.go +``` + +## Set up + +### Prerequisites + +- A [service account](https://www.mongodb.com/docs/atlas/api/service-accounts-overview/#std-label-service-accounts-overview) with access to your Atlas project + +> **NOTE:** Some scripts require an M10+ cluster + +### Set environment variables and config file + +1. Set the following variable values, either as a `.env` file in the root directory or through your IDE: + ```shell + MONGODB_ATLAS_SERVICE_ACCOUNT_ID=your-service-account-id + MONGODB_ATLAS_SERVICE_ACCOUNT_SECRET=your-service-account-secret + ``` +2. Update the placeholders in the `configs/config.json` file with your Atlas cluster information: + ```json + { + + "ATLAS_BASE_URL": "https://cloud.mongodb.com", + "ATLAS_ORG_ID": "", + "ATLAS_PROJECT_ID": "", + "ATLAS_CLUSTER_NAME": "Cluster0", + "ATLAS_PROCESS_ID": "cluster0-shard-00-00.ab1cd.mongodb.net:27017" + + } + ``` + > **NOTE: Group ID == Project ID** Groups and projects are synonymous terms. Groups and projects are synonymous terms. Your group id is the same as your project id. + +## Write Tests + +# TODO + +## Generate Examples + +This project uses Bluehawk to generate code examples from the source code. + +- Usage examples for the docs. These are generated using the `bluehawk snip` + command based on the `snippet` markup in the code file. +- Full project files for the user-facing project repo. These are generated using + the `bluehawk copy` command. + +Run the bluehawk script and enter either `snip` or `copy`. The selected command +runs with the defined defaults. + + ```shell + ./scripts/bluehawk.sh + ``` + +> **NOTE: "Copy" State** This project uses a state named "copy" specifically for any manipulations needed for code copied to the artifact repo. + diff --git a/usage-examples/go/atlas-sdk-go/README.md b/usage-examples/go/atlas-sdk-go/README.md new file mode 100644 index 0000000..ce2bbb0 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/README.md @@ -0,0 +1,24 @@ +# MongoDB Atlas Architecture Center Go SDK Code Examples + +This repository contains [Atlas Go SDK](https://www.mongodb.com/docs/atlas/sdk/) +code examples that follow recommendations in MongoDB's official +[Atlas Architecture Center documentation](https://www.mongodb.com/docs/atlas/architecture/current/). +You can run, download, and modify these code examples as starting points for +configuring your MongoDB Atlas architecture for your use case. + +## License + +This project is licensed under the [Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0). + +## Issues + +To report an issue with any of these code examples, please leave feedback +through the corresponding documentation page in the +[MongoDB Atlas Architecture Center](https://www.mongodb.com/docs/atlas/architecture/current/). +Using the `Rate This Page` button, you can add a comment about the issue after +leaving a star rating. + +## Contributing + +We are not currently accepting public contributions to this repository at this +time. \ No newline at end of file diff --git a/usage-examples/go/atlas-sdk-go/cmd/get_logs/main.go b/usage-examples/go/atlas-sdk-go/cmd/get_logs/main.go new file mode 100644 index 0000000..bf3f84b --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/cmd/get_logs/main.go @@ -0,0 +1,73 @@ +// :snippet-start: get-logs +// :state-remove-start: copy +// See entire project at https://github.com/mongodb/atlas-architecture-go-sdk +// :state-remove-end: [copy] +package main + +import ( + "context" + "fmt" + "log" + "os" + "path/filepath" + "time" + + "github.com/joho/godotenv" + "go.mongodb.org/atlas-sdk/v20250219001/admin" + + "atlas-sdk-go/internal" + "atlas-sdk-go/internal/auth" + "atlas-sdk-go/internal/config" + "atlas-sdk-go/internal/logs" +) + +func main() { + _ = godotenv.Load() + secrets, cfg, err := config.LoadAll("configs/config.json") + if err != nil { + log.Fatalf("config load: %v", err) + } + + sdk, err := auth.NewClient(cfg, secrets) + if err != nil { + log.Fatalf("client init: %v", err) + } + + ctx := context.Background() + p := &admin.GetHostLogsApiParams{ + GroupId: cfg.ProjectID, + HostName: cfg.HostName, + LogName: "mongodb", + } + ts := time.Now().Format("20060102_150405") + base := fmt.Sprintf("%s_%s_%s", p.HostName, p.LogName, ts) + outDir := "logs" + os.MkdirAll(outDir, 0o755) + gzPath := filepath.Join(outDir, base+".gz") + txtPath := filepath.Join(outDir, base+".txt") + + rc, err := logs.FetchHostLogs(ctx, sdk.MonitoringAndLogsApi, p) + if err != nil { + log.Fatalf("download logs: %v", err) + } + defer internal.SafeClose(rc) + + if err := logs.WriteToFile(rc, gzPath); err != nil { + log.Fatalf("save gz: %v", err) + } + fmt.Println("Saved compressed log to", gzPath) + + if err := logs.DecompressGzip(gzPath, txtPath); err != nil { + log.Fatalf("decompress: %v", err) + } + fmt.Println("Uncompressed log to", txtPath) + // :remove-start: + // NOTE Internal function to clean up any downloaded files + if err := internal.SafeDelete(outDir); err != nil { + log.Printf("Cleanup error: %v", err) + } + fmt.Println("Deleted generated files from", outDir) + // :remove-end: +} + +// :snippet-end: [get-logs] diff --git a/usage-examples/go/atlas-sdk-go/cmd/get_metrics_disk/main.go b/usage-examples/go/atlas-sdk-go/cmd/get_metrics_disk/main.go new file mode 100644 index 0000000..2520d47 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/cmd/get_metrics_disk/main.go @@ -0,0 +1,52 @@ +// :snippet-start: get-metrics-dev +// :state-remove-start: copy +// See entire project at https://github.com/mongodb/atlas-architecture-go-sdk +// :state-remove-end: [copy] +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + + "github.com/joho/godotenv" + "go.mongodb.org/atlas-sdk/v20250219001/admin" + + "atlas-sdk-go/internal/auth" + "atlas-sdk-go/internal/config" + "atlas-sdk-go/internal/metrics" +) + +func main() { + _ = godotenv.Load() + secrets, cfg, err := config.LoadAll("configs/config.json") + if err != nil { + log.Fatalf("config load: %v", err) + } + + sdk, err := auth.NewClient(cfg, secrets) + if err != nil { + log.Fatalf("client init: %v", err) + } + + ctx := context.Background() + p := &admin.GetDiskMeasurementsApiParams{ + GroupId: cfg.ProjectID, + ProcessId: cfg.ProcessID, + PartitionName: "data", + M: &[]string{"DISK_PARTITION_SPACE_FREE", "DISK_PARTITION_SPACE_USED"}, + Granularity: admin.PtrString("P1D"), + Period: admin.PtrString("P1D"), + } + + view, err := metrics.FetchDiskMetrics(ctx, sdk.MonitoringAndLogsApi, p) + if err != nil { + log.Fatalf("disk metrics: %v", err) + } + + out, _ := json.MarshalIndent(view, "", " ") + fmt.Println(string(out)) +} + +// :snippet-end: [get-metrics-dev] diff --git a/usage-examples/go/atlas-sdk-go/cmd/get_metrics_process/main.go b/usage-examples/go/atlas-sdk-go/cmd/get_metrics_process/main.go new file mode 100644 index 0000000..db707c4 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/cmd/get_metrics_process/main.go @@ -0,0 +1,57 @@ +// :snippet-start: get-metrics-prod +// :state-remove-start: copy +// See entire project at https://github.com/mongodb/atlas-architecture-go-sdk +// :state-remove-end: [copy] +package main + +import ( + "context" + "encoding/json" + "fmt" + "log" + + "github.com/joho/godotenv" + "go.mongodb.org/atlas-sdk/v20250219001/admin" + + "atlas-sdk-go/internal/auth" + "atlas-sdk-go/internal/config" + "atlas-sdk-go/internal/metrics" +) + +func main() { + _ = godotenv.Load() + secrets, cfg, err := config.LoadAll("configs/config.json") + if err != nil { + log.Fatalf("config load: %v", err) + } + + sdk, err := auth.NewClient(cfg, secrets) + if err != nil { + log.Fatalf("client init: %v", err) + } + + ctx := context.Background() + p := &admin.GetHostMeasurementsApiParams{ + GroupId: cfg.ProjectID, + ProcessId: cfg.ProcessID, + M: &[]string{ + "OPCOUNTER_INSERT", "OPCOUNTER_QUERY", "OPCOUNTER_UPDATE", "TICKETS_AVAILABLE_READS", + "TICKETS_AVAILABLE_WRITE", "CONNECTIONS", "QUERY_TARGETING_SCANNED_OBJECTS_PER_RETURNED", + "QUERY_TARGETING_SCANNED_PER_RETURNED", "SYSTEM_CPU_GUEST", "SYSTEM_CPU_IOWAIT", + "SYSTEM_CPU_IRQ", "SYSTEM_CPU_KERNEL", "SYSTEM_CPU_NICE", "SYSTEM_CPU_SOFTIRQ", + "SYSTEM_CPU_STEAL", "SYSTEM_CPU_USER", + }, + Granularity: admin.PtrString("PT1H"), + Period: admin.PtrString("P7D"), + } + + view, err := metrics.FetchProcessMetrics(ctx, sdk.MonitoringAndLogsApi, p) + if err != nil { + log.Fatalf("process metrics: %v", err) + } + + out, _ := json.MarshalIndent(view, "", " ") + fmt.Println(string(out)) +} + +// :snippet-end: [get-metrics-prod] diff --git a/usage-examples/go/atlas-sdk-go/configs/config.json b/usage-examples/go/atlas-sdk-go/configs/config.json new file mode 100644 index 0000000..9034951 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/configs/config.json @@ -0,0 +1,7 @@ +{ + "MONGODB_ATLAS_BASE_URL": "https://cloud.mongodb.com", + "ATLAS_ORG_ID": "", + "ATLAS_PROJECT_ID": "", + "ATLAS_CLUSTER_NAME": "", + "ATLAS_PROCESS_ID": "" +} diff --git a/usage-examples/go/atlas-sdk-go/go.mod b/usage-examples/go/atlas-sdk-go/go.mod new file mode 100644 index 0000000..29ef1b0 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/go.mod @@ -0,0 +1,18 @@ +module atlas-sdk-go + +go 1.24 + +require ( + github.com/joho/godotenv v1.5.1 + github.com/stretchr/testify v1.10.0 + go.mongodb.org/atlas-sdk/v20250219001 v20250219001.1.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/mongodb-forks/digest v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/usage-examples/go/atlas-sdk-go/go.sum b/usage-examples/go/atlas-sdk-go/go.sum new file mode 100644 index 0000000..20ec82d --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/go.sum @@ -0,0 +1,20 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/mongodb-forks/digest v1.1.0 h1:7eUdsR1BtqLv0mdNm4OXs6ddWvR4X2/OsLwdKksrOoc= +github.com/mongodb-forks/digest v1.1.0/go.mod h1:rb+EX8zotClD5Dj4NdgxnJXG9nwrlx3NWKJ8xttz1Dg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.mongodb.org/atlas-sdk/v20250219001 v20250219001.1.0 h1:tm7d3xvbNFIpuvFcppXc1zdpM/dO7HwivpA+Y4np3uQ= +go.mongodb.org/atlas-sdk/v20250219001 v20250219001.1.0/go.mod h1:huR1gWJhExa60NIRhsLDdc7RmmqKJJwnbdlA1UUh8V4= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/usage-examples/go/atlas-sdk-go/internal/auth/client.go b/usage-examples/go/atlas-sdk-go/internal/auth/client.go new file mode 100644 index 0000000..7e8f192 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/auth/client.go @@ -0,0 +1,26 @@ +package auth + +import ( + "context" + "fmt" + + "atlas-sdk-go/internal/config" + + "go.mongodb.org/atlas-sdk/v20250219001/admin" +) + +// NewClient initializes and returns an authenticated Atlas API client +// using OAuth2 with service account credentials (recommended) +func NewClient(cfg *config.Config, secrets *config.Secrets) (*admin.APIClient, error) { + sdk, err := admin.NewClient( + admin.UseBaseURL(cfg.BaseURL), + admin.UseOAuthAuth(context.Background(), + secrets.ServiceAccountID, + secrets.ServiceAccountSecret, + ), + ) + if err != nil { + return nil, fmt.Errorf("create atlas client: %w", err) + } + return sdk, nil +} diff --git a/usage-examples/go/atlas-sdk-go/internal/config/loadall.go b/usage-examples/go/atlas-sdk-go/internal/config/loadall.go new file mode 100644 index 0000000..6cad472 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/config/loadall.go @@ -0,0 +1,20 @@ +package config + +import ( + "fmt" +) + +// LoadAll loads secrets and config from the specified paths +func LoadAll(configPath string) (*Secrets, *Config, error) { + s, err := LoadSecrets() + if err != nil { + return nil, nil, fmt.Errorf("loading secrets: %w", err) + } + + c, err := LoadConfig(configPath) + if err != nil { + return nil, nil, fmt.Errorf("loading config: %w", err) + } + + return s, c, nil +} diff --git a/usage-examples/go/atlas-sdk-go/internal/config/loadconfig.go b/usage-examples/go/atlas-sdk-go/internal/config/loadconfig.go new file mode 100644 index 0000000..e55cd57 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/config/loadconfig.go @@ -0,0 +1,48 @@ +package config + +import ( + "encoding/json" + "fmt" + "os" + "strings" + + "atlas-sdk-go/internal" +) + +type Config struct { + BaseURL string `json:"MONGODB_ATLAS_BASE_URL"` + OrgID string `json:"ATLAS_ORG_ID"` + ProjectID string `json:"ATLAS_PROJECT_ID"` + ClusterName string `json:"ATLAS_CLUSTER_NAME"` + HostName string + ProcessID string `json:"ATLAS_PROCESS_ID"` +} + +func LoadConfig(path string) (*Config, error) { + f, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("open config %s: %w", path, err) + } + defer internal.SafeClose(f) + + var c Config + if err := json.NewDecoder(f).Decode(&c); err != nil { + return nil, fmt.Errorf("decode %s: %w", path, err) + } + + if c.BaseURL == "" { + c.BaseURL = "https://cloud.mongodb.com" + } + if c.HostName == "" { + // Go 1.18+: + if host, _, ok := strings.Cut(c.ProcessID, ":"); ok { + c.HostName = host + } + } + + if c.OrgID == "" || c.ProjectID == "" { + return nil, fmt.Errorf("ATLAS_ORG_ID and ATLAS_PROJECT_ID are required") + } + + return &c, nil +} diff --git a/usage-examples/go/atlas-sdk-go/internal/config/loadenv.go b/usage-examples/go/atlas-sdk-go/internal/config/loadenv.go new file mode 100644 index 0000000..16dbeb2 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/config/loadenv.go @@ -0,0 +1,38 @@ +package config + +import ( + "fmt" + "os" + "strings" +) + +const ( + EnvSAClientID = "MONGODB_ATLAS_SERVICE_ACCOUNT_ID" + EnvSAClientSecret = "MONGODB_ATLAS_SERVICE_ACCOUNT_SECRET" +) + +type Secrets struct { + ServiceAccountID string + ServiceAccountSecret string +} + +func LoadSecrets() (*Secrets, error) { + s := &Secrets{} + var missing []string + + look := func(key string, dest *string) { + if v, ok := os.LookupEnv(key); ok && v != "" { + *dest = v + } else { + missing = append(missing, key) + } + } + + look(EnvSAClientID, &s.ServiceAccountID) + look(EnvSAClientSecret, &s.ServiceAccountSecret) + + if len(missing) > 0 { + return nil, fmt.Errorf("missing required env vars: %s", strings.Join(missing, ", ")) + } + return s, nil +} diff --git a/usage-examples/go/atlas-sdk-go/internal/logs/fetch.go b/usage-examples/go/atlas-sdk-go/internal/logs/fetch.go new file mode 100644 index 0000000..48ca8bc --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/logs/fetch.go @@ -0,0 +1,26 @@ +package logs + +import ( + "context" + "fmt" + "io" + + "go.mongodb.org/atlas-sdk/v20250219001/admin" +) + +// FetchHostLogs calls the Atlas SDK and returns the raw, compressed log stream. +func FetchHostLogs( + ctx context.Context, + sdk admin.MonitoringAndLogsApi, + p *admin.GetHostLogsApiParams, +) (io.ReadCloser, error) { + req := sdk.GetHostLogs(ctx, p.GroupId, p.HostName, p.LogName) + rc, _, err := req.Execute() + if err != nil { + if apiErr, ok := admin.AsError(err); ok { + return nil, fmt.Errorf("failed to fetch logs: %w – %s", err, apiErr.GetDetail()) + } + return nil, fmt.Errorf("failed to fetch logs: %w", err) + } + return rc, nil +} diff --git a/usage-examples/go/atlas-sdk-go/internal/logs/fetch_test.go b/usage-examples/go/atlas-sdk-go/internal/logs/fetch_test.go new file mode 100644 index 0000000..9afef96 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/logs/fetch_test.go @@ -0,0 +1,84 @@ +package logs + +import ( + "context" + "fmt" + "io" + "strings" + "testing" + + "atlas-sdk-go/internal" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "go.mongodb.org/atlas-sdk/v20250219001/admin" + "go.mongodb.org/atlas-sdk/v20250219001/mockadmin" +) + +func TestFetchHostLogs_Unit(t *testing.T) { + t.Parallel() + ctx := context.Background() + + // common params + params := &admin.GetHostLogsApiParams{ + GroupId: "gID", + HostName: "hName", + LogName: "mongodb", + } + + cases := []struct { + name string + setup func(m *mockadmin.MonitoringAndLogsApi) + wantErr bool + wantBody string + }{ + { + name: "API error", + wantErr: true, + setup: func(m *mockadmin.MonitoringAndLogsApi) { + m.EXPECT(). + GetHostLogs(mock.Anything, params.GroupId, params.HostName, params.LogName). + Return(admin.GetHostLogsApiRequest{ApiService: m}).Once() + m.EXPECT(). + GetHostLogsExecute(mock.Anything). + Return(nil, nil, fmt.Errorf("API error")).Once() + }, + }, + { + name: "Successful response", + wantErr: false, + wantBody: "log-data", + setup: func(m *mockadmin.MonitoringAndLogsApi) { + m.EXPECT(). + GetHostLogs(mock.Anything, params.GroupId, params.HostName, params.LogName). + Return(admin.GetHostLogsApiRequest{ApiService: m}).Once() + m.EXPECT(). + GetHostLogsExecute(mock.Anything). + Return(io.NopCloser(strings.NewReader("log-data")), nil, nil).Once() + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + mockSvc := mockadmin.NewMonitoringAndLogsApi(t) + tc.setup(mockSvc) + + rc, err := FetchHostLogs(ctx, mockSvc, params) + if tc.wantErr { + require.ErrorContainsf(t, err, "failed to fetch logs", "expected API error") + require.Nil(t, rc) + return + } + + require.NoError(t, err) + defer internal.SafeClose(rc) + + data, err := io.ReadAll(rc) + require.NoError(t, err) + require.Equal(t, tc.wantBody, string(data)) + }) + } +} diff --git a/usage-examples/go/atlas-sdk-go/internal/logs/file.go b/usage-examples/go/atlas-sdk-go/internal/logs/file.go new file mode 100644 index 0000000..b68f17f --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/logs/file.go @@ -0,0 +1,24 @@ +package logs + +import ( + "fmt" + "io" + "os" + + "atlas-sdk-go/internal" +) + +// WriteToFile copies everything from r into a new file at path. +// It will create or truncate that file. +func WriteToFile(r io.Reader, path string) error { + f, err := os.Create(path) + if err != nil { + return fmt.Errorf("create %s: %w", path, err) + } + defer internal.SafeClose(f) + + if err := internal.SafeCopy(f, r); err != nil { + return fmt.Errorf("write %s: %w", path, err) + } + return nil +} diff --git a/usage-examples/go/atlas-sdk-go/internal/logs/file_test.go b/usage-examples/go/atlas-sdk-go/internal/logs/file_test.go new file mode 100644 index 0000000..8876d5a --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/logs/file_test.go @@ -0,0 +1,33 @@ +package logs + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestWriteToFile_Success(t *testing.T) { + // create a temp dir and file + tmp := t.TempDir() + path := filepath.Join(tmp, "out.txt") + + input := "hello world" + r := strings.NewReader(input) + + require.NoError(t, WriteToFile(r, path)) + + // verify file exists and content matches + data, err := os.ReadFile(path) + require.NoError(t, err) + require.Equal(t, input, string(data)) +} + +func TestWriteToFile_Error(t *testing.T) { + path := "does-not-exist/out.txt" + err := WriteToFile(strings.NewReader("x"), path) + require.Error(t, err) + require.Contains(t, err.Error(), "create") +} diff --git a/usage-examples/go/atlas-sdk-go/internal/logs/gzip.go b/usage-examples/go/atlas-sdk-go/internal/logs/gzip.go new file mode 100644 index 0000000..13901f6 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/logs/gzip.go @@ -0,0 +1,35 @@ +package logs + +import ( + "compress/gzip" + "fmt" + "os" + + "atlas-sdk-go/internal" +) + +// DecompressGzip opens a .gz file and unpacks to specified destination. +func DecompressGzip(srcPath, destPath string) error { + srcFile, err := os.Open(srcPath) + if err != nil { + return fmt.Errorf("open %s: %w", srcPath, err) + } + defer internal.SafeClose(srcFile) + + gzReader, err := gzip.NewReader(srcFile) + if err != nil { + return fmt.Errorf("gzip reader %s: %w", srcPath, err) + } + defer internal.SafeClose(gzReader) + + destFile, err := os.Create(destPath) + if err != nil { + return fmt.Errorf("create %s: %w", destPath, err) + } + defer internal.SafeClose(destFile) + + if err := internal.SafeCopy(destFile, gzReader); err != nil { + return fmt.Errorf("decompress to %s: %w", destPath, err) + } + return nil +} diff --git a/usage-examples/go/atlas-sdk-go/internal/logs/gzip_test.go b/usage-examples/go/atlas-sdk-go/internal/logs/gzip_test.go new file mode 100644 index 0000000..9cbee64 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/logs/gzip_test.go @@ -0,0 +1,50 @@ +package logs + +import ( + "compress/gzip" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestDecompressGzip_Success(t *testing.T) { + // create a temp dir and file + tmp := t.TempDir() + src := filepath.Join(tmp, "test.gz") + dst := filepath.Join(tmp, "test.txt") + + // create a gzip file and write some data to it + f, err := os.Create(src) + require.NoError(t, err) + + gz := gzip.NewWriter(f) + _, err = gz.Write([]byte("foobar")) + require.NoError(t, err) + require.NoError(t, gz.Close()) + require.NoError(t, f.Close()) + + // call the function to test + require.NoError(t, DecompressGzip(src, dst)) + + // verify the output + data, err := os.ReadFile(dst) + require.NoError(t, err) + require.Equal(t, "foobar", string(data)) +} + +func TestDecompressGzip_SourceNotFound(t *testing.T) { + tmp := t.TempDir() + src := filepath.Join(tmp, "nofile.gz") + dst := filepath.Join(tmp, "out.txt") + + // source file does not exist + err := DecompressGzip(src, dst) + require.Error(t, err) + require.Contains(t, err.Error(), "open") + + // destination file does not exist + _, err2 := os.Stat(dst) + require.True(t, os.IsNotExist(err2)) +} diff --git a/usage-examples/go/atlas-sdk-go/internal/metrics/disk.go b/usage-examples/go/atlas-sdk-go/internal/metrics/disk.go new file mode 100644 index 0000000..e5b1636 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/metrics/disk.go @@ -0,0 +1,27 @@ +package metrics + +import ( + "context" + "fmt" + + "go.mongodb.org/atlas-sdk/v20250219001/admin" +) + +// FetchDiskMetrics returns measurements for a specified disk partition +func FetchDiskMetrics(ctx context.Context, sdk admin.MonitoringAndLogsApi, p *admin.GetDiskMeasurementsApiParams) (*admin.ApiMeasurementsGeneralViewAtlas, error) { + req := sdk.GetDiskMeasurements(ctx, p.GroupId, p.PartitionName, p.ProcessId) + req = req.Granularity(*p.Granularity).Period(*p.Period).M(*p.M) + + r, _, err := req.Execute() + if err != nil { + if apiErr, ok := admin.AsError(err); ok { + return nil, fmt.Errorf("fetch disk metrics: %w – %s", err, apiErr.GetDetail()) + } + return nil, fmt.Errorf("fetch disk metrics: %w", err) + } + if r == nil || !r.HasMeasurements() { + return nil, fmt.Errorf("no metrics for partition %q on process %q", + p.PartitionName, p.ProcessId) + } + return r, nil +} diff --git a/usage-examples/go/atlas-sdk-go/internal/metrics/disk_test.go b/usage-examples/go/atlas-sdk-go/internal/metrics/disk_test.go new file mode 100644 index 0000000..f89fc5f --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/metrics/disk_test.go @@ -0,0 +1,78 @@ +package metrics + +import ( + "context" + "testing" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "go.mongodb.org/atlas-sdk/v20250219001/admin" + "go.mongodb.org/atlas-sdk/v20250219001/mockadmin" +) + +// ----------------------------------------------------------------------------- +// Integration tests against test HTTP server +// ----------------------------------------------------------------------------- +// TODO: Implement integration tests + +// ----------------------------------------------------------------------------- +// Unit‐level tests against SDK mocks +// ----------------------------------------------------------------------------- + +func TestFetchDiskMetrics(t *testing.T) { + t.Parallel() + ctx := context.Background() + + cases := []struct { + name string + view *admin.ApiMeasurementsGeneralViewAtlas + wantErr bool + wantCount int + }{ + {name: "Happy path returns data", + view: &admin.ApiMeasurementsGeneralViewAtlas{ + Measurements: &[]admin.MetricsMeasurementAtlas{{ + Name: admin.PtrString("DISK_METRIC"), + DataPoints: &[]admin.MetricDataPointAtlas{{ + Timestamp: admin.PtrTime(parseTS(t, fixedTS)), + Value: admin.PtrFloat32(4.56)}}}}}, + wantCount: 1}, + {name: "No available data returns error", + view: &admin.ApiMeasurementsGeneralViewAtlas{}, + wantErr: true}, + } + + baseDisk := admin.GetDiskMeasurementsApiParams{ + GroupId: "gID", + PartitionName: "part", + ProcessId: "pID", + Granularity: admin.PtrString("P1D"), + Period: admin.PtrString("P1D"), + M: &[]string{"DISK_METRIC"}, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + // setup mock SDK + mockSvc := mockadmin.NewMonitoringAndLogsApi(t) + mockSvc.EXPECT(). + GetDiskMeasurements(mock.Anything, baseDisk.GroupId, baseDisk.PartitionName, baseDisk.ProcessId). + Return(admin.GetDiskMeasurementsApiRequest{ApiService: mockSvc}).Once() + mockSvc.EXPECT(). + GetDiskMeasurementsExecute(mock.Anything). + Return(tc.view, nil, nil).Once() + + result, err := FetchDiskMetrics(ctx, mockSvc, &baseDisk) + + if tc.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.ElementsMatch(t, *result.Measurements, tc.view.GetMeasurements()) + require.Len(t, *result.Measurements, tc.wantCount) + }) + } +} diff --git a/usage-examples/go/atlas-sdk-go/internal/metrics/process.go b/usage-examples/go/atlas-sdk-go/internal/metrics/process.go new file mode 100644 index 0000000..c250f72 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/metrics/process.go @@ -0,0 +1,26 @@ +package metrics + +import ( + "context" + "fmt" + + "go.mongodb.org/atlas-sdk/v20250219001/admin" +) + +// FetchProcessMetrics returns measurements for a specified host process +func FetchProcessMetrics(ctx context.Context, sdk admin.MonitoringAndLogsApi, p *admin.GetHostMeasurementsApiParams) (*admin.ApiMeasurementsGeneralViewAtlas, error) { + req := sdk.GetHostMeasurements(ctx, p.GroupId, p.ProcessId) + req = req.Granularity(*p.Granularity).Period(*p.Period).M(*p.M) + + r, _, err := req.Execute() + if err != nil { + if apiErr, ok := admin.AsError(err); ok { + return nil, fmt.Errorf("failed to fetch process metrics: %w – %s", err, apiErr.GetDetail()) + } + return nil, fmt.Errorf("failed to fetch process metrics: %w", err) + } + if r == nil || !r.HasMeasurements() { + return nil, fmt.Errorf("no metrics for process %q", p.ProcessId) + } + return r, nil +} diff --git a/usage-examples/go/atlas-sdk-go/internal/metrics/process_test.go b/usage-examples/go/atlas-sdk-go/internal/metrics/process_test.go new file mode 100644 index 0000000..bca7b79 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/metrics/process_test.go @@ -0,0 +1,92 @@ +package metrics + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "go.mongodb.org/atlas-sdk/v20250219001/admin" + "go.mongodb.org/atlas-sdk/v20250219001/mockadmin" +) + +// fixed timestamp for tests +var fixedTS = "2025-01-01T12:00:00Z" + +// parseTS wraps time.Parse and flags error on test failure +func parseTS(t *testing.T, ts string) time.Time { + t.Helper() + parsed, err := time.Parse(time.RFC3339, ts) + require.NoError(t, err) + return parsed +} + +// ----------------------------------------------------------------------------- +// Integration tests against test HTTP server +// ----------------------------------------------------------------------------- +// TODO: Implement integration tests + +// ----------------------------------------------------------------------------- +// Unit‐level tests against SDK mocks +// ----------------------------------------------------------------------------- + +func TestFetchProcessMetrics_Unit(t *testing.T) { + t.Parallel() + ctx := context.Background() + + cases := []struct { + name string + view *admin.ApiMeasurementsGeneralViewAtlas + wantErr bool + wantCount int + }{ + { + name: "Happy path returns data", + view: &admin.ApiMeasurementsGeneralViewAtlas{ + Measurements: &[]admin.MetricsMeasurementAtlas{{ + Name: admin.PtrString("PROCESS_METRIC"), + DataPoints: &[]admin.MetricDataPointAtlas{{ + Timestamp: admin.PtrTime(parseTS(t, fixedTS)), + Value: admin.PtrFloat32(1.23)}}}}}, + wantCount: 1, + }, + { + name: "No available data returns error", + view: &admin.ApiMeasurementsGeneralViewAtlas{}, + wantErr: true, + }, + } + + var baseProcess = admin.GetHostMeasurementsApiParams{ + GroupId: "gID", + ProcessId: "pID", + Granularity: admin.PtrString("PT1H"), + Period: admin.PtrString("P1D"), + M: &[]string{"METRIC"}, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + mockSvc := mockadmin.NewMonitoringAndLogsApi(t) + mockSvc.EXPECT(). + GetHostMeasurements(mock.Anything, baseProcess.GroupId, baseProcess.ProcessId). + Return(admin.GetHostMeasurementsApiRequest{ApiService: mockSvc}).Once() + mockSvc.EXPECT(). + GetHostMeasurementsExecute(mock.Anything). + Return(tc.view, nil, nil).Once() + + result, err := FetchProcessMetrics(ctx, mockSvc, &baseProcess) + + if tc.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.ElementsMatch(t, *result.Measurements, tc.view.GetMeasurements()) + require.Len(t, *result.Measurements, tc.wantCount) + }) + } +} diff --git a/usage-examples/go/atlas-sdk-go/internal/utils.go b/usage-examples/go/atlas-sdk-go/internal/utils.go new file mode 100644 index 0000000..bca14f0 --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/internal/utils.go @@ -0,0 +1,49 @@ +package internal + +import ( + "io" + "log" + "os" // :remove: + "path/filepath" // :remove: +) + +// SafeClose closes c and logs a warning on error. +func SafeClose(c io.Closer) { + if c != nil { + if err := c.Close(); err != nil { + log.Printf("warning: close failed: %v", err) + } + } +} + +// SafeCopy copies src → dst and propagates any error (after logging). +func SafeCopy(dst io.Writer, src io.Reader) error { + if _, err := io.Copy(dst, src); err != nil { + log.Printf("warning: copy failed: %v", err) + return err + } + return nil +} + +// :remove-start: + +// SafeDelete removes files generated in the specified directory. +func SafeDelete(dir string) error { + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + if removeErr := os.Remove(path); removeErr != nil { + log.Printf("warning: failed to delete file %s: %v", path, removeErr) + } + } + return nil + }) + if err != nil { + return err + } + return nil +} + +// :remove-end: diff --git a/usage-examples/go/atlas-sdk-go/scripts/bluehawk.sh b/usage-examples/go/atlas-sdk-go/scripts/bluehawk.sh new file mode 100755 index 0000000..049a5ad --- /dev/null +++ b/usage-examples/go/atlas-sdk-go/scripts/bluehawk.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat < [flags] + +Commands: + snip Extract code examples from Bluehawk snippets + copy Copy sanitized project files + +Example (non‐interactive): + $(basename "$0") copy --ignore="tests/*.go" --rename='{"old.md":"new.md"}' +EOF + exit 1 +} + +# defaults +CMD="" +PROJECT=$(git rev-parse --show-toplevel) +INPUT_DIR="$PROJECT/usage-examples/go/atlas-sdk-go" +OUTPUT_DIR="$PROJECT/generated-usage-examples/go/atlas-sdk-go/" +STATE="" +IGNORE_PATTERNS=( + "internal_*.*" + "scripts/" + "*_test.go" + ".env" + "*.gz" + "*.log" + "./logs" +) + +# ─── Interactive mode ──────────────────────────────────────────────────────── +if [[ $# -eq 0 ]]; then + echo "=== Run Bluehawk ===" + + # 1) pick snip or copy + while true; do + read -rp "Enter command (snip/copy): " CMD + [[ "$CMD" == "snip" || "$CMD" == "copy" ]] && break + echo "enter 'snip' or 'copy'" + done + + STATE=$([[ "$CMD" == "snip" ]] && echo "" || echo "copy") + OUTPUT_DIR=$([[ "$CMD" == "snip" ]] && echo "$OUTPUT_DIR" || echo "$OUTPUT_DIR/project-copy") + + IGNORE_ARGS=() + for pattern in "${IGNORE_PATTERNS[@]}"; do + IGNORE_ARGS+=(--ignore="$pattern") + done + +# RENAME_ARGS=() +#if [[ "$CMD" != "snip" ]]; then +# for rule in "${RENAME_PATTERNS[@]}"; do +# RENAME_ARGS+=(--rename="$rule") +# done +#else +# RENAME_ARGS=() +#fi + + # call bluehawk with all the args + bluehawk "$CMD" \ + --state="$STATE" \ + -o "$OUTPUT_DIR" \ + "${IGNORE_ARGS[@]}" \ + "$INPUT_DIR" +else + usage +fi diff --git a/go/README.md b/usage-examples/go/driver/README.md similarity index 100% rename from go/README.md rename to usage-examples/go/driver/README.md diff --git a/go/bluehawk.sh b/usage-examples/go/driver/bluehawk.sh similarity index 100% rename from go/bluehawk.sh rename to usage-examples/go/driver/bluehawk.sh diff --git a/go/examples/manage-indexes/IndexDefinition.go b/usage-examples/go/driver/examples/manage-indexes/IndexDefinition.go similarity index 100% rename from go/examples/manage-indexes/IndexDefinition.go rename to usage-examples/go/driver/examples/manage-indexes/IndexDefinition.go diff --git a/go/examples/manage-indexes/IndexExpectation.go b/usage-examples/go/driver/examples/manage-indexes/IndexExpectation.go similarity index 100% rename from go/examples/manage-indexes/IndexExpectation.go rename to usage-examples/go/driver/examples/manage-indexes/IndexExpectation.go diff --git a/go/examples/manage-indexes/create-index-basic.go b/usage-examples/go/driver/examples/manage-indexes/create-index-basic.go similarity index 100% rename from go/examples/manage-indexes/create-index-basic.go rename to usage-examples/go/driver/examples/manage-indexes/create-index-basic.go diff --git a/go/examples/manage-indexes/create-index-filter.go b/usage-examples/go/driver/examples/manage-indexes/create-index-filter.go similarity index 100% rename from go/examples/manage-indexes/create-index-filter.go rename to usage-examples/go/driver/examples/manage-indexes/create-index-filter.go diff --git a/go/examples/manage-indexes/drop-index.go b/usage-examples/go/driver/examples/manage-indexes/drop-index.go similarity index 100% rename from go/examples/manage-indexes/drop-index.go rename to usage-examples/go/driver/examples/manage-indexes/drop-index.go diff --git a/go/examples/manage-indexes/edit-index.go b/usage-examples/go/driver/examples/manage-indexes/edit-index.go similarity index 100% rename from go/examples/manage-indexes/edit-index.go rename to usage-examples/go/driver/examples/manage-indexes/edit-index.go diff --git a/go/examples/manage-indexes/verify-index-definition.go b/usage-examples/go/driver/examples/manage-indexes/verify-index-definition.go similarity index 100% rename from go/examples/manage-indexes/verify-index-definition.go rename to usage-examples/go/driver/examples/manage-indexes/verify-index-definition.go diff --git a/go/examples/manage-indexes/view-index.go b/usage-examples/go/driver/examples/manage-indexes/view-index.go similarity index 100% rename from go/examples/manage-indexes/view-index.go rename to usage-examples/go/driver/examples/manage-indexes/view-index.go diff --git a/go/examples/run-queries/ann-basic.go b/usage-examples/go/driver/examples/run-queries/ann-basic.go similarity index 100% rename from go/examples/run-queries/ann-basic.go rename to usage-examples/go/driver/examples/run-queries/ann-basic.go diff --git a/go/examples/run-queries/ann-filter.go b/usage-examples/go/driver/examples/run-queries/ann-filter.go similarity index 100% rename from go/examples/run-queries/ann-filter.go rename to usage-examples/go/driver/examples/run-queries/ann-filter.go diff --git a/go/examples/run-queries/enn.go b/usage-examples/go/driver/examples/run-queries/enn.go similarity index 100% rename from go/examples/run-queries/enn.go rename to usage-examples/go/driver/examples/run-queries/enn.go diff --git a/go/go.mod b/usage-examples/go/driver/go.mod similarity index 100% rename from go/go.mod rename to usage-examples/go/driver/go.mod diff --git a/go/go.sum b/usage-examples/go/driver/go.sum similarity index 100% rename from go/go.sum rename to usage-examples/go/driver/go.sum diff --git a/go/tests/manage-indexes/manage-indexes_test.go b/usage-examples/go/driver/tests/manage-indexes/manage-indexes_test.go similarity index 100% rename from go/tests/manage-indexes/manage-indexes_test.go rename to usage-examples/go/driver/tests/manage-indexes/manage-indexes_test.go diff --git a/go/tests/run-queries/VerifyMovieQueryOutput.go b/usage-examples/go/driver/tests/run-queries/VerifyMovieQueryOutput.go similarity index 100% rename from go/tests/run-queries/VerifyMovieQueryOutput.go rename to usage-examples/go/driver/tests/run-queries/VerifyMovieQueryOutput.go diff --git a/go/tests/run-queries/VerifyMovieQueryOutputWithFilter.go b/usage-examples/go/driver/tests/run-queries/VerifyMovieQueryOutputWithFilter.go similarity index 100% rename from go/tests/run-queries/VerifyMovieQueryOutputWithFilter.go rename to usage-examples/go/driver/tests/run-queries/VerifyMovieQueryOutputWithFilter.go diff --git a/go/tests/run-queries/run-queries_test.go b/usage-examples/go/driver/tests/run-queries/run-queries_test.go similarity index 100% rename from go/tests/run-queries/run-queries_test.go rename to usage-examples/go/driver/tests/run-queries/run-queries_test.go diff --git a/java/README.md b/usage-examples/java/sync/README.md similarity index 100% rename from java/README.md rename to usage-examples/java/sync/README.md diff --git a/java/bluehawk.sh b/usage-examples/java/sync/bluehawk.sh similarity index 100% rename from java/bluehawk.sh rename to usage-examples/java/sync/bluehawk.sh diff --git a/java/pom.xml b/usage-examples/java/sync/pom.xml similarity index 100% rename from java/pom.xml rename to usage-examples/java/sync/pom.xml diff --git a/java/src/main/java/indexes/CreateIndexBasic.java b/usage-examples/java/sync/src/main/java/indexes/CreateIndexBasic.java similarity index 100% rename from java/src/main/java/indexes/CreateIndexBasic.java rename to usage-examples/java/sync/src/main/java/indexes/CreateIndexBasic.java diff --git a/java/src/main/java/indexes/CreateIndexFilter.java b/usage-examples/java/sync/src/main/java/indexes/CreateIndexFilter.java similarity index 100% rename from java/src/main/java/indexes/CreateIndexFilter.java rename to usage-examples/java/sync/src/main/java/indexes/CreateIndexFilter.java diff --git a/java/src/main/java/indexes/DropIndex.java b/usage-examples/java/sync/src/main/java/indexes/DropIndex.java similarity index 100% rename from java/src/main/java/indexes/DropIndex.java rename to usage-examples/java/sync/src/main/java/indexes/DropIndex.java diff --git a/java/src/main/java/indexes/ViewIndex.java b/usage-examples/java/sync/src/main/java/indexes/ViewIndex.java similarity index 100% rename from java/src/main/java/indexes/ViewIndex.java rename to usage-examples/java/sync/src/main/java/indexes/ViewIndex.java diff --git a/java/src/main/java/queries/AnnQueryBasic.java b/usage-examples/java/sync/src/main/java/queries/AnnQueryBasic.java similarity index 100% rename from java/src/main/java/queries/AnnQueryBasic.java rename to usage-examples/java/sync/src/main/java/queries/AnnQueryBasic.java diff --git a/java/src/main/java/queries/AnnQueryFilter.java b/usage-examples/java/sync/src/main/java/queries/AnnQueryFilter.java similarity index 100% rename from java/src/main/java/queries/AnnQueryFilter.java rename to usage-examples/java/sync/src/main/java/queries/AnnQueryFilter.java diff --git a/java/src/test/java/indexes/ManageIndexesTests.java b/usage-examples/java/sync/src/test/java/indexes/ManageIndexesTests.java similarity index 100% rename from java/src/test/java/indexes/ManageIndexesTests.java rename to usage-examples/java/sync/src/test/java/indexes/ManageIndexesTests.java diff --git a/java/src/test/java/indexes/models/Definition.java b/usage-examples/java/sync/src/test/java/indexes/models/Definition.java similarity index 100% rename from java/src/test/java/indexes/models/Definition.java rename to usage-examples/java/sync/src/test/java/indexes/models/Definition.java diff --git a/java/src/test/java/indexes/models/DefinitionVersion.java b/usage-examples/java/sync/src/test/java/indexes/models/DefinitionVersion.java similarity index 100% rename from java/src/test/java/indexes/models/DefinitionVersion.java rename to usage-examples/java/sync/src/test/java/indexes/models/DefinitionVersion.java diff --git a/java/src/test/java/indexes/models/Field.java b/usage-examples/java/sync/src/test/java/indexes/models/Field.java similarity index 100% rename from java/src/test/java/indexes/models/Field.java rename to usage-examples/java/sync/src/test/java/indexes/models/Field.java diff --git a/java/src/test/java/indexes/models/IndexDefinition.java b/usage-examples/java/sync/src/test/java/indexes/models/IndexDefinition.java similarity index 100% rename from java/src/test/java/indexes/models/IndexDefinition.java rename to usage-examples/java/sync/src/test/java/indexes/models/IndexDefinition.java diff --git a/java/src/test/java/indexes/models/MainIndex.java b/usage-examples/java/sync/src/test/java/indexes/models/MainIndex.java similarity index 100% rename from java/src/test/java/indexes/models/MainIndex.java rename to usage-examples/java/sync/src/test/java/indexes/models/MainIndex.java diff --git a/java/src/test/java/indexes/models/MongoDate.java b/usage-examples/java/sync/src/test/java/indexes/models/MongoDate.java similarity index 100% rename from java/src/test/java/indexes/models/MongoDate.java rename to usage-examples/java/sync/src/test/java/indexes/models/MongoDate.java diff --git a/java/src/test/java/indexes/models/StatusDetail.java b/usage-examples/java/sync/src/test/java/indexes/models/StatusDetail.java similarity index 100% rename from java/src/test/java/indexes/models/StatusDetail.java rename to usage-examples/java/sync/src/test/java/indexes/models/StatusDetail.java diff --git a/java/src/test/java/queries/QueryTests.java b/usage-examples/java/sync/src/test/java/queries/QueryTests.java similarity index 100% rename from java/src/test/java/queries/QueryTests.java rename to usage-examples/java/sync/src/test/java/queries/QueryTests.java diff --git a/javascript/README.md b/usage-examples/javascript/README.md similarity index 100% rename from javascript/README.md rename to usage-examples/javascript/README.md diff --git a/javascript/babel.config.cjs b/usage-examples/javascript/babel.config.cjs similarity index 100% rename from javascript/babel.config.cjs rename to usage-examples/javascript/babel.config.cjs diff --git a/javascript/bluehawk.sh b/usage-examples/javascript/bluehawk.sh similarity index 100% rename from javascript/bluehawk.sh rename to usage-examples/javascript/bluehawk.sh diff --git a/javascript/examples/indexes/create-index-basic.js b/usage-examples/javascript/examples/indexes/create-index-basic.js similarity index 100% rename from javascript/examples/indexes/create-index-basic.js rename to usage-examples/javascript/examples/indexes/create-index-basic.js diff --git a/javascript/examples/indexes/create-index-filter.js b/usage-examples/javascript/examples/indexes/create-index-filter.js similarity index 100% rename from javascript/examples/indexes/create-index-filter.js rename to usage-examples/javascript/examples/indexes/create-index-filter.js diff --git a/javascript/examples/indexes/drop-index.js b/usage-examples/javascript/examples/indexes/drop-index.js similarity index 100% rename from javascript/examples/indexes/drop-index.js rename to usage-examples/javascript/examples/indexes/drop-index.js diff --git a/javascript/examples/indexes/view-index.js b/usage-examples/javascript/examples/indexes/view-index.js similarity index 100% rename from javascript/examples/indexes/view-index.js rename to usage-examples/javascript/examples/indexes/view-index.js diff --git a/javascript/examples/queries/ann-query-basic.js b/usage-examples/javascript/examples/queries/ann-query-basic.js similarity index 100% rename from javascript/examples/queries/ann-query-basic.js rename to usage-examples/javascript/examples/queries/ann-query-basic.js diff --git a/javascript/examples/queries/ann-query-filter.js b/usage-examples/javascript/examples/queries/ann-query-filter.js similarity index 100% rename from javascript/examples/queries/ann-query-filter.js rename to usage-examples/javascript/examples/queries/ann-query-filter.js diff --git a/javascript/jest.config.cjs b/usage-examples/javascript/jest.config.cjs similarity index 100% rename from javascript/jest.config.cjs rename to usage-examples/javascript/jest.config.cjs diff --git a/javascript/package-lock.json b/usage-examples/javascript/package-lock.json similarity index 100% rename from javascript/package-lock.json rename to usage-examples/javascript/package-lock.json diff --git a/javascript/package.json b/usage-examples/javascript/package.json similarity index 100% rename from javascript/package.json rename to usage-examples/javascript/package.json diff --git a/javascript/tests/manage-indexes.test.js b/usage-examples/javascript/tests/manage-indexes.test.js similarity index 100% rename from javascript/tests/manage-indexes.test.js rename to usage-examples/javascript/tests/manage-indexes.test.js diff --git a/javascript/tests/run-queries.test.js b/usage-examples/javascript/tests/run-queries.test.js similarity index 100% rename from javascript/tests/run-queries.test.js rename to usage-examples/javascript/tests/run-queries.test.js diff --git a/python/README.md b/usage-examples/python/README.md similarity index 100% rename from python/README.md rename to usage-examples/python/README.md diff --git a/python/bluehawk.sh b/usage-examples/python/bluehawk.sh similarity index 100% rename from python/bluehawk.sh rename to usage-examples/python/bluehawk.sh diff --git a/python/examples/indexes/create_basic.py b/usage-examples/python/examples/indexes/create_basic.py similarity index 100% rename from python/examples/indexes/create_basic.py rename to usage-examples/python/examples/indexes/create_basic.py diff --git a/python/examples/indexes/create_filter.py b/usage-examples/python/examples/indexes/create_filter.py similarity index 100% rename from python/examples/indexes/create_filter.py rename to usage-examples/python/examples/indexes/create_filter.py diff --git a/python/examples/indexes/drop.py b/usage-examples/python/examples/indexes/drop.py similarity index 100% rename from python/examples/indexes/drop.py rename to usage-examples/python/examples/indexes/drop.py diff --git a/python/examples/indexes/view.py b/usage-examples/python/examples/indexes/view.py similarity index 100% rename from python/examples/indexes/view.py rename to usage-examples/python/examples/indexes/view.py diff --git a/python/examples/integration/langchain.py b/usage-examples/python/examples/integration/langchain.py similarity index 100% rename from python/examples/integration/langchain.py rename to usage-examples/python/examples/integration/langchain.py diff --git a/python/examples/queries/ann_basic.py b/usage-examples/python/examples/queries/ann_basic.py similarity index 100% rename from python/examples/queries/ann_basic.py rename to usage-examples/python/examples/queries/ann_basic.py diff --git a/python/examples/queries/ann_filter.py b/usage-examples/python/examples/queries/ann_filter.py similarity index 100% rename from python/examples/queries/ann_filter.py rename to usage-examples/python/examples/queries/ann_filter.py diff --git a/python/tests_package/test_manage_indexes.py b/usage-examples/python/tests_package/test_manage_indexes.py similarity index 100% rename from python/tests_package/test_manage_indexes.py rename to usage-examples/python/tests_package/test_manage_indexes.py diff --git a/python/tests_package/test_queries.py b/usage-examples/python/tests_package/test_queries.py similarity index 100% rename from python/tests_package/test_queries.py rename to usage-examples/python/tests_package/test_queries.py