pkg/about/info.go (136 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 2.0; // you may not use this file except in compliance with the Elastic License 2.0. package about import ( "context" "strings" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" k8suuid "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/client-go/kubernetes" ) const ( // UUIDCfgMapName is the name for the config map containing the operator UUID. // The operator UUID is then used in the OperatorInfo. UUIDCfgMapName = "elastic-operator-uuid" // UUIDCfgMapName is used for the operator UUID inside a config map. UUIDCfgMapKey = "uuid" ) var ( // lookup of valid distribution channels knownDistributionChannels = map[string]struct{}{ "all-in-one": {}, "helm": {}, "upstream-community-operators": {}, "community-operators": {}, "certified-operators": {}, "ironbank": {}, "image": {}, } ) var defaultOperatorNamespaces = []string{"elastic-system"} // OperatorInfo contains information about the operator. type OperatorInfo struct { OperatorUUID types.UID `json:"operator_uuid"` CustomOperatorNamespace bool `json:"custom_operator_namespace"` Distribution string `json:"distribution"` DistributionChannel string `json:"distributionChannel"` BuildInfo BuildInfo `json:"build"` } // BuildInfo contains build metadata information. type BuildInfo struct { Version string `json:"version"` Hash string `json:"hash"` Date string `json:"date"` Snapshot string `json:"snapshot"` } // VersionString returns the version information formatted according to the SemVer specification. func (bi BuildInfo) VersionString() string { var sb strings.Builder sb.WriteString(bi.Version) if bi.Snapshot == "true" && !strings.HasSuffix(bi.Version, "-SNAPSHOT") { sb.WriteString("-SNAPSHOT") } sb.WriteString("+") sb.WriteString(bi.Hash) return sb.String() } // IsDefined returns true if the info's default values have been replaced. func (i OperatorInfo) IsDefined() bool { return i.OperatorUUID != "" && i.Distribution != "" && i.BuildInfo.Version != "0.0.0" && i.BuildInfo.Hash != "00000000" && i.BuildInfo.Date != "1970-01-01T00:00:00Z" } // GetOperatorInfo returns an OperatorInfo given an operator client, a Kubernetes client config, an operator namespace. func GetOperatorInfo(clientset kubernetes.Interface, operatorNs, distributionChannel string) (OperatorInfo, error) { operatorUUID, err := getOperatorUUID(context.Background(), clientset, operatorNs) if err != nil { return OperatorInfo{}, err } distribution, err := getDistribution(clientset) if err != nil { return OperatorInfo{}, err } customOperatorNs := true for _, ns := range defaultOperatorNamespaces { if operatorNs == ns { customOperatorNs = false } } // Check if reported channel is known to us. Passing bogus value to the // operator is treated the same as not passing any value at all if _, ok := knownDistributionChannels[distributionChannel]; !ok { distributionChannel = "" } return OperatorInfo{ OperatorUUID: operatorUUID, CustomOperatorNamespace: customOperatorNs, Distribution: distribution, DistributionChannel: distributionChannel, BuildInfo: GetBuildInfo(), }, nil } // getOperatorUUID returns the operator UUID by retrieving a config map or creating it if it does not exist. func getOperatorUUID(ctx context.Context, clientset kubernetes.Interface, operatorNs string) (types.UID, error) { c := clientset.CoreV1().ConfigMaps(operatorNs) // get the config map reconciledCfgMap, err := c.Get(ctx, UUIDCfgMapName, metav1.GetOptions{}) if err != nil && !apierrors.IsNotFound(err) { return types.UID(""), err } // or create it if err != nil && apierrors.IsNotFound(err) { newUUID := k8suuid.NewUUID() cfgMap := corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: operatorNs, Name: UUIDCfgMapName, }, Data: map[string]string{ UUIDCfgMapKey: string(newUUID), }, } _, err = c.Create(ctx, &cfgMap, metav1.CreateOptions{}) if err != nil { return types.UID(""), err } return newUUID, nil } UUID, ok := reconciledCfgMap.Data[UUIDCfgMapKey] // or update it if !ok { newUUID := k8suuid.NewUUID() if reconciledCfgMap.Data == nil { reconciledCfgMap.Data = map[string]string{} } reconciledCfgMap.Data[UUIDCfgMapKey] = string(newUUID) _, err := c.Update(ctx, reconciledCfgMap, metav1.UpdateOptions{}) if err != nil && !apierrors.IsNotFound(err) { return types.UID(""), err } return newUUID, nil } return types.UID(UUID), nil } // getDistribution returns the k8s distribution by fetching the GitVersion (legacy name) of the Info returned by ServerVersion(). func getDistribution(clientset kubernetes.Interface) (string, error) { version, err := clientset.Discovery().ServerVersion() if err != nil { return "", err } return version.GitVersion, nil } // GetBuildInfo returns information about the current build. func GetBuildInfo() BuildInfo { return BuildInfo{ version, buildHash, buildDate, buildSnapshot, } }