linkerd2/pkg/config/config.go

223 lines
7.5 KiB
Go

package config
import (
"fmt"
"io/ioutil"
"strings"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
pb "github.com/linkerd/linkerd2/controller/gen/config"
l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
log "github.com/sirupsen/logrus"
"sigs.k8s.io/yaml"
)
// Global returns the Global protobuf config from the linkerd-config ConfigMap
func Global(filepath string) (*pb.Global, error) {
config := &pb.Global{}
err := unmarshalFile(filepath, config)
return config, err
}
// Proxy returns the Proxy protobuf config from the linkerd-config ConfigMap
func Proxy(filepath string) (*pb.Proxy, error) {
config := &pb.Proxy{}
err := unmarshalFile(filepath, config)
return config, err
}
// Install returns the Install protobuf config from the linkerd-config ConfigMap
func Install(filepath string) (*pb.Install, error) {
config := &pb.Install{}
err := unmarshalFile(filepath, config)
return config, err
}
// Values returns the Value struct from the linkerd-config ConfigMap
func Values(filepath string) (*l5dcharts.Values, error) {
values := &l5dcharts.Values{}
configYaml, err := ioutil.ReadFile(filepath)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %s", err)
}
log.Debugf("%s config YAML: %s", filepath, configYaml)
if err = yaml.Unmarshal(configYaml, values); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON from: %s: %s", filepath, err)
}
return values, err
}
func unmarshalFile(filepath string, msg proto.Message) error {
configJSON, err := ioutil.ReadFile(filepath)
if err != nil {
return fmt.Errorf("failed to read config file: %s", err)
}
log.Debugf("%s config JSON: %s", filepath, configJSON)
if err = unmarshal(string(configJSON), msg); err != nil {
return fmt.Errorf("failed to unmarshal JSON from: %s: %s", filepath, err)
}
return nil
}
func unmarshal(json string, msg proto.Message) error {
// If a config is missing, then just leave the message as nil and return
// without an error.
if json == "" {
return nil
}
// If we're using older code to read a newer config, blowing up during decoding
// is not helpful. We should detect that through other means.
u := jsonpb.Unmarshaler{AllowUnknownFields: true}
return u.Unmarshal(strings.NewReader(json), msg)
}
// FromConfigMap builds a configuration by reading a map with the keys "global"
// and "proxy", each containing JSON values.
func FromConfigMap(configMap map[string]string) (*pb.All, error) {
c := &pb.All{Global: &pb.Global{}, Proxy: &pb.Proxy{}, Install: &pb.Install{}}
if err := unmarshal(configMap["global"], c.Global); err != nil {
return nil, fmt.Errorf("invalid global config: %s", err)
}
if err := unmarshal(configMap["proxy"], c.Proxy); err != nil {
return nil, fmt.Errorf("invalid proxy config: %s", err)
}
if err := unmarshal(configMap["install"], c.Install); err != nil {
return nil, fmt.Errorf("invalid install config: %s", err)
}
return c, nil
}
// ToJSON encode the configuration to JSON, i.e. to be stored in a ConfigMap.
func ToJSON(configs *pb.All) (global, proxy, install string, err error) {
m := jsonpb.Marshaler{EmitDefaults: true}
global, err = m.MarshalToString(configs.GetGlobal())
if err != nil {
return
}
proxy, err = m.MarshalToString(configs.GetProxy())
if err != nil {
return
}
install, err = m.MarshalToString(configs.GetInstall())
return
}
// ToValues converts configuration into a Values struct, i.e to be consumed by check
// TODO: Remove this once the newer configuration becomes the default i.e 2.10
func ToValues(configs *pb.All) *l5dcharts.Values {
// convert install flags into values
values := &l5dcharts.Values{
Global: &l5dcharts.Global{
CNIEnabled: configs.GetGlobal().GetCniEnabled(),
Namespace: configs.GetGlobal().GetLinkerdNamespace(),
IdentityTrustAnchorsPEM: configs.GetGlobal().GetIdentityContext().GetTrustAnchorsPem(),
IdentityTrustDomain: configs.GetGlobal().GetIdentityContext().GetTrustDomain(),
ClusterDomain: configs.GetGlobal().GetClusterDomain(),
ClusterNetworks: configs.GetProxy().GetDestinationGetNetworks(),
LinkerdVersion: configs.GetGlobal().GetVersion(),
Proxy: &l5dcharts.Proxy{
Image: &l5dcharts.Image{
Name: configs.GetProxy().GetProxyImage().GetImageName(),
PullPolicy: configs.GetProxy().GetProxyImage().GetPullPolicy(),
Version: configs.GetProxy().GetProxyVersion(),
},
Ports: &l5dcharts.Ports{
Control: int32(configs.GetProxy().GetControlPort().GetPort()),
Inbound: int32(configs.GetProxy().GetInboundPort().GetPort()),
Admin: int32(configs.GetProxy().GetAdminPort().GetPort()),
Outbound: int32(configs.GetProxy().GetOutboundPort().GetPort()),
},
Resources: &l5dcharts.Resources{
CPU: l5dcharts.Constraints{
Limit: configs.GetProxy().GetResource().GetLimitCpu(),
Request: configs.GetProxy().GetResource().GetRequestCpu(),
},
Memory: l5dcharts.Constraints{
Limit: configs.GetProxy().GetResource().GetLimitMemory(),
Request: configs.GetProxy().GetResource().GetRequestMemory(),
},
},
EnableExternalProfiles: !configs.Proxy.GetDisableExternalProfiles(),
LogFormat: configs.GetProxy().GetLogFormat(),
OutboundConnectTimeout: configs.GetProxy().GetOutboundConnectTimeout(),
InboundConnectTimeout: configs.GetProxy().GetInboundConnectTimeout(),
},
ProxyInit: &l5dcharts.ProxyInit{
IgnoreInboundPorts: toString(configs.GetProxy().GetIgnoreInboundPorts()),
IgnoreOutboundPorts: toString(configs.GetProxy().GetIgnoreOutboundPorts()),
Image: &l5dcharts.Image{
Name: configs.GetProxy().GetProxyInitImage().GetImageName(),
PullPolicy: configs.GetProxy().GetProxyInitImage().GetPullPolicy(),
Version: configs.GetProxy().GetProxyInitImageVersion(),
},
},
},
Identity: &l5dcharts.Identity{
Issuer: &l5dcharts.Issuer{
Scheme: configs.GetGlobal().GetIdentityContext().GetScheme(),
},
},
OmitWebhookSideEffects: configs.GetGlobal().GetOmitWebhookSideEffects(),
DebugContainer: &l5dcharts.DebugContainer{
Image: &l5dcharts.Image{
Name: configs.GetProxy().GetDebugImage().GetImageName(),
PullPolicy: configs.GetProxy().GetDebugImage().GetPullPolicy(),
Version: configs.GetProxy().GetDebugImageVersion(),
},
},
}
// for non-primitive types set only if they are not nil
if configs.GetGlobal().GetIdentityContext().GetIssuanceLifetime() != nil {
values.Identity.Issuer.IssuanceLifetime = configs.GetGlobal().GetIdentityContext().GetIssuanceLifetime().String()
}
if configs.GetGlobal().GetIdentityContext().GetClockSkewAllowance() != nil {
values.Identity.Issuer.ClockSkewAllowance = configs.GetGlobal().GetIdentityContext().GetClockSkewAllowance().String()
}
if configs.GetProxy().GetLogLevel() != nil {
values.Global.Proxy.LogLevel = configs.GetProxy().GetLogLevel().String()
}
// set HA, and Heartbeat flags as health-check needs them for old config installs
for _, flag := range configs.GetInstall().GetFlags() {
if flag.GetName() == "ha" && flag.GetValue() == "true" {
values.Global.HighAvailability = true
}
if flag.GetName() == "disable-heartbeat" && flag.GetValue() == "true" {
values.DisableHeartBeat = true
}
}
return values
}
func toString(portRanges []*pb.PortRange) string {
var portRangeString string
if len(portRanges) > 0 {
for i := 0; i < len(portRanges)-1; i++ {
portRangeString += portRanges[i].GetPortRange() + ","
}
portRangeString += portRanges[len(portRanges)-1].GetPortRange()
}
return portRangeString
}