From 5ec02bd0212871bdec60d47082c01ec9afdad553 Mon Sep 17 00:00:00 2001 From: Mike Danese Date: Mon, 1 Feb 2016 18:09:02 -0800 Subject: [PATCH] add http handler to export configuration state --- .../app/controllermanager.go | 6 ++ cmd/kube-proxy/app/server.go | 7 ++ cmd/kubelet/app/server.go | 6 ++ pkg/kubelet/server/server.go | 3 + pkg/util/configz/configz.go | 86 +++++++++++++++++++ pkg/util/configz/configz_test.go | 78 +++++++++++++++++ plugin/cmd/kube-scheduler/app/server.go | 7 ++ 7 files changed, 193 insertions(+) create mode 100644 pkg/util/configz/configz.go create mode 100644 pkg/util/configz/configz_test.go diff --git a/cmd/kube-controller-manager/app/controllermanager.go b/cmd/kube-controller-manager/app/controllermanager.go index 7c0963672d..69e5a27001 100644 --- a/cmd/kube-controller-manager/app/controllermanager.go +++ b/cmd/kube-controller-manager/app/controllermanager.go @@ -62,6 +62,7 @@ import ( "k8s.io/kubernetes/pkg/healthz" "k8s.io/kubernetes/pkg/serviceaccount" "k8s.io/kubernetes/pkg/util" + "k8s.io/kubernetes/pkg/util/configz" "k8s.io/kubernetes/pkg/util/wait" "github.com/golang/glog" @@ -100,6 +101,11 @@ func ResyncPeriod(s *options.CMServer) func() time.Duration { // Run runs the CMServer. This should never exit. func Run(s *options.CMServer) error { + if c, err := configz.New("componentconfig"); err == nil { + c.Set(s.KubeControllerManagerConfiguration) + } else { + glog.Errorf("unable to register configz: %s", err) + } kubeconfig, err := clientcmd.BuildConfigFromFlags(s.Master, s.Kubeconfig) if err != nil { return err diff --git a/cmd/kube-proxy/app/server.go b/cmd/kube-proxy/app/server.go index b46df3f0a3..9481a506b9 100644 --- a/cmd/kube-proxy/app/server.go +++ b/cmd/kube-proxy/app/server.go @@ -39,6 +39,7 @@ import ( "k8s.io/kubernetes/pkg/proxy/userspace" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util" + "k8s.io/kubernetes/pkg/util/configz" utildbus "k8s.io/kubernetes/pkg/util/dbus" "k8s.io/kubernetes/pkg/util/exec" utiliptables "k8s.io/kubernetes/pkg/util/iptables" @@ -122,6 +123,11 @@ with the apiserver API to configure the proxy.`, // NewProxyServerDefault creates a new ProxyServer object with default parameters. func NewProxyServerDefault(config *options.ProxyServerConfig) (*ProxyServer, error) { + if c, err := configz.New("componentconfig"); err == nil { + c.Set(config.KubeProxyConfiguration) + } else { + glog.Errorf("unable to register configz: %s", err) + } protocol := utiliptables.ProtocolIpv4 if net.ParseIP(config.BindAddress).To4() == nil { protocol = utiliptables.ProtocolIpv6 @@ -280,6 +286,7 @@ func (s *ProxyServer) Run() error { http.HandleFunc("/proxyMode", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "%s", s.ProxyMode) }) + configz.InstallHandler(http.DefaultServeMux) go wait.Until(func() { err := http.ListenAndServe(s.Config.HealthzBindAddress+":"+strconv.Itoa(s.Config.HealthzPort), nil) if err != nil { diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index cb49bee9b8..7714a4e2fc 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -58,6 +58,7 @@ import ( "k8s.io/kubernetes/pkg/kubelet/server" kubetypes "k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/util" + "k8s.io/kubernetes/pkg/util/configz" "k8s.io/kubernetes/pkg/util/flock" "k8s.io/kubernetes/pkg/util/io" "k8s.io/kubernetes/pkg/util/mount" @@ -277,6 +278,11 @@ func run(s *options.KubeletServer, kcfg *KubeletConfig) (err error) { return fmt.Errorf("unable to aquire file lock on %q: %v", s.LockFilePath, err) } } + if c, err := configz.New("componentconfig"); err == nil { + c.Set(s.KubeletConfiguration) + } else { + glog.Errorf("unable to register configz: %s", err) + } if kcfg == nil { cfg, err := UnsecuredKubeletConfig(s) if err != nil { diff --git a/pkg/kubelet/server/server.go b/pkg/kubelet/server/server.go index 88b7ee9f5f..8246417d87 100644 --- a/pkg/kubelet/server/server.go +++ b/pkg/kubelet/server/server.go @@ -52,6 +52,7 @@ import ( "k8s.io/kubernetes/pkg/kubelet/server/stats" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/types" + "k8s.io/kubernetes/pkg/util/configz" "k8s.io/kubernetes/pkg/util/flushwriter" "k8s.io/kubernetes/pkg/util/httpstream" "k8s.io/kubernetes/pkg/util/httpstream/spdy" @@ -328,6 +329,8 @@ func (s *Server) InstallDebuggingHandlers() { Operation("getContainerLogs")) s.restfulCont.Add(ws) + configz.InstallHandler(s.restfulCont) + handlePprofEndpoint := func(req *restful.Request, resp *restful.Response) { name := strings.TrimPrefix(req.Request.URL.Path, pprofBasePath) switch name { diff --git a/pkg/util/configz/configz.go b/pkg/util/configz/configz.go new file mode 100644 index 0000000000..e8954a4a8f --- /dev/null +++ b/pkg/util/configz/configz.go @@ -0,0 +1,86 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package configz + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "sync" +) + +var ( + configsGuard sync.RWMutex + configs = map[string]*Config{} +) + +type Config struct { + val interface{} +} + +func InstallHandler(m mux) { + m.Handle("/configz", http.HandlerFunc(handle)) +} + +type mux interface { + Handle(string, http.Handler) +} + +func New(name string) (*Config, error) { + configsGuard.Lock() + defer configsGuard.Unlock() + if _, found := configs[name]; found { + return nil, fmt.Errorf("register config %q twice", name) + } + newConfig := Config{} + configs[name] = &newConfig + return &newConfig, nil +} + +func Delete(name string) { + configsGuard.Lock() + defer configsGuard.Unlock() + delete(configs, name) +} + +func (v *Config) Set(val interface{}) { + configsGuard.Lock() + defer configsGuard.Unlock() + v.val = val +} + +func (v *Config) MarshalJSON() ([]byte, error) { + return json.Marshal(v.val) +} + +func handle(w http.ResponseWriter, r *http.Request) { + if err := write(w); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + +func write(w io.Writer) error { + configsGuard.Lock() + defer configsGuard.Unlock() + b, err := json.Marshal(configs) + if err != nil { + return fmt.Errorf("error marshaling json: %v", err) + } + _, err = w.Write(b) + return err +} diff --git a/pkg/util/configz/configz_test.go b/pkg/util/configz/configz_test.go new file mode 100644 index 0000000000..e540d031a3 --- /dev/null +++ b/pkg/util/configz/configz_test.go @@ -0,0 +1,78 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package configz + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" +) + +func TestConfigz(t *testing.T) { + v, err := New("testing") + if err != nil { + t.Fatalf("err: %v", err) + } + + v.Set("blah") + + s := httptest.NewServer(http.HandlerFunc(handle)) + // TODO: Uncomment when fix #19254 + // defer s.Close() + + resp, err := http.Get(s.URL + "/configz") + if err != nil { + t.Fatalf("err: %v", err) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("err: %v", err) + } + if string(body) != `{"testing":"blah"}` { + t.Fatalf("unexpected output: %v", err) + } + + v.Set("bing") + resp, err = http.Get(s.URL + "/configz") + if err != nil { + t.Fatalf("err: %v", err) + } + + body, err = ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("err: %v", err) + } + if string(body) != `{"testing":"bing"}` { + t.Fatalf("unexpected output: %v", err) + } + + Delete("testing") + resp, err = http.Get(s.URL + "/configz") + if err != nil { + t.Fatalf("err: %v", err) + } + + body, err = ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("err: %v", err) + } + if string(body) != `{}` { + t.Fatalf("unexpected output: %v", err) + } +} diff --git a/plugin/cmd/kube-scheduler/app/server.go b/plugin/cmd/kube-scheduler/app/server.go index 5c35b8e13f..2d796ae068 100644 --- a/plugin/cmd/kube-scheduler/app/server.go +++ b/plugin/cmd/kube-scheduler/app/server.go @@ -33,6 +33,7 @@ import ( "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" "k8s.io/kubernetes/pkg/healthz" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/configz" "k8s.io/kubernetes/plugin/cmd/kube-scheduler/app/options" "k8s.io/kubernetes/plugin/pkg/scheduler" _ "k8s.io/kubernetes/plugin/pkg/scheduler/algorithmprovider" @@ -68,6 +69,11 @@ through the API as necessary.`, // Run runs the specified SchedulerServer. This should never exit. func Run(s *options.SchedulerServer) error { + if c, err := configz.New("componentconfig"); err == nil { + c.Set(s.KubeSchedulerConfiguration) + } else { + glog.Errorf("unable to register configz: %s", err) + } kubeconfig, err := clientcmd.BuildConfigFromFlags(s.Master, s.Kubeconfig) if err != nil { return err @@ -90,6 +96,7 @@ func Run(s *options.SchedulerServer) error { mux.HandleFunc("/debug/pprof/profile", pprof.Profile) mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) } + configz.InstallHandler(mux) mux.Handle("/metrics", prometheus.Handler()) server := &http.Server{