internal/pkg/ver/check.go (73 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.
// Package ver will ensure fleet-server and Elasticsearch are running compatible versions.
// Versions are compatible when Elasticsearch's version is greater then or equal to fleet-server's version
package ver
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
esh "github.com/elastic/fleet-server/v7/internal/pkg/es"
"github.com/rs/zerolog"
"github.com/hashicorp/go-version"
"github.com/elastic/go-elasticsearch/v8"
)
// Variables to define errors when comparing versions.
var (
ErrUnsupportedVersion = errors.New("unsupported version")
ErrMalformedVersion = errors.New("malformed version")
)
// CheckCompatiblility will check the remote Elasticsearch version retrieved by the Elasticsearch client with the passed fleet version.
// Versions are compatible when Elasticsearch's version is greater then or equal to fleet-server's version
func CheckCompatibility(ctx context.Context, esCli *elasticsearch.Client, fleetVersion string) (string, error) {
zerolog.Ctx(ctx).Debug().Str("fleet_version", fleetVersion).Msg("check version compatibility with elasticsearch")
esVersion, err := esh.FetchESVersion(ctx, esCli)
if err != nil {
zerolog.Ctx(ctx).Error().Err(err).Msg("failed to fetch elasticsearch version")
return "", err
}
zerolog.Ctx(ctx).Debug().Str("elasticsearch_version", esVersion).Msg("fetched elasticsearch version")
return esVersion, checkCompatibility(ctx, fleetVersion, esVersion)
}
func checkCompatibility(ctx context.Context, fleetVersion, esVersion string) error {
verConst, err := buildVersionConstraint(fleetVersion)
if err != nil {
zerolog.Ctx(ctx).Error().Err(err).Str("fleet_version", fleetVersion).Msg("failed to build constraint")
return err
}
ver, err := parseVersion(esVersion)
if err != nil {
return err
}
if !verConst.Check(ver) {
zerolog.Ctx(ctx).Error().
Err(ErrUnsupportedVersion).
Str("constraint", verConst.String()).
Str("reported", ver.String()).
Msg("failed elasticsearch version check")
return ErrUnsupportedVersion
}
zerolog.Ctx(ctx).Info().Str("fleet_version", fleetVersion).Str("elasticsearch_version", esVersion).Msg("Elasticsearch compatibility check successful")
return nil
}
func buildVersionConstraint(fleetVersion string) (version.Constraints, error) {
ver, err := parseVersion(fleetVersion)
if err != nil {
return nil, err
}
return version.NewConstraint(fmt.Sprintf(">= %s", minimizePatch(ver)))
}
func minimizePatch(ver *version.Version) string {
segments := ver.Segments()
if len(segments) > 2 {
segments = segments[:2]
}
segments = append(segments, 0)
segStrs := make([]string, 0, len(segments))
for _, segment := range segments {
segStrs = append(segStrs, strconv.Itoa(segment))
}
return strings.Join(segStrs, ".")
}
func parseVersion(sver string) (*version.Version, error) {
ver, err := version.NewVersion(strings.Split(sver, "-")[0])
if err != nil {
return nil, fmt.Errorf("%w: %w", err, ErrMalformedVersion)
}
return ver, nil
}