internal/graphconfig: rename to chartconfig, and display at /config

This CL contains a few changes to improve the visibility and coherency
of the "graph config":
- rename to "chart config", since elsewhere we speak of telemetry.go.dev
  as hosting charts, and "graph" is an overloaded term
- move the config.txt file into the chartconfig package, and expose it
  via embedding
- display the chart config at the /config endpoint, since it is easier
  to read than the upload config
- link to the current config in the overview of telemetry.go.dev

Fixes golang/go#63418

Change-Id: Ia3cef815cb18b0b78f9f7c4d2e8fc82297e82f75
Reviewed-on: https://go-review.googlesource.com/c/telemetry/+/572335
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/cmd/gotelemetry/internal/view/view.go b/cmd/gotelemetry/internal/view/view.go
index 4b29f97..25232d7 100644
--- a/cmd/gotelemetry/internal/view/view.go
+++ b/cmd/gotelemetry/internal/view/view.go
@@ -3,7 +3,7 @@
 // license that can be found in the LICENSE file.
 
 // The view command is a server intended to be run on a user's machine to
-// display the local counters and time series graphs of counters.
+// display the local counters and time series charts of counters.
 package view
 
 import (
diff --git a/cmd/gotelemetry/internal/view/view_test.go b/cmd/gotelemetry/internal/view/view_test.go
index 1f4410c..b35395a 100644
--- a/cmd/gotelemetry/internal/view/view_test.go
+++ b/cmd/gotelemetry/internal/view/view_test.go
@@ -3,7 +3,7 @@
 // license that can be found in the LICENSE file.
 
 // The view command is a server intended to be run on a users machine to
-// display the local counters and time series graphs of counters.
+// display the local counters and time series charts of counters.
 package view
 
 import (
diff --git a/godev/cmd/telemetrygodev/main.go b/godev/cmd/telemetrygodev/main.go
index be74d45..47411de 100644
--- a/godev/cmd/telemetrygodev/main.go
+++ b/godev/cmd/telemetrygodev/main.go
@@ -28,6 +28,7 @@
 	ilog "golang.org/x/telemetry/godev/internal/log"
 	"golang.org/x/telemetry/godev/internal/middleware"
 	"golang.org/x/telemetry/godev/internal/storage"
+	"golang.org/x/telemetry/internal/chartconfig"
 	tconfig "golang.org/x/telemetry/internal/config"
 	contentfs "golang.org/x/telemetry/internal/content"
 	"golang.org/x/telemetry/internal/telemetry"
@@ -230,6 +231,7 @@
 }
 
 func handleConfig(fsys fs.FS, ucfg *tconfig.Config) content.HandlerFunc {
+	ccfg := chartconfig.Raw()
 	cfg := ucfg.UploadConfig
 	version := "default"
 
@@ -240,10 +242,12 @@
 		}
 		data := struct {
 			Version      string
-			PrettyConfig string
+			ChartConfig  string
+			UploadConfig string
 		}{
 			Version:      version,
-			PrettyConfig: string(cfgJSON),
+			ChartConfig:  string(ccfg),
+			UploadConfig: string(cfgJSON),
 		}
 		return content.Template(w, fsys, "config.html", data, http.StatusOK)
 	}
diff --git a/godev/cmd/worker/main.go b/godev/cmd/worker/main.go
index cf2f840..81cb70b 100644
--- a/godev/cmd/worker/main.go
+++ b/godev/cmd/worker/main.go
@@ -283,7 +283,7 @@
 		)
 		for _, c := range p.Counters {
 			// TODO: add support for histogram counters by getting the counter type
-			// from the graph config.
+			// from the chart config.
 			prog.Charts = append(prog.Charts,
 				partition(d, p.Name, c.Name, tconfig.Expand(c.Name), xs),
 			)
diff --git a/internal/graphconfig/graphconfig.go b/internal/chartconfig/chartconfig.go
similarity index 73%
rename from internal/graphconfig/graphconfig.go
rename to internal/chartconfig/chartconfig.go
index abab80b..6d5a11d 100644
--- a/internal/graphconfig/graphconfig.go
+++ b/internal/chartconfig/chartconfig.go
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// The graphconfig package defines the GraphConfig type, representing telemetry
-// graph configuration, as well as utilities for parsing and validating this
+// The chartconfig package defines the ChartConfig type, representing telemetry
+// chart configuration, as well as utilities for parsing and validating this
 // configuration.
 //
-// Graph configuration defines the set of aggregations active on the telemetry
+// Chart configuration defines the set of aggregations active on the telemetry
 // server, and are used to derive which data needs to be uploaded by users.
 // See the original blog post for more details:
 //
@@ -24,20 +24,20 @@
 // The following keys are supported. Any entry not marked as (optional) must be
 // provided.
 //
-//   - title: the graph title.
-//   - description: (optional) a longer description of the graph.
-//   - issue: a go issue tracker URL proposing the graph configuration.
+//   - title: the chart title.
+//   - description: (optional) a longer description of the chart.
+//   - issue: a go issue tracker URL proposing the chart configuration.
 //     Multiple issues may be provided by including additional 'issue:' lines.
 //     All proposals must be in the 'accepted' state.
-//   - type: the graph type: currently only partition, histogram, and stack are
+//   - type: the chart type: currently only partition, histogram, and stack are
 //     supported.
-//   - program: the package path of the program for which this graph applies.
-//   - version: (optional) the first version for which this graph applies. Must
+//   - program: the package path of the program for which this chart applies.
+//   - version: (optional) the first version for which this chart applies. Must
 //     be a valid semver value.
-//   - counter: the primary counter this graph illustrates, including buckets
-//     for histogram and partition graphs
+//   - counter: the primary counter this chart illustrates, including buckets
+//     for histogram and partition charts.
 //   - depth: (optional) stack counters only; the maximum stack depth to collect
-//   - error: (optional) the desired error rate for this graph, which
+//   - error: (optional) the desired error rate for this chart, which
 //     determines collection rate
 //
 // Multiple records are separated by "---" lines.
@@ -66,13 +66,13 @@
 //	type: stack
 //	program: golang.org/x/tools/gopls
 //	depth: 10
-package graphconfig
+package chartconfig
 
-// A GraphConfig defines the configuration for a single graph/collection on the
+// A ChartConfig defines the configuration for a single chart/collection on the
 // telemetry server.
 //
 // See the package documentation for field definitions.
-type GraphConfig struct {
+type ChartConfig struct {
 	Title       string
 	Description string
 	Issue       []string
diff --git a/internal/configgen/config.txt b/internal/chartconfig/config.txt
similarity index 90%
rename from internal/configgen/config.txt
rename to internal/chartconfig/config.txt
index b4e697e..bd2bc21 100644
--- a/internal/configgen/config.txt
+++ b/internal/chartconfig/config.txt
@@ -1,5 +1,5 @@
-# Note: this is an approved graph config. This is embedded in the configgen program.
-# For the graph config file format, see golang.org/x/telemetry/internal/graphconfig.
+# Note: these are approved chart configs, used to generate the upload config.
+# For the chart config file format, see chartconfig.go.
 
 title: Editor Distribution
 counter: gopls/client:{vscode,vscodium,vscode-insiders,code-server,eglot,govim,neovim,coc.nvim,sublimetext,other}
diff --git a/internal/graphconfig/parse.go b/internal/chartconfig/load.go
similarity index 85%
rename from internal/graphconfig/parse.go
rename to internal/chartconfig/load.go
index f391130..e83cbab 100644
--- a/internal/graphconfig/parse.go
+++ b/internal/chartconfig/load.go
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package graphconfig
+package chartconfig
 
 import (
+	_ "embed"
 	"fmt"
 	"reflect"
 	"sort"
@@ -12,21 +13,33 @@
 	"strings"
 )
 
-// Parse parses GraphConfig records from the provided raw data, returning an
+//go:embed config.txt
+var chartConfig []byte
+
+func Raw() []byte {
+	return chartConfig
+}
+
+// Load loads and parses the current chart config.
+func Load() ([]ChartConfig, error) {
+	return Parse(chartConfig)
+}
+
+// Parse parses ChartConfig records from the provided raw data, returning an
 // error if the config has invalid syntax. See the package documentation for a
 // description of the record syntax.
 //
-// Even with correct syntax, the resulting GraphConfig may not meet all the
+// Even with correct syntax, the resulting chart config may not meet all the
 // requirements described in the package doc. Call [Validate] to check whether
 // the config data is coherent.
-func Parse(data []byte) ([]GraphConfig, error) {
+func Parse(data []byte) ([]ChartConfig, error) {
 	// Collect field information for the record type.
 	var (
 		prefixes []string                               // for parse errors
 		fields   = make(map[string]reflect.StructField) // key -> struct field
 	)
 	{
-		typ := reflect.TypeOf(GraphConfig{})
+		typ := reflect.TypeOf(ChartConfig{})
 		for i := 0; i < typ.NumField(); i++ {
 			f := typ.Field(i)
 			key := strings.ToLower(f.Name)
@@ -41,15 +54,15 @@
 
 	// Read records, separated by '---'
 	var (
-		records    []GraphConfig
-		inProgress = new(GraphConfig)      // record value currently being parsed
+		records    []ChartConfig
+		inProgress = new(ChartConfig)      // record value currently being parsed
 		set        = make(map[string]bool) // fields that are set so far; empty records are skipped
 	)
 	flushRecord := func() {
 		if len(set) > 0 { // only flush non-empty records
 			records = append(records, *inProgress)
 		}
-		inProgress = new(GraphConfig)
+		inProgress = new(ChartConfig)
 		set = make(map[string]bool)
 	}
 
diff --git a/internal/graphconfig/parse_test.go b/internal/chartconfig/load_test.go
similarity index 85%
rename from internal/graphconfig/parse_test.go
rename to internal/chartconfig/load_test.go
index 19d74fc..b00acf2 100644
--- a/internal/graphconfig/parse_test.go
+++ b/internal/chartconfig/load_test.go
@@ -2,23 +2,23 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package graphconfig_test
+package chartconfig_test
 
 import (
 	"reflect"
 	"testing"
 
-	"golang.org/x/telemetry/internal/graphconfig"
+	"golang.org/x/telemetry/internal/chartconfig"
 )
 
 func TestParse(t *testing.T) {
 	tests := []struct {
 		name  string
 		input string
-		want  []graphconfig.GraphConfig
+		want  []chartconfig.ChartConfig
 	}{
 		{"empty", "", nil},
-		{"single field", "title: A", []graphconfig.GraphConfig{{Title: "A"}}},
+		{"single field", "title: A", []chartconfig.ChartConfig{{Title: "A"}}},
 		{
 			"basic", `
 title: A
@@ -32,7 +32,7 @@
 error: 0.1
 version: v2.0.0
 `,
-			[]graphconfig.GraphConfig{{
+			[]chartconfig.ChartConfig{{
 				Title:       "A",
 				Description: "B",
 				Type:        "C",
@@ -49,7 +49,7 @@
 title: A
 description: B
 `,
-			[]graphconfig.GraphConfig{
+			[]chartconfig.ChartConfig{
 				{Title: "A", Description: "B"},
 			},
 		},
@@ -64,7 +64,7 @@
 
 
 `,
-			[]graphconfig.GraphConfig{
+			[]chartconfig.ChartConfig{
 				{Title: "A", Description: "B"},
 			},
 		},
@@ -80,7 +80,7 @@
 title: C
 description: D
 `,
-			[]graphconfig.GraphConfig{
+			[]chartconfig.ChartConfig{
 				{Title: "A", Description: "B"},
 				{Title: "C", Description: "D"},
 			},
@@ -94,7 +94,7 @@
 issue: TBD
 program: golang.org/x/tools/gopls
 `,
-			[]graphconfig.GraphConfig{
+			[]chartconfig.ChartConfig{
 				{
 					Title:       "Editor Distribution",
 					Description: "measure editor distribution for gopls users.",
@@ -109,7 +109,7 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			got, err := graphconfig.Parse([]byte(test.input))
+			got, err := chartconfig.Parse([]byte(test.input))
 			if err != nil {
 				t.Fatalf("Parse(...) failed: %v", err)
 			}
@@ -161,7 +161,7 @@
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			_, err := graphconfig.Parse([]byte(test.input))
+			_, err := chartconfig.Parse([]byte(test.input))
 			if err == nil {
 				t.Fatalf("Parse(...) succeeded unexpectedly")
 			}
diff --git a/internal/graphconfig/validate.go b/internal/chartconfig/validate.go
similarity index 82%
rename from internal/graphconfig/validate.go
rename to internal/chartconfig/validate.go
index 15b7b71..74106b9 100644
--- a/internal/graphconfig/validate.go
+++ b/internal/chartconfig/validate.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package graphconfig
+package chartconfig
 
 import (
 	"errors"
@@ -11,9 +11,9 @@
 	"golang.org/x/mod/semver"
 )
 
-// Validate checks that a graph config is complete and coherent, returning an
+// Validate checks that a ChartConfig is complete and coherent, returning an
 // error describing all problems encountered, or nil.
-func Validate(cfg GraphConfig) error {
+func Validate(cfg ChartConfig) error {
 	var errs []error
 	reportf := func(format string, args ...any) {
 		errs = append(errs, fmt.Errorf(format, args...))
@@ -37,7 +37,7 @@
 		reportf("invalid depth %d: must be non-negative", cfg.Depth)
 	}
 	if cfg.Depth != 0 && cfg.Type != "stack" {
-		reportf("depth can only be set for \"stack\" graph types")
+		reportf("depth can only be set for \"stack\" chart types")
 	}
 	if cfg.Version != "" && !semver.IsValid(cfg.Version) {
 		reportf("%q is not valid semver", cfg.Version)
diff --git a/internal/graphconfig/validate_test.go b/internal/chartconfig/validate_test.go
similarity index 81%
rename from internal/graphconfig/validate_test.go
rename to internal/chartconfig/validate_test.go
index 25533ef..01fc557 100644
--- a/internal/graphconfig/validate_test.go
+++ b/internal/chartconfig/validate_test.go
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package graphconfig_test
+package chartconfig_test
 
 import (
 	"strings"
 	"testing"
 
-	"golang.org/x/telemetry/internal/graphconfig"
+	"golang.org/x/telemetry/internal/chartconfig"
 )
 
 func TestValidateOK(t *testing.T) {
-	// A minimally valid graph config.
+	// A minimally valid chart config.
 	const input = `
 title: Editor Distribution
 counter: gopls/editor:{emacs,vim,vscode,other}
@@ -20,14 +20,14 @@
 issue: https://go.dev/issue/12345
 program: golang.org/x/tools/gopls
 `
-	records, err := graphconfig.Parse([]byte(input))
+	records, err := chartconfig.Parse([]byte(input))
 	if err != nil {
 		t.Fatal(err)
 	}
 	if len(records) != 1 {
 		t.Fatalf("Parse(%q) returned %d records, want exactly 1", input, len(records))
 	}
-	if err := graphconfig.Validate(records[0]); err != nil {
+	if err := chartconfig.Validate(records[0]); err != nil {
 		t.Errorf("Validate(%q) = %v, want nil", input, err)
 	}
 }
@@ -45,14 +45,14 @@
 	}
 
 	for input, wantErrs := range tests {
-		records, err := graphconfig.Parse([]byte(input))
+		records, err := chartconfig.Parse([]byte(input))
 		if err != nil {
 			t.Fatal(err)
 		}
 		if len(records) != 1 {
 			t.Fatalf("Parse(%q) returned %d records, want exactly 1", input, len(records))
 		}
-		err = graphconfig.Validate(records[0])
+		err = chartconfig.Validate(records[0])
 		if err == nil {
 			t.Fatalf("Validate(%q) succeeded unexpectedly", input)
 		}
diff --git a/internal/configgen/main.go b/internal/configgen/main.go
index f49dd48..5658fc8 100644
--- a/internal/configgen/main.go
+++ b/internal/configgen/main.go
@@ -7,7 +7,7 @@
 //go:build go1.21
 
 // Package configgen generates the upload config file stored in the config.json
-// file of golang.org/x/telemetry/config based on the graphconfig stored in
+// file of golang.org/x/telemetry/config based on the chartconfig stored in
 // config.txt.
 package main
 
@@ -25,10 +25,8 @@
 	"sort"
 	"strings"
 
-	_ "embed"
-
 	"golang.org/x/mod/semver"
-	"golang.org/x/telemetry/internal/graphconfig"
+	"golang.org/x/telemetry/internal/chartconfig"
 	"golang.org/x/telemetry/internal/telemetry"
 )
 
@@ -40,16 +38,18 @@
 	SamplingRate = 1.0
 )
 
-//go:embed config.txt
-var graphConfig []byte
-
 func main() {
 	flag.Parse()
 
+	gcfgs, err := chartconfig.Load()
+	if err != nil {
+		log.Fatal(err)
+	}
+
 	// The padding heuristics below are based on the example of gopls.
 	//
 	// The goal is to pad enough versions for a quarter.
-	cfg, err := generate(graphConfig, padding{
+	uCfg, err := generate(gcfgs, padding{
 		// 6 releases into the future translates to approximately three months for gopls.
 		releases: 6,
 		// We may release gopls 1.0, but won't release 2.0 in a three month timespan!
@@ -66,7 +66,7 @@
 	if err != nil {
 		log.Fatal(err)
 	}
-	cfgJSON, err := json.MarshalIndent(cfg, "", "\t")
+	cfgJSON, err := json.MarshalIndent(uCfg, "", "\t")
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -87,7 +87,7 @@
 			log.Fatal(err)
 		}
 		// Guarantee that we have enough padding to do two patches releases tomorrow.
-		minCfg, err := generate(graphConfig, padding{
+		minCfg, err := generate(gcfgs, padding{
 			releases: 2,
 			maj:      1,
 			majmin:   1, // we're not ever going to do more than one major/minor release in a day
@@ -138,9 +138,9 @@
 	return cfg, nil
 }
 
-// generate computes the upload config from graph configs and module
+// generate computes the upload config from chart configs and module
 // information, returning the resulting formatted JSON.
-func generate(graphConfig []byte, padding padding) (*telemetry.UploadConfig, error) {
+func generate(gcfgs []chartconfig.ChartConfig, padding padding) (*telemetry.UploadConfig, error) {
 	ucfg := &telemetry.UploadConfig{
 		GOOS:   goos(),
 		GOARCH: goarch(),
@@ -153,16 +153,11 @@
 		return nil, fmt.Errorf("querying go info: %v", err)
 	}
 
-	gcfgs, err := graphconfig.Parse(graphConfig)
-	if err != nil {
-		return nil, fmt.Errorf("parsing graph config records: %v", err)
-	}
-
 	for i, r := range gcfgs {
-		if err := graphconfig.Validate(r); err != nil {
+		if err := chartconfig.Validate(r); err != nil {
 			// TODO(rfindley): this is a poor way to identify the faulty record. We
-			// should probably store position information in the GraphConfig.
-			return nil, fmt.Errorf("graph config #%d (%q): %v", i, r.Title, err)
+			// should probably store position information in the ChartConfig.
+			return nil, fmt.Errorf("chart config #%d (%q): %v", i, r.Title, err)
 		}
 	}
 
diff --git a/internal/configgen/main_test.go b/internal/configgen/main_test.go
index 5764871..08d5c13 100644
--- a/internal/configgen/main_test.go
+++ b/internal/configgen/main_test.go
@@ -12,6 +12,7 @@
 	"sort"
 	"testing"
 
+	"golang.org/x/telemetry/internal/chartconfig"
 	"golang.org/x/telemetry/internal/telemetry"
 )
 
@@ -23,7 +24,7 @@
 		"golang.org/toolchain":     {"v0.0.1-go1.21.0.linux-arm", "v0.0.1-go1.20.linux-arm"},
 		"golang.org/x/tools/gopls": {"v0.13.0", "v0.14.0", "v0.15.0-pre.1", "v0.15.0"},
 	}
-	const gcfg = `
+	const raw = `
 title: Editor Distribution
 counter: gopls/editor:{emacs,vim,vscode,other}
 description: measure editor distribution for gopls users.
@@ -32,7 +33,11 @@
 program: golang.org/x/tools/gopls
 version: v0.14.0
 `
-	got, err := generate([]byte(gcfg), padding{2, 1, 1, 2, 2})
+	gcfgs, err := chartconfig.Parse([]byte(raw))
+	if err != nil {
+		t.Fatal(err)
+	}
+	got, err := generate(gcfgs, padding{2, 1, 1, 2, 2})
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/internal/content/telemetrygodev/config.html b/internal/content/telemetrygodev/config.html
index a83659a..8790eaa 100644
--- a/internal/content/telemetrygodev/config.html
+++ b/internal/content/telemetrygodev/config.html
@@ -10,16 +10,29 @@
 
 {{define "content"}}
 <main id="main">
-    <section class="Config">
-        <h2 id="config">Config</h2>
-        <p>
-          The config contains the list of active counters for each program
-          and allowed report metadata.
-        </p>
-        <label>
-            Version: {{.Version}}
-        </label>
-        <pre style="max-height: 100rem">{{.PrettyConfig}}</pre>
-      </section>
+  <section class="Chart Config">
+    <h2 id="config">Chart Config</h2>
+    <p>
+      The chart config contains the list of approved charts to display on
+			telemetry.go.dev. The chart config format is documented by the
+			<a href="https://pkg.go.dev/golang.org/x/telemetry/internal/chartconfig">
+				<code>chartconfig</code>
+			</a> package documentation.
+    </p>
+    <pre style="max-height: 100rem">{{.ChartConfig}}</pre>
+  </section>
+
+  <section class="Upload Config">
+    <h2 id="config">Upload Config</h2>
+    <p>
+      The upload config contains the list of active counters for each program
+      and allowed report metadata. This is generated from the chart config
+      above.
+    </p>
+    <label>
+      Version: {{.Version}}
+    </label>
+    <pre style="max-height: 100rem">{{.UploadConfig}}</pre>
+  </section>
 </main>
-{{end}}
\ No newline at end of file
+{{end}}
diff --git a/internal/content/telemetrygodev/index.html b/internal/content/telemetrygodev/index.html
index 1412d75..c2d81f4 100644
--- a/internal/content/telemetrygodev/index.html
+++ b/internal/content/telemetrygodev/index.html
@@ -15,12 +15,22 @@
     <h2>Overview</h2>
     <p>
     Go Telemetry is a way for Go toolchain programs to collect data about their
-    performance and usage. Uploaded data is used to help improve the Go toolchain
-    and related tools. Go Telemetry is not built into users' binaries.
-    <a href="/privacy#collection">See what Go Telemetry collects</a>.
+    performance and usage. Uploaded data is used to help improve the Go
+    toolchain and related tools. Go Telemetry is not built into users'
+    binaries. Learn more about Go telemetry at
+    <a href="https://go.dev/doc/telemetry">go.dev/doc/telemetry</a>.
     </p>
 
-    </p>For privacy information about this service, see <a href="/privacy">telemetry.go.dev/privacy</a>.</p>
+    <p>
+    Users who have opted in will upload an approved subset of telemetry
+    data approximately once a week. This subset is determined by the current
+    <a href="/config">upload configuration</a>.
+    </p>
+
+    <p>
+    For privacy information about this service, see
+    <a href="/privacy">telemetry.go.dev/privacy</a>.
+    </p>
   </section>
   <section>
     <h2>Charts</h2>
@@ -41,4 +51,4 @@
     </ul>
   </section>
 </main>
-{{end}}
\ No newline at end of file
+{{end}}