diff --git a/cmd/kubelet/app/options/options.go b/cmd/kubelet/app/options/options.go index dc16e64a54b..7ecffc79cc9 100644 --- a/cmd/kubelet/app/options/options.go +++ b/cmd/kubelet/app/options/options.go @@ -84,8 +84,7 @@ type KubeletServer struct { NetworkPluginDir string NetworkPluginName string VolumePluginDir string - NodeLabels []string - NodeLabelsFile string + NodeLabels string NodeStatusUpdateFrequency time.Duration OOMScoreAdj int PodCIDR string @@ -165,8 +164,7 @@ func NewKubeletServer() *KubeletServer { NetworkPluginDir: "/usr/libexec/kubernetes/kubelet-plugins/net/exec/", NetworkPluginName: "", VolumePluginDir: "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/", - NodeLabels: []string{}, - NodeLabelsFile: "", + NodeLabels: "", NodeStatusUpdateFrequency: 10 * time.Second, OOMScoreAdj: qos.KubeletOOMScoreAdj, PodInfraContainerImage: kubetypes.PodInfraContainerImage, @@ -241,8 +239,7 @@ func (s *KubeletServer) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&s.MasterServiceNamespace, "master-service-namespace", s.MasterServiceNamespace, "The namespace from which the kubernetes master services should be injected into pods") fs.IPVar(&s.ClusterDNS, "cluster-dns", s.ClusterDNS, "IP address for a cluster DNS server. If set, kubelet will configure all containers to use this for DNS resolution in addition to the host's DNS servers") fs.DurationVar(&s.StreamingConnectionIdleTimeout, "streaming-connection-idle-timeout", s.StreamingConnectionIdleTimeout, "Maximum time a streaming connection can be idle before the connection is automatically closed. Example: '5m'") - fs.StringSliceVar(&s.NodeLabels, "node-label", []string{}, "add labels when registering the node in the cluster, the flag can be used multiple times (key=value)") - fs.StringVar(&s.NodeLabelsFile, "node-labels-file", "", "the path to a yaml or json file containing a series of key pair labels to apply on node registration") + fs.StringVar(&s.NodeLabels, "node-labels", s.NodeLabels, " Labels to add when registering the node in the cluster. Labels must be specified as a json map of key:value pairs.") fs.DurationVar(&s.NodeStatusUpdateFrequency, "node-status-update-frequency", s.NodeStatusUpdateFrequency, "Specifies how often kubelet posts node status to master. Note: be cautious when changing the constant, it must work with nodeMonitorGracePeriod in nodecontroller. Default: 10s") fs.IntVar(&s.ImageGCHighThresholdPercent, "image-gc-high-threshold", s.ImageGCHighThresholdPercent, "The percent of disk usage after which image garbage collection is always run. Default: 90%") fs.IntVar(&s.ImageGCLowThresholdPercent, "image-gc-low-threshold", s.ImageGCLowThresholdPercent, "The percent of disk usage before which image garbage collection is never run. Lowest disk usage to garbage collect to. Default: 80%") diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index f486213ac8d..6a49bce62bc 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -215,7 +215,6 @@ func UnsecuredKubeletConfig(s *options.KubeletServer) (*KubeletConfig, error) { NetworkPluginName: s.NetworkPluginName, NetworkPlugins: ProbeNetworkPlugins(s.NetworkPluginDir), NodeLabels: s.NodeLabels, - NodeLabelsFile: s.NodeLabelsFile, NodeStatusUpdateFrequency: s.NodeStatusUpdateFrequency, OOMAdjuster: oom.NewOOMAdjuster(), OSInterface: kubecontainer.RealOS{}, @@ -692,8 +691,7 @@ type KubeletConfig struct { NetworkPluginName string NetworkPlugins []network.NetworkPlugin NodeName string - NodeLabels []string - NodeLabelsFile string + NodeLabels string NodeStatusUpdateFrequency time.Duration OOMAdjuster *oom.OOMAdjuster OSInterface kubecontainer.OSInterface @@ -783,7 +781,6 @@ func CreateAndInitKubelet(kc *KubeletConfig) (k KubeletBootstrap, pc *config.Pod kc.DiskSpacePolicy, kc.Cloud, kc.NodeLabels, - kc.NodeLabelsFile, kc.NodeStatusUpdateFrequency, kc.ResourceContainer, kc.OSInterface, diff --git a/docs/admin/kubelet.md b/docs/admin/kubelet.md index 5ad25b3a78f..a124abed684 100644 --- a/docs/admin/kubelet.md +++ b/docs/admin/kubelet.md @@ -114,8 +114,7 @@ kubelet --network-plugin="": The name of the network plugin to be invoked for various events in kubelet/pod lifecycle --network-plugin-dir="/usr/libexec/kubernetes/kubelet-plugins/net/exec/": The full path of the directory in which to search for network plugins --node-ip=: IP address of the node. If set, kubelet will use this IP address for the node - --node-label=[]: add labels when registering the node in the cluster, the flag can be used multiple times (key=value) - --node-labels-file="": the path to a yaml or json file containing a series of key pair labels to apply on node registration + --node-labels="": Labels to add when registering the node in the cluster. Labels must be specified as a json map of key:value pairs. --node-status-update-frequency=10s: Specifies how often kubelet posts node status to master. Note: be cautious when changing the constant, it must work with nodeMonitorGracePeriod in nodecontroller. Default: 10s --oom-score-adj=-999: The oom-score-adj value for kubelet process. Values must be within the range [-1000, 1000] --outofdisk-transition-frequency=5m0s: Duration for which the kubelet has to wait before transitioning out of out-of-disk node condition status. Default: 5m0s @@ -144,7 +143,7 @@ kubelet --volume-plugin-dir="/usr/libexec/kubernetes/kubelet-plugins/volume/exec/": The full path of the directory in which to search for additional third party volume plugins ``` -###### Auto generated by spf13/cobra on 29-Dec-2015 +###### Auto generated by spf13/cobra on 11-Jan-2016 diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index 3f23c22ad82..e79f74d1a6d 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -225,8 +225,7 @@ no-headers no-suggestions node-instance-group node-ip -node-label -node-labels-file +node-labels node-monitor-grace-period node-monitor-period node-name diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index 84823f984ea..8dce92e8331 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -17,7 +17,6 @@ limitations under the License. package kubelet import ( - "bufio" "bytes" "errors" "fmt" @@ -33,6 +32,7 @@ import ( "sync" "time" + "encoding/json" "github.com/golang/glog" cadvisorapi "github.com/google/cadvisor/info/v1" "k8s.io/kubernetes/pkg/api" @@ -168,8 +168,7 @@ func NewMainKubelet( imageGCPolicy ImageGCPolicy, diskSpacePolicy DiskSpacePolicy, cloud cloudprovider.Interface, - nodeLabels []string, - nodeLabelsFile string, + nodeLabels string, nodeStatusUpdateFrequency time.Duration, resourceContainer string, osInterface kubecontainer.OSInterface, @@ -292,7 +291,6 @@ func NewMainKubelet( cloud: cloud, nodeRef: nodeRef, nodeLabels: nodeLabels, - nodeLabelsFile: nodeLabelsFile, nodeStatusUpdateFrequency: nodeStatusUpdateFrequency, resourceContainer: resourceContainer, os: osInterface, @@ -512,10 +510,7 @@ type Kubelet struct { nodeInfo predicates.NodeInfo // a list of node labels to register - nodeLabels []string - - // the path to a yaml or json file container series of node labels - nodeLabelsFile string + nodeLabels string // Last timestamp when runtime responded on ping. // Mutex is used to protect this value. @@ -1025,77 +1020,23 @@ func (kl *Kubelet) initialNodeStatus() (*api.Node, error) { return node, nil } -// getNodeLabels is just a wrapper method for the two below, not to duplicate above +// getNodeLabels extracts the node labels specified on the command line func (kl *Kubelet) getNodeLabels() (map[string]string, error) { - var err error labels := make(map[string]string, 0) - - if kl.nodeLabelsFile != "" { - labels, err = kl.retrieveNodeLabelsFile(kl.nodeLabelsFile) - if err != nil { - return labels, err - } - } - // step: apply the command line label - permitted to override those from file - if len(kl.nodeLabels) > 0 { - nl, err := kl.retrieveNodeLabels(kl.nodeLabels) - if err != nil { - return labels, err - } - for k, v := range nl { - if vl, found := labels[k]; found { - glog.Warningf("the --node-label %s=%s option will overwrite %s from node-labels-file", k, v, vl) - } - labels[k] = v - } + if kl.nodeLabels == "" { + return labels, nil } + rawLabels := make(map[string]json.Number, 0) - return labels, nil -} - -// retrieveNodeLabels extracts the node labels specified on the command line -func (kl *Kubelet) retrieveNodeLabels(labels []string) (map[string]string, error) { - nodeLabels := make(map[string]string, 0) - - for _, label := range labels { - items := strings.Split(label, "=") - if len(items) != 2 { - return nodeLabels, fmt.Errorf("--node-label %s, should be in the form key=pair", label) - } - nodeLabels[strings.TrimSpace(items[0])] = strings.TrimSpace(items[1]) - } - - return nodeLabels, nil -} - -// retrieveNodeLabelsFile reads in and parses the yaml or json node labels file -func (kl *Kubelet) retrieveNodeLabelsFile(path string) (map[string]string, error) { - labels := make(map[string]string, 0) - kps := make(map[string]interface{}, 0) - - fd, err := os.Open(path) + err := yaml.NewYAMLOrJSONDecoder(strings.NewReader(kl.nodeLabels), 12).Decode(&rawLabels) if err != nil { - return nil, err - } - defer fd.Close() - - err = yaml.NewYAMLOrJSONDecoder(bufio.NewReader(fd), 12).Decode(&kps) - if err != nil { - return nil, fmt.Errorf("the --node-labels-file %s content is invalid, %s", path, err) + return nil, fmt.Errorf("the --node-labels content '%s' is invalid, %s", kl.nodeLabels, err) } - for k, v := range kps { - // we ONLY accept key=value pairs, no complex types - switch v.(type) { - case string: - labels[k] = v.(string) - case float64: - labels[k] = fmt.Sprintf("%d", v.(float64)) - default: - return nil, fmt.Errorf("--node-labels-file only supports key:string, not complex values e.g arrays, maps") - } + // Parse the labels + for k, v := range rawLabels { + labels[k] = v.String() } - return labels, nil } diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index 282a8f83c1e..34431ed4ee1 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -3701,41 +3701,42 @@ func TestGetNodeLabels(t *testing.T) { kubelet := newTestKubelet(t).kubelet testCases := []struct { - Expecting map[string]string - LabelOptions []string - FileContent string - Ok bool + Expecting map[string]string + NodeLabels string + Ok bool }{ { - Ok: true, - Expecting: map[string]string{"key1": "pair1", "key2": "pair2", "key3": "pair3", "key4": "pair4", "key5": "pair5"}, - LabelOptions: []string{"key5=pair5"}, - FileContent: `--- -key1: pair1 -key2: pair2 -key3: pair3 -key4: pair4 -`, - }, { - Ok: true, - Expecting: map[string]string{"key1": "pair1", "key2": "override"}, - LabelOptions: []string{"key2=override"}, - FileContent: `--- -key1: pair1 -key2: pair2 -`, + Expecting: map[string]string{"key1": "value1", "key2": "2", "key3": "3.3"}, + NodeLabels: `--- +key1: value1 +key2: 2 +key3: 3.3`, + Ok: true, + }, + { + Expecting: nil, + NodeLabels: `--- +key11: value11 +key12: 12 +key13: 13.3 +key14: + nested_key: nested_value`, + Ok: false, + }, + { + Expecting: map[string]string{"key21": "value21", "key22": "22", "key23": "23.3"}, + NodeLabels: `{"key21": "value21", "key22": "22", "key23": "23.3"}`, + Ok: true, + }, + { + Expecting: nil, + NodeLabels: `{"key31": "value31", "key32": "32", "key33": "33.3", "key34": {"nested_key": "nested_value"}}`, + Ok: false, }, } for i, test := range testCases { - fd := createTestNodeLabelFile(t, test.FileContent) - defer func(f *os.File) { - os.Remove(f.Name()) - }(fd) - - kubelet.nodeLabels = test.LabelOptions - kubelet.nodeLabelsFile = fd.Name() - + kubelet.nodeLabels = test.NodeLabels list, err := kubelet.getNodeLabels() if test.Ok && err != nil { t.Errorf("test case %d should not have failed, error: %s", i, err) @@ -3746,127 +3747,6 @@ key2: pair2 } } -func TestRetrieveNodeLabels(t *testing.T) { - kubelet := newTestKubelet(t).kubelet - - testCases := []struct { - Expecting map[string]string - LabelOptions []string - Ok bool - }{ - { - Expecting: map[string]string{"key1": "pair1", "key2": "pair2", "key3": "pair3", "key4": "pair4"}, - LabelOptions: []string{"key1=pair1", "key2=pair2", "key3=pair3", "key4=pair4"}, - Ok: true, - }, - { - Expecting: map[string]string{"key1": "pair1"}, - LabelOptions: []string{"key1=pair1", "key2paiwdsr2"}, - }, - } - - for i, test := range testCases { - list, err := kubelet.retrieveNodeLabels(test.LabelOptions) - if test.Ok && err != nil { - t.Errorf("test case %d should not have failed, error: %s", i, err) - } - if !reflect.DeepEqual(test.Expecting, list) { - t.Errorf("test case %d are not the same, %v ~ %v", i, list, test.Expecting) - } - } -} - -func TestRetrieveNodeLabelsFile(t *testing.T) { - kubelet := newTestKubelet(t).kubelet - - testCases := []struct { - Expecting map[string]string - Ok bool - FileContent string - }{ - { - Expecting: map[string]string{"key1": "pair1", "key2": "pair2", "key3": "pair3", "key4": "pair4"}, - Ok: true, - FileContent: `--- -key1: pair1 -key2: pair2 -key3: pair3 -key4: pair4`, - }, { - FileContent: `--- -key1: pair1 -hash_map: - key2: pair2 -`, - }, { - Expecting: map[string]string{"key1": "pair1", "key2": "pair2"}, - Ok: true, - FileContent: ` - -key1: pair1 -key2: pair2 -`, - }, { - FileContent: `--- -key1: pair1 -bad_key_pair -`, - }, { - Expecting: nil, - FileContent: `{ - "key1": "pair1", - "key2": "pair2", - "key3": "pair3", - "key4": { - "some_key": "some_value" - } -}`, - }, { - FileContent: "", - }, { - Expecting: map[string]string{"key1": "pair1", "key2": "pair2", "key3": "pair3", "key4": "pair4"}, - Ok: true, - FileContent: `--- -key1: pair1 -key2: pair2 -key3: pair3 -key4: pair4 -`, - }, - } - - for i, test := range testCases { - fd := createTestNodeLabelFile(t, test.FileContent) - defer func(f *os.File) { - os.Remove(f.Name()) - }(fd) - - labels, err := kubelet.retrieveNodeLabelsFile(fd.Name()) - if test.Ok && err != nil { - t.Errorf("test case %d should not have returned an error, %s", i, err) - continue - } - - if test.Expecting != nil && !reflect.DeepEqual(test.Expecting, labels) { - t.Errorf("test case %d not as expected, got: %#v, expecting: %#v", i, labels, test.Expecting) - } - } -} - -func createTestNodeLabelFile(t *testing.T, content string) *os.File { - f, err := ioutil.TempFile("", "node_label_file") - if err != nil { - t.Fatalf("unexpected error creating node_label_file: %v", err) - } - f.Close() - - if err := ioutil.WriteFile(f.Name(), []byte(content), 0700); err != nil { - t.Fatalf("unexpected error writing node label file: %v", err) - } - - return f -} - func TestMakePortMappings(t *testing.T) { tests := []struct { container *api.Container