diff --git a/contrib/completions/bash/openshift b/contrib/completions/bash/openshift index 6363955429d8..8d4d344998ea 100644 --- a/contrib/completions/bash/openshift +++ b/contrib/completions/bash/openshift @@ -33708,6 +33708,8 @@ _openshift_start_network() local_nonpersistent_flags+=("--kubernetes=") flags+=("--latest-images") local_nonpersistent_flags+=("--latest-images") + flags+=("--listen=") + local_nonpersistent_flags+=("--listen=") flags+=("--network-plugin=") local_nonpersistent_flags+=("--network-plugin=") flags+=("--recursive-resolv-conf=") diff --git a/contrib/completions/zsh/openshift b/contrib/completions/zsh/openshift index efa981b7272a..c09ebc7ae163 100644 --- a/contrib/completions/zsh/openshift +++ b/contrib/completions/zsh/openshift @@ -33857,6 +33857,8 @@ _openshift_start_network() local_nonpersistent_flags+=("--kubernetes=") flags+=("--latest-images") local_nonpersistent_flags+=("--latest-images") + flags+=("--listen=") + local_nonpersistent_flags+=("--listen=") flags+=("--network-plugin=") local_nonpersistent_flags+=("--network-plugin=") flags+=("--recursive-resolv-conf=") diff --git a/pkg/cmd/server/api/helpers.go b/pkg/cmd/server/api/helpers.go index 3b37e587a10d..c19c69b8d706 100644 --- a/pkg/cmd/server/api/helpers.go +++ b/pkg/cmd/server/api/helpers.go @@ -334,6 +334,27 @@ func SetProtobufClientDefaults(overrides *ClientConnectionOverrides) { overrides.Burst *= 2 } +// GetKubeConfigOrInClusterConfig loads in-cluster config if kubeConfigFile is empty or the file if not, +// then applies overrides. +func GetKubeConfigOrInClusterConfig(kubeConfigFile string, overrides *ClientConnectionOverrides) (*restclient.Config, error) { + var kubeConfig *restclient.Config + var err error + if len(kubeConfigFile) == 0 { + kubeConfig, err = restclient.InClusterConfig() + } else { + loadingRules := &clientcmd.ClientConfigLoadingRules{} + loadingRules.ExplicitPath = kubeConfigFile + loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{}) + + kubeConfig, err = loader.ClientConfig() + } + if err != nil { + return nil, err + } + applyClientConnectionOverrides(overrides, kubeConfig) + return kubeConfig, nil +} + // TODO: clients should be copied and instantiated from a common client config, tweaked, then // given to individual controllers and other infrastructure components. func GetInternalKubeClient(kubeConfigFile string, overrides *ClientConnectionOverrides) (kclientsetinternal.Interface, *restclient.Config, error) { diff --git a/pkg/cmd/server/api/validation/validation.go b/pkg/cmd/server/api/validation/validation.go index 3999f6b562d6..15829806422d 100644 --- a/pkg/cmd/server/api/validation/validation.go +++ b/pkg/cmd/server/api/validation/validation.go @@ -128,7 +128,7 @@ func ValidateServingInfo(info api.ServingInfo, certificatesRequired bool, fldPat validationResults.AddErrors(ValidateFile(info.ClientCA, fldPath.Child("clientCA"))...) } } else { - if len(info.ClientCA) > 0 { + if certificatesRequired && len(info.ClientCA) > 0 { validationResults.AddErrors(field.Invalid(fldPath.Child("clientCA"), info.ClientCA, "cannot specify a clientCA without a certFile")) } } diff --git a/pkg/cmd/server/kubernetes/network/network_config.go b/pkg/cmd/server/kubernetes/network/network_config.go index 3c56730bd88f..e6fc4a277a5f 100644 --- a/pkg/cmd/server/kubernetes/network/network_config.go +++ b/pkg/cmd/server/kubernetes/network/network_config.go @@ -4,13 +4,12 @@ import ( "fmt" "net" - "github.com/golang/glog" - miekgdns "github.com/miekg/dns" kclientset "k8s.io/client-go/kubernetes" "k8s.io/kubernetes/pkg/apis/componentconfig" kclientsetexternal "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" + kclientsetinternal "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" kinternalinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" configapi "github.com/openshift/origin/pkg/cmd/server/api" @@ -45,11 +44,15 @@ type NetworkConfig struct { // New creates a new network config object for running the networking components of the OpenShift node. func New(options configapi.NodeConfig, clusterDomain string, proxyConfig *componentconfig.KubeProxyConfiguration, enableProxy, enableDNS bool) (*NetworkConfig, error) { - internalKubeClient, kubeConfig, err := configapi.GetInternalKubeClient(options.MasterKubeConfig, options.MasterClientConnectionOverrides) + kubeConfig, err := configapi.GetKubeConfigOrInClusterConfig(options.MasterKubeConfig, options.MasterClientConnectionOverrides) + if err != nil { + return nil, err + } + internalKubeClient, err := kclientsetinternal.NewForConfig(kubeConfig) if err != nil { return nil, err } - externalKubeClient, _, err := configapi.GetExternalKubeClient(options.MasterKubeConfig, options.MasterClientConnectionOverrides) + externalKubeClient, err := kclientsetexternal.NewForConfig(kubeConfig) if err != nil { return nil, err } @@ -127,7 +130,6 @@ func New(options configapi.NodeConfig, clusterDomain string, proxyConfig *compon // TODO: use kubeletConfig.ResolverConfig as an argument to etcd in the event the // user sets it, instead of passing it to the kubelet. - glog.Infof("DNS Bind to %s", options.DNSBindAddress) config.DNSServer = dns.NewServer( dnsConfig, services, diff --git a/pkg/cmd/server/kubernetes/network/options/options.go b/pkg/cmd/server/kubernetes/network/options/options.go new file mode 100644 index 000000000000..1faafe41eca7 --- /dev/null +++ b/pkg/cmd/server/kubernetes/network/options/options.go @@ -0,0 +1,89 @@ +package node + +import ( + "fmt" + "net" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kerrors "k8s.io/apimachinery/pkg/util/errors" + kubeproxyoptions "k8s.io/kubernetes/cmd/kube-proxy/app" + "k8s.io/kubernetes/pkg/apis/componentconfig" + + configapi "github.com/openshift/origin/pkg/cmd/server/api" + cmdflags "github.com/openshift/origin/pkg/cmd/util/flags" +) + +// Build creates the network Kubernetes component configs for a given NodeConfig, or returns +// an error +func Build(options configapi.NodeConfig) (*componentconfig.KubeProxyConfiguration, error) { + proxyOptions, err := kubeproxyoptions.NewOptions() + if err != nil { + return nil, err + } + // get default config + proxyconfig := proxyOptions.GetConfig() + + proxyconfig.HostnameOverride = options.NodeName + + // BindAddress - Override default bind address from our config + addr := options.ServingInfo.BindAddress + host, _, err := net.SplitHostPort(addr) + if err != nil { + return nil, fmt.Errorf("The provided value to bind to must be an ip:port %q", addr) + } + ip := net.ParseIP(host) + if ip == nil { + return nil, fmt.Errorf("The provided value to bind to must be an ip:port: %q", addr) + } + proxyconfig.BindAddress = ip.String() + // MetricsBindAddress - disable by default but allow enablement until we switch to + // reading proxy config directly + proxyconfig.MetricsBindAddress = "" + if arg := options.ProxyArguments["metrics-bind-address"]; len(arg) > 0 { + proxyconfig.MetricsBindAddress = arg[0] + } + delete(options.ProxyArguments, "metrics-bind-address") + + // OOMScoreAdj, ResourceContainer - clear, we don't run in a container + oomScoreAdj := int32(0) + proxyconfig.OOMScoreAdj = &oomScoreAdj + proxyconfig.ResourceContainer = "" + + // use the same client as the node + proxyconfig.ClientConnection.KubeConfigFile = options.MasterKubeConfig + + // ProxyMode, set to iptables + proxyconfig.Mode = "iptables" + + // IptablesSyncPeriod, set to our config value + syncPeriod, err := time.ParseDuration(options.IPTablesSyncPeriod) + if err != nil { + return nil, fmt.Errorf("Cannot parse the provided ip-tables sync period (%s) : %v", options.IPTablesSyncPeriod, err) + } + proxyconfig.IPTables.SyncPeriod = metav1.Duration{ + Duration: syncPeriod, + } + masqueradeBit := int32(0) + proxyconfig.IPTables.MasqueradeBit = &masqueradeBit + + // PortRange, use default + // HostnameOverride, use default + // ConfigSyncPeriod, use default + // MasqueradeAll, use default + // CleanupAndExit, use default + // KubeAPIQPS, use default, doesn't apply until we build a separate client + // KubeAPIBurst, use default, doesn't apply until we build a separate client + // UDPIdleTimeout, use default + + // Resolve cmd flags to add any user overrides + if err := cmdflags.Resolve(options.ProxyArguments, proxyOptions.AddFlags); len(err) > 0 { + return nil, kerrors.NewAggregate(err) + } + + if err := proxyOptions.Complete(); err != nil { + return nil, err + } + + return proxyconfig, nil +} diff --git a/pkg/cmd/server/kubernetes/node/node_config.go b/pkg/cmd/server/kubernetes/node/node_config.go index 5ed343f967c7..26dcb4df8e1f 100644 --- a/pkg/cmd/server/kubernetes/node/node_config.go +++ b/pkg/cmd/server/kubernetes/node/node_config.go @@ -12,6 +12,7 @@ import ( kubeletapp "k8s.io/kubernetes/cmd/kubelet/app" kubeletoptions "k8s.io/kubernetes/cmd/kubelet/app/options" "k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1" + kclientsetexternal "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" "k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/kubelet" dockertools "k8s.io/kubernetes/pkg/kubelet/dockershim/libdocker" @@ -57,7 +58,7 @@ func New(options configapi.NodeConfig, server *kubeletoptions.KubeletServer) (*N return nil, err } // Make a separate client for event reporting, to avoid event QPS blocking node calls - eventClient, _, err := configapi.GetExternalKubeClient(options.MasterKubeConfig, options.MasterClientConnectionOverrides) + eventClient, err := kclientsetexternal.NewForConfig(kubeConfig) if err != nil { return nil, err } diff --git a/pkg/cmd/server/kubernetes/node/options/options.go b/pkg/cmd/server/kubernetes/node/options/options.go index f526ba3bf0f0..3a9794967fda 100644 --- a/pkg/cmd/server/kubernetes/node/options/options.go +++ b/pkg/cmd/server/kubernetes/node/options/options.go @@ -9,7 +9,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kerrors "k8s.io/apimachinery/pkg/util/errors" utilfeature "k8s.io/apiserver/pkg/util/feature" - kubeproxyoptions "k8s.io/kubernetes/cmd/kube-proxy/app" kubeletoptions "k8s.io/kubernetes/cmd/kubelet/app/options" "k8s.io/kubernetes/pkg/apis/componentconfig" "k8s.io/kubernetes/pkg/features" @@ -25,7 +24,7 @@ import ( // Build creates the core Kubernetes component configs for a given NodeConfig, or returns // an error -func Build(options configapi.NodeConfig) (*kubeletoptions.KubeletServer, *componentconfig.KubeProxyConfiguration, error) { +func Build(options configapi.NodeConfig) (*kubeletoptions.KubeletServer, error) { imageTemplate := variable.NewDefaultImageTemplate() imageTemplate.Format = options.ImageConfig.Format imageTemplate.Latest = options.ImageConfig.Latest @@ -39,11 +38,11 @@ func Build(options configapi.NodeConfig) (*kubeletoptions.KubeletServer, *compon kubeAddressStr, kubePortStr, err := net.SplitHostPort(options.ServingInfo.BindAddress) if err != nil { - return nil, nil, fmt.Errorf("cannot parse node address: %v", err) + return nil, fmt.Errorf("cannot parse node address: %v", err) } kubePort, err := strconv.Atoi(kubePortStr) if err != nil { - return nil, nil, fmt.Errorf("cannot parse node port: %v", err) + return nil, fmt.Errorf("cannot parse node port: %v", err) } // Defaults are tested in TestKubeletDefaults @@ -91,7 +90,7 @@ func Build(options configapi.NodeConfig) (*kubeletoptions.KubeletServer, *compon // Setup auth authnTTL, err := time.ParseDuration(options.AuthConfig.AuthenticationCacheTTL) if err != nil { - return nil, nil, err + return nil, err } server.Authentication = componentconfig.KubeletAuthentication{ X509: componentconfig.KubeletX509Authentication{ @@ -107,7 +106,7 @@ func Build(options configapi.NodeConfig) (*kubeletoptions.KubeletServer, *compon } authzTTL, err := time.ParseDuration(options.AuthConfig.AuthorizationCacheTTL) if err != nil { - return nil, nil, err + return nil, err } server.Authorization = componentconfig.KubeletAuthorization{ Mode: componentconfig.KubeletAuthorizationModeWebhook, @@ -121,13 +120,13 @@ func Build(options configapi.NodeConfig) (*kubeletoptions.KubeletServer, *compon // TODO: this should be done in config validation (along with the above) so we can provide // proper errors if err := cmdflags.Resolve(options.KubeletArguments, server.AddFlags); len(err) > 0 { - return nil, nil, kerrors.NewAggregate(err) + return nil, kerrors.NewAggregate(err) } // terminate early if feature gate is incorrect on the node if len(server.FeatureGates) > 0 { if err := utilfeature.DefaultFeatureGate.Set(server.FeatureGates); err != nil { - return nil, nil, err + return nil, err } } if utilfeature.DefaultFeatureGate.Enabled(features.RotateKubeletServerCertificate) { @@ -138,11 +137,6 @@ func Build(options configapi.NodeConfig) (*kubeletoptions.KubeletServer, *compon } } - proxyconfig, err := buildKubeProxyConfig(options) - if err != nil { - return nil, nil, err - } - if network.IsOpenShiftNetworkPlugin(options.NetworkConfig.NetworkPluginName) { // SDN plugin pod setup/teardown is implemented as a CNI plugin server.NetworkPluginName = kubeletcni.CNIPluginName @@ -152,72 +146,7 @@ func Build(options configapi.NodeConfig) (*kubeletoptions.KubeletServer, *compon server.HairpinMode = componentconfig.HairpinNone } - return server, proxyconfig, nil -} - -func buildKubeProxyConfig(options configapi.NodeConfig) (*componentconfig.KubeProxyConfiguration, error) { - proxyOptions, err := kubeproxyoptions.NewOptions() - if err != nil { - return nil, err - } - // get default config - proxyconfig := proxyOptions.GetConfig() - - // BindAddress - Override default bind address from our config - addr := options.ServingInfo.BindAddress - host, _, err := net.SplitHostPort(addr) - if err != nil { - return nil, fmt.Errorf("The provided value to bind to must be an ip:port %q", addr) - } - ip := net.ParseIP(host) - if ip == nil { - return nil, fmt.Errorf("The provided value to bind to must be an ip:port: %q", addr) - } - proxyconfig.BindAddress = ip.String() - // MetricsBindAddress - disable - proxyconfig.MetricsBindAddress = "" - - // OOMScoreAdj, ResourceContainer - clear, we don't run in a container - oomScoreAdj := int32(0) - proxyconfig.OOMScoreAdj = &oomScoreAdj - proxyconfig.ResourceContainer = "" - - // use the same client as the node - proxyconfig.ClientConnection.KubeConfigFile = options.MasterKubeConfig - - // ProxyMode, set to iptables - proxyconfig.Mode = "iptables" - - // IptablesSyncPeriod, set to our config value - syncPeriod, err := time.ParseDuration(options.IPTablesSyncPeriod) - if err != nil { - return nil, fmt.Errorf("Cannot parse the provided ip-tables sync period (%s) : %v", options.IPTablesSyncPeriod, err) - } - proxyconfig.IPTables.SyncPeriod = metav1.Duration{ - Duration: syncPeriod, - } - masqueradeBit := int32(0) - proxyconfig.IPTables.MasqueradeBit = &masqueradeBit - - // PortRange, use default - // HostnameOverride, use default - // ConfigSyncPeriod, use default - // MasqueradeAll, use default - // CleanupAndExit, use default - // KubeAPIQPS, use default, doesn't apply until we build a separate client - // KubeAPIBurst, use default, doesn't apply until we build a separate client - // UDPIdleTimeout, use default - - // Resolve cmd flags to add any user overrides - if err := cmdflags.Resolve(options.ProxyArguments, proxyOptions.AddFlags); len(err) > 0 { - return nil, kerrors.NewAggregate(err) - } - - if err := proxyOptions.Complete(); err != nil { - return nil, err - } - - return proxyconfig, nil + return server, nil } func ToFlags(config *kubeletoptions.KubeletServer) []string { diff --git a/pkg/cmd/server/start/start_node.go b/pkg/cmd/server/start/start_node.go index 4373818586e1..510881b38e16 100644 --- a/pkg/cmd/server/start/start_node.go +++ b/pkg/cmd/server/start/start_node.go @@ -29,6 +29,7 @@ import ( "github.com/openshift/origin/pkg/cmd/server/api/validation" "github.com/openshift/origin/pkg/cmd/server/crypto" "github.com/openshift/origin/pkg/cmd/server/kubernetes/network" + networkoptions "github.com/openshift/origin/pkg/cmd/server/kubernetes/network/options" "github.com/openshift/origin/pkg/cmd/server/kubernetes/node" nodeoptions "github.com/openshift/origin/pkg/cmd/server/kubernetes/node/options" cmdutil "github.com/openshift/origin/pkg/cmd/util" @@ -129,6 +130,7 @@ func NewCommandStartNetwork(basename string, out, errout io.Writer) (*cobra.Comm options.NodeArgs.ListenArg.ListenAddr.DefaultPort = ports.ProxyHealthzPort options.NodeArgs.Components = NewNetworkComponentFlag() BindNodeNetworkArgs(options.NodeArgs, flags, "") + BindListenArg(options.NodeArgs.ListenArg, flags, "") BindImageFormatArgs(options.NodeArgs.ImageFormatArgs, flags, "") BindKubeConnectionArgs(options.NodeArgs.KubeConnectionArgs, flags, "") @@ -219,7 +221,23 @@ func (o NodeOptions) RunNode() error { return err } - validationResults := validation.ValidateNodeConfig(nodeConfig, nil) + // allow listen address to be overriden + if addr := o.NodeArgs.ListenArg.ListenAddr; addr.Provided { + nodeConfig.ServingInfo.BindAddress = addr.HostPort(o.NodeArgs.ListenArg.ListenAddr.DefaultPort) + } + + var validationResults validation.ValidationResults + switch { + case o.NodeArgs.Components.Calculated().Equal(NewNetworkComponentFlag().Calculated()): + if len(nodeConfig.NodeName) == 0 { + nodeConfig.NodeName = o.NodeArgs.NodeName + } + nodeConfig.MasterKubeConfig = o.NodeArgs.KubeConnectionArgs.ClientConfigLoadingRules.ExplicitPath + validationResults = validation.ValidateInClusterNodeConfig(nodeConfig, nil) + default: + validationResults = validation.ValidateNodeConfig(nodeConfig, nil) + } + if len(validationResults.Warnings) != 0 { for _, warning := range validationResults.Warnings { glog.Warningf("Warning: %v, node start will continue.", warning) @@ -231,6 +249,7 @@ func (o NodeOptions) RunNode() error { } if err := ValidateRuntime(nodeConfig, o.NodeArgs.Components); err != nil { + glog.V(4).Infof("Unable to validate runtime configuration: %v", err) return err } @@ -412,8 +431,9 @@ func execKubelet(server *kubeletoptions.KubeletServer) (bool, error) { } func StartNode(nodeConfig configapi.NodeConfig, components *utilflags.ComponentFlag) error { - server, proxyConfig, err := nodeoptions.Build(nodeConfig) + server, err := nodeoptions.Build(nodeConfig) if err != nil { + glog.V(4).Infof("Unable to build node options: %v", err) return err } @@ -429,33 +449,34 @@ func StartNode(nodeConfig configapi.NodeConfig, components *utilflags.ComponentF } } - networkConfig, err := network.New(nodeConfig, server.ClusterDomain, proxyConfig, components.Enabled(ComponentProxy), components.Enabled(ComponentDNS) && len(nodeConfig.DNSBindAddress) > 0) + proxyConfig, err := networkoptions.Build(nodeConfig) if err != nil { + glog.V(4).Infof("Unable to build network options: %v", err) return err } - - config, err := node.New(nodeConfig, server) + networkConfig, err := network.New(nodeConfig, server.ClusterDomain, proxyConfig, components.Enabled(ComponentProxy), components.Enabled(ComponentDNS) && len(nodeConfig.DNSBindAddress) > 0) if err != nil { + glog.V(4).Infof("Unable to initialize network configuration: %v", err) return err } if components.Enabled(ComponentKubelet) { + config, err := node.New(nodeConfig, server) + if err != nil { + glog.V(4).Infof("Unable to create node configuration: %v", err) + return err + } glog.Infof("Starting node %s (%s)", config.KubeletServer.HostnameOverride, version.Get().String()) - } else { - glog.Infof("Starting node networking %s (%s)", config.KubeletServer.HostnameOverride, version.Get().String()) - } - // preconditions - if components.Enabled(ComponentKubelet) { config.EnsureKubeletAccess() config.EnsureVolumeDir() config.EnsureDocker(docker.NewHelper()) config.EnsureLocalQuota(nodeConfig) // must be performed after EnsureVolumeDir - } - - if components.Enabled(ComponentKubelet) { config.RunKubelet() + } else { + glog.Infof("Starting node networking %s (%s)", nodeConfig.NodeName, version.Get().String()) } + if components.Enabled(ComponentPlugins) { networkConfig.RunSDN() }