priority: add policy config parsing (#3936)

diff --git a/xds/internal/balancer/priority/config.go b/xds/internal/balancer/priority/config.go
new file mode 100644
index 0000000..da08590
--- /dev/null
+++ b/xds/internal/balancer/priority/config.go
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright 2020 gRPC authors.
+ *
+ * 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 priority
+
+import (
+	"encoding/json"
+	"fmt"
+
+	internalserviceconfig "google.golang.org/grpc/internal/serviceconfig"
+	"google.golang.org/grpc/serviceconfig"
+)
+
+type child struct {
+	Config *internalserviceconfig.BalancerConfig
+}
+
+type lbConfig struct {
+	serviceconfig.LoadBalancingConfig
+
+	// Children is a map from the child balancer names to their configs. Child
+	// names can be found in field Priorities.
+	Children map[string]*child
+	// Priorities is a list of child balancer names. They are sorted from
+	// highest priority to low. The type/config for each child can be found in
+	// field Children, with the balancer name as the key.
+	Priorities []string
+}
+
+func parseConfig(c json.RawMessage) (*lbConfig, error) {
+	var cfg lbConfig
+	if err := json.Unmarshal(c, &cfg); err != nil {
+		return nil, err
+	}
+
+	prioritiesSet := make(map[string]bool)
+	for _, name := range cfg.Priorities {
+		if _, ok := cfg.Children[name]; !ok {
+			return nil, fmt.Errorf("LB policy name %q found in Priorities field (%v) is not found in Children field (%+v)", name, cfg.Priorities, cfg.Children)
+		}
+		prioritiesSet[name] = true
+	}
+	for name := range cfg.Children {
+		if _, ok := prioritiesSet[name]; !ok {
+			return nil, fmt.Errorf("LB policy name %q found in Children field (%v) is not found in Priorities field (%+v)", name, cfg.Children, cfg.Priorities)
+		}
+	}
+	return &cfg, nil
+}
diff --git a/xds/internal/balancer/priority/config_test.go b/xds/internal/balancer/priority/config_test.go
new file mode 100644
index 0000000..15c4069
--- /dev/null
+++ b/xds/internal/balancer/priority/config_test.go
@@ -0,0 +1,107 @@
+/*
+ *
+ * Copyright 2020 gRPC authors.
+ *
+ * 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 priority
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"google.golang.org/grpc/balancer/roundrobin"
+	internalserviceconfig "google.golang.org/grpc/internal/serviceconfig"
+)
+
+func TestParseConfig(t *testing.T) {
+	tests := []struct {
+		name    string
+		js      string
+		want    *lbConfig
+		wantErr bool
+	}{
+		{
+			name: "child not found",
+			js: `{
+  "priorities": ["child-1", "child-2", "child-3"],
+  "children": {
+    "child-1": {"config": [{"round_robin":{}}]},
+    "child-3": {"config": [{"round_robin":{}}]}
+  }
+}
+			`,
+			wantErr: true,
+		},
+		{
+			name: "child not used",
+			js: `{
+  "priorities": ["child-1", "child-2"],
+  "children": {
+    "child-1": {"config": [{"round_robin":{}}]},
+    "child-2": {"config": [{"round_robin":{}}]},
+    "child-3": {"config": [{"round_robin":{}}]}
+  }
+}
+			`,
+			wantErr: true,
+		},
+		{
+			name: "good",
+			js: `{
+  "priorities": ["child-1", "child-2", "child-3"],
+  "children": {
+    "child-1": {"config": [{"round_robin":{}}]},
+    "child-2": {"config": [{"round_robin":{}}]},
+    "child-3": {"config": [{"round_robin":{}}]}
+  }
+}
+			`,
+			want: &lbConfig{
+				Children: map[string]*child{
+					"child-1": {
+						&internalserviceconfig.BalancerConfig{
+							Name: roundrobin.Name,
+						},
+					},
+					"child-2": {
+						&internalserviceconfig.BalancerConfig{
+							Name: roundrobin.Name,
+						},
+					},
+					"child-3": {
+						&internalserviceconfig.BalancerConfig{
+							Name: roundrobin.Name,
+						},
+					},
+				},
+				Priorities: []string{"child-1", "child-2", "child-3"},
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := parseConfig([]byte(tt.js))
+			if (err != nil) != tt.wantErr {
+				t.Errorf("parseConfig() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if diff := cmp.Diff(got, tt.want); diff != "" {
+				t.Errorf("parseConfig() got = %v, want %v, diff: %s", got, tt.want, diff)
+			}
+		})
+	}
+}
diff --git a/xds/internal/balancer/priority/doc.go b/xds/internal/balancer/priority/doc.go
new file mode 100644
index 0000000..53bd270
--- /dev/null
+++ b/xds/internal/balancer/priority/doc.go
@@ -0,0 +1,24 @@
+/*
+ *
+ * Copyright 2020 gRPC authors.
+ *
+ * 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 priority implements the priority balancer.
+//
+// This balancer will be kept in internal until we use it in the xds balancers,
+// and are confident its functionalities are stable. It will then be exported
+// for more users.
+package priority