linkerd2/cli/cmd/options.go

700 lines
28 KiB
Go

package cmd
import (
"context"
"errors"
"fmt"
"io/ioutil"
"net"
"strings"
"time"
"github.com/linkerd/linkerd2/cli/flag"
l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
"github.com/linkerd/linkerd2/pkg/issuercerts"
"github.com/linkerd/linkerd2/pkg/k8s"
consts "github.com/linkerd/linkerd2/pkg/k8s"
"github.com/linkerd/linkerd2/pkg/tls"
"github.com/linkerd/linkerd2/pkg/version"
log "github.com/sirupsen/logrus"
"github.com/spf13/pflag"
corev1 "k8s.io/api/core/v1"
k8sResource "k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/util/validation"
"sigs.k8s.io/yaml"
)
// makeInstallUpgradeFlags builds the set of flags which are used during the
// "control-plane" stage of install and upgrade. These flags control the
// majority of how the control plane is configured.
func makeInstallUpgradeFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet, error) {
installUpgradeFlags := pflag.NewFlagSet("install", pflag.ExitOnError)
issuanceLifetime, err := time.ParseDuration(defaults.Identity.Issuer.IssuanceLifetime)
if err != nil {
return nil, nil, err
}
clockSkewAllowance, err := time.ParseDuration(defaults.Identity.Issuer.ClockSkewAllowance)
if err != nil {
return nil, nil, err
}
flags := []flag.Flag{
flag.NewUintFlag(installUpgradeFlags, "controller-replicas", defaults.ControllerReplicas,
"Replicas of the controller to deploy", func(values *l5dcharts.Values, value uint) error {
values.ControllerReplicas = value
return nil
}),
flag.NewStringFlag(installUpgradeFlags, "controller-log-level", defaults.Global.ControllerLogLevel,
"Log level for the controller and web components", func(values *l5dcharts.Values, value string) error {
values.Global.ControllerLogLevel = value
return nil
}),
flag.NewBoolFlag(installUpgradeFlags, "ha", false, "Enable HA deployment config for the control plane (default false)",
func(values *l5dcharts.Values, value bool) error {
values.Global.HighAvailability = value
if value {
haValues, err := l5dcharts.NewValues(true)
if err != nil {
return err
}
// The HA flag must be processed before flags that set these values individually so that the
// individual flags can override the HA defaults. This means that the HA flag must appear
// before the individual flags in the slice passed to flags.ApplyIfSet.
values.ControllerReplicas = haValues.ControllerReplicas
values.Global.Proxy.Resources.CPU.Request = haValues.Global.Proxy.Resources.CPU.Request
values.Global.Proxy.Resources.Memory.Request = haValues.Global.Proxy.Resources.Memory.Request
values.Global.Proxy.Resources.CPU.Limit = haValues.Global.Proxy.Resources.CPU.Limit
values.Global.Proxy.Resources.Memory.Limit = haValues.Global.Proxy.Resources.Memory.Limit
}
return nil
}),
flag.NewInt64Flag(installUpgradeFlags, "controller-uid", defaults.ControllerUID,
"Run the control plane components under this user ID", func(values *l5dcharts.Values, value int64) error {
values.ControllerUID = value
return nil
}),
flag.NewBoolFlag(installUpgradeFlags, "disable-h2-upgrade", !defaults.EnableH2Upgrade,
"Prevents the controller from instructing proxies to perform transparent HTTP/2 upgrading (default false)",
func(values *l5dcharts.Values, value bool) error {
values.EnableH2Upgrade = !value
return nil
}),
flag.NewBoolFlag(installUpgradeFlags, "disable-heartbeat", defaults.DisableHeartBeat,
"Disables the heartbeat cronjob (default false)", func(values *l5dcharts.Values, value bool) error {
values.DisableHeartBeat = value
return nil
}),
flag.NewDurationFlag(installUpgradeFlags, "identity-issuance-lifetime", issuanceLifetime,
"The amount of time for which the Identity issuer should certify identity",
func(values *l5dcharts.Values, value time.Duration) error {
values.Identity.Issuer.IssuanceLifetime = value.String()
return nil
}),
flag.NewDurationFlag(installUpgradeFlags, "identity-clock-skew-allowance", clockSkewAllowance,
"The amount of time to allow for clock skew within a Linkerd cluster",
func(values *l5dcharts.Values, value time.Duration) error {
values.Identity.Issuer.ClockSkewAllowance = value.String()
return nil
}),
flag.NewBoolFlag(installUpgradeFlags, "omit-webhook-side-effects", defaults.OmitWebhookSideEffects,
"Omit the sideEffects flag in the webhook manifests, This flag must be provided during install or upgrade for Kubernetes versions pre 1.12",
func(values *l5dcharts.Values, value bool) error {
values.OmitWebhookSideEffects = value
return nil
}),
flag.NewBoolFlag(installUpgradeFlags, "control-plane-tracing", defaults.Global.ControlPlaneTracing,
"Enables Control Plane Tracing with the defaults", func(values *l5dcharts.Values, value bool) error {
defaults.Global.ControlPlaneTracing = value
return nil
}),
flag.NewStringFlag(installUpgradeFlags, "identity-issuer-certificate-file", "",
"A path to a PEM-encoded file containing the Linkerd Identity issuer certificate (generated by default)",
func(values *l5dcharts.Values, value string) error {
if value != "" {
crt, err := loadCrtPEM(value)
if err != nil {
return err
}
values.Identity.Issuer.TLS.CrtPEM = crt
}
return nil
}),
flag.NewStringFlag(installUpgradeFlags, "identity-issuer-key-file", "",
"A path to a PEM-encoded file containing the Linkerd Identity issuer private key (generated by default)",
func(values *l5dcharts.Values, value string) error {
if value != "" {
key, err := loadKeyPEM(value)
if err != nil {
return err
}
values.Identity.Issuer.TLS.KeyPEM = key
}
return nil
}),
flag.NewStringFlag(installUpgradeFlags, "identity-trust-anchors-file", "",
"A path to a PEM-encoded file containing Linkerd Identity trust anchors (generated by default)",
func(values *l5dcharts.Values, value string) error {
if value != "" {
data, err := ioutil.ReadFile(value)
if err != nil {
return err
}
values.Global.IdentityTrustAnchorsPEM = string(data)
}
return nil
}),
flag.NewBoolFlag(installUpgradeFlags, "enable-endpoint-slices", defaults.Global.EnableEndpointSlices,
"Enables the usage of EndpointSlice informers and resources for destination service",
func(values *l5dcharts.Values, value bool) error {
values.Global.EnableEndpointSlices = value
return nil
}),
flag.NewStringFlag(installUpgradeFlags, "control-plane-version", defaults.Global.ControllerImageVersion,
"Tag to be used for the control plane component images",
func(values *l5dcharts.Values, value string) error {
values.Global.ControllerImageVersion = value
return nil
}),
}
// Hide developer focused flags in release builds.
release, err := version.IsReleaseChannel(version.Version)
if err != nil {
log.Errorf("Unable to parse version: %s", version.Version)
}
if release {
installUpgradeFlags.MarkHidden("control-plane-version")
}
installUpgradeFlags.MarkHidden("control-plane-tracing")
return flags, installUpgradeFlags, nil
}
func loadCrtPEM(path string) (string, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return "", err
}
crt, err := tls.DecodePEMCrt(string(data))
if err != nil {
return "", err
}
return crt.EncodeCertificatePEM(), nil
}
func loadKeyPEM(path string) (string, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return "", err
}
key, err := tls.DecodePEMKey(string(data))
if err != nil {
return "", err
}
cred := tls.Cred{PrivateKey: key}
return cred.EncodePrivateKeyPEM(), nil
}
// makeAllStageFlags builds the set of flags which are used during all stages
// of install and upgrade. These flags influence cluster level configuration
// and therefore are available during the "config" stage.
func makeAllStageFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet) {
allStageFlags := pflag.NewFlagSet("all-stage", pflag.ExitOnError)
flags := []flag.Flag{
flag.NewBoolFlag(allStageFlags, "linkerd-cni-enabled", defaults.Global.CNIEnabled,
"Omit the NET_ADMIN capability in the PSP and the proxy-init container when injecting the proxy; requires the linkerd-cni plugin to already be installed",
func(values *l5dcharts.Values, value bool) error {
values.Global.CNIEnabled = value
return nil
}),
flag.NewBoolFlag(allStageFlags, "restrict-dashboard-privileges", defaults.RestrictDashboardPrivileges,
"Restrict the Linkerd Dashboard's default privileges to disallow Tap and Check",
func(values *l5dcharts.Values, value bool) error {
values.RestrictDashboardPrivileges = value
return nil
}),
flag.NewStringFlag(allStageFlags, "config", "",
"A path to a yaml configuration file. The fields in this file will override the values used to install or upgrade Linkerd.",
func(values *l5dcharts.Values, value string) error {
if value != "" {
data, err := ioutil.ReadFile(value)
if err != nil {
return err
}
err = yaml.Unmarshal(data, &values)
if err != nil {
return err
}
}
return nil
}),
}
return flags, allStageFlags
}
// makeInstallFlags builds the set of flags which are used during the
// "control-plane" stage of install. These flags should not be changed during
// an upgrade and are not available to the upgrade command.
func makeInstallFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet) {
installOnlyFlags := pflag.NewFlagSet("install-only", pflag.ExitOnError)
flags := []flag.Flag{
flag.NewStringFlag(installOnlyFlags, "cluster-domain", defaults.Global.ClusterDomain,
"Set custom cluster domain", func(values *l5dcharts.Values, value string) error {
values.Global.ClusterDomain = value
return nil
}),
flag.NewStringFlag(installOnlyFlags, "identity-trust-domain", defaults.Global.IdentityTrustDomain,
"Configures the name suffix used for identities.", func(values *l5dcharts.Values, value string) error {
values.Global.IdentityTrustDomain = value
return nil
}),
flag.NewBoolFlag(installOnlyFlags, "identity-external-issuer", false,
"Whether to use an external identity issuer (default false)", func(values *l5dcharts.Values, value bool) error {
if value {
values.Identity.Issuer.Scheme = string(corev1.SecretTypeTLS)
} else {
values.Identity.Issuer.Scheme = consts.IdentityIssuerSchemeLinkerd
}
return nil
}),
}
return flags, installOnlyFlags
}
// makeProxyFlags builds the set of flags which affect how the proxy is
// configured. These flags are available to the inject command and to the
// install and upgrade commands in the "control-plane" stage.
func makeProxyFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet) {
proxyFlags := pflag.NewFlagSet("proxy", pflag.ExitOnError)
flags := []flag.Flag{
flag.NewStringFlagP(proxyFlags, "proxy-version", "v", defaults.Global.Proxy.Image.Version, "Tag to be used for the Linkerd proxy images",
func(values *l5dcharts.Values, value string) error {
values.Global.Proxy.Image.Version = value
return nil
}),
flag.NewStringFlag(proxyFlags, "proxy-image", defaults.Global.Proxy.Image.Name, "Linkerd proxy container image name",
func(values *l5dcharts.Values, value string) error {
values.Global.Proxy.Image.Name = value
return nil
}),
flag.NewStringFlag(proxyFlags, "init-image", defaults.Global.ProxyInit.Image.Name, "Linkerd init container image name",
func(values *l5dcharts.Values, value string) error {
values.Global.ProxyInit.Image.Name = value
return nil
}),
flag.NewStringFlag(proxyFlags, "init-image-version", defaults.Global.ProxyInit.Image.Version,
"Linkerd init container image version", func(values *l5dcharts.Values, value string) error {
values.Global.ProxyInit.Image.Version = value
return nil
}),
flag.NewStringFlag(proxyFlags, "registry", defaultDockerRegistry, "Docker registry to pull images from",
func(values *l5dcharts.Values, value string) error {
values.WebImage = registryOverride(values.WebImage, value)
values.ControllerImage = registryOverride(values.ControllerImage, value)
values.DebugContainer.Image.Name = registryOverride(values.DebugContainer.Image.Name, value)
values.Global.Proxy.Image.Name = registryOverride(values.Global.Proxy.Image.Name, value)
values.Global.ProxyInit.Image.Name = registryOverride(values.Global.ProxyInit.Image.Name, value)
return nil
}),
flag.NewStringFlag(proxyFlags, "image-pull-policy", defaults.Global.ImagePullPolicy,
"Docker image pull policy", func(values *l5dcharts.Values, value string) error {
values.Global.ImagePullPolicy = value
values.Global.Proxy.Image.PullPolicy = value
values.Global.ProxyInit.Image.PullPolicy = value
values.DebugContainer.Image.PullPolicy = value
return nil
}),
flag.NewUintFlag(proxyFlags, "inbound-port", uint(defaults.Global.Proxy.Ports.Inbound),
"Proxy port to use for inbound traffic", func(values *l5dcharts.Values, value uint) error {
values.Global.Proxy.Ports.Inbound = int32(value)
return nil
}),
flag.NewUintFlag(proxyFlags, "outbound-port", uint(defaults.Global.Proxy.Ports.Outbound),
"Proxy port to use for outbound traffic", func(values *l5dcharts.Values, value uint) error {
values.Global.Proxy.Ports.Outbound = int32(value)
return nil
}),
flag.NewStringSliceFlag(proxyFlags, "skip-inbound-ports", nil, "Ports and/or port ranges (inclusive) that should skip the proxy and send directly to the application",
func(values *l5dcharts.Values, value []string) error {
values.Global.ProxyInit.IgnoreInboundPorts = strings.Join(value, ",")
return nil
}),
flag.NewStringSliceFlag(proxyFlags, "skip-outbound-ports", nil, "Outbound ports and/or port ranges (inclusive) that should skip the proxy",
func(values *l5dcharts.Values, value []string) error {
values.Global.ProxyInit.IgnoreOutboundPorts = strings.Join(value, ",")
return nil
}),
flag.NewInt64Flag(proxyFlags, "proxy-uid", defaults.Global.Proxy.UID, "Run the proxy under this user ID",
func(values *l5dcharts.Values, value int64) error {
values.Global.Proxy.UID = value
return nil
}),
flag.NewStringFlag(proxyFlags, "proxy-log-level", defaults.Global.Proxy.LogLevel, "Log level for the proxy",
func(values *l5dcharts.Values, value string) error {
values.Global.Proxy.LogLevel = value
return nil
}),
flag.NewUintFlag(proxyFlags, "control-port", uint(defaults.Global.Proxy.Ports.Control), "Proxy port to use for control",
func(values *l5dcharts.Values, value uint) error {
values.Global.Proxy.Ports.Control = int32(value)
return nil
}),
flag.NewUintFlag(proxyFlags, "admin-port", uint(defaults.Global.Proxy.Ports.Admin), "Proxy port to serve metrics on",
func(values *l5dcharts.Values, value uint) error {
values.Global.Proxy.Ports.Admin = int32(value)
return nil
}),
flag.NewStringFlag(proxyFlags, "proxy-cpu-request", defaults.Global.Proxy.Resources.CPU.Request, "Amount of CPU units that the proxy sidecar requests",
func(values *l5dcharts.Values, value string) error {
values.Global.Proxy.Resources.CPU.Request = value
return nil
}),
flag.NewStringFlag(proxyFlags, "proxy-memory-request", defaults.Global.Proxy.Resources.Memory.Request, "Amount of Memory that the proxy sidecar requests",
func(values *l5dcharts.Values, value string) error {
values.Global.Proxy.Resources.Memory.Request = value
return nil
}),
flag.NewStringFlag(proxyFlags, "proxy-cpu-limit", defaults.Global.Proxy.Resources.CPU.Limit, "Maximum amount of CPU units that the proxy sidecar can use",
func(values *l5dcharts.Values, value string) error {
values.Global.Proxy.Resources.CPU.Limit = value
return nil
}),
flag.NewStringFlag(proxyFlags, "proxy-memory-limit", defaults.Global.Proxy.Resources.Memory.Limit, "Maximum amount of Memory that the proxy sidecar can use",
func(values *l5dcharts.Values, value string) error {
values.Global.Proxy.Resources.Memory.Limit = value
return nil
}),
flag.NewBoolFlag(proxyFlags, "enable-external-profiles", defaults.Global.Proxy.EnableExternalProfiles, "Enable service profiles for non-Kubernetes services",
func(values *l5dcharts.Values, value bool) error {
values.Global.Proxy.EnableExternalProfiles = value
return nil
}),
// Deprecated flags
flag.NewStringFlag(proxyFlags, "proxy-memory", defaults.Global.Proxy.Resources.Memory.Request, "Amount of Memory that the proxy sidecar requests",
func(values *l5dcharts.Values, value string) error {
values.Global.Proxy.Resources.Memory.Request = value
return nil
}),
flag.NewStringFlag(proxyFlags, "proxy-cpu", defaults.Global.Proxy.Resources.CPU.Request, "Amount of CPU units that the proxy sidecar requests",
func(values *l5dcharts.Values, value string) error {
values.Global.Proxy.Resources.CPU.Request = value
return nil
}),
}
proxyFlags.MarkDeprecated("proxy-memory", "use --proxy-memory-request instead")
proxyFlags.MarkDeprecated("proxy-cpu", "use --proxy-cpu-request instead")
// Hide developer focused flags in release builds.
release, err := version.IsReleaseChannel(version.Version)
if err != nil {
log.Errorf("Unable to parse version: %s", version.Version)
}
if release {
proxyFlags.MarkHidden("proxy-image")
proxyFlags.MarkHidden("proxy-version")
proxyFlags.MarkHidden("image-pull-policy")
proxyFlags.MarkHidden("init-image")
proxyFlags.MarkHidden("init-image-version")
}
return flags, proxyFlags
}
// makeInjectFlags builds the set of flags which are exclusive to the inject
// command. These flags configure the proxy but are not available to the
// install and upgrade commands. This is generally for proxy configuration
// which is intended to be set on individual workloads rather than being
// cluster wide.
func makeInjectFlags(defaults *l5dcharts.Values) ([]flag.Flag, *pflag.FlagSet) {
injectFlags := pflag.NewFlagSet("inject", pflag.ExitOnError)
flags := []flag.Flag{
flag.NewInt64Flag(injectFlags, "wait-before-exit-seconds", int64(defaults.Global.Proxy.WaitBeforeExitSeconds),
"The period during which the proxy sidecar must stay alive while its pod is terminating. "+
"Must be smaller than terminationGracePeriodSeconds for the pod (default 0)",
func(values *l5dcharts.Values, value int64) error {
values.Global.Proxy.WaitBeforeExitSeconds = uint64(value)
return nil
}),
flag.NewBoolFlag(injectFlags, "disable-identity", defaults.Global.Proxy.DisableIdentity,
"Disables resources from participating in TLS identity", func(values *l5dcharts.Values, value bool) error {
values.Global.Proxy.DisableIdentity = value
return nil
}),
flag.NewBoolFlag(injectFlags, "disable-tap", defaults.Global.Proxy.DisableTap,
"Disables resources from being tapped", func(values *l5dcharts.Values, value bool) error {
values.Global.Proxy.DisableTap = value
return nil
}),
flag.NewStringFlag(injectFlags, "trace-collector", defaults.Global.Proxy.Trace.CollectorSvcAddr,
"Collector Service address for the proxies to send Trace Data", func(values *l5dcharts.Values, value string) error {
values.Global.Proxy.Trace.CollectorSvcAddr = value
return nil
}),
flag.NewStringFlag(injectFlags, "trace-collector-svc-account", defaults.Global.Proxy.Trace.CollectorSvcAccount,
"Service account associated with the Trace collector instance", func(values *l5dcharts.Values, value string) error {
values.Global.Proxy.Trace.CollectorSvcAccount = value
return nil
}),
flag.NewStringSliceFlag(injectFlags, "require-identity-on-inbound-ports", strings.Split(defaults.Global.Proxy.RequireIdentityOnInboundPorts, ","),
"Inbound ports on which the proxy should require identity", func(values *l5dcharts.Values, value []string) error {
values.Global.Proxy.RequireIdentityOnInboundPorts = strings.Join(value, ",")
return nil
}),
}
return flags, injectFlags
}
/* Validation */
func validateValues(ctx context.Context, k *k8s.KubernetesAPI, values *l5dcharts.Values) error {
if !alphaNumDashDot.MatchString(values.Global.ControllerImageVersion) {
return fmt.Errorf("%s is not a valid version", values.Global.ControllerImageVersion)
}
if _, err := log.ParseLevel(values.Global.ControllerLogLevel); err != nil {
return fmt.Errorf("--controller-log-level must be one of: panic, fatal, error, warn, info, debug")
}
if values.Global.Proxy.LogLevel == "" {
return errors.New("--proxy-log-level must not be empty")
}
if values.Global.EnableEndpointSlices && k != nil {
k8sAPI, err := k8s.NewAPI(kubeconfigPath, kubeContext, impersonate, impersonateGroup, 0)
if err != nil {
return err
}
err = k8s.EndpointSliceAccess(ctx, k8sAPI)
if err != nil {
return err
}
}
if errs := validation.IsDNS1123Subdomain(values.Global.IdentityTrustDomain); len(errs) > 0 {
return fmt.Errorf("invalid trust domain '%s': %s", values.Global.IdentityTrustDomain, errs[0])
}
err := validateProxyValues(values)
if err != nil {
return err
}
if values.Identity.Issuer.Scheme == string(corev1.SecretTypeTLS) {
if values.Identity.Issuer.TLS.CrtPEM != "" {
return errors.New("--identity-issuer-certificate-file must not be specified if --identity-external-issuer=true")
}
if values.Identity.Issuer.TLS.KeyPEM != "" {
return errors.New("--identity-issuer-key-file must not be specified if --identity-external-issuer=true")
}
}
if values.Identity.Issuer.Scheme == string(corev1.SecretTypeTLS) && k != nil {
externalIssuerData, err := issuercerts.FetchExternalIssuerData(ctx, k, controlPlaneNamespace)
if err != nil {
return err
}
_, err = externalIssuerData.VerifyAndBuildCreds(issuerName(values.Global.IdentityTrustDomain))
if err != nil {
return fmt.Errorf("failed to validate issuer credentials: %s", err)
}
}
if values.Identity.Issuer.Scheme == consts.IdentityIssuerSchemeLinkerd {
issuerData := issuercerts.IssuerCertData{
IssuerCrt: values.Identity.Issuer.TLS.CrtPEM,
IssuerKey: values.Identity.Issuer.TLS.KeyPEM,
TrustAnchors: values.Global.IdentityTrustAnchorsPEM,
}
_, err := issuerData.VerifyAndBuildCreds(issuerName(values.Global.IdentityTrustDomain))
if err != nil {
return fmt.Errorf("failed to validate issuer credentials: %s", err)
}
}
return nil
}
func validateProxyValues(values *l5dcharts.Values) error {
networks := strings.Split(values.Global.ClusterNetworks, ",")
for _, network := range networks {
if _, _, err := net.ParseCIDR(network); err != nil {
return fmt.Errorf("cannot parse destination get networks: %s", err)
}
}
if values.Global.Proxy.DisableIdentity && len(values.Global.Proxy.RequireIdentityOnInboundPorts) > 0 {
return errors.New("Identity must be enabled when --require-identity-on-inbound-ports is specified")
}
if values.Global.Proxy.Image.Version != "" && !alphaNumDashDot.MatchString(values.Global.Proxy.Image.Version) {
return fmt.Errorf("%s is not a valid version", values.Global.Proxy.Image.Version)
}
if !alphaNumDashDot.MatchString(values.Global.ProxyInit.Image.Version) {
return fmt.Errorf("%s is not a valid version", values.Global.ProxyInit.Image.Version)
}
if values.Global.ImagePullPolicy != "Always" && values.Global.ImagePullPolicy != "IfNotPresent" && values.Global.ImagePullPolicy != "Never" {
return fmt.Errorf("--image-pull-policy must be one of: Always, IfNotPresent, Never")
}
if values.Global.Proxy.Resources.CPU.Request != "" {
if _, err := k8sResource.ParseQuantity(values.Global.Proxy.Resources.CPU.Request); err != nil {
return fmt.Errorf("Invalid cpu request '%s' for --proxy-cpu-request flag", values.Global.Proxy.Resources.CPU.Request)
}
}
if values.Global.Proxy.Resources.Memory.Request != "" {
if _, err := k8sResource.ParseQuantity(values.Global.Proxy.Resources.Memory.Request); err != nil {
return fmt.Errorf("Invalid memory request '%s' for --proxy-memory-request flag", values.Global.Proxy.Resources.Memory.Request)
}
}
if values.Global.Proxy.Resources.CPU.Limit != "" {
cpuLimit, err := k8sResource.ParseQuantity(values.Global.Proxy.Resources.CPU.Limit)
if err != nil {
return fmt.Errorf("Invalid cpu limit '%s' for --proxy-cpu-limit flag", values.Global.Proxy.Resources.CPU.Limit)
}
// Not checking for error because option proxyCPURequest was already validated
if cpuRequest, _ := k8sResource.ParseQuantity(values.Global.Proxy.Resources.CPU.Request); cpuRequest.MilliValue() > cpuLimit.MilliValue() {
return fmt.Errorf("The cpu limit '%s' cannot be lower than the cpu request '%s'", values.Global.Proxy.Resources.CPU.Limit, values.Global.Proxy.Resources.CPU.Request)
}
}
if values.Global.Proxy.Resources.Memory.Limit != "" {
memoryLimit, err := k8sResource.ParseQuantity(values.Global.Proxy.Resources.Memory.Limit)
if err != nil {
return fmt.Errorf("Invalid memory limit '%s' for --proxy-memory-limit flag", values.Global.Proxy.Resources.Memory.Limit)
}
// Not checking for error because option proxyMemoryRequest was already validated
if memoryRequest, _ := k8sResource.ParseQuantity(values.Global.Proxy.Resources.Memory.Request); memoryRequest.Value() > memoryLimit.Value() {
return fmt.Errorf("The memory limit '%s' cannot be lower than the memory request '%s'", values.Global.Proxy.Resources.Memory.Limit, values.Global.Proxy.Resources.Memory.Request)
}
}
if !validProxyLogLevel.MatchString(values.Global.Proxy.LogLevel) {
return fmt.Errorf("\"%s\" is not a valid proxy log level - for allowed syntax check https://docs.rs/env_logger/0.6.0/env_logger/#enabling-logging",
values.Global.Proxy.LogLevel)
}
if values.Global.ProxyInit.IgnoreInboundPorts != "" {
if err := validateRangeSlice(strings.Split(values.Global.ProxyInit.IgnoreInboundPorts, ",")); err != nil {
return err
}
}
if values.Global.ProxyInit.IgnoreOutboundPorts != "" {
if err := validateRangeSlice(strings.Split(values.Global.ProxyInit.IgnoreOutboundPorts, ",")); err != nil {
return err
}
}
return nil
}
// initializeIssuerCredentials populates the identity issuer TLS credentials.
// If we are using an externally managed issuer secret, all we need to do here
// is copy the trust root from the issuer secret. Otherwise, if no credentials
// have already been supplied, we generate them.
func initializeIssuerCredentials(ctx context.Context, k *k8s.KubernetesAPI, values *l5dcharts.Values) error {
if values.Identity.Issuer.Scheme == string(corev1.SecretTypeTLS) {
// Using externally managed issuer credentials. We need to copy the
// trust root.
if k == nil {
return errors.New("--ignore-cluster is not supported when --identity-external-issuer=true")
}
externalIssuerData, err := issuercerts.FetchExternalIssuerData(ctx, k, controlPlaneNamespace)
if err != nil {
return err
}
values.Global.IdentityTrustAnchorsPEM = externalIssuerData.TrustAnchors
} else if values.Identity.Issuer.TLS.CrtPEM != "" || values.Identity.Issuer.TLS.KeyPEM != "" || values.Global.IdentityTrustAnchorsPEM != "" {
// If any credentials have already been supplied, check that they are
// all present.
if values.Global.IdentityTrustAnchorsPEM == "" {
return errors.New("a trust anchors file must be specified if other credentials are provided")
}
if values.Identity.Issuer.TLS.CrtPEM == "" {
return errors.New("a certificate file must be specified if other credentials are provided")
}
if values.Identity.Issuer.TLS.KeyPEM == "" {
return errors.New("a private key file must be specified if other credentials are provided")
}
} else {
// No credentials have been supplied so we will generate them.
root, err := tls.GenerateRootCAWithDefaults(issuerName(values.Global.IdentityTrustDomain))
if err != nil {
return fmt.Errorf("failed to generate root certificate for identity: %s", err)
}
values.Identity.Issuer.CrtExpiry = root.Cred.Crt.Certificate.NotAfter
values.Identity.Issuer.TLS.KeyPEM = root.Cred.EncodePrivateKeyPEM()
values.Identity.Issuer.TLS.CrtPEM = root.Cred.Crt.EncodeCertificatePEM()
values.Global.IdentityTrustAnchorsPEM = root.Cred.Crt.EncodeCertificatePEM()
}
return nil
}
func issuerName(trustDomain string) string {
return fmt.Sprintf("identity.%s.%s", controlPlaneNamespace, trustDomain)
}