diff --git a/deploy/traits.yaml b/deploy/traits.yaml index 62f63c06aa..e1ce66dbdc 100755 --- a/deploy/traits.yaml +++ b/deploy/traits.yaml @@ -820,8 +820,17 @@ traits: platform: false profiles: - OpenShift - description: The Route trait can be used to configure the creation of OpenShift - routes for the integration. + description: 'The Route trait can be used to configure the creation of OpenShift + routes for the integration. The certificate and key contents may be sourced either + from the local filesystem or in a Openshift `secret` object. The user may use + the parameters ending in `-secret` (example: `tls-certificate-secret`) to reference + a certificate stored in a `secret`. Parameters ending in `-secret` have higher + priorities and in case the same route parameter is set, for example: `tls-key-secret` + and `tls-key`, then `tls-key-secret` is used. The recommended approach to set + the key and certificates is to use `secrets` to store their contents and use the + following parameters to reference them: `tls-certificate-secret`, `tls-key-secret`, + `tls-ca-certificate-secret`, `tls-destination-ca-certificate-secret` See the examples + section at the end of this page to see the setup options.' properties: - name: enabled type: bool @@ -833,19 +842,40 @@ traits: - name: tls-termination type: string description: The TLS termination type, like `edge`, `passthrough` or `reencrypt`.Refer - to the OpenShift documentation for additional information. + to the OpenShift route documentation for additional information. - name: tls-certificate type: string - description: The TLS certificate contents.Refer to the OpenShift documentation + description: The TLS certificate contents.Refer to the OpenShift route documentation for additional information. + - name: tls-certificate-secret + type: string + description: The secret name and key reference to the TLS certificate. The format + is "secret-name[/key-name]", the value represents the secret name, if there + is only one key in the secret it will be read, otherwise you can set a key name + separated with a "/".Refer to the OpenShift route documentation for additional + information. - name: tls-key type: string - description: The TLS certificate key contents.Refer to the OpenShift documentation + description: The TLS certificate key contents.Refer to the OpenShift route documentation for additional information. + - name: tls-key-secret + type: string + description: The secret name and key reference to the TLS certificate key. The + format is "secret-name[/key-name]", the value represents the secret name, if + there is only one key in the secret it will be read, otherwise you can set a + key name separated with a "/".Refer to the OpenShift route documentation for + additional information. - name: tls-ca-certificate type: string - description: The TLS cert authority certificate contents.Refer to the OpenShift - documentation for additional information. + description: The TLS CA certificate contents.Refer to the OpenShift route documentation + for additional information. + - name: tls-ca-certificate-secret + type: string + description: The secret name and key reference to the TLS CA certificate. The + format is "secret-name[/key-name]", the value represents the secret name, if + there is only one key in the secret it will be read, otherwise you can set a + key name separated with a "/".Refer to the OpenShift route documentation for + additional information. - name: tls-destination-ca-certificate type: string description: The destination CA certificate provides the contents of the ca certificate @@ -854,11 +884,19 @@ traits: connection.If this field is not specified, the router may provide its own destination CA and perform hostname validation usingthe short service name (service.namespace.svc), which allows infrastructure generated certificates to automaticallyverify.Refer - to the OpenShift documentation for additional information. + to the OpenShift route documentation for additional information. + - name: tls-destination-ca-certificate-secret + type: string + description: The secret name and key reference to the destination CA certificate. + The format is "secret-name[/key-name]", the value represents the secret name, + if there is only one key in the secret it will be read, otherwise you can set + a key name separated with a "/".Refer to the OpenShift route documentation for + additional information. - name: tls-insecure-edge-termination-policy type: string description: To configure how to deal with insecure traffic, e.g. `Allow`, `Disable` - or `Redirect` traffic.Refer to the OpenShift documentation for additional information. + or `Redirect` traffic.Refer to the OpenShift route documentation for additional + information. - name: service-binding platform: false profiles: diff --git a/docs/modules/traits/pages/route.adoc b/docs/modules/traits/pages/route.adoc index 3e2254ef30..493586cefc 100755 --- a/docs/modules/traits/pages/route.adoc +++ b/docs/modules/traits/pages/route.adoc @@ -3,6 +3,14 @@ // Start of autogenerated code - DO NOT EDIT! (description) The Route trait can be used to configure the creation of OpenShift routes for the integration. +The certificate and key contents may be sourced either from the local filesystem or in a Openshift `secret` object. +The user may use the parameters ending in `-secret` (example: `tls-certificate-secret`) to reference a certificate stored in a `secret`. +Parameters ending in `-secret` have higher priorities and in case the same route parameter is set, for example: `tls-key-secret` and `tls-key`, +then `tls-key-secret` is used. +The recommended approach to set the key and certificates is to use `secrets` to store their contents and use the +following parameters to reference them: `tls-certificate-secret`, `tls-key-secret`, `tls-ca-certificate-secret`, `tls-destination-ca-certificate-secret` +See the examples section at the end of this page to see the setup options. + This trait is available in the following profiles: **OpenShift**. @@ -33,25 +41,43 @@ The following configuration options are available: | string | The TLS termination type, like `edge`, `passthrough` or `reencrypt`. -Refer to the OpenShift documentation for additional information. +Refer to the OpenShift route documentation for additional information. | route.tls-certificate | string | The TLS certificate contents. -Refer to the OpenShift documentation for additional information. +Refer to the OpenShift route documentation for additional information. + +| route.tls-certificate-secret +| string +| The secret name and key reference to the TLS certificate. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + +Refer to the OpenShift route documentation for additional information. | route.tls-key | string | The TLS certificate key contents. -Refer to the OpenShift documentation for additional information. +Refer to the OpenShift route documentation for additional information. + +| route.tls-key-secret +| string +| The secret name and key reference to the TLS certificate key. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + +Refer to the OpenShift route documentation for additional information. | route.tls-ca-certificate | string -| The TLS cert authority certificate contents. +| The TLS CA certificate contents. -Refer to the OpenShift documentation for additional information. +Refer to the OpenShift route documentation for additional information. + +| route.tls-ca-certificate-secret +| string +| The secret name and key reference to the TLS CA certificate. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + +Refer to the OpenShift route documentation for additional information. | route.tls-destination-ca-certificate | string @@ -61,14 +87,71 @@ If this field is not specified, the router may provide its own destination CA an the short service name (service.namespace.svc), which allows infrastructure generated certificates to automatically verify. -Refer to the OpenShift documentation for additional information. +Refer to the OpenShift route documentation for additional information. + +| route.tls-destination-ca-certificate-secret +| string +| The secret name and key reference to the destination CA certificate. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + +Refer to the OpenShift route documentation for additional information. | route.tls-insecure-edge-termination-policy | string | To configure how to deal with insecure traffic, e.g. `Allow`, `Disable` or `Redirect` traffic. -Refer to the OpenShift documentation for additional information. +Refer to the OpenShift route documentation for additional information. |=== // End of autogenerated code - DO NOT EDIT! (configuration) + +== Examples + +These examples uses *secrets* to store the certificates and keys to be referenced in the integrations. Read Openshift route documentation for detailed information about routes. The https://github.com/apache/camel-k/blob/main/examples/http/PlatformHttpServer.java[PlatformHttpServer.java] is the integration example. + +As a requirement to run these examples, you should have a `secret` with a key and certificate. + +=== Generate a self-signed certificate and create a secret + +[source,console] +openssl genrsa -out tls.key +openssl req -new -key tls.key -out csr.csr -subj "/CN=my-server.com" +openssl x509 -req -in csr.csr -signkey tls.key -out tls.crt +oc create secret tls my-combined-certs --key=tls.key --cert=tls.crt + +=== Making an HTTP request to the route + +For all examples, you can use the following curl command to make an HTTP request. It makes use of inline scripts to retrieve the openshift namespace and cluster base domain, if you are using a shell which doesn't support these inline scripts, you should replace the inline scripts with the values of your actual namespace and base domain. + +[source,console] +curl -k https://platform-http-server-`oc config view --minify -o 'jsonpath={..namespace}'`.`oc get dnses/cluster -ojsonpath='{.spec.baseDomain}'`/hello?name=Camel-K + +* To add an *edge* route using secrets, use the parameters ending in `-secret` to set the secret name which contains the certificate. This route example trait references a secret named `my-combined-certs` which contains two keys named `tls.key` and `tls.crt`. ++ +[source,console] +kamel run --dev PlatformHttpServer.java -t route.tls-termination=edge -t route.tls-certificate-secret=my-combined-certs/tls.crt -t route.tls-key-secret=my-combined-certs/tls.key + +* To add a *passthrough* route using secrets, the TLS is setup in the integration pod, the keys and certificates should be visible in the running integration pod, to achieve this we are using the `--resource` kamel parameter to mount the secret in the integration pod, then we use some camel quarkus parameters to reference these certificate files in the running pod, they start with `-p quarkus.http.ssl.certificate`. This route example trait references a secret named `my-combined-certs` which contains two keys named `tls.key` and `tls.crt`. ++ +[source,console] +kamel run --dev PlatformHttpServer.java --resource secret:my-combined-certs@/etc/ssl/my-combined-certs -p quarkus.http.ssl.certificate.file=/etc/ssl/my-combined-certs/tls.crt -p quarkus.http.ssl.certificate.key-file=/etc/ssl/my-combined-certs/tls.key -t route.tls-termination=passthrough -t container.port=8443 + +* To add a *reencrypt* route using secrets, the TLS is setup in the integration pod, the keys and certificates should be visible in the running integration pod, to achieve this we are using the `--resource` kamel parameter to mount the secret in the integration pod, then we use some camel quarkus parameters to reference these certificate files in the running pod, they start with `-p quarkus.http.ssl.certificate`. This route example trait references a secret named `my-combined-certs` which contains two keys named `tls.key` and `tls.crt`. ++ +[source,console] +kamel run --dev PlatformHttpServer.java --resource secret:my-combined-certs@/etc/ssl/my-combined-certs -p quarkus.http.ssl.certificate.file=/etc/ssl/my-combined-certs/tls.crt -p quarkus.http.ssl.certificate.key-file=/etc/ssl/my-combined-certs/tls.key -t route.tls-termination=reencrypt -t route.tls-destination-ca-certificate-secret=my-combined-certs/tls.crt -t route.tls-certificate-secret=my-combined-certs/tls.crt -t route.tls-key-secret=my-combined-certs/tls.key -t container.port=8443 + +* To add a *reencrypt* route using a specific certificate from a secret for the route and https://docs.openshift.com/container-platform/4.8/security/certificates/service-serving-certificate.html#add-service-certificate_service-serving-certificate[Openshift service serving certificates] for the integration endpoint. This way the Openshift service serving certificates is set up only in the integration pod. The keys and certificates should be visible in the running integration pod, to achieve this we are using the `--resource` kamel parameter to mount the secret in the integration pod, then we use some camel quarkus parameters to reference these certificate files in the running pod, they start with `-p quarkus.http.ssl.certificate`. This route example trait references a secret named `my-combined-certs` which contains two keys named `tls.key` and `tls.crt`. ++ +[source,console] +kamel run --dev PlatformHttpServer.java --resource secret:cert-from-openshift@/etc/ssl/cert-from-openshift -p quarkus.http.ssl.certificate.file=/etc/ssl/cert-from-openshift/tls.crt -p quarkus.http.ssl.certificate.key-file=/etc/ssl/cert-from-openshift/tls.key -t route.tls-termination=reencrypt -t route.tls-certificate-secret=my-combined-certs/tls.crt -t route.tls-key-secret=my-combined-certs/tls.key -t container.port=8443 ++ +Then you should annotate the integration service to inject the Openshift service serving certificates ++ +[source,console] +oc annotate service platform-http-server service.beta.openshift.io/serving-cert-secret-name=cert-from-openshift + +* To add an *edge* route using a certificate and a private key provided from your local filesystem. This example uses inline scripts to read the certificate and private key file contents, then remove all new line characters, (this is required to set the certificate as parameter's values), so the values are in a single line. ++ +[source,console] +kamel run PlatformHttpServer.java --dev -t route.tls-termination=edge -t route.tls-certificate="$(cat tls.crt|awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}')" -t route.tls-key="$(cat tls.key|awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}')" diff --git a/e2e/common/traits/files/PlatformHttpServer.java b/e2e/common/traits/files/PlatformHttpServer.java new file mode 100644 index 0000000000..9e52514812 --- /dev/null +++ b/e2e/common/traits/files/PlatformHttpServer.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +import org.apache.camel.builder.RouteBuilder; + +public class PlatformHttpServer extends RouteBuilder { + @Override + public void configure() throws Exception { + from("platform-http:/hello?httpMethodRestrict=GET").setBody(simple("Hello ${header.name}")); + } +} \ No newline at end of file diff --git a/e2e/common/traits/route_test.go b/e2e/common/traits/route_test.go new file mode 100644 index 0000000000..341215d4f1 --- /dev/null +++ b/e2e/common/traits/route_test.go @@ -0,0 +1,337 @@ +// +build integration + +// To enable compilation of this file in Goland, go to "Settings -> Go -> Vendoring & Build Tags -> Custom Tags" and add "knative" + +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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 traits + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "net/http" + "testing" + "time" + + . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" + + rand2 "math/rand" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + . "github.com/apache/camel-k/e2e/support" + "github.com/apache/camel-k/pkg/util/openshift" +) + +const( + secretName = "test-certificate" + integrationName = "platform-http-server" + waitBeforeHttpRequest = 7 * time.Second +) + +type keyCertificatePair struct { + Key []byte + Certificate []byte +} + +// self-signed certificate used when making the https request +var certPem []byte + +// the e2e tests run on openshift3 in CI and there is no object to retrieve the base domain name +// to create a valid hostname to later have it validate when doing the https request +// as this a e2e test to validate the route object and not the certificate itself +// if the base domain name cannot be retrieved from dns/cluster we can safely skip TLS verification +// if running on openshift4, there is the dns/cluster object to retrieve the base domain name, +// then in this case the http client validates the TLS certificate +var skipClientTLSVerification = true + +func TestRunRoutes(t *testing.T) { + WithNewTestNamespace(t, func(ns string) { + ocp, err := openshift.IsOpenShift(TestClient()) + if !ocp { + t.Skip("This test requires route object which is available on OpenShift only.") + return + } + assert.Nil(t, err) + + Expect(Kamel("install", "-n", ns).Execute()).To(Succeed()) + + // create a test secret of type tls with certificates + // this secret is used to setupt the route TLS object across diferent tests + secret, err := createSecret(ns) + assert.Nil(t, err) + + // they refer to the certificates create in the secret and are reused the different tests + refKey := secretName + "/" + corev1.TLSPrivateKeyKey + refCert := secretName + "/" + corev1.TLSCertKey + + // ============================= + // Insecure Route / No TLS + // ============================= + t.Run("Route unsecure http works", func(t *testing.T) { + Expect(Kamel("run", "-n", ns, "files/PlatformHttpServer.java").Execute()).To(Succeed()) + Eventually(IntegrationPodPhase(ns, integrationName), TestTimeoutMedium).Should(Equal(corev1.PodRunning)) + route := Route(ns, integrationName) + Eventually(route, TestTimeoutMedium).ShouldNot(BeNil()) + // must wait a little time after route is created, before doing an http request, + // otherwise the route is unavailable and the http request will fail + time.Sleep(waitBeforeHttpRequest) + url := fmt.Sprintf("http://%s/hello?name=Simple", route().Spec.Host) + response, err := httpRequest(t, url, false) + assert.Nil(t, err) + assert.Equal(t, "Hello Simple", response) + Expect(Kamel("delete", "--all", "-n", ns).Execute()).Should(BeNil()) + }) + + // ============================= + // TLS Route Edge + // ============================= + + t.Run("Route Edge https works", func(t *testing.T) { + Expect(Kamel("run", "-n", ns, "files/PlatformHttpServer.java", "-t", "route.tls-termination=edge").Execute()).To(Succeed()) + Eventually(IntegrationPodPhase(ns, integrationName), TestTimeoutMedium).Should(Equal(corev1.PodRunning)) + route := Route(ns, integrationName) + Eventually(route, TestTimeoutMedium).ShouldNot(BeNil()) + // must wait a little time after route is created, before an http request, + // otherwise the route is unavailable and the http request will fail + time.Sleep(waitBeforeHttpRequest) + url := fmt.Sprintf("https://%s/hello?name=TLS_Edge", route().Spec.Host) + response, err := httpRequest(t, url, true) + assert.Nil(t, err) + assert.Equal(t, "Hello TLS_Edge", response) + Expect(Kamel("delete", "--all", "-n", ns).Execute()).Should(BeNil()) + }) + + // ============================= + // TLS Route Edge with custom certificate + // ============================= + + t.Run("Route Edge (custom certificate) https works", func(t *testing.T) { + Expect(Kamel("run", "-n", ns, "files/PlatformHttpServer.java", + "-t", "route.tls-termination=edge", + "-t", "route.tls-certificate-secret=" + refCert, + "-t", "route.tls-key-secret=" + refKey, + ).Execute()).To(Succeed()) + Eventually(IntegrationPodPhase(ns, integrationName), TestTimeoutMedium).Should(Equal(corev1.PodRunning)) + route := Route(ns, integrationName) + Eventually(route, TestTimeoutMedium).ShouldNot(BeNil()) + // must wait a little time after route is created, before an http request, + // otherwise the route is unavailable and the http request will fail + time.Sleep(waitBeforeHttpRequest) + code := "TLS_EdgeCustomCertificate" + url := fmt.Sprintf("https://%s/hello?name=%s", route().Spec.Host, code) + response, err := httpRequest(t, url, true) + assert.Nil(t, err) + assert.Equal(t, "Hello " + code, response) + Expect(Kamel("delete", "--all", "-n", ns).Execute()).Should(BeNil()) + }) + + // ============================= + // TLS Route Passthrough + // ============================= + + t.Run("Route passthrough https works", func(t *testing.T) { + Expect(Kamel("run", "-n", ns, "files/PlatformHttpServer.java", + // the --resource mounts the certificates inside secret as files in the integration pod + "--resource", "secret:" + secretName + "@/etc/ssl/" + secretName, + // quarkus platform-http uses these two properties to setup the HTTP endpoint with TLS support + "-p", "quarkus.http.ssl.certificate.file=/etc/ssl/" + secretName + "/tls.crt", + "-p", "quarkus.http.ssl.certificate.key-file=/etc/ssl/" + secretName + "/tls.key", + "-t", "route.tls-termination=passthrough", + "-t", "container.port=8443", + ).Execute()).To(Succeed()) + Eventually(IntegrationPodPhase(ns, integrationName), TestTimeoutMedium).Should(Equal(corev1.PodRunning)) + route := Route(ns, integrationName) + Eventually(route, TestTimeoutMedium).ShouldNot(BeNil()) + // must wait a little time after route is created, before an http request, + // otherwise the route is unavailable and the http request will fail + time.Sleep(waitBeforeHttpRequest) + code := "TLS_Passthrough" + url := fmt.Sprintf("https://%s/hello?name=%s", route().Spec.Host, code) + response, err := httpRequest(t, url, true) + assert.Nil(t, err) + assert.Equal(t, "Hello " + code, response) + Expect(Kamel("delete", "--all", "-n", ns).Execute()).Should(BeNil()) + }) + + // ============================= + // TLS Route Reencrypt + // ============================= + + t.Run("Route Reencrypt https works", func(t *testing.T) { + Expect(Kamel("run", "-n", ns, "files/PlatformHttpServer.java", + // the --resource mounts the certificates inside secret as files in the integration pod + "--resource", "secret:" + secretName + "@/etc/ssl/" + secretName, + // quarkus platform-http uses these two properties to setup the HTTP endpoint with TLS support + "-p", "quarkus.http.ssl.certificate.file=/etc/ssl/" + secretName + "/tls.crt", + "-p", "quarkus.http.ssl.certificate.key-file=/etc/ssl/" + secretName + "/tls.key", + "-t", "route.tls-termination=reencrypt", + // the destination CA certificate which the route service uses to validate the HTTP endpoint TLS certificate + "-t", "route.tls-destination-ca-certificate-secret=" + refCert, + "-t", "route.tls-certificate-secret=" + refCert, + "-t", "route.tls-key-secret=" + refKey, + "-t", "container.port=8443", + ).Execute()).To(Succeed()) + Eventually(IntegrationPodPhase(ns, integrationName), TestTimeoutMedium).Should(Equal(corev1.PodRunning)) + + route := Route(ns, integrationName) + Eventually(route, TestTimeoutMedium).ShouldNot(BeNil()) + // must wait a little time after route is created, before an http request, + // otherwise the route is unavailable and the http request will fail + time.Sleep(waitBeforeHttpRequest) + code := "TLS_Reencrypt" + url := fmt.Sprintf("https://%s/hello?name=%s", route().Spec.Host, code) + response, err := httpRequest(t, url, true) + assert.Nil(t, err) + assert.Equal(t, "Hello " + code, response) + Expect(Kamel("delete", "--all", "-n", ns).Execute()).Should(BeNil()) + }) + Expect(TestClient().Delete(TestContext, &secret)).To(Succeed()) + }) +} + +func httpRequest(t *testing.T, url string, tlsEnabled bool) (string, error) { + var client http.Client + if tlsEnabled { + var transCfg http.Transport + if skipClientTLSVerification { + transCfg = http.Transport{ + TLSClientConfig: &tls.Config { + InsecureSkipVerify: true, + }, + } + } else { + certPool := x509.NewCertPool() + certPool.AppendCertsFromPEM(certPem) + transCfg = http.Transport{ + TLSClientConfig: &tls.Config { + RootCAs: certPool, + }, + } + } + client = http.Client{Transport: &transCfg} + } else { + client = http.Client{} + } + response, err := client.Get(url) + defer func() { + if response != nil { + _ = response.Body.Close() + } + }() + if err != nil { + fmt.Printf("Error making HTTP request. %s\n", err) + return "", err + } + assert.Nil(t, err) + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(response.Body) + if err != nil { + fmt.Printf("Error reading the HTTP response. %s\n", err) + return "", err + } + assert.Nil(t, err) + return buf.String(), nil +} + +func createSecret(ns string) (corev1.Secret, error) { + keyCertPair := generateSampleKeyAndCertificate(ns) + sec := corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: corev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: secretName, + }, + Type: corev1.SecretTypeTLS, + Data: map[string][]byte{ + corev1.TLSPrivateKeyKey: keyCertPair.Key, + corev1.TLSCertKey: keyCertPair.Certificate, + }, + } + return sec, TestClient().Create(TestContext, &sec) +} + +func generateSampleKeyAndCertificate(ns string) keyCertificatePair { + serialNumber := big.NewInt(rand2.Int63()) + domainName, err := ClusterDomainName() + if err != nil { + fmt.Printf("Error retrieving cluster domain object, then the http client request will skip TLS validation: %s\n", err) + skipClientTLSVerification = true + } + var dnsHostname string + if len(domainName) > 0 { + dnsHostname = integrationName + "-" + ns + "." + domainName + } else { + dnsHostname = integrationName + "-" + ns + } + x509Certificate := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Camel K test"}, + }, + IsCA: true, + DNSNames: []string{dnsHostname}, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(1, 0, 0), + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } + + // generate the private key + certPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + fmt.Printf("Error generating private key: %s\n", err) + } + + privateKeyBytes := x509.MarshalPKCS1PrivateKey(certPrivateKey) + // encode for storing into secret + privateKeyPem := pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: privateKeyBytes, + }, + ) + certBytes, err := x509.CreateCertificate(rand.Reader, &x509Certificate, &x509Certificate, &certPrivateKey.PublicKey, certPrivateKey) + if err != nil { + fmt.Printf("Error generating certificate: %s\n", err) + } + + // encode for storing into secret + certPem = pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: certBytes, + }) + + return keyCertificatePair { + Key: privateKeyPem, + Certificate: certPem, + } +} diff --git a/e2e/support/test_support.go b/e2e/support/test_support.go index 323362e8e3..879d371fd7 100644 --- a/e2e/support/test_support.go +++ b/e2e/support/test_support.go @@ -57,6 +57,7 @@ import ( messaging "knative.dev/eventing/pkg/apis/messaging/v1" servingv1 "knative.dev/serving/pkg/apis/serving/v1" + configv1 "github.com/openshift/api/config/v1" projectv1 "github.com/openshift/api/project/v1" routev1 "github.com/openshift/api/route/v1" @@ -120,6 +121,7 @@ func init() { client.FastMapperAllowedAPIGroups["messaging.knative.dev"] = true client.FastMapperAllowedAPIGroups["serving.knative.dev"] = true client.FastMapperAllowedAPIGroups["operators.coreos.com"] = true + client.FastMapperAllowedAPIGroups["config.openshift.io"] = true client.FastMapperAllowedAPIGroups["policy"] = true var err error @@ -1058,6 +1060,19 @@ func Kamelet(name string, ns string) func() *v1alpha1.Kamelet { } } +func ClusterDomainName() (string, error) { + dns := configv1.DNS{} + key := ctrl.ObjectKey{ + Name: "cluster", + } + err := TestClient().Get(TestContext, key, &dns) + if err != nil { + return "", err + } + return dns.Spec.BaseDomain, nil +} + + /* Tekton */ diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go index dce90123b6..04ad9ab9e6 100644 --- a/pkg/resources/resources.go +++ b/pkg/resources/resources.go @@ -467,9 +467,9 @@ var assets = func() http.FileSystem { "/traits.yaml": &vfsgen۰CompressedFileInfo{ name: "traits.yaml", modTime: time.Time{}, - uncompressedSize: 39128, + uncompressedSize: 41625, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x7b\x73\x24\xb7\xf1\xd8\xff\xf7\x29\x50\xfc\xa5\xea\x48\xd6\xee\xf0\x24\xc7\xb6\xc2\x44\x71\x51\x77\x27\x8b\xd2\x3d\x98\x23\x2d\x57\x4a\x51\x79\xb1\x33\xbd\xbb\x38\x62\x80\x31\x80\x21\x6f\x1d\xe7\xbb\xa7\xd0\x8d\xd7\xec\x0e\x97\xcb\x93\xa8\x12\x53\xb1\xff\xd0\x91\x9c\x69\x34\x1a\x8d\x7e\x77\x8f\x33\x5c\x38\x7b\xfa\x6c\xca\x14\x6f\xe1\x94\xf1\xc5\x42\x28\xe1\xd6\xcf\x18\xeb\x24\x77\x0b\x6d\xda\x53\xb6\xe0\xd2\x82\xff\x8d\xd1\x0b\x21\xc1\x9e\x3e\x63\x6c\xca\x7e\xe8\xe7\x60\x14\x38\xb0\xf4\xa3\xe2\x4e\xdc\x00\xfe\xfb\x7d\x07\xea\x72\x25\x16\xee\x19\x63\x0d\xd8\xda\x88\xce\x09\xad\x4e\xd9\x99\x94\xfa\xd6\xb2\x5a\x2b\xeb\x57\x56\x42\x2d\xd9\xed\x4a\xd4\x2b\xa6\x74\x03\x96\xb9\x15\x30\xa1\x1c\x2c\x0d\xf7\x2f\xb0\x4e\x37\x87\xf6\x88\x71\x03\x0c\xa4\x58\x8a\xb9\xf4\x0b\x30\xe6\x34\x9b\x03\xb3\xf5\x0a\x9a\x5e\x42\xc3\xb4\x9a\xb0\x39\xb7\xf8\x2f\x26\xf9\x1c\xa4\xf5\xff\xf2\xe0\x3c\xe0\x09\xd3\x86\xdd\x0a\xb7\x42\xe0\x66\xda\xe9\x26\xed\x94\x71\xd5\x20\x4c\xae\x9c\x98\xc6\xdf\x8e\x82\xeb\x74\xe3\x51\xe4\x0e\x11\xe2\xd2\x00\x6f\xd6\xcc\xf4\x0a\xf7\x51\xac\x67\x2b\x84\x78\xee\x9e\x5b\xd6\x08\xcb\xe7\x1e\xc7\xf9\x9a\x35\xb0\xe0\xbd\x74\x15\xd1\xb2\x03\xe3\x44\xa4\x26\x91\x1f\x14\x3e\x4b\x7b\x5c\x77\x70\xca\xe6\x5a\x4b\xfc\x71\x40\xc7\x97\x5c\x79\x02\xf4\x1e\x45\xa7\xc3\x6b\x7e\x93\x61\x35\xc6\x19\x9e\x6c\xe5\x29\x4e\xff\xb4\xcc\xae\x3c\xda\x6e\x25\xfc\x01\xb4\xad\x56\x08\x37\xa1\xb2\xae\x0a\x44\x3a\xdd\x4c\x0b\x5e\xd8\x8d\xcd\x99\xbc\xe5\x6b\x0f\x74\x2a\x75\xcd\x1d\x58\xd6\xf6\xd2\x89\x4e\x02\x33\xd0\x49\x51\x73\xcb\xf4\x62\xeb\x70\x05\x11\xcc\xf2\x16\x02\x26\x9e\x76\xec\x30\x50\x89\x1d\x23\xdf\x1d\x1f\x6d\xe1\x55\x1e\xd4\xbd\xc8\xbd\x83\x1b\x30\xbf\x09\x6e\xfe\x89\x84\xd7\x94\xd8\xa6\x40\xef\xf9\x4f\x3f\x5b\x67\x84\x5a\x3e\xdf\x46\xf2\x15\x2c\x84\x02\xcb\x38\xb3\xe0\x3c\x3e\x7b\x5f\x07\xba\x0a\x01\xc7\xbd\x2f\xc4\x5d\x47\xfd\x0b\xb1\xc6\x0b\x72\xe8\xc1\xca\x35\x73\x2b\x6d\x81\xb5\xdc\xd5\x2b\x7f\x3d\xfc\xd2\x08\x9d\x59\x90\x50\x3b\x6d\x26\x01\x6b\x03\x12\x45\x87\xdf\x8a\x7f\x6a\x29\x6e\x40\x21\x72\xb6\xe3\x35\x1c\xd1\x95\x73\x2b\x18\x21\x85\x5d\xe9\x5e\x36\xfe\x2e\xa4\x13\x6e\x02\x58\x7f\xdf\x77\xb2\xce\x53\xdd\xac\xd2\x6e\xc7\x86\xe3\x76\xe7\xbd\x90\x0d\x98\x81\x20\x77\xa6\xff\x75\xe4\xf8\xd5\x0a\xe2\x02\x24\x5d\x98\xb0\x24\x5b\x15\x97\x72\x9d\x04\x53\x03\x0e\x4c\x2b\x14\xe0\x5e\xe7\x60\x1d\xf3\x82\xdf\xc1\x72\x9d\xe4\xb8\x07\xe3\x85\xb0\xd7\x0a\x0b\xb1\xec\x0d\xb0\xf3\xbc\xf7\x1f\x84\xb3\x4f\x40\x5e\xde\x80\x99\x6b\x0b\xf7\x22\xf2\x9a\x56\x0e\x8f\x33\xa9\x97\xcb\xa0\x3b\x88\x0e\xb5\x6e\x3b\xad\x40\xb9\xa0\x68\x6c\xdf\x75\xda\x38\x26\x1c\x3b\x84\x6a\x59\x05\x14\x7e\xe0\x4a\x5c\x47\xda\x75\xba\x19\xca\xc8\x44\xaa\x3d\x59\xfb\x8c\x49\x61\x89\xa7\xd3\xab\x41\xc5\x76\x46\xdf\x88\x86\xa8\xe6\xe2\xa1\x33\xc7\xed\x75\x62\xb4\xda\xdf\x80\xc7\x63\xb3\x97\x1e\x7c\x60\xb2\x7a\x78\x8c\x99\x61\x6e\xc0\x58\xa1\x15\x8a\xf2\xb3\x8e\xd7\xe9\xbd\x1f\x70\xb7\xa6\x57\x4e\xb4\x80\x5c\x86\x17\x10\x1a\x26\xc5\xdc\x70\x23\xc0\x4e\x18\x41\x0e\xd7\x2a\xea\xeb\x27\xc0\x74\x61\x5b\xd3\xb0\xfb\x02\x21\x3a\xea\x6d\x94\x3c\x41\xf1\xbc\xa6\xd7\xd3\x48\x94\xf0\xb6\x47\xb1\xb7\xc0\x16\xda\x6c\xea\x9d\x8a\x9d\x3b\xa6\x6f\xc0\x18\xd1\x04\xa6\x62\xf8\x4c\xd4\x86\x11\x84\x97\x8c\x41\x73\x16\x57\x98\x5d\x04\xce\xc8\xc2\xa9\xd6\xca\x71\xa1\x1e\x53\x3c\xbd\x8c\x4b\xdc\xc7\x3b\x05\xcf\x07\x43\xa0\xc4\x8e\xb1\xdb\x15\x18\xd8\x52\xc5\xb7\x42\x4a\x7f\x00\x48\x1b\x2e\xad\x8e\x57\xc5\x26\xd0\xf4\xa0\xa7\xe7\x25\x98\x1b\x51\x7b\xe5\x61\xad\xae\x45\x92\xd9\xe1\x52\xa5\xf5\x9e\x00\xcf\xf1\xde\xe9\x7b\xb1\x38\x38\x28\xb9\x14\xfe\xd9\x83\x75\xd3\xba\xeb\xf7\xe4\xd0\x56\x28\xd1\xf6\x2d\xe3\xad\xee\x15\xca\xa5\x97\x17\x7f\x43\x38\xc2\xd0\xc5\xdc\x84\xdd\x42\xab\xcd\xfa\xb3\xc1\xd3\xeb\xa3\x2b\x48\xd1\x8a\x07\xe1\xce\x3f\xed\x89\x3b\x41\x7e\x18\xe6\x5b\xc0\x77\x60\x0e\x9f\xba\x7d\x34\xd2\x28\xc7\x9c\x44\x76\x41\x20\x28\x61\x05\x67\xd7\xe9\x2a\x46\x8e\x1e\xda\x57\xc6\x15\xab\x09\xe5\x46\x36\x51\x5e\x3c\xce\x1a\xb1\x58\x80\x01\xe5\xf0\xe5\x80\x31\x7a\x4a\x83\x6b\x91\xcd\xee\xd9\x57\x2f\xbe\x7a\x31\x3b\xda\x5c\x76\xaa\xa2\x9d\x7e\x0f\x0d\x77\x2e\xef\x81\x24\xf1\xb7\x13\xa1\x70\x3f\x32\x5a\x2b\xe7\xba\x21\x5a\x96\x08\x34\x7d\x30\x55\x7a\xe5\x8d\x2a\x72\x8a\x03\x10\x42\x66\x48\x12\xb2\x0a\xec\xc0\xfc\x8f\xe8\x96\xe4\xba\x1b\xab\xcf\x22\xda\x9d\xd8\x21\xf1\x46\x51\x8c\xfa\x02\xed\x89\x6d\x14\xb7\x49\xb7\x2f\x5e\x78\x21\x84\x2a\x56\xf4\x6f\x56\xe4\x76\xfb\x7f\x36\x6c\x56\x88\xec\xd9\x86\x07\x1e\x97\x13\x2d\x5f\x7e\xe6\x7a\xf1\xd5\x01\xa8\x69\xd7\x4b\x39\xed\xb4\x14\x75\x79\xaf\x2f\x7a\x29\x2f\xf2\x2f\x07\xa0\x9f\x7b\xd8\xfe\x35\x46\xaf\x45\x97\xfa\xdf\xe8\xbc\xfe\xfb\x7c\xf1\x4e\xbb\x0b\x03\x16\x94\x7b\x3e\x34\xf4\xe6\x60\xa7\xfb\xea\x86\x0b\x7c\x9c\x2c\xd0\x66\xf3\xa2\x13\xac\xe8\x23\x8e\x5d\x3d\xf4\x78\x67\x47\x03\x09\x76\x03\x0a\xac\x9d\x7a\xbf\x73\xaf\x33\xbb\xc4\x07\xa3\xa9\x71\xbb\x02\xa4\xa6\x82\xda\x09\xb5\xac\xbc\x8f\xe5\xd7\x42\xae\xfe\xee\xea\xea\xa2\x62\x67\x5d\x27\x83\x35\x8a\x3e\x55\x58\x31\x2b\xa8\x39\x54\x63\x18\x79\x07\x4f\x70\x39\x6d\x40\xf2\xf5\xf0\xfe\xfd\xe1\xcb\x91\x20\x41\xdf\xce\xc1\x78\x81\x6a\xa1\xd6\xaa\xb1\x8c\x2f\x1c\x98\x0d\x5a\xac\xb8\x65\xd6\x71\xe3\xcd\xc7\x39\x2c\xb4\x19\x47\xc8\xa2\x83\x4e\x18\xb8\x4d\x99\x1f\xf0\xf3\x96\x97\xee\xdd\xe7\x63\x46\x57\x10\x25\x95\x5f\x93\x79\x80\x96\xe9\xde\x6d\xd2\x2c\x60\x16\x57\xde\x41\xb3\x0e\x8c\xd0\xcd\xfd\x28\x7d\xa7\x6f\x99\x5e\x38\x40\x93\xb1\x03\xe3\x8d\xb7\x8c\xc9\x9d\x67\xb6\x63\x65\xdb\xd7\x35\x52\x65\x65\xc0\xae\xb4\xdc\x03\x89\xb7\x41\x89\xd7\x5a\x59\xa8\x7b\x74\xa9\x03\x18\xb0\x59\x8a\x13\x6d\x34\xf9\xcb\xca\x8a\x06\x0c\x34\xf1\xc1\x45\x2f\x03\x75\x88\xa6\x2b\x7e\xe3\x9d\xb1\x05\x17\xde\xfe\x7f\xf8\x36\xfc\x8b\xbd\x81\x5f\xba\x8d\x00\xe6\xde\x5d\x10\x9e\x63\x3b\xc0\xfd\x41\xf3\x90\x4d\x18\xe0\x8d\xf8\x6d\x2f\x73\x5a\xf2\xae\xdb\x9c\x71\xfa\xad\xae\xf3\x28\x4a\x3b\xee\x73\xc6\xf0\x37\xbf\xd0\x69\xe9\x5d\x74\x7b\xa4\x2b\xbd\xd7\xda\x4f\xe1\x52\xef\xb5\x91\xdf\xff\xb5\xde\xda\x46\xf2\xb3\x0d\x3a\x73\x8f\x91\xca\x41\x9b\xe5\xa5\xf1\x06\xc3\xa8\x7f\xdd\x5b\xa7\x5b\xf1\xaf\x18\xf9\xf3\x5b\xd0\x3d\xf2\x3d\x31\xa5\xa8\x89\x8c\xa2\x05\x73\xe2\xf1\x0c\xf1\xea\xc2\x62\xb3\x15\xfb\xfb\x4a\x48\x60\x4a\x9b\x16\xe3\x8a\x5c\x0d\x9c\xf0\xe0\xf6\x58\xc6\x59\xa7\xfd\xb2\x08\x72\x0e\x8c\x53\x46\xa2\xef\x28\xe4\x43\x19\x9a\x09\xb3\xba\x85\xb4\x3c\x46\xb1\xec\xc4\x53\x75\xc5\xb8\x65\x73\xee\xea\x15\xfb\xa8\xe7\x76\x12\x01\x97\x10\x6b\x27\x6e\x30\x70\xc4\x1d\xb3\x1d\xd4\x62\x21\x6a\xb6\xd2\xbd\x49\x61\x83\x86\xaf\x53\x9e\x89\xe7\x65\x50\x66\xa1\xaf\x26\x54\xef\x62\x6e\xe8\x5b\x6d\x68\xe5\x80\x05\x8a\xa6\x21\x35\x5b\xee\xc0\x08\x2e\x23\x11\xcb\x9d\x73\xbf\xe7\xc1\xb1\x31\x3c\x8c\xef\xf5\x9c\x09\x65\x1d\xf0\xc6\x2f\xc9\xbd\x80\x53\x0d\x37\x0d\x6b\xa0\x93\x7a\xdd\x82\x72\x13\x26\x14\xd3\x06\x63\xb7\x9a\x59\x7e\xe3\x19\xc8\xea\xde\xd4\x60\x49\x8c\x47\x29\x53\xae\xd8\x68\xb0\x18\x7a\x56\x40\x27\x8c\xd6\xbc\x67\x6e\x68\xaa\x32\xdc\x13\x23\x97\x5e\xb2\xb2\x85\xd1\x24\x48\x16\x5a\x4a\x7d\x1b\xf5\x48\x11\xe6\xc4\x64\xc6\x0d\x97\x3d\x12\x37\x7a\x5a\x89\x12\xa7\x6c\x86\x2c\x32\x9b\xb0\x99\xff\xad\xff\xef\x3f\x7b\x6e\xdc\xbf\x66\x15\x9a\xe4\xa6\x97\x61\xff\xfe\x5e\xf5\xd6\x5f\x94\x92\x34\x89\x2c\x3c\x44\x71\x12\x26\xa7\x6c\x1a\x81\x9f\xd2\xbe\xe9\xcc\xac\xa7\x7e\x3c\xf7\x5b\x23\x9c\x97\x8b\xdc\x12\x52\xf0\xa9\x33\x60\x2d\x71\xe7\xeb\x6a\x59\x05\x10\xa7\x4e\xd4\xd7\x7f\x21\x00\x5f\xff\xe9\xc5\x8b\x17\x2f\x66\x95\x87\xbf\x81\xf3\x69\x0c\x29\xa9\xbc\xcf\x0c\x32\x13\x39\x68\xa9\xa4\x23\x0e\x83\xcc\x38\x08\xbf\x38\x60\x1d\x27\x07\xd0\x82\x8b\xb1\xa4\x17\x47\x11\x25\x0f\xf7\xd4\xf1\xf9\x5f\x62\x46\xe8\xeb\x17\x27\x5f\xfe\xa7\xff\xdd\xc9\xde\xfe\x9f\xe3\xb1\xff\xfc\x65\xe6\x59\x37\x60\x79\xea\x8c\x58\x2e\xc1\xfc\xc5\x83\xf9\xfa\x05\x3d\xf1\xe2\xe4\xcb\x9d\xef\x57\xcf\x7f\xff\xc1\xab\x48\x8d\x3d\xbd\xbd\xc8\x39\xf1\xb5\x24\xb9\x6f\x57\x5a\x6e\x46\x48\x17\x45\x62\x51\xf7\x2e\xc5\x49\x3d\x76\x0d\xd4\x92\x1b\x68\xf0\x9a\xaf\x59\xdb\x5b\xe7\x65\x3a\xa4\x1c\xe3\xe6\x12\xc2\xb6\x50\xaf\xb8\x12\xb6\xf5\x94\xb8\xd5\xe6\x9a\xd5\xda\x18\xa8\x9d\x1c\xec\x28\x5f\xa4\x3d\xf6\xf4\xfc\x0c\x49\xc4\x99\x85\x8e\x9b\x10\x05\xa7\xc0\xbf\x4b\x11\xf3\xcd\x0c\x44\x71\xdd\x93\x4c\x8f\xda\x29\xc9\x91\x40\x98\x8c\x6c\xe2\xf0\xb4\x31\x61\x59\x60\x2b\xef\x7c\x7e\x4a\xa9\xa2\xf9\xba\xb8\xac\xd5\x59\xcc\x64\x46\x09\x9b\xd6\x34\x1e\x42\x96\xc2\x7e\x45\xe0\xf5\x2a\x3e\x09\x45\xee\x24\xdc\x82\x80\x54\x80\x18\x6e\x7a\x7e\x8a\x64\x2e\x5e\x95\x69\xfc\x5b\xb9\x58\x5e\xeb\x50\xb8\xe7\xcf\xbd\x6e\x45\x0f\x9c\x09\x55\x84\xc0\x67\xda\x2c\x2b\x8e\x29\x87\x0a\x23\xeb\xd5\xf5\x69\x8c\xb0\xe3\xdd\x0f\x89\x86\xf5\x51\x75\x49\xb9\x1c\x68\x36\xc5\x5f\xdd\x1b\x03\xca\xc9\xf5\x69\xc4\x35\x4a\x8d\x80\x97\x57\x62\x49\xea\x95\x11\x80\x05\x97\x72\xce\xeb\xeb\x7b\xaf\xd6\xdf\x2c\x0c\x22\xf6\x74\xd6\xa2\xed\x24\x78\x95\x40\x22\x3e\xf0\x01\xad\xce\x40\x35\x9d\x16\xca\xb1\xc3\xb8\xf4\x51\x3a\xf6\xa4\x60\x9c\x59\x63\xbe\x53\xef\xd2\x56\xdc\x8e\xc8\xe3\x21\x17\x2b\xa2\x41\xbd\xde\x0e\x9c\xdc\xc9\xcd\x97\xe1\xe4\x2d\x5b\xe9\x5b\x34\x85\x0c\x70\x97\x81\xb9\xa0\x9f\x62\x62\x88\x33\xbf\xec\x8f\x5c\x8a\x86\x79\x85\x53\x5e\xd1\xd3\x29\x3b\xc0\xe2\x94\x83\x53\xc6\xa9\x48\x25\xe0\x89\x46\x96\xe9\x55\x01\x57\xae\xff\xeb\x94\x1d\x7c\xab\xcd\x5c\x34\x07\x29\x42\x72\x74\xea\x39\x6e\x2e\x9a\x08\xb6\x40\xc4\xf4\xca\x5b\x1a\xd7\xa2\xeb\x3c\xb9\x14\x7c\xc2\xdf\x31\xb1\xf0\x5c\xe5\x2d\x23\x8b\x3f\xaf\xb8\x55\xcf\x9f\x3b\xb6\x10\x4a\xd8\x15\x34\x6c\x0d\xce\xaf\xf5\x01\x3a\xc9\x6b\x38\x88\x0c\x52\x73\x55\x83\xb4\x99\x73\x52\x15\xca\x47\xaf\xe9\x30\xcd\x85\x6f\x58\x26\x5c\xb4\x48\x14\xdc\x32\xad\xe0\xf9\x43\xa3\xf9\x67\xbd\xd3\x2d\x77\xa2\xc6\xfb\x4a\x76\xc4\x98\x41\x12\xc5\x25\xde\x7d\x2e\x65\x90\x83\x9e\xbc\x20\xdc\x2a\x85\x4d\xd1\x32\x40\x9b\xdc\x1b\x07\x85\xa5\xe4\x8d\xe0\xbe\x05\xc3\x0e\xb5\x92\xeb\x9d\xb7\x00\xef\x8d\x8d\x17\x2a\x32\xa6\x36\x1e\x1c\xb7\xd6\xdb\xdb\x19\x1a\xa0\x4e\x6c\x84\x17\x9f\x33\x14\x23\x5b\x0f\x1d\x55\x18\x35\x8c\x71\x74\x14\x7d\x91\x3b\xa4\xdc\x46\xd1\x6e\xc8\x6f\x7a\x00\x51\xcc\xb6\x70\x50\xec\xde\x66\xb4\xd1\x14\x67\x45\x99\x46\xc4\xec\x8b\x76\x36\xfa\xca\xec\xc5\xc9\x17\xec\x98\xfe\x3f\x9b\xdc\xa2\x29\x3c\xfb\xc3\x1f\x5b\xd2\xd5\x7f\x7c\x61\x67\x21\x6f\x39\x8c\xf1\x06\xf2\x4e\x1b\xe0\x8d\x14\x0a\xa6\xc1\x66\x18\xba\x2d\x7f\xfa\xcf\xdb\x27\xfd\x1e\xff\xcb\x25\x8b\xaf\xb2\xc2\x04\xf1\xe2\x34\x1d\x9d\xdf\xb8\x67\x35\xb1\xf0\xfb\x6d\x05\x3a\x68\xa9\xfc\xc4\x1f\x58\xd8\xab\x7f\x8b\xab\xb5\x77\x51\xac\xd7\x93\xec\xad\xc0\xed\x79\x3b\xbb\xbc\x9f\x98\x4f\x43\x47\xa8\x57\x8e\xb6\x4f\x8e\x90\x67\x59\x9b\x3d\x9a\x06\x3a\x50\x0d\xa8\x9a\xd2\xdb\x8f\x94\x3c\x7c\x55\xac\xb2\xb3\xc0\x81\x0f\xee\x06\x6f\x9a\x98\x94\x0d\xc4\x2d\xc0\xa4\x72\x9c\xcd\xab\x13\x2b\x3e\x3c\x50\xc3\x6e\xb9\x72\x51\xe6\x6c\xe4\x03\xd9\x4f\x3f\x97\x74\x90\x7a\xfd\x98\x09\xd4\xb8\xc2\xb8\x7f\x07\x9f\x3a\x29\x6a\xe1\x45\x0f\x95\xb8\xe0\x0e\xae\x85\x42\xb5\xb0\x12\xcb\x15\x52\x40\xc2\x0d\xc8\xe4\x5e\xd0\x56\xf1\xa8\xc7\xc5\xc8\x13\x48\x80\xfa\x2d\xee\xa1\x9d\x42\xa5\xe3\x9d\x94\x6a\xc0\xa2\xa0\xc9\x6e\x19\x11\x6f\x0e\xee\x16\x40\xb1\x59\xfe\xc3\x6c\x52\x9a\x05\xd3\x8f\x7a\x4e\x02\xe0\x9a\x4e\x72\x1a\xf2\x30\xb3\x10\x82\xf3\x4a\x30\x5e\xd1\xec\xd7\xf9\x7b\x18\x75\x44\x36\x8a\x06\xa4\x1f\xb2\x96\x5f\xf9\x51\x2f\x58\xdc\x76\xba\x5e\x06\x6c\xa7\x95\xc5\xaa\x36\x8f\xee\x12\x14\x98\xbc\x97\xc2\x84\x18\x60\xc8\x0a\xae\x6a\xf9\xb5\x57\x0b\x3b\xf2\xf5\xb1\x3e\xa1\x96\xbd\x75\x5b\x19\xf7\xf2\x86\x81\xba\x11\x46\xab\xc7\xa5\x43\xb1\x48\x26\x44\x1f\xe3\x20\x41\xd8\x38\xcd\x84\xfa\xe8\x39\x27\x79\xf3\x43\xe4\x18\xbb\xe1\x46\x78\xf6\xb6\x71\x7f\xe5\xde\x53\xc8\x33\x07\x3b\x66\xef\xce\xde\xbe\xbe\xbc\x38\x7b\xf9\xda\x2b\xd8\x8b\xf7\xaf\xfe\xe1\x7f\x41\x3a\x56\x7b\x5d\xfd\x14\x8a\xae\xd2\xbe\xa6\x2d\x38\x7e\x2f\x3e\x94\xf9\xb2\x81\x96\xc1\xe0\x2d\x08\x41\x06\x46\xa6\x45\x79\x36\x89\xbe\x5b\xa9\x5f\xcf\x0e\xb3\xa3\xcc\x35\xc6\x68\x33\x5d\x71\xd5\xc8\xc7\x14\xce\x83\x65\x82\x49\x13\x56\x0a\x7c\x14\xc9\x1e\x38\xe7\xb5\x7f\x81\x7d\x97\xf0\x62\x2c\x88\x64\xa1\x42\x10\x71\x10\x58\x23\x25\xf6\x04\x78\xc0\xc0\x62\x4f\x6f\x1e\x49\xc6\x22\xc9\x0c\x2c\x28\x67\x9d\x2a\xdc\xb4\x77\x63\x7b\x6f\xc0\x29\xc6\x3b\x2c\x0d\xa6\xe2\xcb\x5c\x4e\x17\x17\x5d\xd6\x8f\x14\x54\xf5\x78\xfe\xf5\x25\xbb\xc2\x13\x5c\x72\x33\xe7\x4b\x98\xd6\x5a\x7a\xb5\x61\xc9\xa6\x4e\x12\x3d\x15\xa2\x2b\xcd\xa4\x56\x4b\x30\x4c\x41\x0d\xd6\xf2\x50\x4f\xd2\x77\x7a\x18\x2e\xed\xbb\x86\x87\x00\xe4\xef\xfc\x54\x1b\x61\x6b\x7d\x03\x66\x3d\xad\xbd\x67\x5d\x20\x54\x9d\x74\xd7\xcb\x13\x82\x9e\x9e\x7a\xe9\x1f\xba\x5a\x77\xb0\x8d\xea\xab\xf8\x0c\xab\xa5\xf0\x37\x19\x01\x86\x80\x86\xdf\xc0\x84\x91\x73\xe2\x1d\x04\xaa\xfc\xf3\x12\xb1\x11\xf6\x9a\xb4\x2c\x55\xd8\xcc\xb6\xee\x7d\xf8\x7d\xbe\xf9\x42\x2d\x0d\x65\x8e\x1e\xc4\x19\x5b\xe7\x7f\x4e\x70\xee\x34\xbb\x74\x70\xe5\x63\xf9\x45\xae\x29\x43\x47\xef\x59\x8c\x4c\x0c\x4c\x4c\xba\xe2\xba\x77\x56\x34\xc0\x6e\xb5\x91\x4d\x74\x0b\x0b\xbd\x1a\x96\x0e\x25\x14\x81\x1b\xd8\x3c\x56\x2c\xd0\xce\xbd\x95\x81\x35\xf5\x3c\x56\x01\xa1\xfc\x69\x8a\x2a\xd1\x72\xe9\x43\xb7\x32\xba\x5f\x52\x6a\x6b\x16\x6d\x15\xc2\xd2\xef\xf0\xe8\x09\xb0\xe3\x4a\x5b\xb7\x4f\x44\xe2\xf8\xf8\x43\x70\x20\x8f\x8f\xab\x61\xe1\x8c\xdf\xbd\x07\xb3\x59\x53\x14\xb8\xa6\x7a\xb0\x57\x7e\x35\xe6\x7c\x60\x7e\x84\xd8\x27\x1d\xd3\xe6\x81\xf4\x16\x13\x26\xdf\x5d\x5d\x5d\xc4\xba\xa1\x18\xe9\x89\xde\x6d\xb6\x05\x85\x75\x42\x3f\xa2\xb0\x3b\xf7\xf0\x03\xab\xf3\x64\x32\x8f\x16\x67\xc6\xc2\xdd\xc0\x63\xe7\x01\x33\x96\x2e\x42\x0b\x76\x95\x2d\x1c\xcf\xe8\x35\x37\x85\xb6\x47\xdb\xa6\x77\x73\x14\xf2\xe7\x17\xcc\x70\xb5\x7c\x12\xd2\x10\x09\xb3\x07\xff\xbd\x8c\x64\xf3\xe7\x7b\x88\xa1\xde\x69\x0a\xf5\x1e\xa5\x58\xef\xcb\xf3\x57\x1f\x98\xed\xe7\x0a\x52\x95\x79\x6a\x2c\x08\x58\xcc\x89\x65\x4c\x0d\x5d\x91\x95\xa1\xc3\xea\x8c\xfe\xb4\x66\x87\xb3\x2f\x5e\x54\xf8\xff\x93\xaf\x26\x5f\xfc\xf9\xcb\xea\x8b\x3f\xe1\x0f\x5f\x7c\x39\xf9\xe2\xbf\xf8\x9f\xbe\xa2\x1f\xff\x14\x25\x67\x2e\xbe\x1a\x44\x2b\xe8\x78\xee\xa5\xf1\xb7\x3a\xe8\x3c\xa0\xd0\x1d\x3a\x08\xa1\xaf\x65\x16\x8e\xba\x42\x66\xad\x84\x3e\x21\xa0\xb3\x8a\x7d\x93\x16\x2d\x42\xb6\xd4\x98\x41\xa9\x13\x7f\x60\x64\xc2\x79\xcf\xbc\x70\xc6\x3c\xb3\x28\xed\xa8\xd9\x43\x45\x86\xce\x75\x8f\x11\xff\x8f\x5a\xea\x6b\xc1\x1f\xf1\x8a\x7c\x4f\x2b\xc4\x4b\x12\xa2\xd2\x76\xd8\x32\x41\xa4\x89\x8f\x7e\xcf\x6f\x38\xe3\x4b\x50\x8e\x92\x8d\x97\x00\x6c\xe5\x5c\x67\x4f\x4f\x4e\x02\xc2\x95\x36\xcb\x13\x03\x58\xff\x58\xc3\xc9\xca\xb5\xf2\x04\xdf\xb0\x95\xff\xf7\xef\xff\x52\xd4\x7c\x5a\x83\xd9\x47\x2c\x7b\x22\x5e\xbc\x7e\xcb\x40\xd5\xda\x2b\xa9\x97\x67\xcc\xbf\x29\x16\xd1\xc2\xf3\x87\xc4\x3a\xee\x56\x93\x84\xef\x0d\x18\xb1\x88\x36\x43\x0c\xba\xa6\x97\xc0\x4e\x82\x85\xe8\x77\x82\x6e\xf7\xac\x33\xda\xe9\x5a\x4b\x0c\x30\x62\x55\xa3\x0d\x1e\x45\x6f\x61\x6a\xad\x9c\x12\xb0\x29\xef\xdd\x0a\x94\x0b\x8b\xc7\xeb\xe1\x5f\x42\x3e\xcc\x16\xc6\xc9\x0d\x37\x27\xa6\x57\x27\x16\x6a\x03\xce\x9e\xe4\xfa\x5b\xcf\xe4\x41\xec\xf1\x1a\x43\x66\xf1\xc7\x69\xcd\xab\xda\xb8\x59\x11\x7e\x4b\xdc\x35\xb8\x78\x01\x9b\xce\x08\x55\x8b\x8e\xcb\x82\x90\xbb\x7a\x45\xb0\x52\x31\xbe\x73\x68\x8f\x42\xdd\x0a\xa6\xb4\xe6\xb1\x9d\xc9\x5b\xd0\xc9\xde\xca\x54\xc3\x80\x59\x92\x65\x8c\x71\x2c\xd8\x88\x02\x3d\x32\x6f\xd4\x46\xbf\x05\x89\xe9\xf9\x8b\xb8\x9f\xaf\x6b\xf5\xb5\x5d\x5b\x07\xed\x69\xcb\x2d\x76\x8a\x7a\x61\x87\xb9\x67\xf5\xf5\x8a\xdf\x3a\xa1\xa7\x5a\x49\xa1\xa0\xa2\x9f\x2a\x7b\x53\x47\xf8\x88\x49\xad\xbe\x5e\x78\x6c\xbc\x2a\xd5\x12\x2a\xff\x03\x3e\xb4\xe3\x28\xb2\xb5\xbb\xef\xed\x7a\x23\xac\x03\xea\x2d\xc0\xac\x63\xcd\xad\x8b\xc5\xf0\x76\x67\xcd\x26\x7c\x72\xa0\x1a\x68\x22\xa9\xea\x15\xec\x91\x3e\x7a\xcb\x55\x83\x07\x88\x91\x93\xad\x73\x0d\x8e\xb8\xcd\xa7\xbe\x90\x7c\x19\x43\x11\x71\xc9\x40\xa6\x6b\x58\xb3\xde\xf2\xa5\xb7\x60\x51\x31\xff\x16\x07\x4d\x22\xfe\xee\x23\xd8\xd3\xc2\xf3\xdc\xff\x9d\xb7\xe2\x78\xd3\x98\xc0\xbb\xb9\x70\x2b\x72\x30\xca\xd1\xd4\x9a\x28\x94\x97\x28\x98\x21\x9e\x1d\xfc\xaf\xe3\x83\x59\x4e\x56\xcc\x0e\x82\x0e\x3d\xc0\x9d\xe2\xe5\x99\x44\xdb\x1e\x8c\xc5\x97\x29\x88\x88\x2e\x8c\x02\x87\xd9\x55\xd4\xcd\x0b\x5e\xe7\x86\xd4\x18\x21\x3c\x38\x3e\xd8\x28\x8f\xe7\xd6\xde\x6a\xb3\x4f\xc8\x12\xaf\x76\x78\x9c\x04\x21\x06\xef\x07\x24\x9e\xb0\xcd\xc3\x42\xb3\xbe\xb7\x60\xd2\xbe\x3a\xea\xa0\x45\xfd\xfa\xe0\x06\x81\x11\x41\x40\x85\xe4\x45\x51\xfb\x9f\xff\xfc\xd5\x6c\xb3\xe3\x0d\xf9\x65\xdf\x4d\x86\xc7\x43\xe9\x62\xf6\x00\xb1\x16\x1d\x0f\x26\xf0\xdc\xb0\x4c\xdd\x22\x07\x85\x6d\x66\x3e\x2a\x10\xf1\x74\xd8\x13\x09\x0c\xf6\x67\x37\x74\x84\xd6\x43\xb8\x77\xb3\xfd\xbd\xb7\xf7\xef\x2b\xc0\xfd\x6d\xdf\x5c\x5b\x34\xd0\xde\x81\xc5\x16\x8b\xdd\x77\x95\xe8\xfc\x1f\xde\x7b\xc8\x9b\x46\x84\xf4\x53\xe4\x80\x00\xca\x9b\xf3\x0d\xb6\xdf\x36\x42\x3d\xd0\x90\xf9\x0f\xfc\xf7\xf4\xe3\x4d\x3b\x25\x63\xe9\xa7\xef\x7f\x7c\x1b\x05\x36\xde\xd3\x61\xbb\x56\x58\x32\x87\x81\x3f\xde\xb4\x8f\x17\xc6\xfb\xfe\xc7\xb7\x1b\x61\x5f\xb7\xe9\x34\xe2\x23\xde\x48\x37\xbd\xda\xea\x08\x7f\x02\xce\x4b\x03\xf3\x7e\x79\x7f\x8e\x39\x99\xb5\x06\x5a\xed\x80\x5e\x5b\x86\xaa\xbd\x10\xeb\x0a\xbf\xf4\x9c\x4c\x58\x73\xe7\x78\xbd\x82\x54\xf9\xc7\x22\xc5\x26\x0c\xaa\x65\x35\x09\xe5\x60\xd8\xed\xb2\xd0\xe6\x96\x9b\x86\xee\xe3\x00\xb9\xa9\xed\x6d\x07\xea\x7e\x5a\x5d\xd2\x73\x74\x0a\x8e\x9b\x25\x38\x3c\x1e\xd1\xb6\xd0\x08\xee\x40\xae\x63\x21\xb1\x4b\xcd\x22\x92\x5b\xeb\x4f\x57\x6a\x4e\x3a\x30\x0b\x2d\xe1\xf5\xaf\xf7\xd2\xf6\x58\xdb\xdb\x28\xce\x86\x0a\x66\x7c\x25\x9c\x99\xd7\x16\x58\x6a\x17\x99\x45\x6c\xb6\x70\x48\xbd\xb4\xe3\xb7\xf8\x68\x8b\x14\x41\xaf\xed\x23\xc3\x0c\x57\x16\x25\x73\xd4\x85\xdc\x45\x5d\xa8\xf1\x52\x07\x03\x05\x67\x0e\xc0\xad\x5c\x33\xc9\x7b\x85\xc7\xe5\xd1\xdc\x44\xe8\xf8\xf4\x8f\x2f\x5e\xfc\x71\x80\xd2\xe7\x4a\x12\x0f\x3e\xbf\x9b\x0d\x5e\x6e\xad\xb7\xf2\xf7\xc9\xde\x15\xb2\xe8\xc7\xb7\xf9\x55\x76\xd8\x5b\x60\xb3\x37\x42\xf5\x9f\x66\xc5\xaf\x83\x97\xad\x4d\x0e\x07\x5e\xf3\x16\x24\xb8\x47\x4c\x52\xc7\x15\xb2\x04\xb9\x2f\x09\xf0\x43\x7c\x43\xa8\x18\x61\x7a\xb2\x81\xff\xcf\x28\x5d\x09\x54\xa0\x30\x7a\x50\x18\x4d\x26\x8a\xbf\x53\x6e\x05\xc2\xa4\x50\xe7\x40\x35\xc4\x08\x70\x0e\x8b\xa6\x80\xc6\xa0\x19\x64\x2f\x43\xf2\xe5\x1d\x75\x78\x01\x19\x1a\x08\x81\x17\x49\xf3\x26\xe7\x68\x62\x3d\x51\x71\x64\x99\xe1\x86\xe9\xe0\x7d\x22\x12\x89\xd3\xb6\x78\x6b\x23\xde\xb1\x23\x42\x17\x75\x34\x1a\x83\x21\xc3\x3c\xca\x5a\xdc\x26\xa8\x31\x62\x37\x5e\xa9\x5c\x04\xa4\x73\x9a\x98\x02\x19\x1f\x42\x06\xbb\x2c\x12\x2f\x01\xe7\x46\xeb\xa6\xb1\xc8\x22\x53\x5b\x73\xe9\x5f\x39\xf4\xc7\x1b\x7e\x98\x3a\x3d\xfd\x17\x18\x4d\x75\x43\x0b\xe0\xae\x37\x60\x27\x6c\xde\xbb\x30\xc9\x23\xfe\x0e\x13\x2c\x58\x8d\xd4\x02\xf7\x4b\x2f\x7a\x99\xed\xde\x50\xe0\xe4\x65\x02\x05\x54\x93\xcd\x8a\x80\x73\x38\xf5\x49\xdc\xa6\x48\x1c\x14\x6b\x0f\x0b\x37\xba\x82\x65\x0a\x50\x41\xfb\xc5\x05\x43\xbd\x93\xd3\x18\x55\x9b\xad\x3a\x5e\x15\x0f\x57\x81\x81\xab\x06\x6e\x4a\x1f\xe9\x7a\xc7\x63\xe5\x62\x47\xd5\x07\x7f\xa9\x63\x38\x21\xa2\xd3\xe8\xba\x4f\x25\x8e\x45\x54\xa4\xd5\xd8\xd4\xe3\xef\x47\x32\xa5\xc6\xa8\xd1\x82\x33\xb1\x57\xe1\x97\x92\x83\x60\xdd\x45\x8f\xa2\x0a\x32\x05\x4d\xa9\x66\xc8\xb0\x59\xdd\xf5\xb3\xf0\xe3\x03\xf7\x9c\x76\x9b\x6a\x3e\xee\xdd\x33\xd9\x36\xf7\xf9\x6a\x97\x10\x0c\x12\x14\x0b\x58\xd6\x9a\x36\x10\x0a\x7f\xb4\xc1\xf6\xf7\x0e\x4c\xed\xd1\x59\xa2\xc3\xea\x6d\x28\x1a\x7a\x52\x18\x6d\xdb\x64\x3a\xca\x35\xbe\x17\xba\xd9\x73\xa3\x01\xe2\xae\xc3\x6d\x85\x42\xa1\x00\xfb\xf8\xa2\x71\x56\x80\x4a\x5d\x5b\x17\x69\x08\x58\x76\x9d\x62\x61\x8d\x37\x52\xd5\x1a\x7b\x5a\x0a\x64\x46\xa6\x69\x3c\xb7\xec\xf8\xd8\x4b\xa0\xe3\xe3\x42\x8f\x4c\xa2\x90\x41\xf8\x5b\x23\xa9\x2c\x8a\xaf\x14\x47\x69\xf4\x2d\x26\xf6\x3c\x18\x12\x49\x4a\xbb\xc2\x8b\xcb\x32\xba\x29\x06\x06\xa0\x76\x1f\xa3\x65\x82\x3a\xc6\x3a\x77\xd2\x92\x7f\xda\x8f\x96\x67\x8a\xf5\x5d\x07\x86\x51\xf6\x25\xd9\x85\x23\x64\x0d\xb6\xbd\x49\xa2\x9e\x79\x25\x29\x25\xc8\xe2\xf6\x6e\xd2\x34\x32\xc4\x8a\x5b\xe6\x65\x9f\xa7\x4d\xcd\xbb\x90\x2c\x40\xb8\xc4\x78\x36\x57\xe0\x59\xc7\xa5\xa4\xd7\x91\x20\xf1\xb8\xf6\xb9\x4b\xbb\x6e\xd1\x03\xad\x92\xbb\x0b\x6a\x37\xd5\x65\x2c\xac\x8d\xf1\x68\xad\xc8\x4a\xb5\x6c\xa5\x65\x73\x7a\x3c\x18\xba\x82\x0e\x65\xaa\x9d\x0a\x90\x82\xfe\x3f\x46\x35\x91\xcb\x73\xd9\xce\xfa\x5c\x54\x6c\x24\x7a\x52\x4d\xed\x9e\x95\xb6\xd1\xd1\xde\xae\xb7\xdd\x34\x5b\x7e\x1d\x73\x25\x98\x29\x43\xfa\x86\xe8\xaa\x8d\x8e\x0a\x0d\x7a\x89\xaf\xa4\x82\x0a\xc4\x15\x0b\x78\xc9\x4c\xc4\x7e\x86\x64\x79\x99\x6d\xcb\x83\xfa\x8f\x16\xbd\x94\x09\xd8\x30\x96\x10\xf6\x4f\xf0\xb0\xa4\x8b\xfa\x2c\xce\xde\xbe\x7e\xf3\x8f\x1f\xde\x9d\x5d\x9d\xff\xf8\xfa\x1f\x2f\xdf\xbf\xfb\xf6\xfc\xaf\x7f\xfb\x70\x76\x75\xfe\xfe\x9d\x7f\xe4\xfb\xcb\xf7\xef\x18\x31\x57\x55\x4c\x40\x2a\xaf\x69\xea\x1f\xa0\xb2\x36\xe7\xf9\xa2\x0f\x65\x01\x88\xcf\x10\x8f\xad\x98\x02\x9d\x3c\x41\x47\x92\x11\x9e\x65\xca\x7f\x90\xee\x1f\xe3\xa1\xd4\x8e\xf1\x14\x9c\x85\x6d\x43\xfe\x1e\x5d\x3e\x44\x28\x3a\x0e\xc5\x39\xb7\x9d\x04\xb7\x75\xe0\xc3\xd3\x2b\x11\x58\x71\xa5\x40\x4e\x4b\x5e\xbb\xdf\xa5\x7d\x13\xbc\x82\xf0\x76\x08\x11\x71\x1b\x6b\xb7\xf4\x62\xe8\xbc\xd1\xb1\x7a\xe4\x83\xf7\x1f\x6f\x34\x36\x7a\x44\x30\xc1\xb9\xd0\x86\x78\x85\xd8\xeb\x6f\x1f\xce\xed\x28\xc2\x42\x5d\xff\x62\x74\x1b\xb0\x4e\xa8\xd4\x64\xf2\x58\x38\x47\xe3\xfb\x37\xa1\xf2\xe8\xba\x9f\x41\xac\xf8\xf2\xaf\x42\xad\x14\x32\xdf\x8b\x5c\x37\xf0\xd9\xb4\xc2\x77\xf1\x79\x9b\xeb\xd7\xb7\xea\x6d\xe7\xc0\x6c\x3f\xf7\xaf\xcf\xf1\x22\x79\xc4\xb3\xf2\xa2\x06\xc7\x80\x78\x01\x6f\x1b\x6b\x76\x18\x86\x0c\xf0\xdc\x18\x36\x37\xfa\x1a\x4c\x9e\xe1\x13\x8d\x03\xaf\xb3\x0e\x82\xf0\x3a\x38\x1a\xd9\xef\xe7\x9c\xd1\x5e\xbb\xed\x8c\x6e\xfa\x1a\x76\x9c\xce\x67\x6e\x72\xb0\x8b\x85\x90\x0e\x4c\x38\xb6\x69\xe4\xd9\x7b\x45\x6c\x2c\x7e\xa5\xd7\xc3\xcc\x41\x44\x68\xa3\x33\x61\x05\xbc\x01\xc3\x0e\x6a\x98\x06\xd5\xbc\x12\xd6\x69\xb3\x3e\x88\xc3\x07\x2f\x85\xaa\x83\xe0\x0d\x0f\x7b\xab\x6b\x0e\xde\xff\x87\x56\xdf\x90\xa6\x53\x70\x0b\xa6\x1c\xcc\x17\x64\xe7\xa4\x40\x21\x19\x08\x77\x0c\xc9\x4d\x2d\x2d\x42\x5d\x4f\xe7\x42\x35\x29\xb6\xb0\x73\x1a\x2d\x06\x29\xc2\xe3\x63\xa5\x70\x1c\x01\xe2\x48\xab\x2c\xd2\x2f\x85\xba\xfe\xa6\x58\x82\x25\xcb\xa0\xba\x42\x1d\x53\xa8\x84\xa4\x13\x07\x80\xd1\x69\xb2\x04\x7d\x29\x01\x17\xa9\xca\x4a\xba\xe8\xd4\x8d\x28\xd7\x7b\x01\x1d\xc2\xa7\x1a\xba\xf1\x37\x72\xda\x53\xdf\x2a\x22\x62\xde\x17\xed\xe1\xe8\xc1\x46\x6a\x98\x54\x99\x6c\xa9\x9c\xa5\xc6\x70\x4d\xd4\xc3\x85\xe6\xcf\x45\x31\x61\xac\xe5\x23\x16\xc5\xbc\x09\x83\x33\x77\x24\x4f\xce\xb7\xc3\x9a\x05\x62\x2c\x05\xae\x0e\x63\xc5\x58\xad\x25\x36\x2b\x35\x41\x7f\x1f\x91\x81\x14\x67\x74\xce\x79\x7d\x0d\xde\x3c\xb4\xb9\x66\x78\xbe\x66\xff\xa3\xe7\xe6\xba\xb7\xd4\xa3\x71\x8b\xc1\xa3\x4d\x2b\x30\xf9\x10\x34\x3d\x20\x26\xb0\xfe\x49\x6f\x56\x42\x9f\x2c\x7b\xd1\x80\x3d\x09\x4b\x3d\x09\x83\x4a\x6a\x73\x3f\x1a\xfe\xa9\xd8\x10\x2a\xf5\x92\xe9\xde\x75\x38\x24\x25\x49\x33\xa4\xf4\x1e\x16\xd9\x1b\xbd\xb4\xac\x05\x8b\x85\x03\xe9\xad\xc4\x70\x70\x03\xfb\x64\x7f\xcf\x9a\x8f\xbd\x75\x11\x1d\x3c\x56\x0a\x54\xc4\x6c\x04\x06\x37\xcf\xdf\x7d\xfb\xbe\x8c\xe9\x7e\xb4\x7b\x24\x59\xdf\xe3\xd6\x22\x68\x1b\x6d\xc1\x0d\x30\xd3\xce\x80\x73\x6b\x2c\xbb\xb9\xbf\xea\x2d\xdc\xc1\x03\x7a\x89\x32\x46\x42\x2d\x0f\x62\xa7\x2e\x1a\x9b\x7e\xb5\x67\xd9\x21\xb7\x6e\xa3\xef\xe0\x57\x1e\xf9\xf1\x16\x57\xd8\x11\x10\x1e\x13\xb8\xa5\x43\x86\xdb\xf4\x54\x37\xfe\x28\x8b\x60\xef\xb0\x59\xa9\xd1\x74\x3a\xa8\x60\xb0\x6f\x2a\xd6\x70\x26\xff\xf4\x98\x76\x7b\x8c\x10\x83\x37\x8b\xb1\x5a\xad\x30\xc9\xed\xed\x0b\x74\xf3\x55\x0d\x54\xd4\xfc\xbc\x6c\x21\x1f\xba\x89\xb7\xe4\x44\x95\xfd\x51\x04\x3e\x1b\x55\x58\xd6\x84\xeb\x50\x7e\x91\xcd\xbc\xb5\x71\x78\x40\xcf\x9d\x4a\x5d\x5f\xe3\x29\x38\x90\x7e\xf7\xed\xe9\x5c\x3b\x7b\x70\x54\x55\xd5\xac\x62\xef\xde\x5f\xbd\x3e\x0d\x81\x71\x11\x73\x36\x18\xa6\x46\x6d\xcf\xb1\xc9\xb4\x15\x34\x5a\x62\xac\x90\x3a\xd5\x7b\x53\xc1\x57\x6a\xbe\x7f\x16\x82\x16\x06\x78\x73\x72\x6b\x44\xf2\x4a\x5a\xde\xd9\xd0\x0b\xcc\x71\x4a\x70\xa2\x81\x01\x7f\xc1\xb1\x28\x87\x25\xa3\x23\x4f\x43\xcc\x19\x04\x96\x56\x73\x2b\xae\xb2\x5d\x35\x88\xf6\x6f\x05\x63\x9e\xc0\x24\x88\x07\xa8\x40\x5b\xe8\xc0\x8d\x5a\x01\xba\x85\x84\xc9\xa0\xa6\xb5\x96\x7d\x03\xd3\xc6\xf3\x01\x77\xfe\x1f\x83\xa6\xd5\x7b\xab\x35\x14\xed\x82\x8a\xa8\xa2\x9b\x4d\xf3\x0a\xfc\x56\xb8\x43\x3d\xc5\xe5\xfa\x5f\x21\xd8\x1c\x3c\x95\x5a\x37\x90\x73\xe1\xbc\x69\x86\x1d\xa8\xa9\xbb\x19\x2d\x10\xc2\x2d\xfb\x1f\x15\x0e\x4d\x28\xae\xc1\x6c\x8b\xaf\x69\x10\x4b\x0e\x6b\x2a\x36\xc3\x59\x07\xe1\x2f\x88\xeb\x66\x4d\x7a\xae\xd8\x0e\x73\xd3\x4b\x94\x76\x9b\x47\xdb\x1e\x7c\xcb\xbb\x3d\xa4\xfc\xf3\x77\xbc\x85\x62\x98\x2f\xbd\x58\xb4\x09\x16\xac\xe5\x4d\xdb\xa8\x9f\xea\xeb\x3c\xb5\x2c\x07\x03\x0f\xfe\x5b\xc1\xdb\x38\x34\xf3\xbf\x4f\xfd\xb3\x07\xd5\x2b\xe8\x0c\xe0\x04\xf6\xd3\xd8\x6f\x8f\xb4\x3d\x88\x92\x0c\x9f\x3e\x18\xd4\xf6\x0f\xfe\xb4\xc7\x5e\x46\xb7\x72\x22\x81\x5b\x18\x6b\xab\x1d\xdb\x59\xd8\xca\x70\x7f\xbb\x77\x36\x86\xb0\x8b\xfd\x35\xf7\xd4\x05\xac\x3b\x44\x78\x44\xb0\x97\x23\xa6\xfd\x3a\x98\x96\x3b\xa0\xbc\xc9\x5b\xde\x1d\xf8\x0b\x7e\xf0\xc6\x6f\x8d\x1c\x37\xff\xbf\x01\xbe\xf4\xb7\xc1\xec\x3d\x3e\x07\x39\xbd\x86\x7d\x06\x46\xbc\xc1\xba\xef\x51\x5a\x89\x06\x94\x13\x8b\x35\x35\xfb\xe3\x08\x20\xad\x1c\x64\x0f\x02\x89\x37\x86\x12\xcd\xfc\x08\x03\x40\xb4\x59\x9e\x14\x24\x1d\xc1\x14\xe3\xd1\x7b\xe3\x5a\x44\xaf\x1f\x8a\xf1\x9d\x87\xbe\xa9\x56\x70\x66\xea\xb3\x5c\x72\x01\x8a\x77\xe2\xf1\x8a\x16\xfc\x1f\xcf\x2e\xce\xd9\xab\xcb\x37\xbb\x1b\xeb\xbd\x65\x91\x9b\x99\xcb\x84\xef\xb3\xe4\xe7\xf3\x04\xce\xeb\xd0\xcd\x56\x8e\xb2\x99\xd7\x3b\x46\x8f\xd8\x8e\xf9\xfe\x36\x0f\x1a\x07\x65\x43\x12\x90\x53\xad\x01\x6e\x02\x9a\xc2\xbe\x99\x83\xd4\x34\x35\x65\xcc\x93\x9b\x03\xa6\x4e\xc3\x5b\x58\x95\x60\xb8\xb2\x0b\x0c\x61\x2b\xa5\x5d\xcc\x7b\xab\x66\xe3\xd3\x1e\xe5\xb9\xea\x10\xb9\x0e\x23\xa0\xc9\x33\x4c\x28\x3c\x01\x17\x83\xdc\xe0\x69\xb1\xe3\x3d\xa3\x36\x57\x59\xd7\x94\xe4\xa2\x5a\xcb\x48\x4a\x33\xa8\xc5\x0a\x6b\x3d\xe8\x83\x20\xc5\x32\xe1\x14\xb6\x57\x48\xb5\x5e\xcd\xfc\x11\x9d\xe1\x8b\x57\xdf\xdc\x63\x8f\x5f\xe8\xe6\x95\xb0\xa6\xc7\x97\xbe\xe9\x9b\x25\xb8\xc4\x0b\x74\x9d\x42\x4e\xed\x7c\xf8\xd5\x91\xa7\xc0\x27\xad\x50\x53\x7e\xc3\x85\xe4\xf3\xbd\x27\x7b\xe5\xbc\x23\x8a\xce\xb1\xdd\xe3\xf5\xc5\xb6\x06\xeb\x82\xec\x1d\xae\x12\x87\x33\x72\xc5\xe0\x46\xa0\x7b\x52\x9d\xe7\xd9\x2f\x54\x52\xcc\x15\xe3\x73\xab\x65\xef\xf2\xa2\x86\x66\xde\x84\x8c\x78\xf5\x9e\x3c\x96\x08\x54\x2f\xd8\x6c\xb0\xa5\x50\xf9\xde\xf2\x4f\xd3\x5e\x15\xbf\x0d\x0b\xa5\xf9\x57\x9b\x79\xd9\xe2\xe1\x5f\x99\x2a\x31\x14\x96\x17\x20\x52\x44\xb2\xfc\x32\x82\x14\x45\xd7\x5f\xa4\x8a\xfe\x6d\xa2\x78\x5b\x53\x5a\x1d\x9b\xb4\x8e\x12\x1d\x89\x82\x9b\xd4\x22\x1a\x0e\x40\xc4\x48\xf4\x16\x1d\xd3\xad\x0d\xf7\xf5\xf1\xf4\xc6\x46\xb9\x1e\x96\xf0\xcd\xbd\x69\x47\x3f\x23\xb5\x8b\xe0\x16\xb7\x56\x2c\xd5\xc6\x14\x4c\xdc\x46\x06\xa4\x37\xfe\x5c\xb1\x73\xc5\x6a\x1e\xb2\x83\xe9\x39\x61\x71\x6a\x0f\x16\xd8\x26\x27\x86\x88\x8a\xc5\x1c\xd1\xab\x24\x35\x84\xb5\xb7\x74\x28\x11\x42\xc5\x30\x2c\x1a\x0a\xa5\x30\x79\x18\x1c\x59\xd2\xe2\x8b\x5e\xb2\x30\x2c\x1d\x3e\x39\x4b\x9f\xd1\x08\xfe\x2f\x18\x78\x6e\x99\xd2\x69\xb8\x5c\x08\xa8\x31\x1e\x46\xb0\x8d\x7c\x43\x63\x80\x3d\x25\x9a\xb5\x1a\x50\x77\xf8\x4d\x12\x0b\xce\x5b\x0e\x16\xe7\xd1\x4d\x98\x0d\x41\x6b\x5a\xda\xb3\x68\x3b\x07\xf4\x4e\x36\xc6\xb9\x33\x03\x4b\x61\x9d\x59\x3f\x85\xc6\x64\x3a\x9d\x69\x59\x4a\x7b\x4f\xcf\xf0\xd6\x79\x1e\x42\xdb\xb9\xf5\x51\xa6\x6d\x8a\x30\x8f\xf0\x4a\xb9\xf6\x52\xea\xf9\xa0\x6d\x6c\x7c\xcd\x73\xd5\x84\x56\x03\xb1\x18\x82\x2d\xbe\xdd\x15\x6c\x1d\x02\x89\x95\x9a\xe4\xf0\x70\x5b\x88\x45\xfa\x6b\xf6\x80\x93\x9c\xf0\x57\xf2\xe1\x01\xee\xad\x06\xea\x06\x1c\xd4\xc5\xc8\xd2\x72\xfe\x87\x58\x8c\x5c\x81\xa1\x00\x89\x9b\x38\x14\xd9\x5a\x8f\xbf\x2b\x39\x15\x43\x54\x47\x85\x94\xc1\x01\xcd\x8f\x65\x1b\xe0\x5c\xdc\x81\x6d\xb0\xca\x83\x1c\x07\x61\x8c\x2d\xd5\xcf\xce\x89\xa3\x78\x1c\x36\xbc\x02\x36\xbb\xd0\xcd\x65\x07\xf5\x15\xb4\x1e\x63\x98\x79\x85\xd2\xd7\x69\x60\x69\xae\x72\x28\xc1\xcd\x2a\x2f\x1a\xaa\x4e\x37\xe9\x3d\xb2\x3a\x04\xc8\x66\x92\x8b\x2c\xca\x77\x8a\x5e\x5c\xaa\x0d\x0a\x6f\xc6\xa2\xfe\xf0\x1d\x31\x51\xb3\x16\xcc\x12\xfb\x37\xeb\x15\xb9\x68\x5b\xf9\x9a\xad\x79\xc4\xf9\xce\xd3\xd7\x7d\x28\x0b\x17\x42\x88\x61\xaa\xed\xc4\xbb\xf2\xb8\x56\x92\x2d\xc3\xcf\x49\x6c\x7c\x77\xe2\x6e\xe7\xa3\x33\xba\x05\xb7\x82\xfe\xc1\xc3\x21\x1e\x12\x98\xbd\x48\xab\xc4\xd1\x11\x65\x3b\x78\xfe\xeb\xb4\xd6\x6d\xc7\x1d\x8e\x59\x8a\xc1\x9f\xf8\xbd\x4a\x52\xa9\xc4\xb5\xfe\x2d\x7f\xdc\x6f\xb5\x12\x4e\x9b\x59\x32\x18\x73\x47\x06\xdd\x92\x38\x3c\x20\xe8\xd1\xda\xf0\x6e\x33\xba\x1a\xb3\x23\x65\x88\xb5\x44\x38\xde\x69\xca\xb8\x50\xfd\x5f\xa8\x4c\x0a\xe3\x12\xe8\xb5\xb7\xa2\x36\xfa\x82\xe8\x85\x20\xdf\xd2\xa3\x15\xfb\xfb\xd9\x87\x77\xe7\xef\xfe\x1a\x3e\x71\x85\x6e\x63\x31\xd1\x72\x6c\x1b\x79\x7a\x34\xa6\x3d\x43\x52\x66\x29\xdc\xaa\x9f\x57\xb5\x6e\x4f\x6a\x6d\x40\xdb\x93\x7c\x7a\xd3\x88\xe6\x4f\x17\xe5\x89\x62\x2f\x18\xfe\xfe\xe7\xa8\xbe\xd2\x1a\xd8\xb6\x24\x62\x1c\x7c\x9e\xca\xce\xa0\xa9\xd8\xff\xd4\x3d\x12\x0d\x8b\x3f\x3b\xdd\x4c\xdb\x80\x62\xd4\xbd\xa1\x7d\x33\xa9\xbf\xad\x13\x4e\xd3\x56\x85\x5b\xe9\x90\x7c\x28\x1e\x7a\x5f\x52\x15\x81\x6e\x41\x10\xe3\x1f\x28\x7d\x02\x11\xdc\x82\x60\xfb\x84\x54\xc9\xf8\xbc\x83\x13\xfc\x7a\x51\x7a\x6f\x0c\x76\xba\x63\xc9\x87\xbb\x8a\xe3\x2b\x13\x98\xed\xae\xca\x01\x3f\xe4\x3c\x39\x21\x55\xe8\x8e\x5e\xca\x29\xb5\x87\x3f\xa6\x7f\xd9\x4b\xc9\x2e\x71\x95\xc0\x36\x96\xf2\xd3\xf8\xc9\x1a\x5a\x3e\x86\x20\x3a\xdd\x4c\x72\xfc\x66\x63\x42\x39\x66\x29\x9c\x11\x70\xb3\x29\x86\xc9\xf4\xa2\xa0\x8e\x4a\xe3\x81\x93\x2d\x46\x72\xa1\x5c\xae\x18\xd1\x9d\x27\x00\xb7\x5c\xf5\xa8\xca\xb5\xc1\x21\xe7\x68\xf6\xae\x75\xff\xbc\x28\xcd\x23\xd1\x54\x36\x38\xd2\x88\xca\xb4\x28\x82\xcd\x98\x45\x14\xe2\x06\x67\x85\x92\x8a\x1f\xb6\x9b\x91\x0d\x4d\x93\x9c\x09\xbf\xc2\x6a\xf7\x68\x23\x50\xdc\xe4\xf6\x74\x9d\xed\xc9\x3a\x6b\x2f\x19\xf2\x87\xaa\x3e\x07\x5d\x14\xd2\x5e\xeb\x5b\xdb\xb7\x31\x1a\xb5\x49\x57\x11\xca\x3f\x3b\x83\x29\x31\xec\x51\x5e\xeb\x9e\x2e\x54\xda\xf8\x70\xf2\xfb\x08\x36\x7e\x83\x18\xa4\xc3\xfd\x4d\x08\x7d\xae\xd2\x55\xf7\x17\x9a\xce\x3f\x66\x4a\x7e\xe7\xb2\x85\xce\x70\xef\x0f\x64\x6d\xb0\x26\x8e\x15\x0b\x45\xfd\x81\x69\x2e\x74\x83\xc4\x95\xb0\x70\x0c\x0d\x6e\xc2\x64\x33\x61\x12\xb3\x0e\xfc\x1a\x54\x36\x44\x47\x59\x2e\x9f\xcf\xc0\x57\xda\xfa\xb2\x15\x7e\x8e\x0a\x4c\x4c\x46\xed\xd9\x2f\xac\xd2\x24\xfd\x4d\xab\x3b\x4c\x8d\x42\x72\x36\x49\xe4\x4c\x68\x3f\x74\x14\xa9\xaa\x27\x2d\x99\x14\x71\x98\xae\x50\x62\x36\x8b\xb3\x18\x99\xd1\xfe\x18\xd5\x30\xcf\xa5\xe2\xa7\x71\x73\x46\xe6\x9e\xcc\xe8\x2f\xac\xc7\xde\x98\xa9\x92\xdc\x95\x44\xef\x2d\x81\x97\x83\x14\xa4\x51\xfd\x66\xd7\x1d\xb0\xd9\x70\x60\x47\xa3\xeb\x6b\x30\x04\xfe\xa3\xd5\xaa\x90\xe3\xa1\x14\xe4\xf1\x02\x0d\xa1\x4a\x65\x6b\x88\x8c\x2b\xfe\x16\x7b\xff\xee\x92\x4f\x4f\xe0\xe2\x16\x35\xd2\x3b\x5d\xbf\xed\x5d\xd3\x89\x1d\x1a\xc0\xd1\x0b\xd4\xa1\xb0\xe8\x31\xb4\xd1\x5b\xc8\xe5\xea\xe8\x23\xec\xa1\x6b\x77\x9e\xc6\x07\x0f\xe4\xbe\xef\x92\xba\x0d\x43\x76\x18\xea\x08\xbe\xca\x58\x2f\xc4\xef\xff\x98\xf6\x9d\xb6\xb1\xcf\x00\x35\x24\xc4\x20\x6f\x20\xed\x94\xbe\x3a\xbd\x6f\xb5\xb7\x3f\x92\xab\x37\x97\xac\x78\x0b\xdf\x98\x30\x29\xae\x81\xcd\xa0\x59\xc2\x6c\xc2\x66\x1d\xb7\x36\x4c\xb3\xa3\x29\x11\x06\x40\xd5\x66\xdd\xb9\xd9\x58\x07\x49\xf6\xf8\xb7\x7b\x48\x8a\x61\x07\x77\x74\x92\xf8\x6d\x14\x93\x1a\x1e\xb0\x8d\xcd\xc9\x2c\x38\x0b\x61\xd0\xf2\x73\x07\x66\x01\xfd\xfd\xf1\xdb\x2f\xef\x3a\x86\xd7\x35\xac\x1f\x19\xb7\x30\x1f\xea\xf3\xc8\x87\x56\x8e\x36\xc2\xad\x1f\x42\xcd\xa8\xf6\x3e\xf3\xb4\x8b\xba\xef\xcf\xc3\xbe\x2c\x1c\x1f\x0c\xb9\x82\xfc\x71\xe2\x68\x76\x63\xe1\x6f\xac\x2b\xe0\x6c\x73\x31\x16\xff\xb6\x10\x34\x0f\x3e\x41\xae\x58\x69\x1f\xa4\x1b\x30\xb8\x3b\x18\x18\x16\x12\x42\x88\x2f\x16\x76\x14\xc3\x56\xcb\x2f\x10\xe1\x24\x7d\xbc\xc6\x86\x7a\x69\x85\x43\xaa\xad\x80\x4b\xb7\x62\x38\xa1\x28\xa5\x38\x2d\xd4\x7d\x6a\x65\x8c\x9f\x9b\xd3\xaa\x3a\x5f\xc4\x65\x41\x62\x8d\x26\xe6\x05\xa2\x67\x30\xc9\xa2\xc2\xb0\x96\xaf\x53\xc0\x39\xf6\x8a\x15\x1b\x0c\xb0\x5f\x9e\x61\xce\x20\x7e\x0c\xcd\x8b\x1e\x34\xec\x6e\xb8\x14\x4d\x9c\xa4\x28\xd4\x12\x91\x5a\x69\x93\xca\xa6\xc8\xfe\x3b\x0c\x3f\x55\xc9\x7e\xa9\xec\x4d\x7d\x14\x8b\x67\x68\x50\x42\x08\xc9\x09\xb5\x30\x9c\xe2\x68\x5e\xd2\x85\xb9\xd7\xd0\x0c\x27\xb5\x6c\xd6\xd1\xd1\x88\xb2\xc7\x11\x3c\x42\x11\x9d\xa7\x5e\xf4\x95\xd2\x74\xff\xcf\x73\x0c\x84\x77\xf8\x40\x47\x03\x5c\x92\x01\x19\x17\xf0\xda\x63\xb1\x10\x75\x2c\xa8\xc3\xe2\x6d\x2f\x6b\x5f\x91\xaa\x29\x1b\x81\x3f\x40\x6c\x2c\x0b\x2f\xed\x25\x38\x76\xee\x3a\x1b\xfc\x34\x3c\x2d\xd7\x98\x3f\x5a\x98\x2e\x76\xd4\x7f\x13\xeb\xd3\xcb\xe0\x6c\x6f\xfd\x1d\x20\x33\x40\x61\x13\x9a\x66\x17\x9e\x55\xad\xd0\x0a\x9a\xd4\xdf\x4e\x71\x59\x95\x7e\x11\x80\x61\xd5\x69\xc6\xec\x74\x2c\xaa\x75\xfd\x95\x9d\x6e\x6c\xd7\x9e\xf8\x8b\xf2\x1f\xdb\x44\x60\xec\x2c\xd4\x97\x85\x86\x84\xdc\x89\x8a\x39\x51\xb8\xd1\xf2\x86\xc2\x79\xe4\xcc\xd8\x7e\xfe\x31\xa0\x5d\xaf\xb8\x5a\xc2\x13\x08\x24\x6d\x12\x63\xcf\x98\x4e\xec\x0d\x19\x3b\x9e\xbb\x8e\x06\x05\xaa\x97\x26\x3f\xfd\xc4\x3b\xb1\x34\xba\xef\x4e\x7e\x0e\x2d\x12\xa7\x3f\x5f\x0b\xd5\x9c\xfe\x94\xe4\xc5\xc9\xcf\xe8\x7f\x6e\xa0\xf9\x70\xd6\xdc\x69\x89\x0e\xe7\x3b\x90\x69\xb5\x35\x65\x28\xfa\x7c\xf1\xe1\x14\xbd\xb2\x71\x88\x08\x77\xc5\x67\x4f\x68\xac\x1f\x59\x68\x9a\xe2\x6e\x79\x9c\x36\x7d\xca\xac\x2c\xe7\x39\x8a\x94\xc1\xf0\x49\x16\x97\x21\x24\x3d\x1e\x2a\x09\xb9\x24\x31\x4c\x18\xc4\xfe\x5e\xbe\xf9\x2d\xab\x94\xb7\x46\xa0\x61\x88\xf2\xc6\xc8\x86\x27\x60\x37\xff\x3a\x79\x2d\xac\x12\xc5\x84\x56\x3c\x50\x05\xd0\xc4\xf2\x95\x10\x08\x1d\x78\x55\xba\xd9\xfa\x90\xf9\xce\x82\xf5\xc4\x55\x83\x6f\x7e\x73\xcb\xde\xe9\x06\x2e\x3c\xa0\x08\xfa\x0f\xb1\x7b\xfc\xb1\x62\x97\xb4\xc0\xb8\xaf\x35\x24\x53\xcc\x8c\x96\x25\x43\xf1\x2b\x40\x14\xcd\x8c\xb0\x74\xea\x85\x41\x7a\x66\x7d\x9d\x02\x11\xaa\xa1\x4f\x51\x78\xc9\x9e\x6a\x24\xbc\x22\x3a\xbb\x38\x67\x2d\x57\x7c\x09\x79\xa8\xc9\x16\x9a\x77\x04\xe5\xff\x1f\xe1\xcf\xcf\xad\xaa\xde\xfb\xbb\xc4\x57\xe1\xb3\x73\xf9\xdb\xc4\xa4\x50\x1d\x0f\xdf\x5e\x89\xc7\xb4\xf9\x11\xfa\x32\xd4\xbf\xdf\x9c\x28\x1a\x99\xe8\x56\x21\x8d\xe8\x81\xfb\x13\x16\x96\x75\xfd\x5c\xd2\x27\xbc\x8a\x81\xae\xc3\x25\xf6\x1c\x87\x88\x33\xb6\x32\x7c\x9b\x67\xae\x8f\x7f\xef\x7f\x30\xd3\x2b\xc1\x9a\x7e\xfe\x8e\xfc\xfd\x9a\xc6\x22\xcb\x81\x15\x30\xba\xc9\x50\x42\x5a\x61\x88\x2b\x07\x4e\x9c\x96\x90\x0a\x56\x1e\xc9\xba\x4a\x7d\x15\x98\x9f\xb8\x4a\x2b\x5a\x86\x97\x75\x3b\xc3\x5d\x3e\x92\x27\xd3\x1e\xce\x7b\x6f\x44\xa2\x0b\x11\x92\x84\x47\x31\xd6\x87\x62\x32\x7d\xc0\x0b\x8b\x2c\xbd\x78\xb4\xa4\x23\x5b\xee\xea\x15\x1a\x44\x1c\x4b\xea\xd9\x25\x10\xc7\x46\x43\x6c\x2b\x22\x68\x4f\x6a\xad\x6a\xe8\x9c\x3d\x09\x50\x85\x5a\x4e\x63\xfd\xd4\x09\xc2\x99\x72\xd5\x4c\x33\xfd\x4e\x9e\xb1\x72\x86\x4d\x03\x8e\x0b\x19\xbb\xf9\xd3\x53\x45\x79\x45\xfe\xa8\x18\x76\xb0\x58\xd1\x0a\xc9\xbd\x1f\xa4\xb0\x6a\x35\x0a\x39\x7f\xf1\x10\xed\x09\x13\x15\x54\x13\x36\xfb\x01\xd6\x3f\x7d\xfd\x23\x97\x3d\xfc\x7c\xfa\x7a\xb1\x80\xda\xfd\x74\x7a\x49\x9f\x03\xfb\x79\x36\x09\x2c\x42\x9f\xd2\xc3\x2f\x85\xfd\xb3\xf7\xb2\x62\x6e\x78\x7d\x0d\xa1\x84\x9e\xa7\x11\x51\x5c\x56\xf8\xd5\x5b\xf8\xc4\xdb\xce\x9f\x34\x9b\xb2\x19\xaa\x16\xa3\x25\x54\x43\xca\x84\xd6\x83\x77\xfa\x32\x90\x7a\x16\x9f\xde\x78\x30\x8c\x44\x2d\x8b\xbd\x4e\xdf\xe9\xd7\x94\xc2\x3f\xfd\xc3\x8b\x17\x2f\xc8\x85\x98\xd2\x37\x1d\x3c\xf3\x7f\x6d\x6d\x73\x7a\x81\x13\xc0\x4a\xf8\xd4\x1a\xf3\x44\xb3\xa1\x74\x70\x0f\x48\x47\xa6\x0f\x7f\x72\x11\xbe\x37\x16\x58\x07\x26\x03\x4b\x75\x37\x0f\xe4\xdb\x6d\x78\xfd\xb8\x1d\x9f\x57\xb4\xc2\x3e\x9a\x3c\x88\xa5\x88\x54\xe9\xf2\xc5\x34\x24\xa7\x82\x9c\x08\xb4\x28\x89\x08\x5f\x5b\x89\xb5\x08\xb9\x2e\x6e\x4e\xaa\x7f\x7c\xb8\x48\x2a\x11\x89\x6b\xa6\xb2\x88\xac\xff\x03\x59\x93\x85\xcb\x0e\xc3\x07\x0c\x2d\x3b\x3e\xfe\x9e\xc3\x12\xcc\xf1\x71\x68\x3a\xbd\x4a\xf4\x64\xff\xdf\x28\xd8\x30\x0a\x26\xa1\xc1\x0a\xf3\x53\xf1\xf9\x18\x7b\x2b\x9b\x94\xc7\xce\x63\xcc\xf3\x7b\x40\x9e\x4f\x15\xed\x38\x51\x13\xa3\x6f\x11\x55\xa1\x4d\x2b\x36\xdc\xf1\x41\x5f\x69\x0c\xd2\x8c\x34\x60\x1c\x8d\x4c\x93\xd8\x13\xa3\x30\x87\x34\xf1\x5b\x40\xae\xe4\xee\x64\xef\x8c\xf3\xee\x48\xe7\x55\x89\x8f\x45\x71\x6d\xf6\xee\xff\x21\x4f\xce\xbf\x12\x72\x2a\xd1\x34\x38\xa8\xb5\xb2\xee\x60\x0c\x76\xc7\x0d\x6f\x1f\x08\x3c\x0d\x49\xc0\x97\x8b\x65\xbe\x38\x38\x7a\xf6\x7f\x03\x00\x00\xff\xff\x32\xdc\x82\x0e\xd8\x98\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x7d\x6b\x73\x23\x37\xb6\xd8\xf7\xf9\x15\x28\xde\x54\x8d\xa4\x22\x29\xd9\x9b\xdd\x75\x94\x38\x5b\xf2\xcc\x78\x2d\x7b\x1e\xca\x48\xeb\xad\xd4\xc4\xb5\x04\xbb\x0f\x49\x8c\xd0\x40\x2f\x80\x96\x86\x9b\x9b\xff\x9e\xc2\x39\x78\x35\xd9\xa2\xa8\xf1\xc8\xb1\x52\x77\xf7\x83\x47\x52\x37\x70\x70\x70\xde\xaf\x76\x86\x0b\x67\x4f\x9f\x4d\x98\xe2\x0d\x9c\x32\xbe\x58\x08\x25\xdc\xfa\x19\x63\xad\xe4\x6e\xa1\x4d\x73\xca\x16\x5c\x5a\xf0\xbf\x31\x7a\x21\x24\xd8\xd3\x67\x8c\x4d\xd8\x4f\xdd\x1c\x8c\x02\x07\x96\x7e\x54\xdc\x89\x1b\xc0\x7f\xbf\x6b\x41\x5d\xae\xc4\xc2\x3d\x63\xac\x06\x5b\x19\xd1\x3a\xa1\xd5\x29\x3b\x93\x52\xdf\x5a\x56\x69\x65\xfd\xce\x4a\xa8\x25\xbb\x5d\x89\x6a\xc5\x94\xae\xc1\x32\xb7\x02\x26\x94\x83\xa5\xe1\xfe\x05\xd6\xea\xfa\xc0\x1e\x32\x6e\x80\x81\x14\x4b\x31\x97\x7e\x03\xc6\x9c\x66\x73\x60\xb6\x5a\x41\xdd\x49\xa8\x99\x56\x63\x36\xe7\x16\xff\xc5\x24\x9f\x83\xb4\xfe\x5f\x7e\x39\xbf\xf0\x98\x69\xc3\x6e\x85\x5b\xe1\xe2\x66\xd2\xea\x3a\x9d\x94\x71\x55\xe3\x9a\x5c\x39\x31\x89\xbf\x1d\x5c\xae\xd5\xb5\x07\x91\x3b\x04\x88\x4b\x03\xbc\x5e\x33\xd3\x29\x3c\x47\xb1\x9f\x9d\xe2\x8a\xe7\xee\xb9\x65\xb5\xb0\x7c\xee\x61\x9c\xaf\x59\x0d\x0b\xde\x49\x37\x25\x5c\xb6\x60\x9c\x88\xd8\x24\xf4\x83\xc2\x67\xe9\x8c\xeb\x16\x4e\xd9\x5c\x6b\x89\x3f\xf6\xf0\xf8\x82\x2b\x8f\x80\xce\x83\xe8\x74\x78\xcd\x1f\x32\xec\xc6\x38\xc3\x9b\x9d\x7a\x8c\xd3\x3f\x2d\xb3\x2b\x0f\xb6\x5b\x09\x7f\x01\x4d\xa3\x15\xae\x9b\x40\x59\x4f\x0b\x40\x5a\x5d\x4f\x0a\x5a\xd8\x0d\xcd\x99\xbc\xe5\x6b\xbf\xe8\x44\xea\x8a\x3b\xb0\xac\xe9\xa4\x13\xad\x04\x66\xa0\x95\xa2\xe2\x96\xe9\xc5\xd6\xe5\x0a\x42\x98\xe5\x0d\x04\x48\x3c\xee\xd8\x41\xc0\x12\x3b\x42\xba\x3b\x3a\xdc\x82\xab\xbc\xa8\x7b\x81\x7b\x0b\x37\x60\x7e\x13\xd8\xfc\x13\x09\xae\x09\x91\x4d\x01\xde\xf3\x0f\xbf\x58\x67\x84\x5a\x3e\xdf\x06\xf2\x25\x2c\x84\x02\xcb\x38\xb3\xe0\x3c\x3c\x7b\xb3\x03\xb1\x42\x80\x71\x6f\x86\xb8\xeb\xaa\x7f\x25\xd4\xc8\x20\x07\x7e\x59\xb9\x66\x6e\xa5\x2d\xb0\x86\xbb\x6a\xe5\xd9\xc3\x6f\x8d\xab\x33\x0b\x12\x2a\xa7\xcd\x38\x40\x6d\x40\xa2\xe8\xf0\x47\xf1\x4f\x2d\xc5\x0d\x28\x04\xce\xb6\xbc\x82\x43\x62\x39\xb7\x82\x01\x54\xd8\x95\xee\x64\xed\x79\x21\xdd\x70\x1d\x96\xf5\xfc\xbe\x93\x74\x9e\xea\x61\x95\x76\x3b\x0e\x1c\x8f\x3b\xef\x84\xac\xc1\xf4\x04\xb9\x33\xdd\x97\x91\xe3\x57\x2b\x88\x1b\x90\x74\x61\xc2\x92\x6c\x55\x5c\xca\x75\x12\x4c\x35\x38\x30\x8d\x50\x80\x67\x9d\x83\x75\xcc\x0b\x7e\x07\xcb\x75\x92\xe3\x7e\x19\x2f\x84\xbd\x56\x58\x88\x65\x67\x80\x9d\xe7\xb3\xff\x24\x9c\x7d\x02\xf2\xf2\x06\xcc\x5c\x5b\xb8\x17\x90\x57\xb4\x73\x78\x9c\x49\xbd\x5c\x06\xdd\x41\x78\xa8\x74\xd3\x6a\x05\xca\x05\x45\x63\xbb\xb6\xd5\xc6\x31\xe1\xd8\x01\x4c\x97\xd3\x00\xc2\x4f\x5c\x89\xeb\x88\xbb\x56\xd7\x7d\x19\x99\x50\xb5\x27\x69\x9f\x31\x29\x2c\xd1\x74\x7a\x35\xa8\xd8\xd6\xe8\x1b\x51\x13\xd6\x5c\xbc\x74\xe6\xb8\xbd\x4e\x84\x56\x79\x0e\x78\x3c\x32\x7b\xe1\x97\x0f\x44\x56\xf5\xaf\x31\x13\xcc\x0d\x18\x2b\xb4\x42\x51\x7e\xd6\xf2\x2a\xbd\xf7\x13\x9e\xd6\x74\xca\x89\x06\x90\xca\x90\x01\xa1\x66\x52\xcc\x0d\x37\x02\xec\x98\xd1\xca\x81\xad\xa2\xbe\x7e\x02\x44\x17\x8e\x35\x09\xa7\x2f\x00\xa2\xab\xde\x06\xc9\x23\x14\xef\x6b\x72\x3d\x89\x48\x09\x6f\x7b\x10\x3b\x0b\x6c\xa1\xcd\xa6\xde\x99\xb2\x73\xc7\xf4\x0d\x18\x23\xea\x40\x54\x0c\x9f\x89\xda\x30\x2e\xe1\x25\x63\xd0\x9c\x05\x0b\xb3\x8b\x40\x19\x59\x38\x55\x5a\x39\x2e\xd4\x63\x8a\xa7\x17\x71\x8b\xfb\x68\xa7\xa0\xf9\x60\x08\x94\xd0\x31\x76\xbb\x02\x03\x5b\xaa\xf8\x56\x48\xe9\x2f\x00\x71\xc3\xa5\xd5\x91\x55\x6c\x5a\x9a\x1e\xf4\xf8\xbc\x04\x73\x23\x2a\xaf\x3c\xac\xd5\x95\x48\x32\x3b\x30\x55\xda\xef\x09\xd0\x1c\xef\x9c\xbe\x17\x8a\xd1\xa8\xa4\x52\xf8\x67\x07\xd6\x4d\xaa\xb6\xdb\x93\x42\x1b\xa1\x44\xd3\x35\x8c\x37\xba\x53\x28\x97\x5e\x5c\xfc\x0d\xd7\x11\x86\x18\x73\x73\xed\x06\x1a\x6d\xd6\x9f\xbd\x3c\xbd\x3e\xb8\x83\x14\x8d\x78\x10\xec\xfc\xd3\x9e\xb0\xd3\xca\x0f\x83\x7c\x6b\xf1\x1d\x90\xc3\xa7\x76\x1f\x8d\x34\x48\x31\xc7\x91\x5c\x70\x11\x94\xb0\x82\xb3\xeb\xc4\x8a\x91\xa2\xfb\xf6\x95\x71\xc5\x6e\x42\xb9\x81\x43\x94\x8c\xc7\x59\x2d\x16\x0b\x30\xa0\x1c\xbe\x1c\x20\x46\x4f\xa9\xc7\x16\xd9\xec\x9e\x7d\x73\xf2\xcd\xc9\xec\x70\x73\xdb\x89\x8a\x76\xfa\x3d\x38\xdc\xb9\xbd\x5f\x24\x89\xbf\x9d\x00\x05\xfe\xc8\x60\xad\x9c\x6b\xfb\x60\x59\x42\xd0\xe4\xc1\x58\xe9\x94\x37\xaa\xc8\x29\x0e\x8b\x10\x30\x7d\x94\x90\x55\x60\x7b\xe6\x7f\x04\xb7\x44\xd7\xdd\x50\x7d\x16\xd2\xee\x84\x0e\x91\x37\x08\x62\xd4\x17\x68\x4f\x6c\x83\xb8\x8d\xba\x7d\xe1\x42\x86\x10\xaa\xd8\xd1\xbf\x39\x25\xb7\xdb\xff\xb3\x66\xb3\x42\x64\xcf\x36\x3c\xf0\xb8\x9d\x68\xf8\xf2\x33\xf7\x8b\xaf\xf6\x96\x9a\xb4\x9d\x94\x93\x56\x4b\x51\x95\x7c\x7d\xd1\x49\x79\x91\x7f\xd9\x5b\xfa\xb9\x5f\xdb\xbf\xc6\xe8\xb5\xe8\x52\xff\x3b\x3a\xaf\xff\x7e\xbe\x78\xab\xdd\x85\x01\x0b\xca\x3d\xef\x1b\x7a\x73\xb0\x93\x7d\x75\xc3\x05\x3e\x4e\x16\x68\xbd\xc9\xe8\xb4\x56\xf4\x11\x87\x58\x0f\x3d\xde\xd9\x61\x4f\x82\xdd\x80\x02\x6b\x27\xde\xef\xdc\xeb\xce\x2e\xf1\xc1\x68\x6a\xdc\xae\x00\xb1\xa9\xa0\x72\x42\x2d\xa7\xde\xc7\xf2\x7b\x21\x55\xff\x70\x75\x75\x31\x65\x67\x6d\x2b\x83\x35\x8a\x3e\x55\xd8\x31\x2b\xa8\x39\x4c\x87\x20\xf2\x0e\x9e\xe0\x72\x52\x83\xe4\xeb\x3e\xff\xfd\xe1\xeb\x81\x20\x41\xd7\xcc\xc1\x78\x81\x6a\xa1\xd2\xaa\xb6\x8c\x2f\x1c\x98\x0d\x5c\xac\xb8\x65\xd6\x71\xe3\xcd\xc7\x39\x2c\xb4\x19\x06\xc8\xa2\x83\x4e\x10\xb8\x4d\x99\x1f\xe0\xf3\x96\x97\xee\xdc\xe7\x43\x46\x2c\x88\x92\xca\xef\xc9\xfc\x82\x96\xe9\xce\x6d\xe2\x2c\x40\x16\x77\xde\x81\xb3\x16\x8c\xd0\xf5\xfd\x20\xfd\xa0\x6f\x99\x5e\x38\x40\x93\xb1\x05\xe3\x8d\xb7\x0c\xc9\x9d\x77\xb6\x63\x67\xdb\x55\x15\x62\x65\x65\xc0\xae\xb4\xdc\x03\x88\x37\x41\x89\x57\x5a\x59\xa8\x3a\x74\xa9\xc3\x32\x60\xb3\x14\x27\xdc\x68\xf2\x97\x95\x15\x35\x18\xa8\xe3\x83\x8b\x4e\x06\xec\x10\x4e\x57\xfc\xc6\x3b\x63\x0b\x2e\xbc\xfd\xff\xf0\x63\xf8\x17\x3b\x03\xbf\xf6\x18\x61\x99\x7b\x4f\x41\x70\x0e\x9d\x00\xcf\x07\xf5\x43\x0e\x61\x80\xd7\xe2\xb7\x65\xe6\xb4\xe5\x5d\xdc\x9c\x61\xfa\xad\xd8\x79\x10\xa4\x1d\xfc\x9c\x21\xfc\xcd\x19\x3a\x6d\xbd\x0b\x6f\x8f\xc4\xd2\x7b\xed\xfd\x14\x98\x7a\xaf\x83\xfc\xfe\xd9\x7a\xeb\x18\xc9\xcf\x36\xe8\xcc\x3d\x46\x2a\x07\x6d\x96\x17\xc6\x1b\x0c\x83\xfe\x75\x67\x9d\x6e\xc4\xbf\x62\xe4\xcf\x1f\x41\x77\x48\xf7\x44\x94\xa2\x22\x34\x8a\x06\xcc\xb1\x87\x33\xc4\xab\x0b\x8b\xcd\x4e\xd9\xdf\x57\x42\x02\x53\xda\x34\x18\x57\xe4\xaa\xe7\x84\x07\xb7\xc7\x32\xce\x5a\xed\xb7\xc5\x25\xe7\xc0\x38\x65\x24\xba\x96\x42\x3e\x94\xa1\x19\x33\xab\x1b\x48\xdb\x63\x14\xcb\x8e\x3d\x56\x57\x8c\x5b\x36\xe7\xae\x5a\xb1\x8f\x7a\x6e\xc7\x71\xe1\x72\xc5\xca\x89\x1b\x0c\x1c\x71\xc7\x6c\x0b\x95\x58\x88\x8a\xad\x74\x67\x52\xd8\xa0\xe6\xeb\x94\x67\xe2\x79\x1b\x94\x59\xe8\xab\x09\xd5\xb9\x98\x1b\xfa\x5e\x1b\xda\x39\x40\x81\xa2\xa9\x8f\xcd\x86\x3b\x30\x82\xcb\x88\xc4\xf2\xe4\xdc\x9f\xb9\x77\x6d\x0c\x2f\xe3\x47\x3d\x67\x42\x59\x07\xbc\xf6\x5b\x72\x2f\xe0\x54\xcd\x4d\xcd\x6a\x68\xa5\x5e\x37\xa0\xdc\x98\x09\xc5\xb4\xc1\xd8\xad\x66\x96\xdf\x78\x02\xb2\xba\x33\x15\x58\x12\xe3\x51\xca\x94\x3b\xd6\x1a\x2c\x86\x9e\x15\xd0\x0d\xa3\x35\xef\x89\x1b\xea\x69\x19\xee\x89\x91\x4b\x2f\x59\xd9\xc2\x68\x12\x24\x0b\x2d\xa5\xbe\x8d\x7a\xa4\x08\x73\x62\x32\xe3\x86\xcb\x0e\x91\x1b\x3d\xad\x84\x89\x53\x36\x43\x12\x99\x8d\xd9\xcc\xff\xd6\xff\xf7\x9f\x1d\x37\xee\x5f\xb3\x29\x9a\xe4\xa6\x93\xe1\xfc\x9e\xaf\x3a\xeb\x19\xa5\x44\x4d\x42\x0b\x0f\x51\x9c\x04\xc9\x29\x9b\xc4\xc5\x4f\xe9\xdc\x74\x67\xd6\x63\x3f\xde\xfb\xad\x11\xce\xcb\x45\x6e\x09\x28\xf8\xd4\x1a\xb0\x96\xa8\xf3\xd5\x74\x39\x0d\x4b\x9c\x3a\x51\x5d\xff\x85\x16\xf8\xf6\x4f\x27\x27\x27\x27\xb3\xa9\x5f\x7f\x03\xe6\xd3\x18\x52\x52\xf9\x9c\x79\xc9\x8c\xe4\xa0\xa5\x92\x8e\x38\x08\x32\x63\x14\x7e\x31\x62\x2d\x27\x07\xd0\x82\x8b\xb1\xa4\x93\xc3\x08\x92\x5f\xf7\xd4\xf1\xf9\x5f\x62\x46\xe8\xdb\x93\xe3\xaf\xff\xd3\xff\x6e\x65\x67\xff\xcf\xd1\xd0\x7f\xfe\x32\xf3\xa4\x1b\xa0\x3c\x75\x46\x2c\x97\x60\xfe\xe2\x97\xf9\xf6\x84\x9e\x38\x39\xfe\x7a\xe7\xfb\xd3\xe7\xbf\xff\xe0\x55\xc4\xc6\x9e\xde\x5e\xa4\x9c\xf8\x5a\x92\xdc\xb7\x2b\x2d\x37\x23\xa4\x8b\x22\xb1\xa8\x3b\x97\xe2\xa4\x1e\xba\x1a\x2a\xc9\x0d\xd4\xc8\xe6\x6b\xd6\x74\xd6\x79\x99\x0e\x29\xc7\xb8\xb9\x85\xb0\x0d\x54\x2b\xae\x84\x6d\x3c\x26\x6e\xb5\xb9\x66\x95\x36\x06\x2a\x27\x7b\x27\xca\x8c\xb4\xc7\x99\x9e\x9f\x21\x8a\x38\xb3\xd0\x72\x13\xa2\xe0\x14\xf8\x77\x29\x62\xbe\x99\x81\x28\xd8\x3d\xc9\xf4\xa8\x9d\x92\x1c\x09\x88\xc9\xc0\x26\x0a\x4f\x07\x13\x96\x05\xb2\xf2\xce\xe7\xa7\x94\x2a\x9a\xaf\x0b\x66\x9d\x9e\xc5\x4c\x66\x94\xb0\x69\x4f\xe3\x57\xc8\x52\xd8\xef\x08\xbc\x5a\xc5\x27\xa1\xc8\x9d\x04\x2e\x08\x40\x85\x15\x03\xa7\xe7\xa7\x48\xe6\x22\xab\x4c\xe2\xdf\xca\xcd\xf2\x5e\x07\xc2\x3d\x7f\xee\x75\x2b\x7a\xe0\x4c\xa8\x22\x04\x3e\xd3\x66\x39\xe5\x98\x72\x98\x62\x64\x7d\x7a\x7d\x1a\x23\xec\xc8\xfb\x21\xd1\xb0\x3e\x9c\x5e\x52\x2e\x07\xea\x4d\xf1\x57\x75\xc6\x80\x72\x72\x7d\x1a\x61\x8d\x52\x23\xc0\xe5\x95\x58\x92\x7a\x65\x04\x60\xc1\xa5\x9c\xf3\xea\xfa\x5e\xd6\xfa\x9b\x85\x5e\xc4\x9e\xee\x5a\x34\xad\x04\xaf\x12\x48\xc4\x07\x3a\xa0\xdd\x19\xa8\xba\xd5\x42\x39\x76\x10\xb7\x3e\x4c\xd7\x9e\x14\x8c\x33\x6b\xcc\x77\xea\x5d\xda\x8a\xdb\x01\x79\xdc\xa7\x62\x45\x38\xa8\xd6\xdb\x81\x93\x3b\xa9\xf9\x32\xdc\xbc\x65\x2b\x7d\x8b\xa6\x90\x01\xee\xf2\x62\x2e\xe8\xa7\x98\x18\xe2\xcc\x6f\xfb\x33\x97\xa2\x66\x5e\xe1\x94\x2c\x7a\x3a\x61\x23\x2c\x4e\x19\x9d\x32\x4e\x45\x2a\x01\x4e\x34\xb2\x4c\xa7\x8a\x75\xe5\xfa\xbf\x4e\xd8\xe8\x7b\x6d\xe6\xa2\x1e\xa5\x08\xc9\xe1\xa9\xa7\xb8\xb9\xa8\xe3\xb2\x05\x20\xa6\x53\xde\xd2\xb8\x16\x6d\xeb\xd1\xa5\xe0\x13\xfe\x8e\x89\x85\xa7\x2a\x6f\x19\x59\xfc\x79\xc5\xad\x7a\xfe\xdc\xb1\x85\x50\xc2\xae\xa0\x66\x6b\x70\x7e\xaf\xf7\xd0\x4a\x5e\xc1\x28\x12\x48\xc5\x55\x05\xd2\x66\xca\x49\x55\x28\x1f\xbd\xa6\xc3\x34\x17\xbe\x61\x99\x70\xd1\x22\x51\x70\xcb\xb4\x82\xe7\x0f\x8d\xe6\x9f\x75\x4e\x37\xdc\x89\x0a\xf9\x95\xec\x88\x21\x83\x24\x8a\x4b\xe4\x7d\x2e\x65\x90\x83\x1e\xbd\x20\xdc\x2a\x85\x4d\xd1\x32\x40\x9b\xdc\x1b\x07\x85\xa5\xe4\x8d\xe0\xae\x01\xc3\x0e\xb4\x92\xeb\x9d\x5c\x80\x7c\x63\x23\x43\x45\xc2\xd4\xc6\x2f\xc7\xad\xf5\xf6\x76\x5e\x0d\x50\x27\xd6\xc2\x8b\xcf\x19\x8a\x91\xad\x87\x0e\xa7\x18\x35\x8c\x71\x74\x14\x7d\x91\x3a\xa4\xdc\x06\xd1\x6e\xc8\x6f\x7a\x00\x41\xcc\xb6\x70\x50\xec\xde\x66\xb4\xd1\x14\x67\x45\x99\x46\x84\xec\xab\x66\x36\xf8\xca\xec\xe4\xf8\x2b\x76\x44\xff\x9f\x8d\x6f\xd1\x14\x9e\xfd\xe1\x8f\x0d\xe9\xea\x3f\x9e\xd8\x59\xc8\x5b\xf6\x63\xbc\x01\xbd\x93\x1a\x78\x2d\x85\x82\x49\xb0\x19\xfa\x6e\xcb\x9f\xfe\xf3\xf6\x4d\xbf\xc3\xff\x72\xc9\xe2\xab\xac\x30\x41\xbc\x38\x4d\x57\xe7\x0f\xee\x49\x4d\x2c\xfc\x79\x1b\x81\x0e\x5a\x2a\x3f\xf1\x17\x16\xce\xea\xdf\xe2\x6a\xed\x5d\x14\xeb\xf5\x24\x7b\x23\xf0\x78\xde\xce\x2e\xf9\x13\xf3\x69\xe8\x08\x75\xca\xd1\xf1\xc9\x11\xf2\x24\x6b\xb3\x47\x53\x43\x0b\xaa\x06\x55\x51\x7a\xfb\x91\x92\x87\x2f\x8b\x5d\x76\x16\x38\xf0\x1e\x6f\xf0\xba\x8e\x49\xd9\x80\xdc\x62\x99\x54\x8e\xb3\xc9\x3a\xb1\xe2\xc3\x2f\x6a\xd8\x2d\x57\x2e\xca\x9c\x8d\x7c\x20\xfb\xf0\x4b\x89\x07\xa9\xd7\x8f\x99\x40\x8d\x3b\x0c\xfb\x77\xf0\xa9\x95\xa2\x12\x5e\xf4\x50\x89\x0b\x9e\xe0\x5a\x28\x54\x0b\x2b\xb1\x5c\x21\x06\x24\xdc\x80\x4c\xee\x05\x1d\x15\xaf\x7a\x58\x8c\x3c\x81\x04\xa8\x3f\xe2\x1e\xda\x29\x54\x3a\xde\x89\xa9\x1a\x2c\x0a\x9a\xec\x96\x11\xf2\xe6\xe0\x6e\x01\x14\x9b\xe5\x3f\xcc\xc6\xa5\x59\x30\xf9\xa8\xe7\x24\x00\xae\xe9\x26\x27\x21\x0f\x33\x0b\x21\x38\xaf\x04\x23\x8b\x66\xbf\xce\xf3\x61\xd4\x11\xd9\x28\xea\xa1\xbe\x4f\x5a\x7e\xe7\x47\x65\xb0\x78\xec\xc4\x5e\x06\x6c\xab\x95\xc5\xaa\x36\x0f\xee\x12\x14\x98\x7c\x96\xc2\x84\xe8\x41\xc8\x0a\xaa\x6a\xf8\xb5\x57\x0b\x3b\xf2\xf5\xb1\x3e\xa1\x92\x9d\x75\x5b\x19\xf7\x92\xc3\x40\xdd\x08\xa3\xd5\xe3\xe2\xa1\xd8\x24\x23\xa2\x8b\x71\x90\x20\x6c\x9c\x66\x42\x7d\xf4\x94\x93\xbc\xf9\x3e\x70\x8c\xdd\x70\x23\x3c\x79\xdb\x78\xbe\xf2\xec\x29\xe4\x99\x83\x1d\xb3\xb7\x67\x6f\x5e\x5d\x5e\x9c\xbd\x78\xe5\x15\xec\xc5\xbb\x97\xff\xf0\xbf\x20\x1d\xab\xbd\xae\x7e\x0a\x45\x57\xe9\x5c\x93\x06\x1c\xbf\x17\x1e\xca\x7c\xd9\x80\xcb\x60\xf0\x16\x88\x20\x03\x23\xe3\xa2\xbc\x9b\x84\xdf\xad\xd4\xaf\x27\x87\xd9\x61\xa6\x1a\x63\xb4\x99\xac\xb8\xaa\xe5\x63\x0a\xe7\xde\x36\xc1\xa4\x09\x3b\x05\x3a\x8a\x68\x0f\x94\xf3\xca\xbf\xc0\x7e\x48\x70\x31\x16\x44\xb2\x50\x21\x88\xd8\x0b\xac\x91\x12\x7b\x02\x34\x60\x60\xb1\xa7\x37\x8f\x28\x63\x11\x65\x06\x16\x94\xb3\x4e\x15\x6e\xda\xbb\xb1\x9d\x37\xe0\x14\xe3\x2d\x96\x06\x53\xf1\x65\x2e\xa7\x8b\x9b\x2e\xab\x47\x0a\xaa\x7a\x38\xff\xfa\x82\x5d\xe1\x0d\x2e\xb9\x99\xf3\x25\x4c\x2a\x2d\xbd\xda\xb0\x64\x53\x27\x89\x9e\x0a\xd1\x95\x66\x52\xab\x25\x18\xa6\xa0\x02\x6b\x79\xa8\x27\xe9\x5a\xdd\x0f\x97\x76\x6d\xcd\x43\x00\xf2\x77\x7e\xab\xb5\xb0\x95\xbe\x01\xb3\x9e\x54\xde\xb3\x2e\x00\x9a\x1e\xb7\xd7\xcb\x63\x5a\x3d\x3d\xf5\xc2\x3f\x74\xb5\x6e\x61\x1b\xd4\x97\xf1\x19\x56\x49\xe1\x39\x19\x17\x0c\x01\x0d\x7f\x80\x31\x23\xe7\xc4\x3b\x08\x54\xf9\xe7\x25\x62\x2d\xec\x35\x69\x59\xaa\xb0\x99\x6d\xf1\x7d\xf8\x7d\xe6\x7c\xa1\x96\x86\x32\x47\x0f\xa2\x8c\xad\xfb\x3f\xa7\x75\xee\x34\xbb\x74\x70\xe5\x63\xf9\x45\xae\x29\x43\x47\xef\x59\x8c\x4c\xf4\x4c\x4c\x62\x71\xdd\x39\x2b\x6a\x60\xb7\xda\xc8\x3a\xba\x85\x85\x5e\x0d\x5b\x87\x12\x8a\x40\x0d\x6c\x1e\x2b\x16\xe8\xe4\xde\xca\xc0\x9a\x7a\x1e\xab\x80\x50\xfe\xd4\x45\x95\x68\xb9\xf5\x81\x5b\x19\xdd\x2d\x29\xb5\x35\x8b\xb6\x0a\x41\xe9\x4f\x78\xf8\x04\xc8\x71\xa5\xad\xdb\x27\x22\x71\x74\xf4\x3e\x38\x90\x47\x47\xd3\x7e\xe1\x8c\x3f\xbd\x5f\x66\xb3\xa6\x28\x50\xcd\xf4\xc1\x5e\xf9\xd5\x90\xf3\x81\xf9\x11\x22\x9f\x74\x4d\x9b\x17\xd2\x59\x4c\x98\xfc\x70\x75\x75\x11\xeb\x86\x62\xa4\x27\x7a\xb7\xd9\x16\x14\xd6\x09\xfd\x88\xc2\xee\xdc\xaf\x1f\x48\x9d\x27\x93\x79\xb0\x38\x33\x16\xee\x06\x1a\x3b\x0f\x90\xb1\xc4\x08\x0d\xd8\x55\xb6\x70\x3c\xa1\x57\xdc\x14\xda\x1e\x6d\x9b\xce\xcd\x51\xc8\x9f\x5f\x30\xc3\xd5\xf2\x49\x48\x43\x44\xcc\x1e\xf4\xf7\x22\xa2\xcd\xdf\xef\x01\x86\x7a\x27\x29\xd4\x7b\x98\x62\xbd\x2f\xce\x5f\xbe\x67\xb6\x9b\x2b\x48\x55\xe6\xa9\xb1\x20\x40\x31\x27\x92\x31\x15\xb4\x45\x56\x86\x2e\xab\x35\xfa\xd3\x9a\x1d\xcc\xbe\x3a\x99\xe2\xff\x8f\xbf\x19\x7f\xf5\xe7\xaf\xa7\x5f\xfd\x09\x7f\xf8\xea\xeb\xf1\x57\xff\xc5\xff\xf4\x0d\xfd\xf8\xa7\x28\x39\x73\xf1\x55\x2f\x5a\x41\xd7\x73\x2f\x8e\xbf\xd7\x41\xe7\x01\x85\xee\xd0\x41\x08\x7d\x2d\xb3\x70\xd5\x53\x24\xd6\xa9\xd0\xc7\xb4\xe8\x6c\xca\xbe\x4b\x9b\x16\x21\x5b\x6a\xcc\xa0\xd4\x89\xbf\x30\x32\xe1\xbc\x67\x5e\x38\x63\x9e\x58\x94\x76\xd4\xec\xa1\x22\x41\xe7\xba\xc7\x08\xff\x47\x2d\xf5\xb5\xe0\x8f\xc8\x22\x3f\xd2\x0e\x91\x49\x42\x54\xda\xf6\x5b\x26\x08\x35\xf1\xd1\x1f\xf9\x0d\x67\x7c\x09\xca\x51\xb2\xf1\x12\x80\xad\x9c\x6b\xed\xe9\xf1\x71\x00\x78\xaa\xcd\xf2\xd8\x00\xd6\x3f\x56\x70\xbc\x72\x8d\x3c\xc6\x37\xec\xd4\xff\xfb\xf7\xcf\x14\x15\x9f\x54\x60\xf6\x11\xcb\x1e\x89\x17\xaf\xde\x30\x50\x95\xf6\x4a\xea\xc5\x19\xf3\x6f\x8a\x45\xb4\xf0\xfc\x25\xb1\x96\xbb\xd5\x38\xc1\x7b\x03\x46\x2c\xa2\xcd\x10\x83\xae\xe9\x25\xb0\xe3\x60\x21\xfa\x93\xa0\xdb\x3d\x6b\x8d\x76\xba\xd2\x12\x03\x8c\x58\xd5\x68\x83\x47\xd1\x59\x98\x58\x2b\x27\xb4\xd8\x84\x77\x6e\x05\xca\x85\xcd\x23\x7b\xf8\x97\x90\x0e\xb3\x85\x71\x7c\xc3\xcd\xb1\xe9\xd4\xb1\x85\xca\x80\xb3\xc7\xb9\xfe\xd6\x13\x79\x10\x7b\xbc\xc2\x90\x59\xfc\x71\x52\xf1\x69\x65\xdc\xac\x08\xbf\x25\xea\xea\x31\x5e\x80\xa6\x35\x42\x55\xa2\xe5\xb2\x40\xe4\xae\x5e\x11\xac\x54\x8c\xef\x1c\xd8\xc3\x50\xb7\x82\x29\xad\x79\x6c\x67\xf2\x16\x74\xb2\xb7\x32\xd6\x30\x60\x96\x64\x19\x63\x1c\x0b\x36\xa2\x40\x8f\xc4\x1b\xb5\xd1\x6f\x81\x62\x7a\xfe\x22\x9e\xe7\xdb\x4a\x7d\x6b\xd7\xd6\x41\x73\xda\x70\x8b\x9d\xa2\x5e\xd8\x61\xee\x59\x7d\xbb\xe2\xb7\x4e\xe8\x89\x56\x52\x28\x98\xd2\x4f\x53\x7b\x53\xc5\xf5\x11\x92\x4a\x7d\xbb\xf0\xd0\x78\x55\xaa\x25\x4c\xfd\x0f\xf8\xd0\x8e\xab\xc8\xd6\xee\xbe\xdc\xf5\x5a\x58\x07\xd4\x5b\x80\x59\xc7\x8a\x5b\x17\x8b\xe1\xed\xce\x9a\x4d\xf8\xe4\x40\xd5\x50\x47\x54\x55\x2b\xd8\x23\x7d\xf4\x86\xab\x1a\x2f\x10\x23\x27\x5b\xf7\x1a\x1c\x71\x9b\x6f\x7d\x21\xf9\x32\x86\x22\xe2\x96\x01\x4d\xd7\xb0\x66\x9d\xe5\x4b\x6f\xc1\xa2\x62\xfe\x2d\x2e\x9a\x44\xfc\xdd\x57\xb0\xa7\x85\xe7\xa9\xff\x07\x6f\xc5\xf1\xba\x36\x81\x76\x73\xe1\x56\xa4\x60\x94\xa3\xa9\x35\x51\x28\x2f\x51\x30\x43\x3c\x1b\xfd\xaf\xa3\xd1\x2c\x27\x2b\x66\xa3\xa0\x43\x47\x78\x52\x64\x9e\x71\xb4\xed\xc1\x58\x7c\x99\x82\x88\xe8\xc2\x28\x70\x98\x5d\x45\xdd\xbc\xe0\x55\x6e\x48\x8d\x11\xc2\xd1\xd1\x68\xa3\x3c\x9e\x5b\x7b\xab\xcd\x3e\x21\x4b\x64\xed\xf0\x38\x09\x42\x0c\xde\xf7\x50\x3c\x66\x9b\x97\x85\x66\x7d\x67\xc1\xa4\x73\xb5\xd4\x41\x8b\xfa\xf5\xc1\x0d\x02\x03\x82\x80\x0a\xc9\x8b\xa2\xf6\x3f\xff\xf9\x9b\xd9\x66\xc7\x1b\xd2\xcb\xbe\x87\x0c\x8f\x87\xd2\xc5\xec\x01\x62\x2d\x3a\x5e\x4c\xa0\xb9\x7e\x99\xba\x45\x0a\x0a\xc7\xcc\x74\x54\x00\xe2\xf1\xb0\x27\x10\x18\xec\xcf\x6e\xe8\x00\xae\xfb\xeb\xde\x4d\xf6\xf7\x72\xef\xdf\x57\x80\xe7\xdb\xe6\x5c\x5b\x34\xd0\xde\x01\xc5\x16\x89\xdd\xc7\x4a\x74\xff\x0f\xef\x3d\xe4\x75\x2d\x42\xfa\x29\x52\x40\x58\xca\x9b\xf3\x35\xb6\xdf\xd6\x42\x3d\xd0\x90\xf9\x37\xfc\xf7\xe4\xe3\x4d\x33\x21\x63\xe9\xc3\x8f\x3f\xbf\x89\x02\x1b\xf9\xb4\xdf\xae\x15\xb6\xcc\x61\xe0\x8f\x37\xcd\xe3\x85\xf1\x7e\xfc\xf9\xcd\x46\xd8\xd7\x6d\x3a\x8d\xf8\x88\x37\xd2\x4d\xa7\xb6\x3a\xc2\x9f\x80\xf3\x52\xc3\xbc\x5b\xde\x9f\x63\x4e\x66\xad\x81\x46\x3b\xa0\xd7\x96\xa1\x6a\x2f\xc4\xba\xc2\x2f\x3d\x25\x13\xd4\xdc\x39\x5e\xad\x20\x55\xfe\xb1\x88\xb1\x31\x83\xe9\x72\x3a\x0e\xe5\x60\xd8\xed\xb2\xd0\xe6\x96\x9b\x9a\xf8\xb1\x07\xdc\xc4\x76\xb6\x05\x75\x3f\xae\x2e\xe9\x39\xba\x05\xc7\xcd\x12\x1c\x5e\x8f\x68\x1a\xa8\x05\x77\x20\xd7\xb1\x90\xd8\xa5\x66\x11\xc9\xad\xf5\xb7\x2b\x35\x27\x1d\x98\x85\x96\xf0\xfa\xd7\x7b\x69\x7b\xec\xed\x6d\x14\x67\x43\x05\x33\xbe\x12\xee\xcc\x6b\x0b\x2c\xb5\x8b\xc4\x22\x36\x5b\x38\xa4\x5e\xda\x61\x2e\x3e\xdc\x42\x45\xd0\x6b\xfb\xc8\x30\xc3\x95\x45\xc9\x1c\x75\x21\x77\x51\x17\x6a\x64\xea\x60\xa0\xe0\xcc\x01\xb8\x95\x6b\x26\x79\xa7\xf0\xba\x3c\x98\x9b\x00\x1d\x9d\xfe\xf1\xe4\xe4\x8f\x3d\x90\x3e\x57\x92\xf8\xe5\xf3\xbb\xd9\xe0\xe5\xd6\x7a\x2b\x7f\x9f\xec\x5d\x21\x8b\x7e\x7e\x93\x5f\x65\x07\x9d\x05\x36\x7b\x2d\x54\xf7\x69\x56\xfc\x3a\x78\xd9\xda\xe4\x70\xe0\x35\x6f\x40\x82\x7b\xc4\x24\x75\xdc\x21\x4b\x90\xfb\x92\x00\x3f\xc5\x37\x84\x8a\x11\xa6\x27\x1b\xf8\xff\x8c\xd2\x95\x80\x05\x0a\xa3\x07\x85\x51\x67\xa4\x78\x9e\x72\x2b\x10\x26\x85\x3a\x7b\xaa\x21\x46\x80\x73\x58\x34\x05\x34\x7a\xcd\x20\x7b\x19\x92\x2f\xee\xa8\xc3\x0b\xc0\xd0\x40\x08\x64\x24\xcd\xeb\x9c\xa3\x89\xf5\x44\xc5\x95\x65\x82\xeb\xa7\x83\xf7\x89\x48\x24\x4a\xdb\xa2\xad\x8d\x78\xc7\x8e\x08\x5d\xd4\xd1\x68\x0c\x86\x0c\xf3\x20\x69\x71\x9b\x56\x8d\x11\xbb\xe1\x4a\xe5\x22\x20\x9d\xd3\xc4\x14\xc8\x78\x1f\x32\xd8\x65\x91\x78\xb9\x70\x6e\xb4\xae\x6b\x8b\x24\x32\xb1\x15\x97\xfe\x95\x03\x7f\xbd\xe1\x87\x89\xd3\x93\x7f\x81\xd1\x54\x37\xb4\x00\xee\x3a\x03\x76\xcc\xe6\x9d\x0b\x93\x3c\xe2\xef\x30\xc1\x82\xd5\x48\x0d\x70\xbf\xf5\xa2\x93\xd9\xee\x0d\x05\x4e\x5e\x26\x50\x40\x35\xd9\xac\xb8\x70\x0e\xa7\x3e\x09\x6e\x8a\xc8\x41\xb1\xf6\xb0\x70\xa3\x2b\x48\xa6\x58\x2a\x68\xbf\xb8\x61\xa8\x77\x72\x1a\xa3\x6a\xb3\x55\xcb\xa7\xc5\xc3\xd3\x40\xc0\xd3\x1a\x6e\x4a\x1f\xe9\x7a\xc7\x63\xe5\x66\x87\xd3\xf7\x9e\xa9\x63\x38\x21\x82\x53\xeb\xaa\x4b\x25\x8e\x45\x54\xa4\xd1\xd8\xd4\xe3\xf9\x23\x99\x52\x43\xd8\x68\xc0\x99\xd8\xab\xf0\x6b\xd1\x41\x6b\xdd\x85\x8f\xa2\x0a\x32\x05\x4d\xa9\x66\xc8\xb0\x59\xd5\x76\xb3\xf0\xe3\x03\xcf\x9c\x4e\x9b\x6a\x3e\xee\x3d\x33\xd9\x36\xf7\xf9\x6a\x97\x10\x0c\x12\x14\x0b\x58\xd6\x9a\x0e\x10\x0a\x7f\xb4\xc1\xf6\xf7\x16\x4c\xe5\xc1\x59\xa2\xc3\xea\x6d\x28\x1a\x7a\x52\x18\x6d\xdb\x68\x3a\xcc\x35\xbe\x17\xba\xde\xf3\xa0\x61\xc5\x5d\x97\xdb\x08\x85\x42\x01\xf6\xf1\x45\xe3\xac\x00\x95\xba\xb6\x2e\xd2\x10\xb0\xec\x3a\xc5\xc2\x1a\x6f\xa4\xaa\x35\xf6\xb4\x14\xc0\x0c\x4c\xd3\x78\x6e\xd9\xd1\x91\x97\x40\x47\x47\x85\x1e\x19\x47\x21\x83\xeb\x6f\x8d\xa4\xb2\x28\xbe\x52\x1c\xa5\xd6\xb7\x98\xd8\xf3\xcb\x90\x48\x52\xda\x15\x5e\x5c\x96\xd1\x75\x31\x30\x00\xb5\xfb\x10\x2e\xd3\xaa\x43\xa4\x73\x27\x2e\xf9\xa7\xfd\x70\x79\xa6\x58\xd7\xb6\x60\x18\x65\x5f\x92\x5d\x38\x80\xd6\x60\xdb\x9b\x24\xea\x99\x57\x92\x52\x82\x2c\xb8\x77\x13\xa7\x91\x20\x56\xdc\x32\x2f\xfb\x3c\x6e\x2a\xde\x86\x64\x01\xae\x4b\x84\x67\x73\x05\x9e\x75\x5c\x4a\x7a\x1d\x11\x12\xaf\x6b\x1f\x5e\xda\xc5\x45\x0f\xb4\x4a\xee\x2e\xa8\xdd\x54\x97\xb1\xb0\x36\xc6\xa3\xb5\x22\x2b\xd5\xb2\x95\x96\xf5\xe9\x51\x6f\xe8\x0a\x3a\x94\xa9\x76\x2a\xac\x14\xf4\xff\x11\xaa\x89\x5c\x9e\xcb\x76\xd6\xe7\xa2\x62\x23\xd1\x93\x6a\x6a\xf7\xac\xb4\x8d\x8e\xf6\x76\xbd\xed\xa6\xd9\xf2\x65\xcc\x95\x60\xa6\xf4\xf1\x1b\xa2\xab\x36\x3a\x2a\x34\xe8\x25\xbe\x92\x0a\x2a\x10\x56\x2c\xe0\x25\x33\x11\xfb\x19\x92\xe5\x65\xb6\x2d\x0f\xea\x3f\x5a\x74\x52\xa6\xc5\xfa\xb1\x84\x70\x7e\x5a\x0f\x4b\xba\xa8\xcf\xe2\xec\xcd\xab\xd7\xff\xf8\xe9\xed\xd9\xd5\xf9\xcf\xaf\xfe\xf1\xe2\xdd\xdb\xef\xcf\xff\xfa\xb7\xf7\x67\x57\xe7\xef\xde\xfa\x47\x7e\xbc\x7c\xf7\x96\x11\x71\x4d\x8b\x09\x48\x25\x9b\xa6\xfe\x01\x2a\x6b\x73\x9e\x2e\xba\x50\x16\x80\xf0\xf4\xe1\xd8\x8a\x29\xd0\xcd\xd3\xea\x88\x32\x82\xb3\x4c\xf9\xf7\xd2\xfd\x43\x34\x94\xda\x31\x9e\x82\xb3\xb0\x6d\xc8\xdf\xa3\xcb\xfb\x00\x45\xc7\xa1\xb8\xe7\xa6\x95\xe0\xb6\x2e\xbc\x7f\x7b\x25\x00\x2b\xae\x14\xc8\x49\x49\x6b\xf7\xbb\xb4\xaf\x83\x57\x10\xde\x0e\x21\x22\x6e\x63\xed\x96\x5e\xf4\x9d\x37\xba\x56\x0f\x7c\xf0\xfe\x23\x47\x63\xa3\x47\x5c\x26\x38\x17\xda\x10\xad\x10\x79\xfd\xed\xfd\xb9\x1d\x04\x58\xa8\xeb\x5f\x0d\x6e\x0d\xd6\x09\x95\x9a\x4c\x1e\x0b\xe6\x68\x7c\xff\x26\x58\x1e\xdc\xf7\x33\x90\x15\x5f\xfe\x22\xd8\x4a\x21\xf3\xbd\xd0\x75\x03\x9f\x8d\x2b\x7c\x17\x9f\xb7\xb9\x7e\x7d\xab\xde\x76\x0e\xcc\x76\x73\xff\xfa\x1c\x19\xc9\x03\x9e\x95\x17\x35\x38\x06\xc0\x8b\xf5\xb6\xa1\x66\x07\x61\xc8\x00\xcf\x8d\x61\x73\xa3\xaf\xc1\xe4\x19\x3e\xd1\x38\xf0\x3a\x6b\x14\x84\xd7\xe8\x70\xe0\xbc\x9f\x73\x47\x7b\x9d\xb6\x35\xba\xee\x2a\xd8\x71\x3b\x9f\x79\xc8\xde\x29\x16\x42\x3a\x30\xe1\xda\x26\x91\x66\xef\x15\xb1\xb1\xf8\x95\x5e\x0f\x33\x07\x11\xa0\x8d\xce\x84\x15\xf0\x1a\x0c\x1b\x55\x30\x09\xaa\x79\x25\xac\xd3\x66\x3d\x8a\xc3\x07\x2f\x85\xaa\x82\xe0\x0d\x0f\x7b\xab\x6b\x0e\xde\xff\x87\x46\xdf\x90\xa6\x53\x70\x0b\xa6\x1c\xcc\x17\x64\xe7\xb8\x00\x21\x19\x08\x77\x0c\xc9\x4d\x2d\x2d\x42\x5d\x4f\xe6\x42\xd5\x29\xb6\xb0\x73\x1a\x2d\x06\x29\xc2\xe3\x43\xa5\x70\x1c\x17\xc4\x91\x56\x59\xa4\x5f\x0a\x75\xfd\x5d\xb1\x05\x4b\x96\xc1\xf4\x0a\x75\x4c\xa1\x12\x92\x4e\xec\x2d\x8c\x4e\x93\xa5\xd5\x97\x12\x70\x93\x69\x59\x49\x17\x9d\xba\x01\xe5\x7a\xef\x42\x07\xf0\xa9\x82\x76\xf8\x8d\x9c\xf6\xd4\xb7\x8a\x90\x98\xcf\x45\x67\x38\x7c\xb0\x91\x1a\x26\x55\x26\x5b\x2a\x67\xa9\x31\x5c\x13\xf5\x70\xa1\xf9\x73\x51\x4c\x18\x6b\xf9\x88\x45\x31\xaf\xc3\xe0\xcc\x1d\xc9\x93\xf3\xed\xb0\x66\x01\x18\x4b\x81\xab\x83\x58\x31\x56\x69\x89\xcd\x4a\x75\xd0\xdf\x87\x64\x20\xc5\x19\x9d\x73\x5e\x5d\x83\x37\x0f\x6d\xae\x19\x9e\xaf\xd9\xff\xe8\xb8\xb9\xee\x2c\xf5\x68\xdc\x62\xf0\x68\xd3\x0a\x4c\x3e\x04\x4d\x0f\x88\x09\xac\x7f\xd2\x9b\x53\xa1\x8f\x97\x9d\xa8\xc1\x1e\x87\xad\x9e\x84\x41\x25\xb5\xb9\x1f\x0c\xff\x54\x6c\x08\x95\x7a\xc9\x74\xe7\x5a\x1c\x92\x92\xa4\x19\x62\x7a\x0f\x8b\xec\xb5\x5e\x5a\xd6\x80\xc5\xc2\x81\xf4\x56\x22\x38\xb8\x81\x7d\xb2\xbf\x67\xf5\xc7\xce\xba\x08\x0e\x5e\x2b\x05\x2a\x62\x36\x02\x83\x9b\xe7\x6f\xbf\x7f\x57\xc6\x74\x3f\xda\x3d\x92\xac\xef\xf0\x68\x71\x69\x1b\x6d\xc1\x8d\x65\x26\xad\x01\xe7\xd6\x58\x76\x73\x7f\xd5\x5b\xe0\xc1\x11\xbd\x44\x19\x23\xa1\x96\xa3\xd8\xa9\x8b\xc6\xa6\xdf\xed\x59\x76\xc8\xad\xdb\xe8\x3b\xf8\xc2\x23\x3f\xde\xe0\x0e\x3b\x02\xc2\x43\x02\xb7\x74\xc8\xf0\x98\x1e\xeb\xc6\x5f\x65\x11\xec\xed\x37\x2b\xd5\x9a\x6e\x07\x15\x0c\xf6\x4d\xc5\x1a\xce\xe4\x9f\x1e\xd1\x69\x8f\x70\xc5\xe0\xcd\x62\xac\x56\x2b\x4c\x72\x7b\xfb\x02\xdd\x7c\x55\x01\x15\x35\x3f\x2f\x5b\xc8\xfb\x6e\xe2\x2d\x39\x51\x65\x7f\x14\x2d\x9f\x8d\x2a\x2c\x6b\xc2\x7d\x28\xbf\xc8\x66\xde\xda\x38\x18\xd1\x73\xa7\x52\x57\xd7\x78\x0b\x0e\xa4\x3f\x7d\x73\x3a\xd7\xce\x8e\x0e\xa7\xd3\xe9\x6c\xca\xde\xbe\xbb\x7a\x75\x1a\x02\xe3\x22\xe6\x6c\x30\x4c\x8d\xda\x9e\x63\x93\x69\x23\x68\xb4\xc4\x50\x21\x75\xaa\xf7\xa6\x82\xaf\xd4\x7c\xff\x2c\x04\x2d\x0c\xf0\xfa\xf8\xd6\x88\xe4\x95\x34\xbc\xb5\xa1\x17\x98\xe3\x94\xe0\x84\x03\x03\x9e\xc1\xb1\x28\x87\x25\xa3\x23\x4f\x43\xcc\x19\x04\x96\x76\x73\x2b\xae\xb2\x5d\xd5\x8b\xf6\x6f\x05\x63\x9e\xc0\x24\x88\x07\xa8\x40\x5b\xe8\xc0\x8d\x5a\x01\xe2\x42\x82\xa4\x57\xd3\x5a\xc9\xae\x86\x49\xed\xe9\x80\x3b\xff\x8f\x5e\xd3\xea\xbd\xd5\x1a\x8a\x4e\x41\x45\x54\xd1\xcd\xa6\x79\x05\xfe\x28\xdc\xa1\x9e\xe2\x72\xfd\xaf\x10\x6c\x0e\x9e\x4a\xa5\x6b\xc8\xb9\x70\x5e\xd7\xfd\x0e\xd4\xd4\xdd\x8c\x16\x08\xc1\x96\xfd\x8f\x29\x0e\x4d\x28\xd8\x60\xb6\x45\xd7\x34\x88\x25\x87\x35\x15\x9b\xe1\xac\x83\xf0\x17\x84\x75\xb3\x26\x3d\x57\x6c\x87\xb9\xe9\x25\x48\xbb\xcd\xa3\x6d\x0f\xbe\xe1\xed\x1e\x52\xfe\xf9\x5b\xde\x40\x31\xcc\x97\x5e\x2c\xda\x04\x0b\xd2\xf2\xa6\x6d\xd4\x4f\xd5\x75\x9e\x5a\x96\x83\x81\xa3\xff\x56\xd0\x36\x0e\xcd\xfc\xef\x13\xff\xec\x68\xfa\x12\x5a\x03\x38\x81\xfd\x34\xf6\xdb\x23\x6e\x47\x51\x92\xe1\xd3\xa3\x5e\x6d\x7f\xef\x4f\x7b\x9c\x65\xf0\x28\xc7\x12\xb8\x85\xa1\xb6\xda\xa1\x93\x85\xa3\xf4\xcf\xb7\xfb\x64\x43\x00\xbb\xd8\x5f\x73\x4f\x5d\xc0\xba\x45\x80\x07\x04\x7b\x39\x62\xda\xef\x83\x69\xb9\x11\xe5\x4d\xde\xf0\x76\xe4\x19\x7c\xf4\xda\x1f\x8d\x1c\x37\xff\xbf\x1e\xbc\xf4\xb7\xde\xec\x3d\x3e\x07\x39\xb9\x86\x7d\x06\x46\xbc\xc6\xba\xef\x41\x5c\x89\x1a\x94\x13\x8b\x35\x35\xfb\xe3\x08\x20\xad\x1c\x64\x0f\x02\x91\x37\x04\x12\xcd\xfc\x08\x03\x40\xb4\x59\x1e\x17\x28\x1d\x80\x14\xe3\xd1\x7b\xc3\x5a\x44\xaf\x1f\x0a\xf1\x9d\x97\xbe\xa9\x56\x70\x66\xea\xb3\x5c\x72\x01\x8a\xb7\xe2\xf1\x8a\x16\xfc\x1f\xcf\x2e\xce\xd9\xcb\xcb\xd7\xbb\x1b\xeb\xbd\x65\x91\x9b\x99\xcb\x84\xef\xb3\xe4\xe7\xf3\xb4\x9c\xd7\xa1\x9b\xad\x1c\x65\x33\xaf\x77\x8c\x1e\xb1\x1d\xf3\xdd\x6d\x1e\x34\x0e\xca\x86\x24\x20\xa7\x5a\x03\x3c\x04\xd4\x85\x7d\x33\x07\xa9\x69\x6a\xca\x90\x27\x37\x07\x4c\x9d\x86\xb7\xb0\x2a\xc1\x70\x65\x17\x18\xc2\x56\x4a\xbb\x98\xf7\x56\xf5\xc6\xa7\x3d\xca\x7b\xd5\x21\x72\x1d\x46\x40\x93\x67\x98\x40\x78\x02\x2e\x06\xb9\xc1\x93\xe2\xc4\x7b\x46\x6d\xae\xb2\xae\x29\xd1\x45\xb5\x96\x11\x95\xa6\x57\x8b\x15\xf6\x7a\xd0\x07\x41\x8a\x6d\xc2\x2d\x6c\xef\x90\x6a\xbd\xea\xf9\x23\x3a\xc3\x17\x2f\xbf\xbb\xc7\x1e\xbf\xd0\xf5\x4b\x61\x4d\x87\x2f\x7d\xd7\xd5\x4b\x70\x89\x16\x88\x9d\x42\x4e\xed\xbc\xff\xd5\x91\xa7\x40\x27\x8d\x50\x13\x7e\xc3\x85\xe4\xf3\xbd\x27\x7b\xe5\xbc\x23\x8a\xce\xa1\xd3\x23\xfb\x62\x5b\x83\x75\x41\xf6\xf6\x77\x89\xc3\x19\xb9\x62\x70\x23\xd0\x3d\x99\x9e\xe7\xd9\x2f\x54\x52\xcc\x15\xe3\x73\xab\x65\xe7\xf2\xa6\x86\x66\xde\x84\x8c\xf8\xf4\x1d\x79\x2c\x71\x51\xbd\x60\xb3\xde\x91\x42\xe5\x7b\xc3\x3f\x4d\x3a\x55\xfc\x36\x6c\x94\xe6\x5f\x6d\xe6\x65\x8b\x87\xbf\x30\x56\x62\x28\x2c\x6f\x40\xa8\x88\x68\xf9\x75\x08\x29\x8a\xae\xbf\x4a\x15\xfd\xdb\x48\xf1\xb6\xa6\xb4\x3a\x36\x69\x1d\x26\x3c\x12\x06\x37\xb1\x45\x38\xec\x2d\x11\x23\xd1\x5b\x78\x4c\x5c\x1b\xf8\xf5\xf1\xf4\xc6\x46\xb9\x1e\x96\xf0\xcd\xbd\x69\x47\x3f\x23\xb6\x8b\xe0\x16\xb7\x56\x2c\xd5\xc6\x14\x4c\x3c\x46\x5e\x48\x6f\xfc\x79\xca\xce\x15\xab\x78\xc8\x0e\xa6\xe7\x84\xc5\xa9\x3d\x58\x60\x9b\x9c\x18\x42\x2a\x16\x73\x44\xaf\x92\xd4\x10\xd6\xde\xd2\xa5\xc4\x15\xa6\x0c\xc3\xa2\xa1\x50\x0a\x93\x87\xc1\x91\x25\x2d\xbe\xe8\x24\x0b\xc3\xd2\xe1\x93\xb3\xf4\x19\x8d\xe0\xff\x82\x81\xe7\x96\x29\x9d\x86\xcb\x85\x80\x1a\xe3\x61\x04\xdb\xc0\x37\x34\x7a\xd0\x53\xa2\x59\xab\x1e\x76\xfb\xdf\x24\xb1\xe0\xbc\xe5\x60\x71\x1e\xdd\x98\xd9\x10\xb4\xa6\xad\x3d\x89\x36\x73\x40\xef\x64\x63\x9c\x3b\x33\xb0\x14\xd6\x99\xf5\x53\x68\x4c\xa6\xdb\x99\x94\xa5\xb4\xf7\xf4\x0c\x6f\xdd\xe7\x01\x34\xad\x5b\x1f\x66\xdc\xa6\x08\xf3\x00\xad\x94\x7b\x2f\xa5\x9e\xf7\xda\xc6\x86\xf7\x3c\x57\x75\x68\x35\x10\x8b\xfe\xb2\xc5\xb7\xbb\x82\xad\x43\x4b\x62\xa5\x26\x39\x3c\xdc\x16\x62\x91\xfe\x9a\x3d\xe0\x24\x27\x3c\x4b\x3e\x3c\xc0\xbd\xd5\x40\x5d\x83\x83\xaa\x18\x59\x5a\xce\xff\x10\x8b\x01\x16\xe8\x0b\x90\x78\x88\x03\x91\xad\xf5\xf8\xbb\x92\x52\x31\x44\x75\x58\x48\x19\x1c\xd0\xfc\x58\xb6\x01\xce\xc5\xed\xd9\x06\xab\x3c\xc8\xb1\x17\xc6\xd8\x52\xfd\xec\x9c\x28\x8a\xc7\x61\xc3\x2b\x60\xb3\x0b\x5d\x5f\xb6\x50\x5d\x41\xe3\x21\x86\x99\x57\x28\x5d\x95\x06\x96\xe6\x2a\x87\x72\xb9\xd9\xd4\x8b\x86\x69\xab\xeb\xf4\x1e\x59\x1d\x02\x64\x3d\xce\x45\x16\xe5\x3b\x45\x2f\x2e\xd5\x06\x85\x37\x63\x51\x7f\xf8\x8e\x98\xa8\x58\x03\x66\x89\xfd\x9b\xd5\x8a\x5c\xb4\xad\x7c\xcd\xd6\x3c\xe2\xcc\xf3\xf4\x75\x1f\xca\xc2\x85\x10\x62\x98\x6a\x3b\xf6\xae\x3c\xee\x95\x64\x4b\xff\x73\x12\x1b\xdf\x9d\xb8\xdb\xf9\x68\x8d\x6e\xc0\xad\xa0\x7b\xf0\x70\x88\x87\x04\x66\x2f\xd2\x2e\x71\x74\x44\xd9\x0e\x9e\xff\x3a\xa9\x74\xd3\x72\x87\x63\x96\x62\xf0\x27\x7e\xaf\x92\x54\x2a\x51\xad\x7f\xcb\x5f\xf7\x1b\xad\x84\xd3\x66\x96\x0c\xc6\xdc\x91\x41\x5c\x12\x87\x07\x04\x3d\x5a\x19\xde\x6e\x46\x57\x63\x76\xa4\x0c\xb1\x96\x00\x47\x9e\xa6\x8c\x0b\xd5\xff\x85\xca\xa4\x30\x2e\x81\x5e\x7b\x23\x2a\xa3\x2f\x08\x5f\xb8\xe4\x1b\x7a\x74\xca\xfe\x7e\xf6\xfe\xed\xf9\xdb\xbf\x86\x4f\x5c\xa1\xdb\x58\x4c\xb4\x1c\x3a\x46\x9e\x1e\x8d\x69\xcf\x90\x94\x59\x0a\xb7\xea\xe6\xd3\x4a\x37\xc7\x95\x36\xa0\xed\x71\xbe\xbd\x49\x04\xf3\xc3\x45\x79\xa3\xd8\x0b\x86\xbf\xff\x25\xaa\xaf\xb4\x07\xb6\x2d\x89\x18\x07\x9f\xa7\xb2\x33\xa8\xa7\xec\x7f\xea\x0e\x91\x86\xc5\x9f\xad\xae\x27\x4d\x00\x31\xea\xde\xd0\xbe\x99\xd4\xdf\xd6\x0d\xa7\x69\xab\xc2\xad\x74\x48\x3e\x14\x0f\xbd\x2b\xb1\x8a\x8b\x6e\xad\x20\x86\x3f\x50\xfa\x04\x22\xb8\x05\xc2\xf6\x09\xa9\x92\xf1\x79\x07\x25\xf8\xfd\xa2\xf4\xde\x18\xec\x74\xc7\x96\x0f\x77\x15\x87\x77\xa6\x65\xb6\xbb\x2a\x7b\xf4\x90\xf3\xe4\x04\x54\xa1\x3b\x3a\x29\x27\xd4\x1e\xfe\x98\xfe\x65\x27\x25\xbb\xc4\x5d\x02\xd9\x58\xca\x4f\xe3\x27\x6b\x68\xfb\x18\x82\x68\x75\x3d\xce\xf1\x9b\x8d\x09\xe5\x98\xa5\x70\x46\xc0\xcd\xa6\x18\x26\xd3\x8b\x82\x3a\x2a\x8d\x07\x4e\xb6\x18\xc9\x85\x72\xbb\x62\x44\x77\x9e\x00\xdc\x70\xd5\xa1\x2a\xd7\x06\x87\x9c\xa3\xd9\xbb\xd6\xdd\xf3\xa2\x34\x8f\x44\x53\xd9\xe0\x48\x23\x2a\xd3\xa6\xb8\x6c\x86\x2c\x82\x10\x0f\x38\x2b\x94\x54\xfc\xb0\xdd\x8c\x6c\x68\x9a\xe4\x4c\xf0\x15\x56\xbb\x07\x1b\x17\xc5\x43\x6e\x4f\xd7\xd9\x9e\xac\xb3\xf6\x92\x21\x7f\xa8\xea\x73\xc0\x45\x21\xed\xb5\xbe\xb5\x5d\x13\xa3\x51\x9b\x78\x15\xa1\xfc\xb3\x35\x98\x12\xc3\x1e\xe5\xb5\xee\x88\xa1\xd2\xc1\xfb\x93\xdf\x07\xa0\xf1\x07\xc4\x20\x1d\x9e\x6f\x4c\xe0\x73\x95\x58\xdd\x33\x34\xdd\x7f\xcc\x94\xfc\xce\x65\x0b\xdd\xe1\xde\x1f\xc8\xda\x20\x4d\x1c\x2b\x16\x8a\xfa\x03\xd1\x5c\xe8\x1a\x91\x2b\x61\xe1\x18\x1a\xdc\x04\xc9\x66\xc2\x24\x66\x1d\xf8\x35\xa8\x6c\x88\x0e\x92\x5c\xbe\x9f\x9e\xaf\xb4\xf5\x65\x2b\xfc\x1c\x15\x98\x98\x8c\xda\xb3\x5f\x58\xa5\x49\xfa\x9b\x56\x77\x98\x1a\x85\xe8\xac\x93\xc8\x19\xd3\x79\xe8\x2a\x52\x55\x4f\xda\x32\x29\xe2\x30\x5d\xa1\x84\x6c\x16\x67\x31\x32\xa3\xfd\x35\xaa\x7e\x9e\x4b\xc5\x4f\xe3\xe6\x8c\xcc\x3d\x99\xd1\x5f\x59\x8f\xbd\x31\x53\x25\xb9\x2b\x09\xdf\x5b\x02\x2f\x07\x29\x48\xa3\xfa\xc3\xae\x5b\x60\xb3\xfe\xc0\x8e\x5a\x57\xd7\x60\x68\xf9\x8f\x56\xab\x42\x8e\x87\x52\x90\xc7\x0b\x34\x84\x2a\x95\xad\x21\x32\xae\xf8\x5b\xec\xfd\xbb\x4b\x3e\x3d\x01\xc6\x2d\x6a\xa4\x77\xba\x7e\xdb\xa7\xa6\x1b\x3b\x30\x80\xa3\x17\xa8\x43\x61\xd1\x61\x68\xa3\xb3\x90\xcb\xd5\xd1\x47\xd8\x43\xd7\xee\xb6\xd5\xdf\xfb\x55\xee\xfb\x30\xa9\xdb\xb0\x64\xfb\xb1\x8e\xe0\xac\x0c\x7e\xae\x15\x6d\xe0\x62\x3e\x07\x57\x35\x8e\xdd\xa0\x14\x91\xb3\xac\xe1\x6b\xd4\x9c\x68\x84\xd4\x65\xf9\x7f\x12\x3a\x52\x57\x5c\xe2\x68\x1c\xe2\x59\x7f\x4d\x58\x00\xe1\xc1\xb0\xe8\xc8\xce\x88\x13\x66\x4c\xcf\x3f\x42\x15\xaa\xd5\x71\xae\x81\x5f\xbf\xb3\x29\xdc\x83\x1d\x1c\x0d\x38\x30\x9e\xae\xea\xd0\xd6\x31\x9b\xc4\xf7\x0f\xe0\x13\x6f\x5a\x09\xa7\x6c\xe6\xa4\x9d\x14\xa0\xc7\x47\x0e\xc9\x76\x08\x4d\x9b\xe4\x96\xf6\x8e\x88\xd9\x4e\x9a\xdd\x98\xe0\x9a\xb2\x8b\xdd\xfb\xe2\x0c\xef\x95\x58\xc6\xc3\xb7\x46\x68\x23\x70\xda\x18\x35\x02\xe4\xc0\x19\xea\x76\xc4\x79\x3e\x4c\x98\x71\x31\xa6\x46\xa5\xde\x11\xae\x61\x1d\x77\x21\x60\x55\x9d\xfe\x40\xd6\x82\xda\x7a\x30\xda\x0c\xe1\xbb\x26\x45\x8d\x06\x6f\x5b\xa3\x39\x75\x56\xa7\xef\x7d\xac\x00\xef\x14\xc7\x40\x95\x13\x1d\xe8\xa3\x90\xd8\xa7\x1c\x06\x07\xcd\x7a\xe9\x60\x61\x32\x1d\x84\x36\xf2\x94\x30\x4c\xdf\x46\x29\x6f\xac\xc4\x3c\x76\x45\xdc\x7d\x4d\xe3\xad\x43\x91\xe7\x47\xcf\xf3\x1d\xaf\x14\x45\xcf\x77\x3c\x88\x53\xac\x68\x9e\x0c\x62\x9a\xb4\x8c\x8d\x85\x41\xc9\x1b\x25\x5d\x23\x2c\x6b\xbd\x49\x89\x18\x8b\x03\x13\x5d\xd7\xc6\xb2\xbf\xa7\xe0\xe2\xec\x3b\x94\x66\x9f\x39\x83\x48\xba\xbd\xf4\x9a\xb4\x13\xfa\x38\xfb\xbe\x4d\x11\x9e\x2a\xaf\x5e\x5f\xb2\xe2\x2d\x7c\x63\xcc\xa4\xb8\x06\x36\x83\x7a\x09\xfe\x3a\x5b\x6e\x6d\x18\xfa\x48\xc3\x54\x0c\x80\xaa\xcc\xba\x75\xb3\xa1\x46\xab\x1c\x18\x23\xf6\xda\x6e\xb8\x2a\x26\x83\xdc\xd1\x76\xb5\x41\x8e\x0f\x38\xcc\xe6\x18\x23\x1c\x1c\xd2\xeb\x8f\xdb\x09\x5f\x38\xca\x67\x41\x99\x1d\xb7\x7d\x80\x2d\x8d\xcb\x28\xcf\x0b\xb6\x24\x58\x37\x4e\x14\x3a\x94\x72\x8d\x25\x16\x09\x8f\x0a\xf3\xf6\xc3\xb1\xe7\x55\xff\xaf\x5f\x46\xe4\xc4\x50\xcd\x81\x81\xf0\xb5\x93\x50\x5f\x94\x37\x1f\x87\x38\x6e\xee\x27\x4d\x45\x77\x24\x90\x42\xfc\x2f\xfa\x41\x39\x18\x6a\x80\xd7\x63\x9a\x2e\x7d\x2b\xc8\x31\x4b\xf1\x0f\x8e\xaf\x26\x83\x9b\x15\xad\xf1\xc1\xe0\x1c\x1d\x8f\x1e\x70\x2f\x1b\x37\x12\x41\xbd\xfb\x5e\xf6\x2b\x21\x19\xa2\x9a\x52\xb1\x3e\x26\xe5\x64\xa1\xfa\x88\x14\xe3\x1f\xca\xe1\x22\x16\x68\xe7\xcb\x50\x4d\x64\x7b\x8c\xb8\x7e\x19\xaa\x09\x4b\x46\xda\xf9\x12\x54\x13\x93\x98\xfb\x71\x33\xff\x4c\xb1\xd3\x9b\x41\xf8\x1b\x49\x9e\x21\xad\xfa\xa5\x49\xa9\x7f\xae\xff\xa0\xa4\xbd\x29\xe9\x6e\xfb\x67\xcf\x2b\x2a\xbb\xc6\x36\xa8\x2b\x64\x57\x6d\x8a\xb9\x21\x5e\x63\x51\x61\xcf\x8e\xce\xd9\x36\xec\x25\x15\xf4\x31\x98\xb4\xf2\x94\x95\xc1\x81\xa4\xd7\x7b\x16\x01\x66\x85\x85\x84\x90\xdf\x0b\x2b\xce\x8b\x49\xeb\xe5\xe7\x07\xd1\x04\x47\x04\x1a\x1a\xa4\x21\x1c\xd2\xf4\x0a\xb8\x74\x2b\x86\xe3\x09\x53\x7d\x93\x85\xaa\x4b\x7a\x27\x7e\x6b\x56\xab\xe9\xf9\x22\x6e\x0b\x12\x1b\x34\xb0\x28\x20\x86\x05\xc7\xd9\x00\x22\xcf\x24\x66\x9b\x63\xa3\x78\x71\xc0\xb0\xf6\x8b\x33\x24\xf3\xf8\x25\x54\x6f\x50\x21\x55\xdc\x70\x29\xea\x38\x46\x59\xa8\x25\x02\xb5\xd2\x26\xd5\x4c\x13\xf1\x1c\x84\x9f\xa6\x29\x78\x31\xb5\x37\xd5\x61\xac\x9c\xa5\x29\x49\x21\x1f\x27\xd4\xc2\x70\x4a\xa2\x79\xfb\x2d\x7c\xf4\x02\x36\x8c\xfa\xcd\x22\x7a\x9a\x4f\xfa\x98\xe6\xd4\xbd\x06\xf9\x97\x14\x1d\x77\x13\x6f\x2c\xba\xcc\x86\xcc\x17\x10\x21\x39\x60\xf3\xe5\x44\x48\xe4\xf6\xff\x77\x22\x44\x28\xe2\x8f\x89\x37\xc4\x4b\xdb\x7e\xff\x6f\xaa\xf5\x5c\x89\xf0\x55\xb5\x1a\xb8\xa4\x13\xc4\x0d\xbc\x2f\xb3\x58\x88\x2a\x76\x41\x60\xc7\x9d\xb7\xfc\x5f\x92\xe3\x53\x4e\x6f\x79\x0f\x71\x1a\x40\x78\xe9\xcb\x1a\x71\x39\x62\x4b\xd3\x6f\x73\x93\xe0\xa3\xe5\x59\xe3\x48\xa4\xef\x62\x83\x61\x99\x5d\xef\x6c\xf0\x98\x83\x74\xf2\xff\xbc\xf0\xe2\xc6\x0a\xad\xa0\x4e\x03\x8a\x52\x50\x20\xfc\x22\x2c\x86\x6d\x43\x19\xb2\xd3\xa1\xb4\xe4\xf5\x37\x76\xb2\x71\x5c\x7b\xec\x85\xdd\xbf\x6d\x23\x81\xb1\xb3\x40\xfd\xa1\xa3\x34\x8f\x12\xc1\xa2\x36\xb8\xd1\xf2\x86\xf2\xb1\x14\x8d\xb6\xdd\xfc\x63\x00\xbb\x5a\x71\xb5\x84\x27\xe0\x26\x6f\x22\x63\xcf\xa4\x5c\x6c\xee\x1d\xba\x9e\xbb\xae\xc6\x05\x29\xc4\x3e\x7c\xe0\xad\x58\x1a\xdd\xb5\xc7\xbf\x84\x1e\xd7\xd3\x5f\xae\x85\xaa\x4f\x3f\x24\x99\x7f\xfc\x0b\xfa\x33\x1b\x60\x3e\x9c\x34\x77\x06\x76\xfb\x03\xba\xc8\xe9\xdf\x1a\x13\x19\x05\x50\x7c\x38\xa5\x1f\x6d\x9c\x02\xc7\x5d\xf1\xdd\x3a\x9a\xcb\x4c\xb1\x03\x4d\x89\xd3\xfc\x3d\x14\xfa\x16\x6d\x59\x8f\x7d\x98\xe4\xa5\x97\x7a\x59\xe5\x85\x9a\x82\xe1\x5c\x57\x28\x06\x12\xfd\x8a\x8f\x38\xa0\x85\x6f\x7e\x8c\x34\x15\x1e\xe2\xa2\xe1\x2b\x18\x1b\x33\xb7\x9e\x40\x7c\xfa\xcb\x14\x26\x61\x9b\x0f\x56\x24\xc5\x0b\x55\x00\x75\xac\x3f\x0e\x99\xec\x5e\x58\x5c\xd7\x30\xd9\x18\xbf\xbb\xb3\xe3\x30\x51\x55\xf8\xcc\x33\x85\x92\xb8\x65\x6f\x75\x0d\x17\x7e\xa1\xb8\xf4\x1f\xe2\xf8\x9f\xc7\x4a\x3e\xd3\x06\xc3\xb1\xf2\x3e\x9a\x62\x69\x5b\x59\xf3\x1d\x3f\xe3\x48\xe9\xe8\xb8\x96\x4e\xcd\xcc\x88\xcf\x6c\x73\xa5\x4c\x92\xaa\xe9\x5b\x62\x5e\xb2\xa7\x22\x57\xaf\x8e\xce\x2e\xce\x59\xc3\x15\x5f\x42\x9e\x4a\xb7\x05\xe6\x1d\x55\x15\xff\x9f\xd0\xe7\xe7\xb6\xc5\xd9\x6a\x05\x7b\x27\x54\xe9\xe1\x18\xc9\x26\x85\xea\x78\xf8\x78\x5e\xbc\xa6\xfe\x78\xe6\xde\xec\xd0\x3d\x07\x7d\xd2\xcc\x6b\xb7\x0a\x75\x60\x7e\x71\x7f\xc3\xc2\xb2\xb6\x9b\x4b\xfa\x06\x6b\x31\x91\xbf\xbf\xc5\x9e\xf3\xac\x71\x48\x6a\x5e\xdf\xe6\x8f\xe6\x44\xc6\x2a\x66\x5b\x9f\x6c\x0c\x65\x4d\x6b\x4d\x3e\xff\x44\x9e\xbf\x26\xb1\x4b\xa6\x67\x05\x0c\x1e\x32\xf4\x00\x4d\x31\x47\x99\x33\x5f\x4e\x4b\x48\x15\xc7\x8f\x64\x5d\xa5\xc6\x58\x2c\x30\xb9\x4a\x3b\x5a\x86\xcc\xba\x5d\xa2\x58\x3e\x92\x3f\x2d\x70\x30\xef\x1c\xab\xa9\x36\x3c\x54\x79\x1d\xc6\x64\x2d\x8a\xc9\xf4\x05\x56\xec\x92\xf1\xe2\xd1\x92\x8e\x6c\xb8\xab\x56\x68\x10\x71\xec\x89\x64\x97\x40\x14\x1b\x0d\xb1\xad\x94\xae\x3d\xae\xb4\xaa\xa0\x75\xf6\x38\xac\x2a\xd4\x72\x12\x0b\xe0\x8f\x71\x9d\x09\x57\xf5\x24\xe3\xef\xf8\x19\x2b\x87\x10\xd6\xe0\xb8\x90\x71\x1c\x53\x7a\xaa\xa8\x8f\xcd\x5f\x85\xc5\xfc\x96\x15\x8d\x90\xdc\xfb\xb2\x0a\xdb\x8e\xa2\x90\xf3\x8c\x87\x60\x8f\x99\x98\xc2\x74\xcc\x66\x3f\xc1\xfa\xc3\xb7\x3f\x7b\x77\xe8\x97\xd3\x57\x8b\x05\x54\xee\xc3\xe9\x25\x7d\xcf\xf5\x97\xd9\x38\x90\x08\x7d\x0b\x19\x3f\xf5\xfa\xcf\xce\xcb\x8a\xb9\xe1\xd5\x35\x84\x1e\x48\x9e\x66\x7c\x72\x39\x65\xdf\xe7\x4c\x97\xc5\xef\xe7\xa3\x6a\x31\x5a\xc2\xb4\x8f\x99\xd0\x3b\xfa\x56\x5f\x06\x54\xcf\xe2\xd3\x1b\x0f\x86\x99\xf6\x65\xb5\xfe\xe9\x5b\xfd\x8a\x6a\x30\x4f\xff\x70\x72\x72\x42\xee\xc4\x84\x3e\xca\xe5\x89\xff\x5b\x6b\xeb\xd3\x0b\x74\x22\xcb\xf5\xa9\xb7\xf9\x89\x96\xb3\xd1\xc5\xed\x69\xba\xe2\x58\x8a\xf8\xe5\x76\x2e\xc2\x07\x63\x03\xe9\xc0\xb8\x67\xa9\xee\xa6\x81\xcc\xdd\x86\x57\x8f\x3b\xb2\xe3\x8a\x76\xd8\x47\x93\x07\xb1\x14\x81\x2a\x5d\xbe\x67\x21\xd4\xc1\xa9\xa2\x3a\x2e\x5a\xd4\xb4\x86\xcf\xe5\xc5\x62\xd2\xdc\xd8\x30\x27\xd5\x3f\x3c\x1d\x2e\xe5\x54\xe3\x9e\xa9\xae\x35\xeb\xff\x80\xd6\x64\xe1\xb2\x83\xf0\x05\x6a\xcb\x8e\x8e\x7e\xe4\xb0\x04\x73\x74\x14\xa6\x86\x5c\x25\x7c\xb2\xff\x30\x0a\x36\x8c\x82\x71\xe8\x90\xc7\x02\xa3\xf8\x7c\xac\x19\x2a\xa7\xcc\x0c\xdd\xc7\x90\xe7\xf7\x80\x42\x2d\x55\xf4\x53\x47\x4d\x8c\xbe\x45\x54\x85\x36\xed\x58\x73\xc7\x7b\x83\x41\x72\xd8\x79\xab\x83\xf6\x70\x60\x1c\xd8\x9e\x10\x85\x41\xf2\x89\xde\x02\x70\x25\x75\x27\x7b\x67\x98\x76\x07\x5a\xe7\x4b\x78\x2c\x8a\x6b\xb3\x77\x03\x37\x79\x72\xfe\x95\x50\x14\x13\x4d\x83\x51\xa5\x95\x75\xa3\xa1\xb5\xb1\x5c\xe0\x81\x8b\xa7\x29\x57\xf8\x72\xb1\xcd\x57\xa3\xc3\x67\xff\x37\x00\x00\xff\xff\x1f\xb2\x0b\x70\x99\xa2\x00\x00"), }, } fs["/"].(*vfsgen۰DirInfo).entries = []os.FileInfo{ diff --git a/pkg/trait/route.go b/pkg/trait/route.go index 72d20f75e6..20f0c4378c 100644 --- a/pkg/trait/route.go +++ b/pkg/trait/route.go @@ -20,6 +20,7 @@ package trait import ( "fmt" "reflect" + "strings" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -28,10 +29,19 @@ import ( routev1 "github.com/openshift/api/route/v1" v1 "github.com/apache/camel-k/pkg/apis/camel/v1" + "github.com/apache/camel-k/pkg/util/kubernetes" ) // The Route trait can be used to configure the creation of OpenShift routes for the integration. // +// The certificate and key contents may be sourced either from the local filesystem or in a Openshift `secret` object. +// The user may use the parameters ending in `-secret` (example: `tls-certificate-secret`) to reference a certificate stored in a `secret`. +// Parameters ending in `-secret` have higher priorities and in case the same route parameter is set, for example: `tls-key-secret` and `tls-key`, +// then `tls-key-secret` is used. +// The recommended approach to set the key and certificates is to use `secrets` to store their contents and use the +// following parameters to reference them: `tls-certificate-secret`, `tls-key-secret`, `tls-ca-certificate-secret`, `tls-destination-ca-certificate-secret` +// See the examples section at the end of this page to see the setup options. +// // +camel-k:trait=route type routeTrait struct { BaseTrait `property:",squash"` @@ -39,31 +49,47 @@ type routeTrait struct { Host string `property:"host" json:"host,omitempty"` // The TLS termination type, like `edge`, `passthrough` or `reencrypt`. // - // Refer to the OpenShift documentation for additional information. + // Refer to the OpenShift route documentation for additional information. TLSTermination string `property:"tls-termination" json:"tlsTermination,omitempty"` // The TLS certificate contents. // - // Refer to the OpenShift documentation for additional information. + // Refer to the OpenShift route documentation for additional information. TLSCertificate string `property:"tls-certificate" json:"tlsCertificate,omitempty"` + // The secret name and key reference to the TLS certificate. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + // + // Refer to the OpenShift route documentation for additional information. + TLSCertificateSecret string `property:"tls-certificate-secret" json:"tlsCertificateSecret,omitempty"` // The TLS certificate key contents. // - // Refer to the OpenShift documentation for additional information. + // Refer to the OpenShift route documentation for additional information. TLSKey string `property:"tls-key" json:"tlsKey,omitempty"` - // The TLS cert authority certificate contents. + // The secret name and key reference to the TLS certificate key. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + // + // Refer to the OpenShift route documentation for additional information. + TLSKeySecret string `property:"tls-key-secret" json:"tlsKeySecret,omitempty"` + // The TLS CA certificate contents. // - // Refer to the OpenShift documentation for additional information. + // Refer to the OpenShift route documentation for additional information. TLSCACertificate string `property:"tls-ca-certificate" json:"tlsCACertificate,omitempty"` + // The secret name and key reference to the TLS CA certificate. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + // + // Refer to the OpenShift route documentation for additional information. + TLSCACertificateSecret string `property:"tls-ca-certificate-secret" json:"tlsCACertificateSecret,omitempty"` // The destination CA certificate provides the contents of the ca certificate of the final destination. When using reencrypt // termination this file should be provided in order to have routers use it for health checks on the secure connection. // If this field is not specified, the router may provide its own destination CA and perform hostname validation using // the short service name (service.namespace.svc), which allows infrastructure generated certificates to automatically // verify. // - // Refer to the OpenShift documentation for additional information. + // Refer to the OpenShift route documentation for additional information. TLSDestinationCACertificate string `property:"tls-destination-ca-certificate" json:"tlsDestinationCACertificate,omitempty"` + // The secret name and key reference to the destination CA certificate. The format is "secret-name[/key-name]", the value represents the secret name, if there is only one key in the secret it will be read, otherwise you can set a key name separated with a "/". + // + // Refer to the OpenShift route documentation for additional information. + TLSDestinationCACertificateSecret string `property:"tls-destination-ca-certificate-secret" json:"tlsDestinationCACertificateSecret,omitempty"` // To configure how to deal with insecure traffic, e.g. `Allow`, `Disable` or `Redirect` traffic. // - // Refer to the OpenShift documentation for additional information. + // Refer to the OpenShift route documentation for additional information. TLSInsecureEdgeTerminationPolicy string `property:"tls-insecure-edge-termination-policy" json:"tlsInsecureEdgeTerminationPolicy,omitempty"` service *corev1.Service @@ -122,6 +148,10 @@ func (t *routeTrait) Apply(e *Environment) error { servicePortName = dt.(*containerTrait).ServicePortName } + tlsConfig, err := t.getTLSConfig() + if err != nil { + return err + } route := routev1.Route{ TypeMeta: metav1.TypeMeta{ Kind: "Route", @@ -143,7 +173,7 @@ func (t *routeTrait) Apply(e *Environment) error { Name: t.service.Name, }, Host: t.Host, - TLS: t.getTLSConfig(), + TLS: tlsConfig, }, } @@ -174,19 +204,83 @@ func (t *routeTrait) Apply(e *Environment) error { return nil } -func (t *routeTrait) getTLSConfig() *routev1.TLSConfig { +func (t *routeTrait) getTLSConfig() (*routev1.TLSConfig, error) { + // a certificate is a multiline text, but to set it as value in a single line in CLI, the user must escape the new line character as \\n + // but in the TLS configuration, the certificates should be a multiline string + // then we need to replace the incoming escaped new lines \\n for a real new line \n + key := strings.ReplaceAll(t.TLSKey, "\\n", "\n") + certificate := strings.ReplaceAll(t.TLSCertificate, "\\n", "\n") + CACertificate := strings.ReplaceAll(t.TLSCACertificate, "\\n", "\n") + destinationCAcertificate := strings.ReplaceAll(t.TLSDestinationCACertificate, "\\n", "\n") + var err error + if t.TLSKeySecret != "" { + key, err = t.readContentIfExists(t.TLSKeySecret) + if err != nil { + return nil, err + } + } + if t.TLSCertificateSecret != "" { + certificate, err = t.readContentIfExists(t.TLSCertificateSecret) + if err != nil { + return nil, err + } + } + if t.TLSCACertificateSecret != "" { + CACertificate, err = t.readContentIfExists(t.TLSCACertificateSecret) + if err != nil { + return nil, err + } + } + if t.TLSDestinationCACertificateSecret != "" { + destinationCAcertificate, err = t.readContentIfExists(t.TLSDestinationCACertificateSecret) + if err != nil { + return nil, err + } + } + config := routev1.TLSConfig{ Termination: routev1.TLSTerminationType(t.TLSTermination), - Certificate: t.TLSCertificate, - Key: t.TLSKey, - CACertificate: t.TLSCACertificate, - DestinationCACertificate: t.TLSDestinationCACertificate, + Key: key, + Certificate: certificate, + CACertificate: CACertificate, + DestinationCACertificate: destinationCAcertificate, InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyType(t.TLSInsecureEdgeTerminationPolicy), } if reflect.DeepEqual(config, routev1.TLSConfig{}) { - return nil + return nil, nil } - return &config + return &config, nil } + +func (t *routeTrait) readContentIfExists(secretName string) (string, error) { + key := "" + strs := strings.Split(secretName, "/") + if len(strs) > 1 { + secretName = strs[0] + key = strs[1] + } + + secret := kubernetes.LookupSecret(t.Ctx, t.Client, t.service.Namespace, secretName) + if secret == nil { + return "", fmt.Errorf("%s secret not found in %s namespace, make sure to provide it before the Integration can run", secretName, t.service.Namespace) + } + if len(secret.Data) > 1 && len(key) == 0 { + return "", fmt.Errorf("secret %s contains multiple data keys, but no key was provided", secretName) + } + if len(secret.Data) == 1 && len(key) == 0 { + for _, value := range secret.Data { + content := string(value) + return content, nil + } + } + if len(key) > 0 { + content := string(secret.Data[key]) + if len(content) == 0 { + return "", fmt.Errorf("Could not find key %s in secret %s in namespace %s", key, secretName, t.service.Namespace) + } + return content, nil + } + return "", nil +} \ No newline at end of file diff --git a/pkg/trait/route_test.go b/pkg/trait/route_test.go index 764fedc4e6..fe3f1533ca 100644 --- a/pkg/trait/route_test.go +++ b/pkg/trait/route_test.go @@ -35,13 +35,105 @@ import ( "github.com/apache/camel-k/pkg/util/test" ) +const ( + host = "my-host1" + key = `-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAKulUTZ8B1qccZ8c +DXRGSY08gW8KvLlcxxxGC4gZHNT3CBUF8n5R4KE30aZyYZ/rtsQZu05juZJxaJ0q +mbe75dlQ5d+Xc9BMXeQg/MpTZw5TAN7OIdGYYpFBe+1PLZ6wEfjkYrMqMUcfq2Lq +hTLdAbvBJnuRcYZLqmBeOQ8FTrKrAgMBAAECgYEAnkHRbEPU3/WISSQrP36iyCb2 +S/SBZwKkzmvCrBxDWhPeDswp9c/2JY76rNWfLzy8iXgUG8WUzvHje61Qh3gmBcKe +bUaTGl4Vy8Ha1YBADo5RfRrdm0FE4tvgvu/TkqFqpBBZweu54285hk5zlG7n/D7Y +dnNXUpu5MlNb5x3gW0kCQQDUL//cwcXUxY/evaJP4jSe+ZwEQZo+zXRLiPUulBoV +aw28CVMuxdgwqAo1X1IKefPeUaf7RQu8gCKaRnpGuEuXAkEAzxZTfMmvmCUDIew4 +5Gk6bK265XQWdhcgiq254lpBGOYmDj9yCE7yA+zmASQwMsXTdQOi1hOCEyrXuSJ5 +c++EDQJAFh3WrnzoEPByuYXMmET8tSFRWMQ5vpgNqh3haHR5b4gUC2hxaiunCBNL +1RpVY9AoUiDywGcG/SPh93CnKB3niwJBAKP7AtsifZgVXtiizB4aMThTjVYaSZrz +D0Kg9DuHylpkDChmFu77TGrNUQgAVuYtfhb/bRblVa/F0hJ4eQHT3JUCQBVT68tb +OgRUk0aP9tC3021VN82X6+klowSQN8oBPX8+TfDWSUilp/+j24Hky+Z29Do7yR/R +qutnL92CvBlVLV4= +-----END PRIVATE KEY----- +` + cert = `-----BEGIN CERTIFICATE----- +MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw +EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 +MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH +A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf +3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO +PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz +pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H +-----END CERTIFICATE----- +` + caCert = `-----BEGIN CERTIFICATE----- +BLAajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw +EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 +MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH +A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf +3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO +PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz +pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H +-----END CERTIFICATE----- +` + destinationCaCert = `-----BEGIN CERTIFICATE----- +FOOBARCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw +EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 +MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH +A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf +3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO +PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz +pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H +-----END CERTIFICATE----- +` + + tlsKeySecretName = "tls-test" + tlsKeySecretOnlyKeyName = "tls.key" + tlsKeySecretKeyName = tlsKeySecretName + "/" + tlsKeySecretOnlyKeyName + + tlsMultipleSecretsName = "tls-multiple-test" + tlsMultipleSecretsCert1Key = "cert1.crt" + tlsMultipleSecretsCert2Key = "cert2.crt" + tlsMultipleSecretsCert3Key = "cert3.crt" +) + func createTestRouteEnvironment(t *testing.T, name string) *Environment { catalog, err := camel.DefaultCatalog() assert.Nil(t, err) - + client, _ := test.NewFakeClient( + &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test-ns", + Name: tlsKeySecretName, + }, + Data: map[string][]byte{ + tlsKeySecretOnlyKeyName: []byte(key), + }, + }, + &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test-ns", + Name: tlsMultipleSecretsName, + }, + Data: map[string][]byte{ + tlsMultipleSecretsCert1Key: []byte(cert), + tlsMultipleSecretsCert2Key: []byte(caCert), + tlsMultipleSecretsCert3Key: []byte(destinationCaCert), + }, + }, + ) res := &Environment{ CamelCatalog: catalog, - Catalog: NewCatalog(context.TODO(), nil), + Catalog: NewCatalog(context.TODO(), client), Integration: &v1.Integration{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -154,14 +246,14 @@ func TestRoute_Configure_IntegrationKitOnly(t *testing.T) { assert.Nil(t, err) } -func TestRoute_TLS(t *testing.T) { +func TestRoute_Host(t *testing.T) { name := xid.New().String() environment := createTestRouteEnvironment(t, name) traitsCatalog := environment.Catalog environment.Integration.Spec.Traits = map[string]v1.TraitSpec{ "route": test.TraitSpecFromMap(t, map[string]interface{}{ - "tlsTermination": string(routev1.TLSTerminationEdge), + "host": host, }), } @@ -175,9 +267,230 @@ func TestRoute_TLS(t *testing.T) { return r.ObjectMeta.Name == name }) + assert.NotNil(t, route) + assert.Equal(t, host, route.Spec.Host) + assert.Nil(t, route.Spec.TLS) +} + +func TestRoute_TLS_From_Secret_reencrypt(t *testing.T) { + name := xid.New().String() + environment := createTestRouteEnvironment(t, name) + traitsCatalog := environment.Catalog + + environment.Integration.Spec.Traits = map[string]v1.TraitSpec{ + "route": test.TraitSpecFromMap(t, map[string]interface{}{ + "tlsTermination": string(routev1.TLSTerminationReencrypt), + "host": host, + "tlsKeySecret": tlsKeySecretName, + "tlsCertificateSecret": tlsMultipleSecretsName + "/" + tlsMultipleSecretsCert1Key, + "tlsCACertificateSecret": tlsMultipleSecretsName + "/" + tlsMultipleSecretsCert2Key, + "tlsDestinationCACertificateSecret": tlsMultipleSecretsName + "/" + tlsMultipleSecretsCert3Key, + }), + } + err := traitsCatalog.apply(environment) + + assert.Nil(t, err) + assert.NotEmpty(t, environment.ExecutedTraits) + assert.NotNil(t, environment.GetTrait("route")) + + route := environment.Resources.GetRoute(func(r *routev1.Route) bool { + return r.ObjectMeta.Name == name + }) + + assert.NotNil(t, route) + assert.NotNil(t, route.Spec.TLS) + assert.Equal(t, routev1.TLSTerminationReencrypt, route.Spec.TLS.Termination) + assert.Equal(t, key, route.Spec.TLS.Key) + assert.Equal(t, host, route.Spec.Host) + assert.Equal(t, cert, route.Spec.TLS.Certificate) + assert.Equal(t, caCert, route.Spec.TLS.CACertificate) + assert.Equal(t, destinationCaCert, route.Spec.TLS.DestinationCACertificate) +} + +func TestRoute_TLS_wrong_secret(t *testing.T) { + name := xid.New().String() + environment := createTestRouteEnvironment(t, name) + traitsCatalog := environment.Catalog + + environment.Integration.Spec.Traits = map[string]v1.TraitSpec{ + "route": test.TraitSpecFromMap(t, map[string]interface{}{ + "tlsTermination": string(routev1.TLSTerminationReencrypt), + "host": host, + "tlsKeySecret": "foo", + "tlsCertificateSecret": "bar", + "tlsCACertificateSecret": "test", + "tlsDestinationCACertificateSecret": "404", + }), + } + err := traitsCatalog.apply(environment) + + // there must be errors as the trait has wrong configuration + assert.NotNil(t, err) + assert.Nil(t, environment.GetTrait("route")) + + route := environment.Resources.GetRoute(func(r *routev1.Route) bool { + return r.ObjectMeta.Name == name + }) + + // route trait is expected to not be created + assert.Nil(t, route) +} + +func TestRoute_TLS_secret_wrong_key(t *testing.T) { + name := xid.New().String() + environment := createTestRouteEnvironment(t, name) + traitsCatalog := environment.Catalog + + environment.Integration.Spec.Traits = map[string]v1.TraitSpec{ + "route": test.TraitSpecFromMap(t, map[string]interface{}{ + "tlsTermination": string(routev1.TLSTerminationReencrypt), + "host": host, + "tlsKeySecret": tlsKeySecretName, + "tlsCertificateSecret": tlsMultipleSecretsName + "/" + tlsMultipleSecretsCert1Key, + "tlsCACertificateSecret": tlsMultipleSecretsName + "/foo", + }), + } + err := traitsCatalog.apply(environment) + + // there must be errors as the trait has wrong configuration + assert.NotNil(t, err) + assert.Nil(t, environment.GetTrait("route")) + + route := environment.Resources.GetRoute(func(r *routev1.Route) bool { + return r.ObjectMeta.Name == name + }) + + // route trait is expected to not be created + assert.Nil(t, route) +} + +func TestRoute_TLS_secret_missing_key(t *testing.T) { + name := xid.New().String() + environment := createTestRouteEnvironment(t, name) + traitsCatalog := environment.Catalog + + environment.Integration.Spec.Traits = map[string]v1.TraitSpec{ + "route": test.TraitSpecFromMap(t, map[string]interface{}{ + "tlsTermination": string(routev1.TLSTerminationReencrypt), + "host": host, + "tlsKeySecret": tlsKeySecretName, + "tlsCertificateSecret": tlsMultipleSecretsName + "/" + tlsMultipleSecretsCert1Key, + "tlsCACertificateSecret": tlsMultipleSecretsName, + }), + } + err := traitsCatalog.apply(environment) + + // there must be errors as the trait has wrong configuration + assert.NotNil(t, err) + assert.Nil(t, environment.GetTrait("route")) + + route := environment.Resources.GetRoute(func(r *routev1.Route) bool { + return r.ObjectMeta.Name == name + }) + + // route trait is expected to not be created + assert.Nil(t, route) +} + +func TestRoute_TLS_reencrypt(t *testing.T) { + name := xid.New().String() + environment := createTestRouteEnvironment(t, name) + traitsCatalog := environment.Catalog + + environment.Integration.Spec.Traits = map[string]v1.TraitSpec{ + "route": test.TraitSpecFromMap(t, map[string]interface{}{ + "tlsTermination": string(routev1.TLSTerminationReencrypt), + "host": host, + "tlsKey": key, + "tlsCertificate": cert, + "tlsCACertificate": caCert, + "tlsDestinationCACertificate": destinationCaCert, + }), + } + err := traitsCatalog.apply(environment) + + assert.Nil(t, err) + assert.NotEmpty(t, environment.ExecutedTraits) + assert.NotNil(t, environment.GetTrait("route")) + + route := environment.Resources.GetRoute(func(r *routev1.Route) bool { + return r.ObjectMeta.Name == name + }) + + assert.NotNil(t, route) + assert.NotNil(t, route.Spec.TLS) + assert.Equal(t, routev1.TLSTerminationReencrypt, route.Spec.TLS.Termination) + assert.Equal(t, key, route.Spec.TLS.Key) + assert.Equal(t, host, route.Spec.Host) + assert.Equal(t, cert, route.Spec.TLS.Certificate) + assert.Equal(t, caCert, route.Spec.TLS.CACertificate) + assert.Equal(t, destinationCaCert, route.Spec.TLS.DestinationCACertificate) +} + +func TestRoute_TLS_edge(t *testing.T) { + name := xid.New().String() + environment := createTestRouteEnvironment(t, name) + traitsCatalog := environment.Catalog + + environment.Integration.Spec.Traits = map[string]v1.TraitSpec{ + "route": test.TraitSpecFromMap(t, map[string]interface{}{ + "tlsTermination": string(routev1.TLSTerminationEdge), + "host": host, + "tlsKey": key, + "tlsCertificate": cert, + "tlsCACertificate": caCert, + }), + } + err := traitsCatalog.apply(environment) + + assert.Nil(t, err) + assert.NotEmpty(t, environment.ExecutedTraits) + assert.NotNil(t, environment.GetTrait("route")) + + route := environment.Resources.GetRoute(func(r *routev1.Route) bool { + return r.ObjectMeta.Name == name + }) + assert.NotNil(t, route) assert.NotNil(t, route.Spec.TLS) assert.Equal(t, routev1.TLSTerminationEdge, route.Spec.TLS.Termination) + assert.Equal(t, key, route.Spec.TLS.Key) + assert.Equal(t, host, route.Spec.Host) + assert.Equal(t, cert, route.Spec.TLS.Certificate) + assert.Equal(t, caCert, route.Spec.TLS.CACertificate) + assert.Empty(t, route.Spec.TLS.DestinationCACertificate) +} + +func TestRoute_TLS_passthrough(t *testing.T) { + name := xid.New().String() + environment := createTestRouteEnvironment(t, name) + traitsCatalog := environment.Catalog + + environment.Integration.Spec.Traits = map[string]v1.TraitSpec{ + "route": test.TraitSpecFromMap(t, map[string]interface{}{ + "tlsTermination": string(routev1.TLSTerminationPassthrough), + "host": host, + "tlsInsecureEdgeTerminationPolicy": routev1.InsecureEdgeTerminationPolicyAllow, + }), + } + err := traitsCatalog.apply(environment) + + assert.Nil(t, err) + assert.NotEmpty(t, environment.ExecutedTraits) + assert.NotNil(t, environment.GetTrait("route")) + + route := environment.Resources.GetRoute(func(r *routev1.Route) bool { + return r.ObjectMeta.Name == name + }) + + assert.NotNil(t, route) + assert.NotNil(t, route.Spec.TLS) + assert.Equal(t, routev1.TLSTerminationPassthrough, route.Spec.TLS.Termination) + assert.Equal(t, host, route.Spec.Host) + assert.Equal(t, routev1.InsecureEdgeTerminationPolicyAllow, route.Spec.TLS.InsecureEdgeTerminationPolicy) + assert.Empty(t, route.Spec.TLS.Certificate) + assert.Empty(t, route.Spec.TLS.CACertificate) + assert.Empty(t, route.Spec.TLS.DestinationCACertificate) } func TestRoute_WithCustomServicePort(t *testing.T) { diff --git a/pkg/util/openshift/register.go b/pkg/util/openshift/register.go index 1687b1f3ba..b7af756fb3 100644 --- a/pkg/util/openshift/register.go +++ b/pkg/util/openshift/register.go @@ -23,6 +23,7 @@ import ( apps "github.com/openshift/api/apps/v1" authorization "github.com/openshift/api/authorization/v1" build "github.com/openshift/api/build/v1" + config "github.com/openshift/api/config/v1" console "github.com/openshift/api/console/v1" image "github.com/openshift/api/image/v1" project "github.com/openshift/api/project/v1" @@ -46,6 +47,7 @@ func AddToScheme(scheme *runtime.Scheme) error { err = doAdd(build.Install, scheme, err) err = doAdd(authorization.Install, scheme, err) err = doAdd(project.Install, scheme, err) + err = doAdd(config.Install, scheme, err) // OpenShift console API err = doAdd(console.Install, scheme, err)