linkerd2/cli/cmd/install-cni-plugin.go

176 lines
6.5 KiB
Go

package cmd
import (
"bytes"
"fmt"
"io"
"os"
"strings"
"text/template"
"github.com/linkerd/linkerd2/cli/install"
"github.com/linkerd/linkerd2/pkg/k8s"
"github.com/linkerd/linkerd2/pkg/version"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
type installCNIPluginConfig struct {
Namespace string
CNIPluginImage string
LogLevel string
InboundPort uint
OutboundPort uint
IgnoreInboundPorts string
IgnoreOutboundPorts string
ProxyUID int64
DestCNINetDir string
DestCNIBinDir string
CreatedByAnnotation string
CliVersion string
}
type cniPluginOptions struct {
linkerdVersion string
dockerRegistry string
proxyControlPort uint
proxyAdminPort uint
inboundPort uint
outboundPort uint
ignoreInboundPorts []uint
ignoreOutboundPorts []uint
proxyUID int64
cniPluginImage string
logLevel string
destCNINetDir string
destCNIBinDir string
}
func newCNIPluginOptions() *cniPluginOptions {
return &cniPluginOptions{
linkerdVersion: version.Version,
dockerRegistry: defaultDockerRegistry,
proxyControlPort: 4190,
proxyAdminPort: 4191,
inboundPort: 4143,
outboundPort: 4140,
ignoreInboundPorts: nil,
ignoreOutboundPorts: nil,
proxyUID: 2102,
cniPluginImage: defaultDockerRegistry + "/cni-plugin",
logLevel: "info",
destCNINetDir: "/etc/cni/net.d",
destCNIBinDir: "/opt/cni/bin",
}
}
func (options *cniPluginOptions) validate() error {
if !alphaNumDashDot.MatchString(options.linkerdVersion) {
return fmt.Errorf("%s is not a valid version", options.linkerdVersion)
}
if !alphaNumDashDotSlashColon.MatchString(options.dockerRegistry) {
return fmt.Errorf("%s is not a valid Docker registry. The url can contain only letters, numbers, dash, dot, slash and colon", options.dockerRegistry)
}
if _, err := log.ParseLevel(options.logLevel); err != nil {
return fmt.Errorf("--cni-log-level must be one of: panic, fatal, error, warn, info, debug")
}
return nil
}
func (options *cniPluginOptions) taggedCNIPluginImage() string {
image := registryOverride(options.cniPluginImage, options.dockerRegistry)
return fmt.Sprintf("%s:%s", image, options.linkerdVersion)
}
func newCmdInstallCNIPlugin() *cobra.Command {
options := newCNIPluginOptions()
cmd := &cobra.Command{
Use: "install-cni [flags]",
Short: "Output Kubernetes configs to install Linkerd CNI (experimental)",
Long: `Output Kubernetes configs to install Linkerd CNI (experimental).
This command installs a DaemonSet into the Linkerd control plane. The DaemonSet
copies the necessary linkerd-cni plugin binaries and configs onto the host. It
assumes that the 'linkerd install' command will be executed with the
'--linkerd-cni-enabled' flag. This command needs to be executed before the
'linkerd install --linkerd-cni-enabled' command.`,
RunE: func(cmd *cobra.Command, args []string) error {
config, err := validateAndBuildCNIConfig(options)
if err != nil {
return err
}
return renderCNIPlugin(os.Stdout, config)
},
}
cmd.PersistentFlags().StringVarP(&options.linkerdVersion, "linkerd-version", "v", options.linkerdVersion, "Tag to be used for Linkerd images")
cmd.PersistentFlags().StringVar(&options.dockerRegistry, "registry", options.dockerRegistry, "Docker registry to pull images from")
cmd.PersistentFlags().Int64Var(&options.proxyUID, "proxy-uid", options.proxyUID, "Run the proxy under this user ID")
cmd.PersistentFlags().UintVar(&options.inboundPort, "inbound-port", options.inboundPort, "Proxy port to use for inbound traffic")
cmd.PersistentFlags().UintVar(&options.outboundPort, "outbound-port", options.outboundPort, "Proxy port to use for outbound traffic")
cmd.PersistentFlags().UintVar(&options.proxyControlPort, "control-port", options.proxyControlPort, "Proxy port to use for control")
cmd.PersistentFlags().UintVar(&options.proxyAdminPort, "admin-port", options.proxyAdminPort, "Proxy port to serve metrics on")
cmd.PersistentFlags().UintSliceVar(&options.ignoreInboundPorts, "skip-inbound-ports", options.ignoreInboundPorts, "Ports that should skip the proxy and send directly to the application")
cmd.PersistentFlags().UintSliceVar(&options.ignoreOutboundPorts, "skip-outbound-ports", options.ignoreOutboundPorts, "Outbound ports that should skip the proxy")
cmd.PersistentFlags().StringVar(&options.cniPluginImage, "cni-image", options.cniPluginImage, "Image for the cni-plugin.")
cmd.PersistentFlags().StringVar(&options.logLevel, "cni-log-level", options.logLevel, "Log level for the cni-plugin.")
cmd.PersistentFlags().StringVar(&options.destCNINetDir, "dest-cni-net-dir", options.destCNINetDir, "Directory on the host where the CNI configuration will be placed.")
cmd.PersistentFlags().StringVar(&options.destCNIBinDir, "dest-cni-bin-dir", options.destCNIBinDir, "Directory on the host where the CNI plugin binaries reside.")
return cmd
}
func validateAndBuildCNIConfig(options *cniPluginOptions) (*installCNIPluginConfig, error) {
if err := options.validate(); err != nil {
return nil, err
}
ignoreInboundPorts := []string{
fmt.Sprintf("%d", options.proxyControlPort),
fmt.Sprintf("%d", options.proxyAdminPort),
}
for _, p := range options.ignoreInboundPorts {
ignoreInboundPorts = append(ignoreInboundPorts, fmt.Sprintf("%d", p))
}
ignoreOutboundPorts := []string{}
for _, p := range options.ignoreOutboundPorts {
ignoreOutboundPorts = append(ignoreOutboundPorts, fmt.Sprintf("%d", p))
}
return &installCNIPluginConfig{
Namespace: controlPlaneNamespace,
CNIPluginImage: options.taggedCNIPluginImage(),
LogLevel: options.logLevel,
InboundPort: options.inboundPort,
OutboundPort: options.outboundPort,
IgnoreInboundPorts: strings.Join(ignoreInboundPorts, ","),
IgnoreOutboundPorts: strings.Join(ignoreOutboundPorts, ","),
ProxyUID: options.proxyUID,
DestCNINetDir: options.destCNINetDir,
DestCNIBinDir: options.destCNIBinDir,
CreatedByAnnotation: k8s.CreatedByAnnotation,
CliVersion: k8s.CreatedByAnnotationValue(),
}, nil
}
func renderCNIPlugin(w io.Writer, config *installCNIPluginConfig) error {
template, err := template.New("linkerd-cni").Parse(install.CNITemplate)
if err != nil {
return err
}
buf := &bytes.Buffer{}
err = template.Execute(buf, config)
if err != nil {
return err
}
w.Write(buf.Bytes())
w.Write([]byte("---\n"))
return nil
}