diff --git a/agent/agent_endpoint_test.go b/agent/agent_endpoint_test.go index 5835ca4cf2..796b977e3b 100644 --- a/agent/agent_endpoint_test.go +++ b/agent/agent_endpoint_test.go @@ -399,7 +399,7 @@ func TestAgent_Service(t *testing.T) { Service: "web-sidecar-proxy", Port: 8000, Proxy: expectProxy.ToAPI(), - ContentHash: "518ece989813bc13", + ContentHash: "854327a458fe02a6", Weights: api.AgentWeights{ Passing: 1, Warning: 1, @@ -413,7 +413,7 @@ func TestAgent_Service(t *testing.T) { // Copy and modify updatedResponse := *expectedResponse updatedResponse.Port = 9999 - updatedResponse.ContentHash = "6cc7a4afb000afb1" + updatedResponse.ContentHash = "b80a4d9370ed1104" // Simple response for non-proxy service registered in TestAgent config expectWebResponse := &api.AgentService{ diff --git a/agent/structs/connect_proxy_config.go b/agent/structs/connect_proxy_config.go index 20037a71d6..f3d7cc0536 100644 --- a/agent/structs/connect_proxy_config.go +++ b/agent/structs/connect_proxy_config.go @@ -133,12 +133,20 @@ type TransparentProxyConfig struct { } func (c TransparentProxyConfig) ToAPI() *api.TransparentProxyConfig { + if c.IsZero() { + return nil + } return &api.TransparentProxyConfig{ OutboundListenerPort: c.OutboundListenerPort, DialedDirectly: c.DialedDirectly, } } +func (c *TransparentProxyConfig) IsZero() bool { + zeroVal := TransparentProxyConfig{} + return *c == zeroVal +} + // ConnectProxyConfig describes the configuration needed for any proxy managed // or unmanaged. It describes a single logical service's listener and optionally // upstreams and sidecar-related config for a single instance. To describe a @@ -242,16 +250,25 @@ func (t *ConnectProxyConfig) UnmarshalJSON(data []byte) (err error) { } func (c *ConnectProxyConfig) MarshalJSON() ([]byte, error) { - type typeCopy ConnectProxyConfig - copy := typeCopy(*c) + type Alias ConnectProxyConfig + out := struct { + TransparentProxy *TransparentProxyConfig `json:",omitempty"` + Alias + }{ + Alias: (Alias)(*c), + } - proxyConfig, err := lib.MapWalk(copy.Config) + proxyConfig, err := lib.MapWalk(c.Config) if err != nil { return nil, err } - copy.Config = proxyConfig + out.Alias.Config = proxyConfig - return json.Marshal(©) + if !c.TransparentProxy.IsZero() { + out.TransparentProxy = &out.Alias.TransparentProxy + } + + return json.Marshal(&out) } // ToAPI returns the api struct with the same fields. We have duplicates to diff --git a/agent/structs/connect_proxy_config_test.go b/agent/structs/connect_proxy_config_test.go index e57054a8fe..f47e268a69 100644 --- a/agent/structs/connect_proxy_config_test.go +++ b/agent/structs/connect_proxy_config_test.go @@ -94,6 +94,121 @@ func TestConnectProxyConfig_ToAPI(t *testing.T) { } } +func TestConnectProxyConfig_MarshalJSON(t *testing.T) { + tests := []struct { + name string + in ConnectProxyConfig + want string + wantErr bool + }{ + { + name: "direct proxy", + in: ConnectProxyConfig{ + DestinationServiceName: "api", + DestinationServiceID: "api-1", + LocalServiceAddress: "127.0.0.1", + LocalServicePort: 8080, + Mode: ProxyModeDirect, + Config: map[string]interface{}{ + "connect_timeout_ms": 5000, + }, + Upstreams: Upstreams{ + Upstream{ + DestinationType: UpstreamDestTypeService, + DestinationName: "db", + Datacenter: "dc1", + LocalBindPort: 1234, + }, + }, + MeshGateway: MeshGatewayConfig{Mode: MeshGatewayModeLocal}, + Expose: ExposeConfig{Checks: true}, + + // No transparent proxy config, since proxy is set to "direct" mode. + // Field should be omitted from json output. + // TransparentProxy: TransparentProxyConfig{}, + }, + want: `{ + "DestinationServiceName": "api", + "DestinationServiceID": "api-1", + "LocalServiceAddress": "127.0.0.1", + "LocalServicePort": 8080, + "Mode": "direct", + "Config": { + "connect_timeout_ms": 5000 + }, + "Upstreams": [ + { + "DestinationType": "service", + "DestinationName": "db", + "Datacenter": "dc1", + "LocalBindPort": 1234, + "MeshGateway": {} + } + ], + "MeshGateway": { + "Mode": "local" + }, + "Expose": { + "Checks": true + } + }`, + wantErr: false, + }, + { + name: "transparent proxy", + in: ConnectProxyConfig{ + DestinationServiceName: "billing", + DestinationServiceID: "billing-1", + LocalServiceAddress: "127.0.0.1", + LocalServicePort: 8080, + Mode: ProxyModeTransparent, + Config: map[string]interface{}{ + "connect_timeout_ms": 5000, + }, + MeshGateway: MeshGatewayConfig{Mode: MeshGatewayModeLocal}, + Expose: ExposeConfig{Checks: true}, + TransparentProxy: TransparentProxyConfig{ + DialedDirectly: true, + OutboundListenerPort: 16001, + }, + }, + want: `{ + "DestinationServiceName": "billing", + "DestinationServiceID": "billing-1", + "LocalServiceAddress": "127.0.0.1", + "LocalServicePort": 8080, + "Mode": "transparent", + "Config": { + "connect_timeout_ms": 5000 + }, + "TransparentProxy": { + "DialedDirectly": true, + "OutboundListenerPort": 16001 + }, + "MeshGateway": { + "Mode": "local" + }, + "Expose": { + "Checks": true + } + }`, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + got, err := tt.in.MarshalJSON() + if tt.wantErr { + require.Error(err) + return + } + require.NoError(err) + require.JSONEq(tt.want, string(got)) + }) + } +} + func TestUpstream_MarshalJSON(t *testing.T) { tests := []struct { name string