From 3f0846a6be34c9800ea803c500c550e65d045362 Mon Sep 17 00:00:00 2001 From: liwenwu-amazon Date: Mon, 13 Feb 2023 15:17:36 -0800 Subject: [PATCH] Support for Vanity Name and Bring Your Own Certificates (#85) * Add environment variable to allow specifying lattice service endpoint * Update lattice sdk APIs to the version which contains BYOC * Update with right byoc SDK * Use 1st hostname of HTTProute as lattice customer-domain-name * Add logic to parse certARN and pass it to lattice * Postpone targetgroup/target reconcile during HTTPRoute delete (#83) * Add some examples that uses vanity name and customer own certificates * Add unit test on hostname parse logic * Add unit test for parsing certificate ARN * Fix go fmt * Add lattice-assigned-dns annotation to httproute * Add doc on how to configure custom domain name * minor update on custom domain name doc * Minor update to custom doman name doc * Add doc on BYOC certification * Add doc on BYOC * Add nil check to avoid crashing --- controllers/httproute_controller.go | 11 +- docs/customer_domain_name.md | 33 ++++ docs/https_byoc.md | 113 ++++++++++++++ examples/http-route-use-vanity-name.yaml | 19 +++ examples/my-hotel-gateway-tls.yaml | 20 +++ examples/tls-route-with-own-cert.yaml | 19 +++ pkg/deploy/lattice/service_manager.go | 24 +++ pkg/gateway/model_build_lattice_service.go | 12 ++ .../model_build_lattice_service_test.go | 32 ++++ pkg/gateway/model_build_listener.go | 30 +++- pkg/gateway/model_build_listener_test.go | 147 +++++++++++++++++- pkg/gateway/model_build_rule.go | 2 +- pkg/model/lattice/service.go | 2 + .../apis/vpc-lattice/2022-11-30/api-2.json | 110 ++++++++----- 14 files changed, 526 insertions(+), 48 deletions(-) create mode 100644 docs/customer_domain_name.md create mode 100644 docs/https_byoc.md create mode 100644 examples/http-route-use-vanity-name.yaml create mode 100644 examples/my-hotel-gateway-tls.yaml create mode 100644 examples/tls-route-with-own-cert.yaml diff --git a/controllers/httproute_controller.go b/controllers/httproute_controller.go index e1e65e2e..9ed32941 100644 --- a/controllers/httproute_controller.go +++ b/controllers/httproute_controller.go @@ -275,6 +275,13 @@ func (r *HTTPRouteReconciler) updateHTTPRouteStatus(ctx context.Context, dns str httproute.Status.RouteStatus.Parents[0].Conditions = make([]metav1.Condition, 1) httproute.Status.RouteStatus.Parents[0].Conditions[0].LastTransitionTime = eventhandlers.ZeroTransitionTime } + + if len(httproute.ObjectMeta.Annotations) == 0 { + httproute.ObjectMeta.Annotations = make(map[string]string) + } + + httproute.ObjectMeta.Annotations["application-networking.k8s.aws/lattice-assigned-domain-name"] = dns + httproute.Status.RouteStatus.Parents[0].ControllerName = config.LatticeGatewayControllerName httproute.Status.RouteStatus.Parents[0].Conditions[0].Type = "httproute" @@ -290,8 +297,8 @@ func (r *HTTPRouteReconciler) updateHTTPRouteStatus(ctx context.Context, dns str httproute.Status.RouteStatus.Parents[0].ParentRef.Kind = httproute.Spec.ParentRefs[0].Kind httproute.Status.RouteStatus.Parents[0].ParentRef.Name = httproute.Spec.ParentRefs[0].Name - if err := r.Client.Status().Patch(ctx, httproute, client.MergeFrom(httprouteOld)); err != nil { - glog.V(6).Infof("updateHTTPRouteStatus: Patch() received err %v \n", err) + if err := r.Client.Patch(ctx, httproute, client.MergeFrom(httprouteOld)); err != nil { + glog.V(2).Infof("updateHTTPRouteStatus: Patch() received err %v \n", err) return errors.Wrapf(err, "failed to update httproute status") } diff --git a/docs/customer_domain_name.md b/docs/customer_domain_name.md new file mode 100644 index 00000000..d6dd20ae --- /dev/null +++ b/docs/customer_domain_name.md @@ -0,0 +1,33 @@ +# Configure a Custom Domain Name for HTTPRoute +Today when you create a HTTPRoute using `amazon-vpc-lattice` gatewayclass, Lattice gateway-api-controller creates a AWS VPC Lattice Service during reconciliation. And VPC Lattice generates a unique Fully Qualified Domain Name (FQDN). However, this VPC Lattice generated domain name is not easy for customers to remember and use. + +If you'd prefer to use a custom domain name for a HTTPRoute, you can specify them in hostname field of HTTPRoute. Here is one example + +``` +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: HTTPRoute +metadata: + name: review +spec: + hostnames: + - review.my-test.com <----------- this is the custom domain name + parentRefs: + - name: my-hotel + sectionName: http + rules: + - backendRefs: + - name: review2 + kind: Service + port: 8090 + matches: + - path: + type: PathPrefix + value: /review2 + +``` + +## Notes + +* You MUST have a registered domain name (e.g. `my-test.com`) in route53 and complete the `Prerequisites` mentioned in [TODO - public BYOC doc](http://dev-dsk-tnmat-1d-8836d755.us-east-1.amazon.com/mercury/build/AWSMercuryDocs/AWSMercuryDocs-3.0/AL2_x86_64/DEV.STD.PTHREAD/build/server-root/vpc-lattice/latest/ug/service-custom-domain-name.html#dns-associate-custom) + +* In addition, you NEED to manually associate your custom domain name with your service following [TODO - public BYOC doc](http://dev-dsk-tnmat-1d-8836d755.us-east-1.amazon.com/mercury/build/AWSMercuryDocs/AWSMercuryDocs-3.0/AL2_x86_64/DEV.STD.PTHREAD/build/server-root/vpc-lattice/latest/ug/service-custom-domain-name.html#dns-associate-custom). We do have [github issue](https://github.com/aws/aws-application-networking-k8s/issues/88), an enhancement request, to automate this process \ No newline at end of file diff --git a/docs/https_byoc.md b/docs/https_byoc.md new file mode 100644 index 00000000..c61a2736 --- /dev/null +++ b/docs/https_byoc.md @@ -0,0 +1,113 @@ +# HTTPS and Bring Your Own Certificte (BYOC) +## Securing Traffic using HTTPS + +Today, the HTTPRoute owner can specify all incoming traffic `MUST` use HTTPs. e.g. + +``` +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: Gateway +metadata: + name: my-hotel +spec: + gatewayClassName: amazon-vpc-lattice + listeners: + - name: http + protocol: HTTP + port: 80 + - name: https <-------------- specify HTTPs listener + protocol: HTTPS + port: 443 +``` + +``` +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: HTTPRoute +metadata: + name: rates +spec: + parentRefs: + - name: my-hotel + sectionName: http + - name: my-hotel + sectionName: https <--- specify all traffic MUST use HTTPs + rules: + - backendRefs: + - name: parking + kind: Service + port: 8090 + matches: + - path: + type: PathPrefix + value: /parking + - backendRefs: + - name: review + kind: Service + port: 8090 + matches: + - path: + type: PathPrefix + value: /review +``` + +In this case, VPC Lattice service will automatically generate a managed ACM certificate and use it for encryting client to service traffic. + +## Bring Your Own Certificate (BYOC) + +If customer desires to use custom domain name along with their own certificate, they can do following: +* follow [TODO Bring Your Own Certicate DOC](http://dev-dsk-tnmat-1d-8836d755.us-east-1.amazon.com/mercury/build/AWSMercuryDocs/AWSMercuryDocs-3.0/AL2_x86_64/DEV.STD.PTHREAD/build/server-root/vpc-lattice/latest/ug/service-byoc.html), and get ACM certificate ARN +* specify certificate ARN + +``` +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: Gateway +metadata: + name: my-hotel +spec: + gatewayClassName: amazon-vpc-lattice + listeners: + - name: http + protocol: HTTP + port: 80 + - name: https + protocol: HTTPS + port: 443 + - name: rates-with-custom-cert + protocol: HTTPS + port: 443 + tls: + mode: Terminate + options: + application-networking.k8s.aws/certificate-arn: arn:aws:acm:us-west-2::certificate/4555204d-07e1-43f0-a533-d02750f41545 +``` + +* associate HTTPRoute to this + +``` +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: HTTPRoute +metadata: + name: rates +spec: + parentRefs: + - name: my-hotel + sectionName: http + - name: my-hotel + sectionName: rates-with-custom-cert <-----using custom defined certification + rules: + - backendRefs: + - name: parking + kind: Service + port: 8090 + matches: + - path: + type: PathPrefix + value: /parking + - backendRefs: + - name: review + kind: Service + port: 8090 + matches: + - path: + type: PathPrefix + value: /review +``` \ No newline at end of file diff --git a/examples/http-route-use-vanity-name.yaml b/examples/http-route-use-vanity-name.yaml new file mode 100644 index 00000000..7faf006c --- /dev/null +++ b/examples/http-route-use-vanity-name.yaml @@ -0,0 +1,19 @@ +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: HTTPRoute +metadata: + name: review +spec: + hostnames: + - review.my-test.com + parentRefs: + - name: my-hotel + sectionName: http + rules: + - backendRefs: + - name: review2 + kind: Service + port: 8090 + matches: + - path: + type: PathPrefix + value: /review2 diff --git a/examples/my-hotel-gateway-tls.yaml b/examples/my-hotel-gateway-tls.yaml new file mode 100644 index 00000000..48861296 --- /dev/null +++ b/examples/my-hotel-gateway-tls.yaml @@ -0,0 +1,20 @@ +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: Gateway +metadata: + name: my-hotel +spec: + gatewayClassName: amazon-vpc-lattice + listeners: + - name: http + protocol: HTTP + port: 80 + - name: https + protocol: HTTPS + port: 443 + - name: tls-with-customer-cert + protocol: HTTPS + port: 443 + tls: + mode: Terminate + options: + application-networking.k8s.aws/certificate-arn: arn:aws:acm:us-west-2::certificate/4555204d-07e1-43f0-a533-d02750f41545 diff --git a/examples/tls-route-with-own-cert.yaml b/examples/tls-route-with-own-cert.yaml new file mode 100644 index 00000000..5edc5016 --- /dev/null +++ b/examples/tls-route-with-own-cert.yaml @@ -0,0 +1,19 @@ +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: HTTPRoute +metadata: + name: review +spec: + hostnames: + - review.my-test.com + parentRefs: + - name: my-hotel + sectionName: tls-with-customer-cert + rules: + - backendRefs: + - name: review1 + kind: Service + port: 8090 + matches: + - path: + type: PathPrefix + value: /review1 diff --git a/pkg/deploy/lattice/service_manager.go b/pkg/deploy/lattice/service_manager.go index 51f6c924..c5607486 100644 --- a/pkg/deploy/lattice/service_manager.go +++ b/pkg/deploy/lattice/service_manager.go @@ -76,6 +76,14 @@ func (s *defaultServiceManager) Create(ctx context.Context, service *latticemode Name: &svcName, Tags: nil, } + + if len(service.Spec.CustomerDomainName) > 0 { + serviceInput.CustomDomainName = &service.Spec.CustomerDomainName + } + + if len(service.Spec.CustomerCertARN) > 0 { + serviceInput.SetCertificateArn(service.Spec.CustomerCertARN) + } latticeSess := s.cloud.Lattice() resp, err := latticeSess.CreateServiceWithContext(ctx, &serviceInput) glog.V(2).Infof("CreateServiceWithContext >>>> req %v resp %v err %v\n", serviceInput, resp, err) @@ -92,6 +100,22 @@ func (s *defaultServiceManager) Create(ctx context.Context, service *latticemode if serviceSummary.DnsEntry != nil { serviceDNS = aws.StringValue(serviceSummary.DnsEntry.DomainName) } + + if len(service.Spec.CustomerCertARN) > 0 { + serviceUpdateInput := vpclattice.UpdateServiceInput{ + ServiceIdentifier: serviceSummary.Id, + CertificateArn: aws.String(service.Spec.CustomerCertARN), + } + + latticeSess := s.cloud.Lattice() + resp, err := latticeSess.UpdateServiceWithContext(ctx, &serviceUpdateInput) + glog.V(2).Infof("UpdateServiceWithContext >>>> req %v resp %v err %v\n", serviceUpdateInput, resp, err) + if err != nil { + glog.V(6).Infoln("fail to update service") + return latticemodel.ServiceStatus{ServiceARN: "", ServiceID: ""}, err + } + + } isServiceAssociatedWithServiceNetwork, serviceDNS, err = s.isServiceAssociatedWithServiceNetwork(ctx, serviceID, serviceNetwork.ID) if err != nil { return latticemodel.ServiceStatus{ServiceARN: "", ServiceID: ""}, err diff --git a/pkg/gateway/model_build_lattice_service.go b/pkg/gateway/model_build_lattice_service.go index 1c312d03..8c849bd0 100644 --- a/pkg/gateway/model_build_lattice_service.go +++ b/pkg/gateway/model_build_lattice_service.go @@ -125,6 +125,18 @@ func (t *latticeServiceModelBuildTask) buildLatticeService(ctx context.Context) ServiceNetworkName: string(t.httpRoute.Spec.ParentRefs[0].Name), } + if len(t.httpRoute.Spec.Hostnames) > 0 { + // The 1st hostname will be used as lattice customer-domain-name + spec.CustomerDomainName = string(t.httpRoute.Spec.Hostnames[0]) + + glog.V(2).Infof("Setting customer-domain-name: %v for httpRoute %v-%v", + spec.CustomerDomainName, t.httpRoute.Name, t.httpRoute.Namespace) + } else { + glog.V(2).Infof("No customter-domain-name for httproute :%v-%v", + t.httpRoute.Name, t.httpRoute.Namespace) + spec.CustomerDomainName = "" + } + if t.httpRoute.DeletionTimestamp.IsZero() { spec.IsDeleted = false } else { diff --git a/pkg/gateway/model_build_lattice_service_test.go b/pkg/gateway/model_build_lattice_service_test.go index c9419389..1a9d8f75 100644 --- a/pkg/gateway/model_build_lattice_service_test.go +++ b/pkg/gateway/model_build_lattice_service_test.go @@ -56,6 +56,32 @@ func Test_LatticeServiceModelBuild(t *testing.T) { wantName string wantIsDeleted bool }{ + { + name: "Add LatticeService with hostname", + httpRoute: &v1alpha2.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "service1", + }, + Spec: v1alpha2.HTTPRouteSpec{ + CommonRouteSpec: v1alpha2.CommonRouteSpec{ + ParentRefs: []v1alpha2.ParentRef{ + { + Name: "gateway1", + }, + }, + }, + Hostnames: []v1alpha2.Hostname{ + "test1.test.com", + "test2.test.com", + }, + }, + }, + + wantError: nil, + wantName: "service1", + wantIsDeleted: false, + wantErrIsNil: true, + }, { name: "Add LatticeService", httpRoute: &v1alpha2.HTTPRoute{ @@ -159,6 +185,12 @@ func Test_LatticeServiceModelBuild(t *testing.T) { assert.Equal(t, false, task.latticeService.Spec.IsDeleted) assert.Equal(t, tt.httpRoute.Name, task.latticeService.Spec.Name) assert.Equal(t, tt.httpRoute.Namespace, task.latticeService.Spec.Namespace) + + if len(tt.httpRoute.Spec.Hostnames) > 0 { + assert.Equal(t, string(tt.httpRoute.Spec.Hostnames[0]), task.latticeService.Spec.CustomerDomainName) + } else { + assert.Equal(t, "", task.latticeService.Spec.CustomerDomainName) + } } if tt.wantErrIsNil { diff --git a/pkg/gateway/model_build_listener.go b/pkg/gateway/model_build_listener.go index 53750e5f..78cb8189 100644 --- a/pkg/gateway/model_build_listener.go +++ b/pkg/gateway/model_build_listener.go @@ -14,9 +14,11 @@ import ( const ( resourceIDListenerConfig = "ListenerConfig" + + awsCustomCertARN = "application-networking.k8s.aws/certificate-arn" ) -func (t *latticeServiceModelBuildTask) extractListnerInfo(ctx context.Context, parentRef v1alpha2.ParentRef) (int64, string, error) { +func (t *latticeServiceModelBuildTask) extractListnerInfo(ctx context.Context, parentRef v1alpha2.ParentRef) (int64, string, string, error) { var protocol v1alpha2.ProtocolType = v1alpha2.HTTPProtocolType if parentRef.SectionName != nil { @@ -39,9 +41,10 @@ func (t *latticeServiceModelBuildTask) extractListnerInfo(ctx context.Context, p if err := t.Client.Get(ctx, gwName, gw); err != nil { glog.V(6).Infof("Failed to build Listener due to unknow http parent ref , Name %v, err %v \n", gwName, err) - return 0, "", err + return 0, "", "", err } + var certARN = "" // go through parent find out the matching section name if parentRef.SectionName != nil { glog.V(6).Infof("HTTP SectionName %s \n", *parentRef.SectionName) @@ -51,6 +54,20 @@ func (t *latticeServiceModelBuildTask) extractListnerInfo(ctx context.Context, p listenerPort = int(section.Port) protocol = section.Protocol + if section.TLS != nil { + if section.TLS.Mode != nil && *section.TLS.Mode == v1alpha2.TLSModeTerminate { + curCertARN, ok := section.TLS.Options[awsCustomCertARN] + + if ok { + glog.V(6).Infof("Found certification %v under section %v", + curCertARN, section.Name) + certARN = string(curCertARN) + } + + } + + } + } } } else { @@ -59,12 +76,12 @@ func (t *latticeServiceModelBuildTask) extractListnerInfo(ctx context.Context, p if len(gw.Spec.Listeners) == 0 { glog.V(6).Infof("Error building listener, there is NO listeners on GW for %v\n", gwName) - return 0, "", errors.New("Error building listener, there is NO listeners on GW") + return 0, "", "", errors.New("Error building listener, there is NO listeners on GW") } listenerPort = int(gw.Spec.Listeners[0].Port) } - return int64(listenerPort), string(protocol), nil + return int64(listenerPort), string(protocol), certARN, nil } @@ -72,12 +89,15 @@ func (t *latticeServiceModelBuildTask) buildListener(ctx context.Context) error for _, parentRef := range t.httpRoute.Spec.ParentRefs { - port, protocol, err := t.extractListnerInfo(ctx, parentRef) + port, protocol, certARN, err := t.extractListnerInfo(ctx, parentRef) if err != nil { glog.V(6).Infof("Error on buildListener %v\n", err) return err } + if t.latticeService != nil { + t.latticeService.Spec.CustomerCertARN = certARN + } glog.V(6).Infof("Building Listener: found matching listner Port %v\n", port) diff --git a/pkg/gateway/model_build_listener_test.go b/pkg/gateway/model_build_listener_test.go index a54abf98..2299fbcf 100644 --- a/pkg/gateway/model_build_listener_test.go +++ b/pkg/gateway/model_build_listener_test.go @@ -54,6 +54,10 @@ func Test_ListenerModelBuild(t *testing.T) { wantErrIsNil bool k8sGetGatewayCall bool k8sGatewayReturnOK bool + tlsTerminate bool + noTLSOption bool + wrongTLSOption bool + certARN string }{ { name: "listener, default service action", @@ -87,6 +91,108 @@ func Test_ListenerModelBuild(t *testing.T) { }, }, }, + { + name: "listener, tls with cert arn", + gwListenerPort: *PortNumberPtr(80), + wantErrIsNil: true, + k8sGetGatewayCall: true, + k8sGatewayReturnOK: true, + tlsTerminate: true, + certARN: "test-cert-ARN", + httpRoute: &v1alpha2.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "service1", + Namespace: "default", + }, + Spec: v1alpha2.HTTPRouteSpec{ + CommonRouteSpec: v1alpha2.CommonRouteSpec{ + ParentRefs: []v1alpha2.ParentRef{ + { + Name: "mesh1", + SectionName: &httpSectionName, + }, + }, + }, + Rules: []v1alpha2.HTTPRouteRule{ + { + BackendRefs: []v1alpha2.HTTPBackendRef{ + { + BackendRef: backendRef, + }, + }, + }, + }, + }, + }, + }, + { + name: "listener, tls mode is not terminate", + gwListenerPort: *PortNumberPtr(80), + wantErrIsNil: true, + k8sGetGatewayCall: true, + k8sGatewayReturnOK: true, + tlsTerminate: false, + certARN: "test-cert-ARN", + httpRoute: &v1alpha2.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "service1", + Namespace: "default", + }, + Spec: v1alpha2.HTTPRouteSpec{ + CommonRouteSpec: v1alpha2.CommonRouteSpec{ + ParentRefs: []v1alpha2.ParentRef{ + { + Name: "mesh1", + SectionName: &httpSectionName, + }, + }, + }, + Rules: []v1alpha2.HTTPRouteRule{ + { + BackendRefs: []v1alpha2.HTTPBackendRef{ + { + BackendRef: backendRef, + }, + }, + }, + }, + }, + }, + }, + { + name: "listener, with wrong annotation", + gwListenerPort: *PortNumberPtr(80), + wantErrIsNil: true, + k8sGetGatewayCall: true, + k8sGatewayReturnOK: true, + tlsTerminate: false, + certARN: "test-cert-ARN", + httpRoute: &v1alpha2.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "service1", + Namespace: "default", + }, + Spec: v1alpha2.HTTPRouteSpec{ + CommonRouteSpec: v1alpha2.CommonRouteSpec{ + ParentRefs: []v1alpha2.ParentRef{ + { + Name: "mesh1", + SectionName: &httpSectionName, + }, + }, + }, + Rules: []v1alpha2.HTTPRouteRule{ + { + BackendRefs: []v1alpha2.HTTPBackendRef{ + { + BackendRef: backendRef, + }, + }, + }, + }, + }, + }, + }, { name: "listener, default serviceimport action", gwListenerPort: *PortNumberPtr(80), @@ -193,11 +299,39 @@ func Test_ListenerModelBuild(t *testing.T) { func(ctx context.Context, gwName types.NamespacedName, gw *v1alpha2.Gateway) error { if tt.k8sGatewayReturnOK { - gw.Spec.Listeners = append(gw.Spec.Listeners, v1alpha2.Listener{ + listener := v1alpha2.Listener{ Port: tt.gwListenerPort, Protocol: "HTTP", Name: *tt.httpRoute.Spec.ParentRefs[0].SectionName, - }) + } + + if tt.tlsTerminate { + mode := v1alpha2.TLSModeTerminate + var tlsConfig v1alpha2.GatewayTLSConfig + + if tt.noTLSOption { + tlsConfig = v1alpha2.GatewayTLSConfig{ + Mode: &mode, + } + + } else { + + tlsConfig = v1alpha2.GatewayTLSConfig{ + Mode: &mode, + Options: make(map[v1alpha2.AnnotationKey]v1alpha2.AnnotationValue), + } + + if tt.wrongTLSOption { + tlsConfig.Options["wrong-annotation"] = v1alpha2.AnnotationValue(tt.certARN) + + } else { + tlsConfig.Options[awsCustomCertARN] = v1alpha2.AnnotationValue(tt.certARN) + } + } + listener.TLS = &tlsConfig + + } + gw.Spec.Listeners = append(gw.Spec.Listeners, listener) return nil } else { return errors.New("unknown k8s object") @@ -218,6 +352,9 @@ func Test_ListenerModelBuild(t *testing.T) { Datastore: ds, } + service := latticemodel.Service{} + task.latticeService = &service + err := task.buildListener(ctx) fmt.Printf("task.buildListener err: %v \n", err) @@ -254,5 +391,11 @@ func Test_ListenerModelBuild(t *testing.T) { assert.Equal(t, resListener[0].Spec.DefaultAction.Is_Import, true) } + if tt.tlsTerminate && !tt.noTLSOption && !tt.wrongTLSOption { + assert.Equal(t, task.latticeService.Spec.CustomerCertARN, tt.certARN) + } else { + assert.Equal(t, task.latticeService.Spec.CustomerCertARN, "") + } + } } diff --git a/pkg/gateway/model_build_rule.go b/pkg/gateway/model_build_rule.go index 93b7743b..6adc16d3 100644 --- a/pkg/gateway/model_build_rule.go +++ b/pkg/gateway/model_build_rule.go @@ -15,7 +15,7 @@ const ( func (t *latticeServiceModelBuildTask) buildRules(ctx context.Context) error { var ruleID = 1 for _, parentRef := range t.httpRoute.Spec.ParentRefs { - port, protocol, err := t.extractListnerInfo(ctx, parentRef) + port, protocol, _, err := t.extractListnerInfo(ctx, parentRef) if err != nil { glog.V(6).Infof("Error on buildRules %v \n", err) diff --git a/pkg/model/lattice/service.go b/pkg/model/lattice/service.go index 5d7d4ef6..3bdbc870 100644 --- a/pkg/model/lattice/service.go +++ b/pkg/model/lattice/service.go @@ -17,6 +17,8 @@ type ServiceSpec struct { Namespace string `json:"namespace"` Protocols []*string `json:"protocols"` ServiceNetworkName string `json:"servicenetworkhname"` + CustomerDomainName string `json:"customerdomainname"` + CustomerCertARN string `json:"customercertarn"` IsDeleted bool } diff --git a/scripts/aws_sdk_model_override/models/apis/vpc-lattice/2022-11-30/api-2.json b/scripts/aws_sdk_model_override/models/apis/vpc-lattice/2022-11-30/api-2.json index ee412716..b7bfd427 100644 --- a/scripts/aws_sdk_model_override/models/apis/vpc-lattice/2022-11-30/api-2.json +++ b/scripts/aws_sdk_model_override/models/apis/vpc-lattice/2022-11-30/api-2.json @@ -1,15 +1,16 @@ { "version":"2.0", "metadata":{ - "apiVersion":"2021-08-17", - "endpointPrefix":"mercury", + "apiVersion":"2022-11-30", + "endpointPrefix":"vpc-lattice", "jsonVersion":"1.1", "protocol":"rest-json", + "ripServiceName":"vpc-lattice", "serviceFullName":"VPC Lattice", "serviceId":"VpcLattice", "signatureVersion":"v4", "signingName":"vpc-lattice", - "uid":"vpclattice-2021-08-17" + "uid":"vpclattice-2022-11-30" }, "operations":{ "BatchUpdateRule":{ @@ -965,19 +966,19 @@ "type":"string", "max":2048, "min":20, - "pattern":"^arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:accesslogsubscription/als-[0-9a-z]{17}$" + "pattern":"^arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:accesslogsubscription/als-[0-9a-z]{17}$" }, "AccessLogSubscriptionId":{ "type":"string", - "max":17, - "min":17, + "max":21, + "min":21, "pattern":"^als-[0-9a-z]{17}$" }, "AccessLogSubscriptionIdentifier":{ "type":"string", "max":2048, "min":17, - "pattern":"^((als-[0-9a-z]{17})|(arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:accesslogsubscription/als-[0-9a-z]{17}))$" + "pattern":"^((als-[0-9a-z]{17})|(arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:accesslogsubscription/als-[0-9a-z]{17}))$" }, "AccessLogSubscriptionList":{ "type":"list", @@ -1014,7 +1015,7 @@ "type":"string", "max":1224, "min":0, - "pattern":"^arn:[a-z0-9][-.a-z0-9]{0,62}:[a-z0-9][-.a-z0-9]{0,62}:([a-z0-9][-.a-z0-9]{0,62})?:([a-z0-9][-.a-z0-9]{0,62})?:[^/].{0,1023}$" + "pattern":"^arn:[a-z0-9][-.a-z0-9]{0,62}:vpc-lattice:([a-z0-9][-.a-z0-9]{0,62})?:\\d{12}?:[^/].{0,1023}$" }, "AuthPolicyState":{ "type":"string", @@ -1064,7 +1065,16 @@ "unsuccessful":{"shape":"RuleUpdateFailureList"} } }, - "Boolean":{"type":"boolean"}, + "Boolean":{ + "type":"boolean", + "box":true + }, + "CertificateArn":{ + "type":"string", + "max":2048, + "min":0, + "pattern":"^(arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:certificate/[0-9a-z-]+)?$" + }, "ClientToken":{ "type":"string", "max":64, @@ -1246,6 +1256,7 @@ "members":{ "arn":{"shape":"ServiceNetworkServiceAssociationArn"}, "createdBy":{"shape":"AccountId"}, + "customDomainName":{"shape":"ServiceCustomDomainName"}, "dnsEntry":{"shape":"DnsEntry"}, "id":{"shape":"ServiceNetworkServiceAssociationIdentifier"}, "status":{"shape":"ServiceNetworkServiceAssociationStatus"} @@ -1289,10 +1300,12 @@ "required":["name"], "members":{ "authType":{"shape":"AuthType"}, + "certificateArn":{"shape":"CertificateArn"}, "clientToken":{ "shape":"ClientToken", "idempotencyToken":true }, + "customDomainName":{"shape":"ServiceCustomDomainName"}, "name":{"shape":"ServiceName"}, "tags":{"shape":"TagMap"} } @@ -1302,6 +1315,8 @@ "members":{ "arn":{"shape":"ServiceArn"}, "authType":{"shape":"AuthType"}, + "certificateArn":{"shape":"CertificateArn"}, + "customDomainName":{"shape":"ServiceCustomDomainName"}, "dnsEntry":{"shape":"DnsEntry"}, "id":{"shape":"ServiceId"}, "name":{"shape":"ServiceName"}, @@ -1758,6 +1773,7 @@ "arn":{"shape":"ServiceNetworkServiceAssociationArn"}, "createdAt":{"shape":"Timestamp"}, "createdBy":{"shape":"AccountId"}, + "customDomainName":{"shape":"ServiceCustomDomainName"}, "dnsEntry":{"shape":"DnsEntry"}, "failureCode":{"shape":"String"}, "failureMessage":{"shape":"String"}, @@ -1816,7 +1832,9 @@ "members":{ "arn":{"shape":"ServiceArn"}, "authType":{"shape":"AuthType"}, + "certificateArn":{"shape":"CertificateArn"}, "createdAt":{"shape":"Timestamp"}, + "customDomainName":{"shape":"ServiceCustomDomainName"}, "dnsEntry":{"shape":"DnsEntry"}, "failureCode":{"shape":"FailureCode"}, "failureMessage":{"shape":"FailureMessage"}, @@ -1923,7 +1941,8 @@ "HealthCheckPath":{ "type":"string", "max":2048, - "min":1 + "min":1, + "pattern":"^/[a-zA-Z0-9@:%_+.~#?&/=]*$" }, "HealthCheckTimeoutSeconds":{ "type":"integer", @@ -2271,19 +2290,19 @@ "type":"string", "max":2048, "min":20, - "pattern":"^arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:service/svc-[0-9a-z]{17}/listener/listener-[0-9a-z]{17}$" + "pattern":"^arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:service/svc-[0-9a-z]{17}/listener/listener-[0-9a-z]{17}$" }, "ListenerId":{ "type":"string", - "max":17, - "min":17, + "max":26, + "min":26, "pattern":"^listener-[0-9a-z]{17}$" }, "ListenerIdentifier":{ "type":"string", "max":2048, "min":20, - "pattern":"^((listener-[0-9a-z]{17})|(arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:service/svc-[0-9a-z]{17}/listener/listener-[0-9a-z]{17}))$" + "pattern":"^((listener-[0-9a-z]{17})|(^arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:service/svc-[0-9a-z]{17}/listener/listener-[0-9a-z]{17}$))$" }, "ListenerName":{ "type":"string", @@ -2347,12 +2366,14 @@ "PathMatchExact":{ "type":"string", "max":200, - "min":1 + "min":1, + "pattern":"^/[a-zA-Z0-9@:%_+.~#?&/=]*$" }, "PathMatchPrefix":{ "type":"string", "max":200, - "min":1 + "min":1, + "pattern":"^/[a-zA-Z0-9@:%_+.~#?&/=]*$" }, "PathMatchType":{ "type":"structure", @@ -2491,19 +2512,19 @@ "type":"string", "max":2048, "min":20, - "pattern":"^arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:service/svc-[0-9a-z]{17}/listener/listener-[0-9a-z]{17}/rule/((rule-[0-9a-z]{17})|(default))$" + "pattern":"^arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:service/svc-[0-9a-z]{17}/listener/listener-[0-9a-z]{17}/rule/rule-[0-9a-z]{17}$" }, "RuleId":{ "type":"string", - "max":17, - "min":17, - "pattern":"^((rule-[0-9a-z]{17})|(default))$" + "max":22, + "min":5, + "pattern":"^rule-[0-9a-z]{17}$" }, "RuleIdentifier":{ "type":"string", "max":2048, "min":20, - "pattern":"^((rule-[0-9a-z]{17})|(arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:service/svc-[0-9a-z]{17}/listener/listener-[0-9a-z]{17}/rule/((rule-[0-9a-z]{17})|(default))))$" + "pattern":"^((rule-[0-9a-z]{17})|(^arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:service/svc-[0-9a-z]{17}/listener/listener-[0-9a-z]{17}/rule/rule-[0-9a-z]{17}$))$" }, "RuleMatch":{ "type":"structure", @@ -2597,23 +2618,28 @@ "type":"string", "max":2048, "min":20, - "pattern":"^arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:service/svc-[0-9a-z]{17}$" + "pattern":"^arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:service/svc-[0-9a-z]{17}$" }, "ServiceArnList":{ "type":"list", "member":{"shape":"ServiceArn"} }, + "ServiceCustomDomainName":{ + "type":"string", + "max":255, + "min":3 + }, "ServiceId":{ "type":"string", - "max":17, - "min":17, + "max":21, + "min":21, "pattern":"^svc-[0-9a-z]{17}$" }, "ServiceIdentifier":{ "type":"string", "max":2048, "min":17, - "pattern":"^((svc-[0-9a-z]{17})|(arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:service/svc-[0-9a-z]{17}))$" + "pattern":"^((svc-[0-9a-z]{17})|(arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:service/svc-[0-9a-z]{17}))$" }, "ServiceList":{ "type":"list", @@ -2629,7 +2655,7 @@ "type":"string", "max":2048, "min":32, - "pattern":"^arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:servicenetwork/sn-[0-9a-z]{17}$" + "pattern":"^arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:servicenetwork/sn-[0-9a-z]{17}$" }, "ServiceNetworkId":{ "type":"string", @@ -2641,7 +2667,7 @@ "type":"string", "max":2048, "min":3, - "pattern":"^((sn-[0-9a-z]{17})|(arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:servicenetwork/sn-[0-9a-z]{17}))$" + "pattern":"^((sn-[0-9a-z]{17})|(arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:servicenetwork/sn-[0-9a-z]{17}))$" }, "ServiceNetworkList":{ "type":"list", @@ -2657,13 +2683,13 @@ "type":"string", "max":2048, "min":20, - "pattern":"^arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:servicenetworkserviceassociation/snsa-[0-9a-z]{17}$" + "pattern":"^arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:servicenetworkserviceassociation/snsa-[0-9a-z]{17}$" }, "ServiceNetworkServiceAssociationIdentifier":{ "type":"string", "max":2048, "min":17, - "pattern":"^((snsa-[0-9a-z]{17})|(arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:servicenetworkserviceassociation/snsa-[0-9a-z]{17}))$" + "pattern":"^((snsa-[0-9a-z]{17})|(arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:servicenetworkserviceassociation/snsa-[0-9a-z]{17}))$" }, "ServiceNetworkServiceAssociationList":{ "type":"list", @@ -2685,6 +2711,7 @@ "arn":{"shape":"ServiceNetworkServiceAssociationArn"}, "createdAt":{"shape":"Timestamp"}, "createdBy":{"shape":"AccountId"}, + "customDomainName":{"shape":"ServiceCustomDomainName"}, "dnsEntry":{"shape":"DnsEntry"}, "id":{"shape":"ServiceNetworkServiceAssociationIdentifier"}, "serviceArn":{"shape":"ServiceArn"}, @@ -2712,19 +2739,19 @@ "type":"string", "max":2048, "min":20, - "pattern":"^arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:servicenetworkvpcassociation/snva-[0-9a-z]{17}$" + "pattern":"^arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:servicenetworkvpcassociation/snva-[0-9a-z]{17}$" }, "ServiceNetworkVpcAssociationId":{ "type":"string", - "max":17, - "min":17, + "max":22, + "min":22, "pattern":"^snva-[0-9a-z]{17}$" }, "ServiceNetworkVpcAssociationIdentifier":{ "type":"string", "max":2048, "min":17, - "pattern":"^((snva-[0-9a-z]{17})|(arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:servicenetworkvpcassociation/snva-[0-9a-z]{17}))$" + "pattern":"^((snva-[0-9a-z]{17})|(arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:servicenetworkvpcassociation/snva-[0-9a-z]{17}))$" }, "ServiceNetworkVpcAssociationList":{ "type":"list", @@ -2793,6 +2820,7 @@ "members":{ "arn":{"shape":"ServiceArn"}, "createdAt":{"shape":"Timestamp"}, + "customDomainName":{"shape":"ServiceCustomDomainName"}, "dnsEntry":{"shape":"DnsEntry"}, "id":{"shape":"ServiceId"}, "lastUpdatedAt":{"shape":"Timestamp"}, @@ -2869,7 +2897,7 @@ "type":"string", "max":2048, "min":20, - "pattern":"^arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:targetgroup/tg-[0-9a-z]{17}$" + "pattern":"^arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:targetgroup/tg-[0-9a-z]{17}$" }, "TargetGroupConfig":{ "type":"structure", @@ -2888,15 +2916,15 @@ }, "TargetGroupId":{ "type":"string", - "max":17, - "min":17, + "max":20, + "min":20, "pattern":"^tg-[0-9a-z]{17}$" }, "TargetGroupIdentifier":{ "type":"string", "max":2048, "min":17, - "pattern":"^((tg-[0-9a-z]{17})|(arn(:[a-z0-9]+([.-][a-z0-9]+)*){2}(:([a-z0-9]+([.-][a-z0-9]+)*)?){2}:targetgroup/tg-[0-9a-z]{17}))$" + "pattern":"^((tg-[0-9a-z]{17})|(arn:[a-z0-9\\-]+:vpc-lattice:[a-zA-Z0-9\\-]+:\\d{12}:targetgroup/tg-[0-9a-z]{17}))$" }, "TargetGroupList":{ "type":"list", @@ -2941,9 +2969,12 @@ "id":{"shape":"TargetGroupId"}, "lastUpdatedAt":{"shape":"Timestamp"}, "name":{"shape":"TargetGroupName"}, + "port":{"shape":"Port"}, + "protocol":{"shape":"TargetGroupProtocol"}, "serviceArns":{"shape":"ServiceArnList"}, "status":{"shape":"TargetGroupStatus"}, - "type":{"shape":"TargetGroupType"} + "type":{"shape":"TargetGroupType"}, + "vpcIdentifier":{"shape":"VpcId"} } }, "TargetGroupType":{ @@ -3213,6 +3244,7 @@ "required":["serviceIdentifier"], "members":{ "authType":{"shape":"AuthType"}, + "certificateArn":{"shape":"CertificateArn"}, "serviceIdentifier":{ "shape":"ServiceIdentifier", "location":"uri", @@ -3225,6 +3257,8 @@ "members":{ "arn":{"shape":"ServiceArn"}, "authType":{"shape":"AuthType"}, + "certificateArn":{"shape":"CertificateArn"}, + "customDomainName":{"shape":"ServiceCustomDomainName"}, "id":{"shape":"ServiceId"}, "name":{"shape":"ServiceName"} }