Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions cmd/migrate/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package main

import (
"os"
"os/exec"
"strings"
"testing"
)

func TestCLIFunctionality(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode")
}

// Test that the CLI can be built
buildCmd := exec.Command("go", "build", "-o", "migrate-test")
buildCmd.Env = os.Environ()
output, err := buildCmd.CombinedOutput()
if err != nil {
t.Fatalf("Failed to build CLI: %v\nOutput: %s", err, output)
}
defer os.Remove("migrate-test")

// Test the version command
versionCmd := exec.Command("./migrate-test", "-version")
output, err = versionCmd.CombinedOutput()
if err != nil {
t.Fatalf("Failed to run version command: %v\nOutput: %s", err, output)
}

if !strings.Contains(string(output), "migrate version") {
t.Errorf("Expected version output, got: %s", output)
}

// Test the help command
helpCmd := exec.Command("./migrate-test", "-help")
output, err = helpCmd.CombinedOutput()
if err != nil {
t.Fatalf("Failed to run help command: %v\nOutput: %s", err, output)
}

if !strings.Contains(string(output), "Usage:") || !strings.Contains(string(output), "Commands:") {
t.Errorf("Expected help output, got: %s", output)
}
}
5 changes: 5 additions & 0 deletions database/victoria/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# VictoriaMetrics

This driver enables golang-migrate to work with [VictoriaMetrics](https://victoriametrics.com/), a high-performance time series database.

## Usage
2 changes: 2 additions & 0 deletions database/victoria/example/migration_example.down.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- This is just a placeholder as VictoriaMetrics doesn't have a way to revert imports
-- Instead, you would typically filter by time range or labels to exclude the data
3 changes: 3 additions & 0 deletions database/victoria/example/migration_example.up.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{"metric":{"__name__":"up","job":"migrate_test"},"values":[1],"timestamps":[1596698684000]}
{"metric":{"__name__":"cpu_usage","instance":"server1","job":"migrate_test"},"values":[0.45,0.52,0.48],"timestamps":[1596698684000,1596698694000,1596698704000]}
{"metric":{"__name__":"memory_usage","instance":"server1","job":"migrate_test"},"values":[0.25,0.28,0.22],"timestamps":[1596698684000,1596698694000,1596698704000]}
59 changes: 59 additions & 0 deletions database/victoria/export.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package victoria

import (
"context"
"fmt"
"io"
"net/http"
"net/url"
)

// Export reads data from VictoriaMetrics based on label filters and time range
func (v *Victoria) Export(ctx context.Context, w io.Writer) error {
if !v.isOpen {
return ErrClosed
}

// Build query parameters
query := url.Values{}
if v.config.LabelFilter != "" {
query.Set("match[]", v.config.LabelFilter)
}
if v.config.StartTime != "" {
query.Set("start", v.config.StartTime)
}
if v.config.EndTime != "" {
query.Set("end", v.config.EndTime)
}

// Create request with context
req, err := http.NewRequestWithContext(ctx, http.MethodGet, v.exportURL+"?"+query.Encode(), nil)
if err != nil {
return fmt.Errorf("failed to create export request: %w", err)
}

// Set headers
req.Header.Set("Accept", "application/json")
req.Header.Set("Accept-Encoding", "gzip")

// Execute request
resp, err := v.client.Do(req)
if err != nil {
return fmt.Errorf("failed to execute export request: %w", err)
}
defer resp.Body.Close()

// Check response status
if resp.StatusCode != http.StatusOK {
bodyBytes, _ := io.ReadAll(resp.Body)
return fmt.Errorf("export failed with status %d: %s", resp.StatusCode, string(bodyBytes))
}

// Copy response body to writer
_, err = io.Copy(w, resp.Body)
if err != nil {
return fmt.Errorf("failed to read export data: %w", err)
}

return nil
}
82 changes: 82 additions & 0 deletions database/victoria/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package victoria

import (
"bytes"
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

func TestVictoriaExport(t *testing.T) {
// Sample export data
exportData := `{"metric":{"__name__":"up","job":"test"},"values":[1],"timestamps":[1596698684000]}
{"metric":{"__name__":"cpu_usage","instance":"server1"},"values":[0.45],"timestamps":[1596698684000]}`

// Setup test server
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/api/v1/export" {
// Check query parameters
query := r.URL.Query()
assert.Equal(t, "{__name__=\"up\"}", query.Get("match[]"))
assert.Equal(t, "2020-01-01T00:00:00Z", query.Get("start"))
assert.Equal(t, "2020-01-02T00:00:00Z", query.Get("end"))

// Return export data
w.WriteHeader(http.StatusOK)
w.Write([]byte(exportData))
} else {
w.WriteHeader(http.StatusNotFound)
}
}))
defer testServer.Close()

// Parse the URL for our test server
serverURL := strings.TrimPrefix(testServer.URL, "http://")

// Create driver
d := &Victoria{}
dsn := "victoria://" + serverURL + "?label_filter={__name__=\"up\"}&start=2020-01-01T00:00:00Z&end=2020-01-02T00:00:00Z"
// No need to store the returned driver since we're testing the receiver methods directly
_, err := d.Open(dsn)
assert.NoError(t, err)

// Test export
var buf bytes.Buffer
err = d.Export(context.Background(), &buf)
assert.NoError(t, err)
assert.Equal(t, exportData, buf.String())

// Test export with closed connection
d.Close()
err = d.Export(context.Background(), &buf)
assert.Error(t, err)
assert.Equal(t, ErrClosed, err)
}

func TestVictoriaExportError(t *testing.T) {
// Setup test server that returns an error
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Internal server error"))
}))
defer testServer.Close()

// Parse the URL for our test server
serverURL := strings.TrimPrefix(testServer.URL, "http://")

// Create driver
d := &Victoria{}
dsn := "victoria://" + serverURL
_, err := d.Open(dsn)
assert.NoError(t, err)

// Test export
var buf bytes.Buffer
err = d.Export(context.Background(), &buf)
assert.Error(t, err)
assert.Contains(t, err.Error(), "status 500")
}
Loading
Loading