From d5a41477628a87ada8a340558fb5233aa9f770fb Mon Sep 17 00:00:00 2001 From: Derek Nola Date: Thu, 11 Nov 2021 22:18:10 -0800 Subject: [PATCH] [Engine-1.21] Parser improvements, allow config values to be used with etcd-snapshot (#4477) * Match to last After keyword for parser (#4383) * Made parser able to skip over subcommands * Fix to allow etcd-snapshot to use config file with flags that are only used with k3s server. (#4464) Signed-off-by: Derek Nola --- pkg/cli/cmds/server.go | 718 ++++++++++---------- pkg/configfilearg/defaultparser.go | 18 +- pkg/configfilearg/defaultparser_test.go | 74 ++ pkg/configfilearg/parser.go | 80 ++- pkg/configfilearg/parser_test.go | 44 +- pkg/configfilearg/testdata/defaultdata.yaml | 5 + 6 files changed, 568 insertions(+), 371 deletions(-) create mode 100644 pkg/configfilearg/defaultparser_test.go create mode 100644 pkg/configfilearg/testdata/defaultdata.yaml diff --git a/pkg/cli/cmds/server.go b/pkg/cli/cmds/server.go index 29dd9084a6..5ee53b2d5e 100644 --- a/pkg/cli/cmds/server.go +++ b/pkg/cli/cmds/server.go @@ -146,6 +146,365 @@ var ( } ) +var ServerFlags = []cli.Flag{ + ConfigFlag, + DebugFlag, + VLevel, + VModule, + LogFile, + AlsoLogToStderr, + cli.StringFlag{ + Name: "bind-address", + Usage: "(listener) " + version.Program + " bind address (default: 0.0.0.0)", + Destination: &ServerConfig.BindAddress, + }, + cli.IntFlag{ + Name: "https-listen-port", + Usage: "(listener) HTTPS listen port", + Value: 6443, + Destination: &ServerConfig.HTTPSPort, + }, + cli.StringFlag{ + Name: "advertise-address", + Usage: "(listener) IPv4 address that apiserver uses to advertise to members of the cluster (default: node-external-ip/node-ip)", + Destination: &ServerConfig.AdvertiseIP, + }, + cli.IntFlag{ + Name: "advertise-port", + Usage: "(listener) Port that apiserver uses to advertise to members of the cluster (default: listen-port)", + Destination: &ServerConfig.AdvertisePort, + }, + cli.StringSliceFlag{ + Name: "tls-san", + Usage: "(listener) Add additional hostnames or IPv4/IPv6 addresses as Subject Alternative Names on the server TLS cert", + Value: &ServerConfig.TLSSan, + }, + cli.StringFlag{ + Name: "data-dir,d", + Usage: "(data) Folder to hold state default /var/lib/rancher/" + version.Program + " or ${HOME}/.rancher/" + version.Program + " if not root", + Destination: &ServerConfig.DataDir, + }, + ClusterCIDR, + ServiceCIDR, + ServiceNodePortRange, + ClusterDNS, + ClusterDomain, + cli.StringFlag{ + Name: "flannel-backend", + Usage: "(networking) One of 'none', 'vxlan', 'ipsec', 'host-gw', or 'wireguard'", + Destination: &ServerConfig.FlannelBackend, + Value: "vxlan", + }, + cli.StringFlag{ + Name: "token,t", + Usage: "(cluster) Shared secret used to join a server or agent to a cluster", + Destination: &ServerConfig.Token, + EnvVar: version.ProgramUpper + "_TOKEN", + }, + cli.StringFlag{ + Name: "token-file", + Usage: "(cluster) File containing the cluster-secret/token", + Destination: &ServerConfig.TokenFile, + EnvVar: version.ProgramUpper + "_TOKEN_FILE", + }, + cli.StringFlag{ + Name: "write-kubeconfig,o", + Usage: "(client) Write kubeconfig for admin client to this file", + Destination: &ServerConfig.KubeConfigOutput, + EnvVar: version.ProgramUpper + "_KUBECONFIG_OUTPUT", + }, + cli.StringFlag{ + Name: "write-kubeconfig-mode", + Usage: "(client) Write kubeconfig with this mode", + Destination: &ServerConfig.KubeConfigMode, + EnvVar: version.ProgramUpper + "_KUBECONFIG_MODE", + }, + ExtraAPIArgs, + ExtraEtcdArgs, + ExtraControllerArgs, + ExtraSchedulerArgs, + cli.StringSliceFlag{ + Name: "kube-cloud-controller-manager-arg", + Usage: "(flags) Customized flag for kube-cloud-controller-manager process", + Value: &ServerConfig.ExtraCloudControllerArgs, + }, + cli.StringFlag{ + Name: "datastore-endpoint", + Usage: "(db) Specify etcd, Mysql, Postgres, or Sqlite (default) data source name", + Destination: &ServerConfig.DatastoreEndpoint, + EnvVar: version.ProgramUpper + "_DATASTORE_ENDPOINT", + }, + cli.StringFlag{ + Name: "datastore-cafile", + Usage: "(db) TLS Certificate Authority file used to secure datastore backend communication", + Destination: &ServerConfig.DatastoreCAFile, + EnvVar: version.ProgramUpper + "_DATASTORE_CAFILE", + }, + cli.StringFlag{ + Name: "datastore-certfile", + Usage: "(db) TLS certification file used to secure datastore backend communication", + Destination: &ServerConfig.DatastoreCertFile, + EnvVar: version.ProgramUpper + "_DATASTORE_CERTFILE", + }, + cli.StringFlag{ + Name: "datastore-keyfile", + Usage: "(db) TLS key file used to secure datastore backend communication", + Destination: &ServerConfig.DatastoreKeyFile, + EnvVar: version.ProgramUpper + "_DATASTORE_KEYFILE", + }, + &cli.BoolFlag{ + Name: "etcd-expose-metrics", + Usage: "(db) Expose etcd metrics to client interface. (Default false)", + Destination: &ServerConfig.EtcdExposeMetrics, + }, + &cli.BoolFlag{ + Name: "etcd-disable-snapshots", + Usage: "(db) Disable automatic etcd snapshots", + Destination: &ServerConfig.EtcdDisableSnapshots, + }, + &cli.StringFlag{ + Name: "etcd-snapshot-name", + Usage: "(db) Set the base name of etcd snapshots. Default: etcd-snapshot-", + Destination: &ServerConfig.EtcdSnapshotName, + Value: "etcd-snapshot", + }, + &cli.StringFlag{ + Name: "etcd-snapshot-schedule-cron", + Usage: "(db) Snapshot interval time in cron spec. eg. every 5 hours '* */5 * * *'", + Destination: &ServerConfig.EtcdSnapshotCron, + Value: "0 */12 * * *", + }, + &cli.IntFlag{ + Name: "etcd-snapshot-retention", + Usage: "(db) Number of snapshots to retain", + Destination: &ServerConfig.EtcdSnapshotRetention, + Value: defaultSnapshotRentention, + }, + &cli.StringFlag{ + Name: "etcd-snapshot-dir", + Usage: "(db) Directory to save db snapshots. (Default location: ${data-dir}/db/snapshots)", + Destination: &ServerConfig.EtcdSnapshotDir, + }, + &cli.BoolFlag{ + Name: "etcd-s3", + Usage: "(db) Enable backup to S3", + Destination: &ServerConfig.EtcdS3, + }, + &cli.StringFlag{ + Name: "etcd-s3-endpoint", + Usage: "(db) S3 endpoint url", + Destination: &ServerConfig.EtcdS3Endpoint, + Value: "s3.amazonaws.com", + }, + &cli.StringFlag{ + Name: "etcd-s3-endpoint-ca", + Usage: "(db) S3 custom CA cert to connect to S3 endpoint", + Destination: &ServerConfig.EtcdS3EndpointCA, + }, + &cli.BoolFlag{ + Name: "etcd-s3-skip-ssl-verify", + Usage: "(db) Disables S3 SSL certificate validation", + Destination: &ServerConfig.EtcdS3SkipSSLVerify, + }, + &cli.StringFlag{ + Name: "etcd-s3-access-key", + Usage: "(db) S3 access key", + EnvVar: "AWS_ACCESS_KEY_ID", + Destination: &ServerConfig.EtcdS3AccessKey, + }, + &cli.StringFlag{ + Name: "etcd-s3-secret-key", + Usage: "(db) S3 secret key", + EnvVar: "AWS_SECRET_ACCESS_KEY", + Destination: &ServerConfig.EtcdS3SecretKey, + }, + &cli.StringFlag{ + Name: "etcd-s3-bucket", + Usage: "(db) S3 bucket name", + Destination: &ServerConfig.EtcdS3BucketName, + }, + &cli.StringFlag{ + Name: "etcd-s3-region", + Usage: "(db) S3 region / bucket location (optional)", + Destination: &ServerConfig.EtcdS3Region, + Value: "us-east-1", + }, + &cli.StringFlag{ + Name: "etcd-s3-folder", + Usage: "(db) S3 folder", + Destination: &ServerConfig.EtcdS3Folder, + }, + &cli.BoolFlag{ + Name: "etcd-s3-insecure", + Usage: "(db) Disables S3 over HTTPS", + Destination: &ServerConfig.EtcdS3Insecure, + }, + &cli.DurationFlag{ + Name: "etcd-s3-timeout", + Usage: "(db) S3 timeout", + Destination: &ServerConfig.EtcdS3Timeout, + Value: 30 * time.Second, + }, + cli.StringFlag{ + Name: "default-local-storage-path", + Usage: "(storage) Default local storage path for local provisioner storage class", + Destination: &ServerConfig.DefaultLocalStoragePath, + }, + cli.StringSliceFlag{ + Name: "disable", + Usage: "(components) Do not deploy packaged components and delete any deployed components (valid items: " + DisableItems + ")", + }, + cli.BoolFlag{ + Name: "disable-scheduler", + Usage: "(components) Disable Kubernetes default scheduler", + Destination: &ServerConfig.DisableScheduler, + }, + cli.BoolFlag{ + Name: "disable-cloud-controller", + Usage: "(components) Disable " + version.Program + " default cloud controller manager", + Destination: &ServerConfig.DisableCCM, + }, + cli.BoolFlag{ + Name: "disable-kube-proxy", + Usage: "(components) Disable running kube-proxy", + Destination: &ServerConfig.DisableKubeProxy, + }, + cli.BoolFlag{ + Name: "disable-network-policy", + Usage: "(components) Disable " + version.Program + " default network policy controller", + Destination: &ServerConfig.DisableNPC, + }, + cli.BoolFlag{ + Name: "disable-helm-controller", + Usage: "(components) Disable Helm controller", + Destination: &ServerConfig.DisableHelmController, + }, + cli.BoolFlag{ + Name: "disable-apiserver", + Hidden: true, + Usage: "(experimental/components) Disable running api server", + Destination: &ServerConfig.DisableAPIServer, + }, + cli.BoolFlag{ + Name: "disable-controller-manager", + Hidden: true, + Usage: "(experimental/components) Disable running kube-controller-manager", + Destination: &ServerConfig.DisableControllerManager, + }, + cli.BoolFlag{ + Name: "disable-etcd", + Hidden: true, + Usage: "(experimental/components) Disable running etcd", + Destination: &ServerConfig.DisableETCD, + }, + NodeNameFlag, + WithNodeIDFlag, + NodeLabels, + NodeTaints, + ImageCredProvBinDirFlag, + ImageCredProvConfigFlag, + DockerFlag, + CRIEndpointFlag, + PauseImageFlag, + SnapshotterFlag, + PrivateRegistryFlag, + AirgapExtraRegistryFlag, + NodeIPFlag, + NodeExternalIPFlag, + ResolvConfFlag, + FlannelIfaceFlag, + FlannelConfFlag, + ExtraKubeletArgs, + ExtraKubeProxyArgs, + ProtectKernelDefaultsFlag, + cli.BoolFlag{ + Name: "rootless", + Usage: "(experimental) Run rootless", + Destination: &ServerConfig.Rootless, + }, + cli.StringFlag{ + Name: "agent-token", + Usage: "(cluster) Shared secret used to join agents to the cluster, but not servers", + Destination: &ServerConfig.AgentToken, + EnvVar: version.ProgramUpper + "_AGENT_TOKEN", + }, + cli.StringFlag{ + Name: "agent-token-file", + Usage: "(cluster) File containing the agent secret", + Destination: &ServerConfig.AgentTokenFile, + EnvVar: version.ProgramUpper + "_AGENT_TOKEN_FILE", + }, + cli.StringFlag{ + Name: "server,s", + Usage: "(cluster) Server to connect to, used to join a cluster", + EnvVar: version.ProgramUpper + "_URL", + Destination: &ServerConfig.ServerURL, + }, + cli.BoolFlag{ + Name: "cluster-init", + Usage: "(cluster) Initialize a new cluster using embedded Etcd", + EnvVar: version.ProgramUpper + "_CLUSTER_INIT", + Destination: &ServerConfig.ClusterInit, + }, + cli.BoolFlag{ + Name: "cluster-reset", + Usage: "(cluster) Forget all peers and become sole member of a new cluster", + EnvVar: version.ProgramUpper + "_CLUSTER_RESET", + Destination: &ServerConfig.ClusterReset, + }, + &cli.StringFlag{ + Name: "cluster-reset-restore-path", + Usage: "(db) Path to snapshot file to be restored", + Destination: &ServerConfig.ClusterResetRestorePath, + }, + cli.BoolFlag{ + Name: "secrets-encryption", + Usage: "(experimental) Enable Secret encryption at rest", + Destination: &ServerConfig.EncryptSecrets, + }, + cli.StringFlag{ + Name: "system-default-registry", + Usage: "(image) Private registry to be used for all system images", + EnvVar: version.ProgramUpper + "_SYSTEM_DEFAULT_REGISTRY", + Destination: &ServerConfig.SystemDefaultRegistry, + }, + &SELinuxFlag, + LBServerPortFlag, + + // Hidden/Deprecated flags below + + &DisableSELinuxFlag, + FlannelFlag, + cli.StringSliceFlag{ + Name: "no-deploy", + Usage: "(deprecated) Do not deploy packaged components (valid items: " + DisableItems + ")", + }, + cli.StringFlag{ + Name: "cluster-secret", + Usage: "(deprecated) use --token", + Destination: &ServerConfig.ClusterSecret, + EnvVar: version.ProgramUpper + "_CLUSTER_SECRET", + }, + cli.BoolFlag{ + Name: "disable-agent", + Usage: "Do not run a local agent and register a local kubelet", + Hidden: true, + Destination: &ServerConfig.DisableAgent, + }, + cli.StringSliceFlag{ + Hidden: true, + Name: "kube-controller-arg", + Usage: "(flags) Customized flag for kube-controller-manager process", + Value: &ServerConfig.ExtraControllerArgs, + }, + cli.StringSliceFlag{ + Hidden: true, + Name: "kube-cloud-controller-arg", + Usage: "(flags) Customized flag for kube-cloud-controller-manager process", + Value: &ServerConfig.ExtraCloudControllerArgs, + }, +} + func NewServerCommand(action func(*cli.Context) error) cli.Command { return cli.Command{ Name: "server", @@ -153,363 +512,6 @@ func NewServerCommand(action func(*cli.Context) error) cli.Command { UsageText: appName + " server [OPTIONS]", Before: SetupDebug(CheckSELinuxFlags), Action: action, - Flags: []cli.Flag{ - ConfigFlag, - DebugFlag, - VLevel, - VModule, - LogFile, - AlsoLogToStderr, - cli.StringFlag{ - Name: "bind-address", - Usage: "(listener) " + version.Program + " bind address (default: 0.0.0.0)", - Destination: &ServerConfig.BindAddress, - }, - cli.IntFlag{ - Name: "https-listen-port", - Usage: "(listener) HTTPS listen port", - Value: 6443, - Destination: &ServerConfig.HTTPSPort, - }, - cli.StringFlag{ - Name: "advertise-address", - Usage: "(listener) IPv4 address that apiserver uses to advertise to members of the cluster (default: node-external-ip/node-ip)", - Destination: &ServerConfig.AdvertiseIP, - }, - cli.IntFlag{ - Name: "advertise-port", - Usage: "(listener) Port that apiserver uses to advertise to members of the cluster (default: listen-port)", - Destination: &ServerConfig.AdvertisePort, - }, - cli.StringSliceFlag{ - Name: "tls-san", - Usage: "(listener) Add additional hostnames or IPv4/IPv6 addresses as Subject Alternative Names on the server TLS cert", - Value: &ServerConfig.TLSSan, - }, - cli.StringFlag{ - Name: "data-dir,d", - Usage: "(data) Folder to hold state default /var/lib/rancher/" + version.Program + " or ${HOME}/.rancher/" + version.Program + " if not root", - Destination: &ServerConfig.DataDir, - }, - ClusterCIDR, - ServiceCIDR, - ServiceNodePortRange, - ClusterDNS, - ClusterDomain, - cli.StringFlag{ - Name: "flannel-backend", - Usage: "(networking) One of 'none', 'vxlan', 'ipsec', 'host-gw', or 'wireguard'", - Destination: &ServerConfig.FlannelBackend, - Value: "vxlan", - }, - cli.StringFlag{ - Name: "token,t", - Usage: "(cluster) Shared secret used to join a server or agent to a cluster", - Destination: &ServerConfig.Token, - EnvVar: version.ProgramUpper + "_TOKEN", - }, - cli.StringFlag{ - Name: "token-file", - Usage: "(cluster) File containing the cluster-secret/token", - Destination: &ServerConfig.TokenFile, - EnvVar: version.ProgramUpper + "_TOKEN_FILE", - }, - cli.StringFlag{ - Name: "write-kubeconfig,o", - Usage: "(client) Write kubeconfig for admin client to this file", - Destination: &ServerConfig.KubeConfigOutput, - EnvVar: version.ProgramUpper + "_KUBECONFIG_OUTPUT", - }, - cli.StringFlag{ - Name: "write-kubeconfig-mode", - Usage: "(client) Write kubeconfig with this mode", - Destination: &ServerConfig.KubeConfigMode, - EnvVar: version.ProgramUpper + "_KUBECONFIG_MODE", - }, - ExtraAPIArgs, - ExtraEtcdArgs, - ExtraControllerArgs, - ExtraSchedulerArgs, - cli.StringSliceFlag{ - Name: "kube-cloud-controller-manager-arg", - Usage: "(flags) Customized flag for kube-cloud-controller-manager process", - Value: &ServerConfig.ExtraCloudControllerArgs, - }, - cli.StringFlag{ - Name: "datastore-endpoint", - Usage: "(db) Specify etcd, Mysql, Postgres, or Sqlite (default) data source name", - Destination: &ServerConfig.DatastoreEndpoint, - EnvVar: version.ProgramUpper + "_DATASTORE_ENDPOINT", - }, - cli.StringFlag{ - Name: "datastore-cafile", - Usage: "(db) TLS Certificate Authority file used to secure datastore backend communication", - Destination: &ServerConfig.DatastoreCAFile, - EnvVar: version.ProgramUpper + "_DATASTORE_CAFILE", - }, - cli.StringFlag{ - Name: "datastore-certfile", - Usage: "(db) TLS certification file used to secure datastore backend communication", - Destination: &ServerConfig.DatastoreCertFile, - EnvVar: version.ProgramUpper + "_DATASTORE_CERTFILE", - }, - cli.StringFlag{ - Name: "datastore-keyfile", - Usage: "(db) TLS key file used to secure datastore backend communication", - Destination: &ServerConfig.DatastoreKeyFile, - EnvVar: version.ProgramUpper + "_DATASTORE_KEYFILE", - }, - &cli.BoolFlag{ - Name: "etcd-expose-metrics", - Usage: "(db) Expose etcd metrics to client interface. (Default false)", - Destination: &ServerConfig.EtcdExposeMetrics, - }, - &cli.BoolFlag{ - Name: "etcd-disable-snapshots", - Usage: "(db) Disable automatic etcd snapshots", - Destination: &ServerConfig.EtcdDisableSnapshots, - }, - &cli.StringFlag{ - Name: "etcd-snapshot-name", - Usage: "(db) Set the base name of etcd snapshots. Default: etcd-snapshot-", - Destination: &ServerConfig.EtcdSnapshotName, - Value: "etcd-snapshot", - }, - &cli.StringFlag{ - Name: "etcd-snapshot-schedule-cron", - Usage: "(db) Snapshot interval time in cron spec. eg. every 5 hours '* */5 * * *'", - Destination: &ServerConfig.EtcdSnapshotCron, - Value: "0 */12 * * *", - }, - &cli.IntFlag{ - Name: "etcd-snapshot-retention", - Usage: "(db) Number of snapshots to retain", - Destination: &ServerConfig.EtcdSnapshotRetention, - Value: defaultSnapshotRentention, - }, - &cli.StringFlag{ - Name: "etcd-snapshot-dir", - Usage: "(db) Directory to save db snapshots. (Default location: ${data-dir}/db/snapshots)", - Destination: &ServerConfig.EtcdSnapshotDir, - }, - &cli.BoolFlag{ - Name: "etcd-s3", - Usage: "(db) Enable backup to S3", - Destination: &ServerConfig.EtcdS3, - }, - &cli.StringFlag{ - Name: "etcd-s3-endpoint", - Usage: "(db) S3 endpoint url", - Destination: &ServerConfig.EtcdS3Endpoint, - Value: "s3.amazonaws.com", - }, - &cli.StringFlag{ - Name: "etcd-s3-endpoint-ca", - Usage: "(db) S3 custom CA cert to connect to S3 endpoint", - Destination: &ServerConfig.EtcdS3EndpointCA, - }, - &cli.BoolFlag{ - Name: "etcd-s3-skip-ssl-verify", - Usage: "(db) Disables S3 SSL certificate validation", - Destination: &ServerConfig.EtcdS3SkipSSLVerify, - }, - &cli.StringFlag{ - Name: "etcd-s3-access-key", - Usage: "(db) S3 access key", - EnvVar: "AWS_ACCESS_KEY_ID", - Destination: &ServerConfig.EtcdS3AccessKey, - }, - &cli.StringFlag{ - Name: "etcd-s3-secret-key", - Usage: "(db) S3 secret key", - EnvVar: "AWS_SECRET_ACCESS_KEY", - Destination: &ServerConfig.EtcdS3SecretKey, - }, - &cli.StringFlag{ - Name: "etcd-s3-bucket", - Usage: "(db) S3 bucket name", - Destination: &ServerConfig.EtcdS3BucketName, - }, - &cli.StringFlag{ - Name: "etcd-s3-region", - Usage: "(db) S3 region / bucket location (optional)", - Destination: &ServerConfig.EtcdS3Region, - Value: "us-east-1", - }, - &cli.StringFlag{ - Name: "etcd-s3-folder", - Usage: "(db) S3 folder", - Destination: &ServerConfig.EtcdS3Folder, - }, - &cli.BoolFlag{ - Name: "etcd-s3-insecure", - Usage: "(db) Disables S3 over HTTPS", - Destination: &ServerConfig.EtcdS3Insecure, - }, - &cli.DurationFlag{ - Name: "etcd-s3-timeout", - Usage: "(db) S3 timeout", - Destination: &ServerConfig.EtcdS3Timeout, - Value: 30 * time.Second, - }, - cli.StringFlag{ - Name: "default-local-storage-path", - Usage: "(storage) Default local storage path for local provisioner storage class", - Destination: &ServerConfig.DefaultLocalStoragePath, - }, - cli.StringSliceFlag{ - Name: "disable", - Usage: "(components) Do not deploy packaged components and delete any deployed components (valid items: " + DisableItems + ")", - }, - cli.BoolFlag{ - Name: "disable-scheduler", - Usage: "(components) Disable Kubernetes default scheduler", - Destination: &ServerConfig.DisableScheduler, - }, - cli.BoolFlag{ - Name: "disable-cloud-controller", - Usage: "(components) Disable " + version.Program + " default cloud controller manager", - Destination: &ServerConfig.DisableCCM, - }, - cli.BoolFlag{ - Name: "disable-kube-proxy", - Usage: "(components) Disable running kube-proxy", - Destination: &ServerConfig.DisableKubeProxy, - }, - cli.BoolFlag{ - Name: "disable-network-policy", - Usage: "(components) Disable " + version.Program + " default network policy controller", - Destination: &ServerConfig.DisableNPC, - }, - cli.BoolFlag{ - Name: "disable-helm-controller", - Usage: "(components) Disable Helm controller", - Destination: &ServerConfig.DisableHelmController, - }, - cli.BoolFlag{ - Name: "disable-apiserver", - Hidden: true, - Usage: "(experimental/components) Disable running api server", - Destination: &ServerConfig.DisableAPIServer, - }, - cli.BoolFlag{ - Name: "disable-controller-manager", - Hidden: true, - Usage: "(experimental/components) Disable running kube-controller-manager", - Destination: &ServerConfig.DisableControllerManager, - }, - cli.BoolFlag{ - Name: "disable-etcd", - Hidden: true, - Usage: "(experimental/components) Disable running etcd", - Destination: &ServerConfig.DisableETCD, - }, - NodeNameFlag, - WithNodeIDFlag, - NodeLabels, - NodeTaints, - ImageCredProvBinDirFlag, - ImageCredProvConfigFlag, - DockerFlag, - CRIEndpointFlag, - PauseImageFlag, - SnapshotterFlag, - PrivateRegistryFlag, - AirgapExtraRegistryFlag, - NodeIPFlag, - NodeExternalIPFlag, - ResolvConfFlag, - FlannelIfaceFlag, - FlannelConfFlag, - ExtraKubeletArgs, - ExtraKubeProxyArgs, - ProtectKernelDefaultsFlag, - cli.BoolFlag{ - Name: "rootless", - Usage: "(experimental) Run rootless", - Destination: &ServerConfig.Rootless, - }, - cli.StringFlag{ - Name: "agent-token", - Usage: "(cluster) Shared secret used to join agents to the cluster, but not servers", - Destination: &ServerConfig.AgentToken, - EnvVar: version.ProgramUpper + "_AGENT_TOKEN", - }, - cli.StringFlag{ - Name: "agent-token-file", - Usage: "(cluster) File containing the agent secret", - Destination: &ServerConfig.AgentTokenFile, - EnvVar: version.ProgramUpper + "_AGENT_TOKEN_FILE", - }, - cli.StringFlag{ - Name: "server,s", - Usage: "(cluster) Server to connect to, used to join a cluster", - EnvVar: version.ProgramUpper + "_URL", - Destination: &ServerConfig.ServerURL, - }, - cli.BoolFlag{ - Name: "cluster-init", - Usage: "(cluster) Initialize a new cluster using embedded Etcd", - EnvVar: version.ProgramUpper + "_CLUSTER_INIT", - Destination: &ServerConfig.ClusterInit, - }, - cli.BoolFlag{ - Name: "cluster-reset", - Usage: "(cluster) Forget all peers and become sole member of a new cluster", - EnvVar: version.ProgramUpper + "_CLUSTER_RESET", - Destination: &ServerConfig.ClusterReset, - }, - &cli.StringFlag{ - Name: "cluster-reset-restore-path", - Usage: "(db) Path to snapshot file to be restored", - Destination: &ServerConfig.ClusterResetRestorePath, - }, - cli.BoolFlag{ - Name: "secrets-encryption", - Usage: "(experimental) Enable Secret encryption at rest", - Destination: &ServerConfig.EncryptSecrets, - }, - cli.StringFlag{ - Name: "system-default-registry", - Usage: "(image) Private registry to be used for all system images", - EnvVar: version.ProgramUpper + "_SYSTEM_DEFAULT_REGISTRY", - Destination: &ServerConfig.SystemDefaultRegistry, - }, - &SELinuxFlag, - LBServerPortFlag, - - // Hidden/Deprecated flags below - - &DisableSELinuxFlag, - FlannelFlag, - cli.StringSliceFlag{ - Name: "no-deploy", - Usage: "(deprecated) Do not deploy packaged components (valid items: " + DisableItems + ")", - }, - cli.StringFlag{ - Name: "cluster-secret", - Usage: "(deprecated) use --token", - Destination: &ServerConfig.ClusterSecret, - EnvVar: version.ProgramUpper + "_CLUSTER_SECRET", - }, - cli.BoolFlag{ - Name: "disable-agent", - Usage: "Do not run a local agent and register a local kubelet", - Hidden: true, - Destination: &ServerConfig.DisableAgent, - }, - cli.StringSliceFlag{ - Hidden: true, - Name: "kube-controller-arg", - Usage: "(flags) Customized flag for kube-controller-manager process", - Value: &ServerConfig.ExtraControllerArgs, - }, - cli.StringSliceFlag{ - Hidden: true, - Name: "kube-cloud-controller-arg", - Usage: "(flags) Customized flag for kube-cloud-controller-manager process", - Value: &ServerConfig.ExtraCloudControllerArgs, - }, - }, + Flags: ServerFlags, } } diff --git a/pkg/configfilearg/defaultparser.go b/pkg/configfilearg/defaultparser.go index 3b17b95115..5e4f5c2402 100644 --- a/pkg/configfilearg/defaultparser.go +++ b/pkg/configfilearg/defaultparser.go @@ -1,18 +1,22 @@ package configfilearg import ( + "github.com/rancher/k3s/pkg/cli/cmds" "github.com/rancher/k3s/pkg/version" "github.com/sirupsen/logrus" + "github.com/urfave/cli" ) +var defaultParser = &Parser{ + After: []string{"server", "agent", "etcd-snapshot:1"}, + FlagNames: []string{"--config", "-c"}, + EnvName: version.ProgramUpper + "_CONFIG_FILE", + DefaultConfig: "/etc/rancher/" + version.Program + "/config.yaml", + ValidFlags: map[string][]cli.Flag{"server": cmds.ServerFlags, "etcd-snapshot": cmds.EtcdSnapshotFlags}, +} + func MustParse(args []string) []string { - parser := &Parser{ - After: []string{"server", "agent", "etcd-snapshot"}, - FlagNames: []string{"--config", "-c"}, - EnvName: version.ProgramUpper + "_CONFIG_FILE", - DefaultConfig: "/etc/rancher/" + version.Program + "/config.yaml", - } - result, err := parser.Parse(args) + result, err := defaultParser.Parse(args) if err != nil { logrus.Fatal(err) } diff --git a/pkg/configfilearg/defaultparser_test.go b/pkg/configfilearg/defaultparser_test.go new file mode 100644 index 0000000000..afb270a6a0 --- /dev/null +++ b/pkg/configfilearg/defaultparser_test.go @@ -0,0 +1,74 @@ +package configfilearg + +import ( + "reflect" + "testing" +) + +func Test_UnitMustParse(t *testing.T) { + tests := []struct { + name string + args []string + config string + want []string + }{ + { + name: "Basic server", + args: []string{"k3s", "server"}, + + want: []string{"k3s", "server"}, + }, + { + name: "Server with known flags", + args: []string{"k3s", "server", "-t 12345", "--write-kubeconfig-mode 644"}, + + want: []string{"k3s", "server", "-t 12345", "--write-kubeconfig-mode 644"}, + }, + { + name: "Server with known flags and config with known and unknown flags", + args: []string{"k3s", "server", "--write-kubeconfig-mode 644"}, + config: "./testdata/defaultdata.yaml", + want: []string{"k3s", "server", "--token=12345", "--node-label=DEAFBEEF", + "--etcd-s3=true", "--etcd-s3-bucket=my-backup", "--write-kubeconfig-mode 644"}, + }, + { + name: "Basic etcd-snapshot", + args: []string{"k3s", "etcd-snapshot", "save"}, + + want: []string{"k3s", "etcd-snapshot", "save"}, + }, + { + name: "Etcd-snapshot with known flags", + args: []string{"k3s", "etcd-snapshot", "save", "--s3=true"}, + + want: []string{"k3s", "etcd-snapshot", "save", "--s3=true"}, + }, + { + name: "Etcd-snapshot with config with known and unknown flags", + args: []string{"k3s", "etcd-snapshot", "save"}, + config: "./testdata/defaultdata.yaml", + want: []string{"k3s", "etcd-snapshot", "save", "--etcd-s3=true", "--etcd-s3-bucket=my-backup"}, + }, + { + name: "Agent with known flags", + args: []string{"k3s", "agent", "--token=12345"}, + + want: []string{"k3s", "agent", "--token=12345"}, + }, + { + name: "Agent with config with known and unknown flags, flags are not skipped", + args: []string{"k3s", "agent"}, + config: "./testdata/defaultdata.yaml", + want: []string{"k3s", "agent", "--token=12345", "--node-label=DEAFBEEF", + "--etcd-s3=true", "--etcd-s3-bucket=my-backup", "--notaflag=true"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defaultParser.DefaultConfig = tt.config + if got := MustParse(tt.args); !reflect.DeepEqual(got, tt.want) { + t.Errorf("MustParse() = %+v\nWant = %+v", got, tt.want) + } + }) + } +} diff --git a/pkg/configfilearg/parser.go b/pkg/configfilearg/parser.go index 7994ca37ac..181369ab1e 100644 --- a/pkg/configfilearg/parser.go +++ b/pkg/configfilearg/parser.go @@ -7,10 +7,14 @@ import ( "net/url" "os" "path/filepath" + "regexp" + "strconv" "strings" "github.com/rancher/k3s/pkg/agent/util" "github.com/rancher/wrangler/pkg/data/convert" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" "gopkg.in/yaml.v2" ) @@ -19,6 +23,7 @@ type Parser struct { FlagNames []string EnvName string DefaultConfig string + ValidFlags map[string][]cli.Flag } // Parse will parse an os.Args style slice looking for Parser.FlagNames after Parse.After. @@ -42,12 +47,55 @@ func (p *Parser) Parse(args []string) ([]string, error) { } else if err != nil { return nil, err } + if len(args) > 1 { + values, err = p.stripInvalidFlags(args[1], values) + if err != nil { + return nil, err + } + } return append(prefix, append(values, suffix...)...), nil } return args, nil } +func (p *Parser) stripInvalidFlags(command string, args []string) ([]string, error) { + var result []string + var cmdFlags []cli.Flag + for k, v := range p.ValidFlags { + if k == command { + cmdFlags = v + } + } + if len(cmdFlags) == 0 { + return args, nil + } + validFlags := make(map[string]bool, len(cmdFlags)) + for _, f := range cmdFlags { + //split flags with aliases into 2 entries + for _, s := range strings.Split(f.GetName(), ",") { + validFlags[s] = true + } + } + + re, err := regexp.Compile("^-+(.+)=") + if err != nil { + return args, err + } + for _, arg := range args { + mArg := arg + if match := re.FindAllStringSubmatch(arg, -1); match != nil { + mArg = match[0][1] + } + if validFlags[mArg] { + result = append(result, arg) + } else { + logrus.Warnf("Unknown flag %s found in config.yaml, skipping\n", arg) + } + } + return result, nil +} + func (p *Parser) FindString(args []string, target string) (string, error) { configFile, isSet := p.findConfigFileFlag(args) if configFile != "" { @@ -100,15 +148,37 @@ func (p *Parser) findStart(args []string) ([]string, []string, bool) { if len(p.After) == 0 { return []string{}, args, true } - - for i, val := range args { - for _, test := range p.After { - if val == test { - return args[0 : i+1], args[i+1:], true + afterTemp := append([]string{}, p.After...) + afterIndex := make(map[string]int) + re, err := regexp.Compile(`(.+):(\d+)`) + if err != nil { + return args, nil, false + } + // After keywords ending with ":" can set + NUM of arguments as the split point. + // used for matching on subcommmands + for i, arg := range afterTemp { + if match := re.FindAllStringSubmatch(arg, -1); match != nil { + afterTemp[i] = match[0][1] + afterIndex[match[0][1]], err = strconv.Atoi(match[0][2]) + if err != nil { + return args, nil, false } } } + for i, val := range args { + for _, test := range afterTemp { + if val == test { + if skip := afterIndex[test]; skip != 0 { + if len(args) <= i+skip || strings.HasPrefix(args[i+skip], "-") { + return args[0 : i+1], args[i+1:], true + } + return args[0 : i+skip+1], args[i+skip+1:], true + } + return args[0 : i+1], args[i+1:], true + } + } + } return args, nil, false } diff --git a/pkg/configfilearg/parser_test.go b/pkg/configfilearg/parser_test.go index 18128be350..776d43774f 100644 --- a/pkg/configfilearg/parser_test.go +++ b/pkg/configfilearg/parser_test.go @@ -48,11 +48,53 @@ func Test_UnitParser_findStart(t *testing.T) { prefix: []string{"not-server", "foo", "bar"}, found: false, }, + { + name: "command (with optional subcommands) but no flags", + args: []string{"etcd-snapshot"}, + prefix: []string{"etcd-snapshot"}, + suffix: []string{}, + found: true, + }, + { + name: "command (with optional subcommands) and flags", + args: []string{"etcd-snapshot", "-f"}, + prefix: []string{"etcd-snapshot"}, + suffix: []string{"-f"}, + found: true, + }, + { + name: "command and subcommand with no flags", + args: []string{"etcd-snapshot", "list"}, + prefix: []string{"etcd-snapshot", "list"}, + suffix: []string{}, + found: true, + }, + { + name: "command and subcommand with flags", + args: []string{"etcd-snapshot", "list", "-f"}, + prefix: []string{"etcd-snapshot", "list"}, + suffix: []string{"-f"}, + found: true, + }, + { + name: "another command and subcommand with flags", + args: []string{"etcd-snapshot", "save", "--s3"}, + prefix: []string{"etcd-snapshot", "save"}, + suffix: []string{"--s3"}, + found: true, + }, + { + name: "command and too many subcommands", + args: []string{"etcd-snapshot", "list", "delete", "foo", "bar"}, + prefix: []string{"etcd-snapshot", "list"}, + suffix: []string{"delete", "foo", "bar"}, + found: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { p := Parser{ - After: []string{"server", "agent"}, + After: []string{"server", "agent", "etcd-snapshot:1"}, } prefix, suffix, found := p.findStart(tt.args) if !reflect.DeepEqual(prefix, tt.prefix) { diff --git a/pkg/configfilearg/testdata/defaultdata.yaml b/pkg/configfilearg/testdata/defaultdata.yaml new file mode 100644 index 0000000000..18c068740f --- /dev/null +++ b/pkg/configfilearg/testdata/defaultdata.yaml @@ -0,0 +1,5 @@ +token: 12345 +node-label: DEAFBEEF +etcd-s3: true +etcd-s3-bucket: my-backup +notaflag : true \ No newline at end of file