mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-11 22:14:02 +01:00
114 lines
3.1 KiB
Go
114 lines
3.1 KiB
Go
package clickhouse
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
|
|
"github.com/ClickHouse/clickhouse-go/v2"
|
|
|
|
"akvorado/common/reporter"
|
|
)
|
|
|
|
type migrationStep struct {
|
|
// CheckQuery to execute to check if the step is needed.
|
|
CheckQuery string
|
|
// Arguments to use for the query
|
|
Args []interface{}
|
|
// Function to execute if the query returns no row or returns `0'.
|
|
Do func() error
|
|
}
|
|
|
|
// migrateDatabase execute database migration
|
|
func (c *Component) migrateDatabase() error {
|
|
if c.config.OrchestratorURL == "" {
|
|
baseURL, err := c.getHTTPBaseURL("1.1.1.1:80")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.config.OrchestratorURL = baseURL
|
|
}
|
|
|
|
ctx := c.t.Context(nil)
|
|
steps := []struct {
|
|
Description string
|
|
Step func(context.Context, reporter.Logger, clickhouse.Conn) migrationStep
|
|
}{
|
|
{"create flows table", c.migrateStepCreateFlowsTable},
|
|
{"create exporters view", c.migrateStepCreateExportersView},
|
|
{"create protocols dictionary", c.migrateStepCreateProtocolsDictionary},
|
|
{"create asns dictionary", c.migrateStepCreateASNsDictionary},
|
|
{"create raw flows table", c.migrateStepCreateRawFlowsTable},
|
|
{"create raw flows consumer view", c.migrateStepCreateRawFlowsConsumerView},
|
|
{"drop schema_migrations table", c.migrateStepDropSchemaMigrationsTable},
|
|
}
|
|
|
|
count := 0
|
|
total := 0
|
|
for _, step := range steps {
|
|
total++
|
|
l := c.r.Logger.With().Str("step", step.Description).Logger()
|
|
l.Debug().Msg("checking migration step")
|
|
step := step.Step(ctx, l, c.d.ClickHouse)
|
|
rows, err := c.d.ClickHouse.Query(ctx, step.CheckQuery, step.Args...)
|
|
if err != nil {
|
|
l.Err(err).Msg("cannot execute check")
|
|
return fmt.Errorf("cannot execute check: %w", err)
|
|
}
|
|
if rows.Next() {
|
|
var val uint8
|
|
if err := rows.Scan(&val); err != nil {
|
|
rows.Close()
|
|
l.Err(err).Msg("cannot parse check result")
|
|
return fmt.Errorf("cannot parse check result: %w", err)
|
|
}
|
|
if val != 0 {
|
|
rows.Close()
|
|
l.Debug().Msg("result not equal to 0, skipping step")
|
|
continue
|
|
}
|
|
}
|
|
rows.Close()
|
|
if err := step.Do(); err != nil {
|
|
l.Err(err).Msg("cannot execute migration step")
|
|
return fmt.Errorf("during migration step: %w", err)
|
|
}
|
|
l.Info().Msg("migration step executed successfully")
|
|
count++
|
|
}
|
|
|
|
if count == 0 {
|
|
c.r.Debug().Msg("no migration needed")
|
|
} else {
|
|
c.r.Info().Msg("migrations done")
|
|
}
|
|
close(c.migrationsDone)
|
|
c.metrics.migrationsRunning.Set(0)
|
|
c.metrics.migrationsVersion.Set(float64(total))
|
|
|
|
return nil
|
|
}
|
|
|
|
// getHTTPBaseURL tries to guess the appropriate URL to access our
|
|
// HTTP daemon. It tries to get our IP address using an unconnected
|
|
// UDP socket.
|
|
func (c *Component) getHTTPBaseURL(address string) (string, error) {
|
|
// Get IP address
|
|
conn, err := net.Dial("udp", address)
|
|
if err != nil {
|
|
return "", fmt.Errorf("cannot get our IP address: %w", err)
|
|
}
|
|
defer conn.Close()
|
|
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
|
|
|
// Combine with HTTP port
|
|
_, port, err := net.SplitHostPort(c.d.HTTP.Address.String())
|
|
if err != nil {
|
|
return "", fmt.Errorf("cannot get HTTP port: %w", err)
|
|
}
|
|
base := fmt.Sprintf("http://%s",
|
|
net.JoinHostPort(localAddr.IP.String(), port))
|
|
c.r.Debug().Msgf("detected base URL is %s", base)
|
|
return base, nil
|
|
}
|