linkerd2/cli/cmd/upgrade_legacy.go

184 lines
5.7 KiB
Go

package cmd
import (
"context"
"fmt"
"strings"
"github.com/golang/protobuf/ptypes"
"github.com/linkerd/linkerd2/cli/flag"
pb "github.com/linkerd/linkerd2/controller/gen/config"
charts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
"github.com/linkerd/linkerd2/pkg/healthcheck"
"github.com/linkerd/linkerd2/pkg/issuercerts"
"github.com/linkerd/linkerd2/pkg/k8s"
"github.com/linkerd/linkerd2/pkg/version"
"github.com/spf13/pflag"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes"
)
func loadStoredValuesLegacy(ctx context.Context, k *k8s.KubernetesAPI) (*charts.Values, error) {
// We fetch the configs directly from kubernetes because we need to be able
// to upgrade/reinstall the control plane when the API is not available; and
// this also serves as a passive check that we have privileges to access this
// control plane.
_, configs, err := healthcheck.FetchLinkerdConfigMap(ctx, k, controlPlaneNamespace)
if err != nil {
return nil, fmt.Errorf("could not fetch configs from kubernetes: %s", err)
}
repairConfigs(configs)
values, err := charts.NewValues()
if err != nil {
return nil, err
}
allStageFlags, allStageFlagSet := makeAllStageFlags(values)
installFlags, installFlagSet := makeInstallFlags(values)
upgradeFlags, installUpgradeFlagSet, err := makeInstallUpgradeFlags(values)
if err != nil {
return nil, err
}
proxyFlags, proxyFlagSet := makeProxyFlags(values)
flagSet := pflag.NewFlagSet("loaded_flags", pflag.ExitOnError)
flagSet.AddFlagSet(allStageFlagSet)
flagSet.AddFlagSet(installFlagSet)
flagSet.AddFlagSet(installUpgradeFlagSet)
flagSet.AddFlagSet(proxyFlagSet)
setFlagsFromInstall(flagSet, configs.GetInstall().GetFlags())
flags := flattenFlags(allStageFlags, installFlags, upgradeFlags, proxyFlags)
err = flag.ApplySetFlags(values, flags)
if err != nil {
return nil, err
}
idctx := configs.GetGlobal().GetIdentityContext()
if idctx.GetTrustDomain() != "" && idctx.GetTrustAnchorsPem() != "" {
err = fetchIdentityValues(ctx, k, idctx, values)
if err != nil {
return nil, err
}
}
return values, nil
}
func setFlagsFromInstall(flags *pflag.FlagSet, installFlags []*pb.Install_Flag) {
for _, i := range installFlags {
if f := flags.Lookup(i.GetName()); f != nil && !f.Changed {
// The function recordFlags() stores the string representation of flags in the ConfigMap
// so a stringSlice is stored e.g. as [a,b].
// To avoid having f.Value.Set() interpreting that as a string we need to remove
// the brackets
value := i.GetValue()
if f.Value.Type() == "stringSlice" {
value = strings.Trim(value, "[]")
}
f.Value.Set(value)
f.Changed = true
}
}
}
func repairConfigs(configs *pb.All) {
// Repair the "install" section; install flags are updated separately
if configs.Install == nil {
configs.Install = &pb.Install{}
}
// ALWAYS update the CLI version to the most recent.
configs.Install.CliVersion = version.Version
// Repair the "proxy" section
if configs.Proxy == nil {
configs.Proxy = &pb.Proxy{}
}
if configs.Proxy.DebugImage == nil {
configs.Proxy.DebugImage = &pb.Image{}
}
if configs.GetProxy().GetDebugImage().GetImageName() == "" {
configs.Proxy.DebugImage.ImageName = k8s.DebugSidecarImage
}
if configs.GetProxy().GetDebugImageVersion() == "" {
configs.Proxy.DebugImageVersion = version.Version
}
}
// fetchIdentityValue checks the kubernetes API to fetch an existing
// linkerd identity configuration.
//
// This bypasses the public API so that we can access secrets and validate
// permissions.
func fetchIdentityValues(ctx context.Context, k kubernetes.Interface, idctx *pb.IdentityContext, values *charts.Values) error {
if idctx == nil {
return nil
}
if idctx.Scheme == "" {
// if this is empty, then we are upgrading from a version
// that did not support issuer schemes. Just default to the
// linkerd one.
idctx.Scheme = k8s.IdentityIssuerSchemeLinkerd
}
var trustAnchorsPEM string
var issuerData *issuercerts.IssuerCertData
var err error
trustAnchorsPEM = idctx.GetTrustAnchorsPem()
issuerData, err = fetchIssuer(ctx, k, trustAnchorsPEM, idctx.Scheme)
if err != nil {
return err
}
clockSkewDuration, err := ptypes.Duration(idctx.GetClockSkewAllowance())
if err != nil {
return fmt.Errorf("could not convert clock skew protobuf Duration format into golang Duration: %s", err)
}
issuanceLifetimeDuration, err := ptypes.Duration(idctx.GetIssuanceLifetime())
if err != nil {
return fmt.Errorf("could not convert issuance Lifetime protobuf Duration format into golang Duration: %s", err)
}
values.GetGlobal().IdentityTrustAnchorsPEM = trustAnchorsPEM
values.Identity.Issuer.Scheme = idctx.Scheme
values.Identity.Issuer.ClockSkewAllowance = clockSkewDuration.String()
values.Identity.Issuer.IssuanceLifetime = issuanceLifetimeDuration.String()
if issuerData.Expiry != nil {
values.Identity.Issuer.CrtExpiry = *issuerData.Expiry
}
values.Identity.Issuer.TLS.KeyPEM = issuerData.IssuerKey
values.Identity.Issuer.TLS.CrtPEM = issuerData.IssuerCrt
return nil
}
func fetchIssuer(ctx context.Context, k kubernetes.Interface, trustPEM string, scheme string) (*issuercerts.IssuerCertData, error) {
var (
issuerData *issuercerts.IssuerCertData
err error
)
switch scheme {
case string(corev1.SecretTypeTLS):
// Do not return external issuer certs as no need of storing them in config and upgrade secrets
// Also contradicts condition in https://github.com/linkerd/linkerd2/blob/main/cli/cmd/options.go#L550
return &issuercerts.IssuerCertData{}, nil
default:
issuerData, err = issuercerts.FetchIssuerData(ctx, k, trustPEM, controlPlaneNamespace)
if issuerData != nil && issuerData.TrustAnchors != trustPEM {
issuerData.TrustAnchors = trustPEM
}
}
if err != nil {
return nil, err
}
return issuerData, nil
}