HTRACE-277: htraced: Add /server/conf endpoint to get server configuration (cmccabe)
diff --git a/htrace-htraced/go/src/org/apache/htrace/client/client.go b/htrace-htraced/go/src/org/apache/htrace/client/client.go
index dd3f8a3..dd8597d 100644
--- a/htrace-htraced/go/src/org/apache/htrace/client/client.go
+++ b/htrace-htraced/go/src/org/apache/htrace/client/client.go
@@ -82,6 +82,21 @@
 	return &stats, nil
 }
 
+// Get the htraced server statistics.
+func (hcl *Client) GetServerConf() (map[string]string, error) {
+	buf, _, err := hcl.makeGetRequest("server/conf")
+	if err != nil {
+		return nil, err
+	}
+	cnf := make(map[string]string)
+	err = json.Unmarshal(buf, &cnf)
+	if err != nil {
+		return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+
+			"body %s: %s", string(buf), err.Error()))
+	}
+	return cnf, nil
+}
+
 // Get information about a trace span.  Returns nil, nil if the span was not found.
 func (hcl *Client) FindSpan(sid common.SpanId) (*common.Span, error) {
 	buf, rc, err := hcl.makeGetRequest(fmt.Sprintf("span/%s", sid.String()))
diff --git a/htrace-htraced/go/src/org/apache/htrace/conf/config.go b/htrace-htraced/go/src/org/apache/htrace/conf/config.go
index 76af7a6..d038723 100644
--- a/htrace-htraced/go/src/org/apache/htrace/conf/config.go
+++ b/htrace-htraced/go/src/org/apache/htrace/conf/config.go
@@ -266,3 +266,15 @@
 	}
 	return ncnf
 }
+
+// Export the configuration as a map
+func (cnf *Config) Export() map[string]string {
+	m := make(map[string]string)
+	for k, v := range cnf.defaults {
+		m[k] = v
+	}
+	for k, v := range cnf.settings {
+		m[k] = v
+	}
+	return m
+}
diff --git a/htrace-htraced/go/src/org/apache/htrace/htrace/cmd.go b/htrace-htraced/go/src/org/apache/htrace/htrace/cmd.go
index b5549c5..f6972bb 100644
--- a/htrace-htraced/go/src/org/apache/htrace/htrace/cmd.go
+++ b/htrace-htraced/go/src/org/apache/htrace/htrace/cmd.go
@@ -65,6 +65,7 @@
 	serverInfo := app.Command("serverInfo", "Print information retrieved from an htraced server.")
 	serverStats := app.Command("serverStats", "Print statistics retrieved from the htraced server.")
 	serverStatsJson := serverStats.Flag("json", "Display statistics as raw JSON.").Default("false").Bool()
+	serverConf := app.Command("serverConf", "Print the server configuration retrieved from the htraced server.")
 	findSpan := app.Command("findSpan", "Print information about a trace span with a given ID.")
 	findSpanId := findSpan.Arg("id", "Span ID to find. Example: be305e54-4534-2110-a0b2-e06b9effe112").Required().String()
 	findChildren := app.Command("findChildren", "Print out the span IDs that are children of a given span ID.")
@@ -131,6 +132,8 @@
 		} else {
 			os.Exit(printServerStats(hcl))
 		}
+	case serverConf.FullCommand():
+		os.Exit(printServerConfJson(hcl))
 	case findSpan.FullCommand():
 		var id *common.SpanId
 		id.FromString(*findSpanId)
@@ -221,6 +224,22 @@
 	return EXIT_SUCCESS
 }
 
+// Print information retrieved from an htraced server via /server/conf as JSON
+func printServerConfJson(hcl *htrace.Client) int {
+	cnf, err := hcl.GetServerConf()
+	if err != nil {
+		fmt.Println(err.Error())
+		return EXIT_FAILURE
+	}
+	buf, err := json.MarshalIndent(cnf, "", "  ")
+	if err != nil {
+		fmt.Printf("Error marshalling server conf: %s", err.Error())
+		return EXIT_FAILURE
+	}
+	fmt.Printf("%s\n", string(buf))
+	return EXIT_SUCCESS
+}
+
 // Print information about a trace span.
 func doFindSpan(hcl *htrace.Client, sid common.SpanId) int {
 	span, err := hcl.FindSpan(sid)
diff --git a/htrace-htraced/go/src/org/apache/htrace/htraced/client_test.go b/htrace-htraced/go/src/org/apache/htrace/htraced/client_test.go
index 02a00f3..540e688 100644
--- a/htrace-htraced/go/src/org/apache/htrace/htraced/client_test.go
+++ b/htrace-htraced/go/src/org/apache/htrace/htraced/client_test.go
@@ -204,3 +204,32 @@
 		t.Fatalf("got dump error %s\n", dumpErr.Error())
 	}
 }
+
+const EXAMPLE_CONF_KEY = "example.conf.key"
+const EXAMPLE_CONF_VALUE = "foo.bar.baz"
+
+func TestClientGetServerConf(t *testing.T) {
+	htraceBld := &MiniHTracedBuilder{Name: "TestClientGetServerConf",
+		Cnf: map[string]string {
+			EXAMPLE_CONF_KEY: EXAMPLE_CONF_VALUE,
+		},
+		DataDirs: make([]string, 2)}
+	ht, err := htraceBld.Build()
+	if err != nil {
+		t.Fatalf("failed to create datastore: %s", err.Error())
+	}
+	defer ht.Close()
+	var hcl *htrace.Client
+	hcl, err = htrace.NewClient(ht.ClientConf())
+	if err != nil {
+		t.Fatalf("failed to create client: %s", err.Error())
+	}
+	serverCnf, err2 := hcl.GetServerConf()
+	if err2 != nil {
+		t.Fatalf("failed to call GetServerConf: %s", err2.Error())
+	}
+	if serverCnf[EXAMPLE_CONF_KEY] != EXAMPLE_CONF_VALUE {
+		t.Fatalf("unexpected value for %s: %s",
+				EXAMPLE_CONF_KEY, EXAMPLE_CONF_VALUE)
+	}
+}
diff --git a/htrace-htraced/go/src/org/apache/htrace/htraced/rest.go b/htrace-htraced/go/src/org/apache/htrace/htraced/rest.go
index c038e59..16c3a75 100644
--- a/htrace-htraced/go/src/org/apache/htrace/htraced/rest.go
+++ b/htrace-htraced/go/src/org/apache/htrace/htraced/rest.go
@@ -88,6 +88,25 @@
 	w.Write(buf)
 }
 
+type serverConfHandler struct {
+	cnf *conf.Config
+	lg *common.Logger
+}
+
+func (hand *serverConfHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	setResponseHeaders(w.Header())
+	hand.lg.Debugf("serverConfHandler\n")
+	cnfMap := hand.cnf.Export()
+	buf, err := json.Marshal(&cnfMap)
+	if err != nil {
+		writeError(hand.lg, w, http.StatusInternalServerError,
+			fmt.Sprintf("error marshalling serverConf: %s\n", err.Error()))
+		return
+	}
+	hand.lg.Debugf("Returned server configuration %s\n", string(buf))
+	w.Write(buf)
+}
+
 type dataStoreHandler struct {
 	lg    *common.Logger
 	store *dataStore
@@ -295,6 +314,9 @@
 		store: store, lg: rsv.lg}}
 	r.Handle("/server/stats", serverStatsH).Methods("GET")
 
+	serverConfH := &serverConfHandler{cnf: cnf, lg: rsv.lg}
+	r.Handle("/server/conf", serverConfH).Methods("GET")
+
 	writeSpansH := &writeSpansHandler{dataStoreHandler: dataStoreHandler{
 		store: store, lg: rsv.lg}}
 	r.Handle("/writeSpans", writeSpansH).Methods("POST")