code: func makeClusterFromUserConfig(configJSON string) (*envoy.Cluster, error) {
  	var jsonFields map[string]*json.RawMessage
  	if err := json.Unmarshal([]byte(configJSON), &jsonFields); err != nil {
  		fmt.Println("Custom error", err, configJSON)
  		return nil, err
  	}
  
  	var c envoy.Cluster
  
  	if _, ok := jsonFields["@type"]; ok {
  		// Type field is present so decode it as a types.Any
  		var any types.Any
  		err := jsonpb.UnmarshalString(configJSON, &any)
  		if err != nil {
  			return nil, err
  		}
  		// And then unmarshal the listener again...
  		err = proto.Unmarshal(any.Value, &c)
  		if err != nil {
  			return nil, err
  		}
  		return &c, err
  	}
  
  	// No @type so try decoding as a straight listener.
  	err := jsonpb.UnmarshalString(configJSON, &c)
  	return &c, err
  }
docs: // makeClusterFromUserConfig returns the listener config decoded from an
  // arbitrary proto3 json format string or an error if it's invalid.
  //
  // For now we only support embedding in JSON strings because of the hcl parsing
  // pain (see config.go comment above call to patchSliceOfMaps). Until we
  // refactor config parser a _lot_ user's opaque config that contains arrays will
  // be mangled. We could actually fix that up in mapstructure which knows the
  // type of the target so could resolve the slices to singletons unambiguously
  // and it would work for us here... but we still have the problem that the
  // config would render incorrectly in general in our HTTP API responses so we
  // really need to fix it "properly".
  //
  // When we do that we can support just nesting the config directly into the
  // JSON/hcl naturally but this is a stop-gap that gets us an escape hatch
  // immediately. It's also probably not a bad thing to support long-term since
  // any config generated by other systems will likely be in canonical protobuf
  // from rather than our slight variant in JSON/hcl.