diff --git a/api/services.go b/api/services.go index 34440124c7a..9017b139603 100644 --- a/api/services.go +++ b/api/services.go @@ -636,9 +636,9 @@ func (e *ConsulIngressConfigEntry) Copy() *ConsulIngressConfigEntry { type ConsulLinkedService struct { Name string `hcl:"name,optional"` - CAFile string `hcl:"ca_file,optional"` - CertFile string `hcl:"cert_file,optional"` - KeyFile string `hcl:"key_file,optional"` + CAFile string `hcl:"ca_file,optional" mapstructure:"ca_file"` + CertFile string `hcl:"cert_file,optional" mapstructure:"cert_file"` + KeyFile string `hcl:"key_file,optional" mapstructure:"key_file"` SNI string `hcl:"sni,optional"` } diff --git a/jobspec/parse_service.go b/jobspec/parse_service.go index 05a8735a655..974acae6e74 100644 --- a/jobspec/parse_service.go +++ b/jobspec/parse_service.go @@ -225,6 +225,7 @@ func parseGateway(o *ast.ObjectItem) (*api.ConsulGateway, error) { valid := []string{ "proxy", "ingress", + "terminating", } if err := checkHCLKeys(o.Val, valid); err != nil { @@ -239,6 +240,7 @@ func parseGateway(o *ast.ObjectItem) (*api.ConsulGateway, error) { delete(m, "proxy") delete(m, "ingress") + delete(m, "terminating") dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), @@ -273,15 +275,30 @@ func parseGateway(o *ast.ObjectItem) (*api.ConsulGateway, error) { // extract and parse the ingress block io := listVal.Filter("ingress") - if len(io.Items) != 1 { - // in the future, may be terminating or mesh block instead - return nil, fmt.Errorf("must have one 'ingress' block") + if len(io.Items) == 1 { + ingress, err := parseIngressConfigEntry(io.Items[0]) + if err != nil { + return nil, fmt.Errorf("ingress, %v", err) + } + gateway.Ingress = ingress } - ingress, err := parseIngressConfigEntry(io.Items[0]) - if err != nil { - return nil, fmt.Errorf("ingress, %v", err) + + if len(io.Items) > 1 { + return nil, fmt.Errorf("ingress, %s", "multiple ingress stanzas not allowed") + } + + to := listVal.Filter("terminating") + if len(to.Items) == 1 { + terminating, err := parseTerminatingConfigEntry(to.Items[0]) + if err != nil { + return nil, fmt.Errorf("terminating, %v", err) + } + gateway.Terminating = terminating + } + + if len(to.Items) > 1 { + return nil, fmt.Errorf("terminating, %s", "multiple terminating stanzas not allowed") } - gateway.Ingress = ingress return &gateway, nil } @@ -410,6 +427,39 @@ func parseConsulIngressService(o *ast.ObjectItem) (*api.ConsulIngressService, er return &service, nil } +func parseConsulLinkedService(o *ast.ObjectItem) (*api.ConsulLinkedService, error) { + valid := []string{ + "name", + "ca_file", + "cert_file", + "key_file", + "sni", + } + + if err := checkHCLKeys(o.Val, valid); err != nil { + return nil, multierror.Prefix(err, "service ->") + } + + var service api.ConsulLinkedService + var m map[string]interface{} + if err := hcl.DecodeObject(&m, o.Val); err != nil { + return nil, err + } + + dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + Result: &service, + }) + if err != nil { + return nil, err + } + + if err := dec.Decode(m); err != nil { + return nil, err + } + + return &service, nil +} + func parseConsulIngressListener(o *ast.ObjectItem) (*api.ConsulIngressListener, error) { valid := []string{ "port", @@ -545,6 +595,41 @@ func parseIngressConfigEntry(o *ast.ObjectItem) (*api.ConsulIngressConfigEntry, return &ingress, nil } +func parseTerminatingConfigEntry(o *ast.ObjectItem) (*api.ConsulTerminatingConfigEntry, error) { + valid := []string{ + "service", + } + + if err := checkHCLKeys(o.Val, valid); err != nil { + return nil, multierror.Prefix(err, "terminating ->") + } + + var terminating api.ConsulTerminatingConfigEntry + + // Parse service(s) + + var listVal *ast.ObjectList + if ot, ok := o.Val.(*ast.ObjectType); ok { + listVal = ot.List + } else { + return nil, fmt.Errorf("terminating: should be an object") + } + + lo := listVal.Filter("service") + if len(lo.Items) > 0 { + terminating.Services = make([]*api.ConsulLinkedService, len(lo.Items)) + for i := range lo.Items { + service, err := parseConsulLinkedService(lo.Items[i]) + if err != nil { + return nil, err + } + terminating.Services[i] = service + } + } + + return &terminating, nil +} + func parseSidecarService(o *ast.ObjectItem) (*api.ConsulSidecarService, error) { valid := []string{ "port", diff --git a/jobspec/parse_test.go b/jobspec/parse_test.go index 8a65647b50b..48d8b547598 100644 --- a/jobspec/parse_test.go +++ b/jobspec/parse_test.go @@ -1532,6 +1532,45 @@ func TestParse(t *testing.T) { }, false, }, + { + "tg-service-connect-gateway-terminating.hcl", + &api.Job{ + ID: stringToPtr("connect_gateway_terminating"), + Name: stringToPtr("connect_gateway_terminating"), + TaskGroups: []*api.TaskGroup{{ + Name: stringToPtr("group"), + Services: []*api.Service{{ + Name: "terminating-gateway-service", + Connect: &api.ConsulConnect{ + Gateway: &api.ConsulGateway{ + Proxy: &api.ConsulGatewayProxy{ + ConnectTimeout: timeToPtr(3 * time.Second), + EnvoyGatewayBindTaggedAddresses: true, + EnvoyGatewayBindAddresses: map[string]*api.ConsulGatewayBindAddress{ + "listener1": {Name: "listener1", Address: "10.0.0.1", Port: 8888}, + "listener2": {Name: "listener2", Address: "10.0.0.2", Port: 8889}, + }, + EnvoyGatewayNoDefaultBind: true, + Config: map[string]interface{}{"foo": "bar"}, + }, + Terminating: &api.ConsulTerminatingConfigEntry{ + Services: []*api.ConsulLinkedService{{ + Name: "service1", + CAFile: "ca.pem", + CertFile: "cert.pem", + KeyFile: "key.pem", + }, { + Name: "service2", + SNI: "myhost", + }}, + }, + }, + }, + }}, + }}, + }, + false, + }, { "tg-scaling-policy-minimal.hcl", &api.Job{ diff --git a/jobspec/test-fixtures/tg-service-connect-gateway-terminating.hcl b/jobspec/test-fixtures/tg-service-connect-gateway-terminating.hcl new file mode 100644 index 00000000000..82a8bfec051 --- /dev/null +++ b/jobspec/test-fixtures/tg-service-connect-gateway-terminating.hcl @@ -0,0 +1,46 @@ +job "connect_gateway_terminating" { + group "group" { + service { + name = "terminating-gateway-service" + + connect { + gateway { + proxy { + connect_timeout = "3s" + envoy_gateway_bind_tagged_addresses = true + + envoy_gateway_bind_addresses "listener1" { + address = "10.0.0.1" + port = 8888 + } + + envoy_gateway_bind_addresses "listener2" { + address = "10.0.0.2" + port = 8889 + } + + envoy_gateway_no_default_bind = true + + config { + foo = "bar" + } + } + + terminating { + service { + name = "service1" + ca_file = "ca.pem" + cert_file = "cert.pem" + key_file = "key.pem" + } + + service { + name = "service2" + sni = "myhost" + } + } + } + } + } + } +} diff --git a/vendor/github.com/hashicorp/nomad/api/services.go b/vendor/github.com/hashicorp/nomad/api/services.go index 34440124c7a..9017b139603 100644 --- a/vendor/github.com/hashicorp/nomad/api/services.go +++ b/vendor/github.com/hashicorp/nomad/api/services.go @@ -636,9 +636,9 @@ func (e *ConsulIngressConfigEntry) Copy() *ConsulIngressConfigEntry { type ConsulLinkedService struct { Name string `hcl:"name,optional"` - CAFile string `hcl:"ca_file,optional"` - CertFile string `hcl:"cert_file,optional"` - KeyFile string `hcl:"key_file,optional"` + CAFile string `hcl:"ca_file,optional" mapstructure:"ca_file"` + CertFile string `hcl:"cert_file,optional" mapstructure:"cert_file"` + KeyFile string `hcl:"key_file,optional" mapstructure:"key_file"` SNI string `hcl:"sni,optional"` }