gRPC transcoder in Istio 테스트

gRPC transcoder in Istio 테스트
gRPC transcoder in Istio
💡
gRPC transcoder 를 envoy가 아닌 istio 레벨에서 사용해보는 예제입니다. gRPC transcoder 는 gRPC 서버에서 기존 RESTful API 혹은 HTTP API라고 불리는 json 기반의 API 호출을 gRPC로 변환해주는 일종의 프록시라고 할 수 있습니다. 이것을 통해 우리는 gRPC 엔드포인트와 HTTP 엔드포인트를 동시에 제공 할 수 있습니다.

테스트 코드들

GitHub - kimsehwan96/gRPC-python-example

GitHub - kimsehwan96/istio-example

gRPC 테스트 서버

gRPC 의 경우 직렬화/역직렬화 과정에서 protobuf 를 사용합니다. 따라서 .proto 파일을 통해 주고받을 데이터의 형식을 지정해야 합니다.

// Copyright 2015 gRPC authors.
//
// Licensed 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.

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

import "google/api/annotations.proto";

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/v1/hello"
    };
  }

  // Another Method
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}

  rpc SayHelloStreamReply (HelloRequest) returns (stream HelloReply) {}

  rpc SayHelloBidiStream (stream HelloRequest) returns (stream HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

proto 파일 예제

위와 같이 간단한 proto 파일을 생성합니다. 여기서 json 트랜스코더에서 사용할 option (google.api.http) 부분을 추가하면 기존 Restful API(HTTP API) 형태로 사용이 가능합니다. 다만 이때 proto descriptor 생성을 할 때 googleapis 를 proto descriptor 빌드하는 환경에 추가해야 합니다.

굳이 위 예제에서의 gRPC 서버가 아니더라도, HTTP 엔드포인트를 노출한 protobuf + gRPC 서버라면 어찌되었든 이 예제에서 사용하려는 테스트는 진행 가능합니다.

Istio Envoy filter

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: grpc-transcoder
spec:
  workloadSelector:
    labels:
      app: grpc-python
  configPatches:
    - applyTo: HTTP_FILTER
      match:
        context: SIDECAR_INBOUND
        listener:
          portNumber: 7777 # 5xxxx 번대 포트를 지정하면 반영이 안된다. 이건 왜그런지;
          filterChain:
            filter:
              name: "envoy.filters.network.http_connection_manager"
              subFilter:
                name: "envoy.filters.http.router"
      patch:
        operation: INSERT_BEFORE
        value:
          name: envoy.filters.http.grpc_json_transcoder
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
            services:
              - helloworld.Greeter
            print_options:
              add_whitespace: true
              always_print_enums_as_ints: false
              always_print_primitive_fields: true
              preserve_proto_field_names: false
            convert_grpc_status: true
            proto_descriptor_bin: CuF4ChVnb29nbGUvYXBpL2h0dHAucHJvdG8SCmdvb2dsZS5hcGkieQoESHR0cBIqCgVydWxlcxgBIAMoCzIULmdvb2dsZS5hcGkuSHR0cFJ1bGVSBXJ1bGVzEkUKH2Z1bGx5X2RlY29kZV9yZXNlcnZlZF9leHBhbnNpb24YAiABKAhSHGZ1bGx5RGVjb2RlUmVzZXJ2ZWRFeHBhbnNpb24i2gIKCEh0dHBSdWxlEhoKCHNlbGVjdG9yGAEgASgJUghzZWxlY3RvchISCgNnZXQYAiABKAlIAFIDZ2V0EhIKA3B1dBgDIAEoCUgAUgNwdXQSFAoEcG9zdBgEIAEoCUgAUgRwb3N0EhgKBmRlbGV0ZRgFIAEoCUgAUgZkZWxldGUSFgoFcGF0Y2gYBiABKAlIAFIFcGF0Y2gSNwoGY3VzdG9tGAggASgLMh0uZ29vZ2xlLmFwaS5DdXN0b21IdHRwUGF0dGVybkgAUgZjdXN0b20SEgoEYm9keRgHIAEoCVIEYm9keRIjCg1yZXNwb25zZV9ib2R5GAwgASgJUgxyZXNwb25zZUJvZHkSRQoTYWRkaXRpb25hbF9iaW5kaW5ncxgLIAMoCzIULmdvb2dsZS5hcGkuSHR0cFJ1bGVSEmFkZGl0aW9uYWxCaW5kaW5nc0IJCgdwYXR0ZXJuIjsKEUN1c3RvbUh0dHBQYXR0ZXJuEhIKBGtpbmQYASABKAlSBGtpbmQSEgoEcGF0aBgCIAEoCVIEcGF0aEJqCg5jb20uZ29vZ2xlLmFwaUIJSHR0cFByb3RvUAFaQWdvb2dsZS5nb2xhbmcub3JnL2dlbnByb3RvL2dvb2dsZWFwaXMvYXBpL2Fubm90YXRpb25zO2Fubm90YXRpb25z+AEBogIER0FQSUqycwoHEgUOAPoCAQq8BAoBDBIDDgASMrEEIENvcHlyaWdodCAyMDIzIEdvb2dsZSBMTEMKCiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgIkxpY2Vuc2UiKTsKIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS4KIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdAoKICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjAKCiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlCiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiAiQVMgSVMiIEJBU0lTLAogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuCiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kCiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS4KCggKAQISAxAAEwoICgEIEgMSAB8KCQoCCB8SAxIAHwoICgEIEgMTAFgKCQoCCAsSAxMAWAoICgEIEgMUACIKCQoCCAoSAxQAIgoICgEIEgMVACoKCQoCCAgSAxUAKgoICgEIEgMWACcKCQoCCAESAxYAJwoICgEIEgMXACIKCQoCCCQSAxcAIgrNAQoCBAASBBwAKQEawAEgRGVmaW5lcyB0aGUgSFRUUCBjb25maWd1cmF0aW9uIGZvciBhbiBBUEkgc2VydmljZS4gSXQgY29udGFpbnMgYSBsaXN0IG9mCiBbSHR0cFJ1bGVdW2dvb2dsZS5hcGkuSHR0cFJ1bGVdLCBlYWNoIHNwZWNpZnlpbmcgdGhlIG1hcHBpbmcgb2YgYW4gUlBDIG1ldGhvZAogdG8gb25lIG9yIG1vcmUgSFRUUCBSRVNUIEFQSSBtZXRob2RzLgoKCgoDBAABEgMcCAwKogEKBAQAAgASAyACHhqUASBBIGxpc3Qgb2YgSFRUUCBjb25maWd1cmF0aW9uIHJ1bGVzIHRoYXQgYXBwbHkgdG8gaW5kaXZpZHVhbCBBUEkgbWV0aG9kcy4KCiAqKk5PVEU6KiogQWxsIHNlcnZpY2UgY29uZmlndXJhdGlvbiBydWxlcyBmb2xsb3cgImxhc3Qgb25lIHdpbnMiIG9yZGVyLgoKDAoFBAACAAQSAyACCgoMCgUEAAIABhIDIAsTCgwKBQQAAgABEgMgFBkKDAoFBAACAAMSAyAcHQqUAgoEBAACARIDKAIrGoYCIFdoZW4gc2V0IHRvIHRydWUsIFVSTCBwYXRoIHBhcmFtZXRlcnMgd2lsbCBiZSBmdWxseSBVUkktZGVjb2RlZCBleGNlcHQgaW4KIGNhc2VzIG9mIHNpbmdsZSBzZWdtZW50IG1hdGNoZXMgaW4gcmVzZXJ2ZWQgZXhwYW5zaW9uLCB3aGVyZSAiJTJGIiB3aWxsIGJlCiBsZWZ0IGVuY29kZWQuCgogVGhlIGRlZmF1bHQgYmVoYXZpb3IgaXMgdG8gbm90IGRlY29kZSBSRkMgNjU3MCByZXNlcnZlZCBjaGFyYWN0ZXJzIGluIG11bHRpCiBzZWdtZW50IG1hdGNoZXMuCgoMCgUEAAIBBRIDKAIGCgwKBQQAAgEBEgMoByYKDAoFBAACAQMSAygpKgq7UwoCBAESBrsCAPECARqsUyAjIGdSUEMgVHJhbnNjb2RpbmcKCiBnUlBDIFRyYW5zY29kaW5nIGlzIGEgZmVhdHVyZSBmb3IgbWFwcGluZyBiZXR3ZWVuIGEgZ1JQQyBtZXRob2QgYW5kIG9uZSBvcgogbW9yZSBIVFRQIFJFU1QgZW5kcG9pbnRzLiBJdCBhbGxvd3MgZGV2ZWxvcGVycyB0byBidWlsZCBhIHNpbmdsZSBBUEkgc2VydmljZQogdGhhdCBzdXBwb3J0cyBib3RoIGdSUEMgQVBJcyBhbmQgUkVTVCBBUElzLiBNYW55IHN5c3RlbXMsIGluY2x1ZGluZyBbR29vZ2xlCiBBUElzXShodHRwczovL2dpdGh1Yi5jb20vZ29vZ2xlYXBpcy9nb29nbGVhcGlzKSwKIFtDbG91ZCBFbmRwb2ludHNdKGh0dHBzOi8vY2xvdWQuZ29vZ2xlLmNvbS9lbmRwb2ludHMpLCBbZ1JQQwogR2F0ZXdheV0oaHR0cHM6Ly9naXRodWIuY29tL2dycGMtZWNvc3lzdGVtL2dycGMtZ2F0ZXdheSksCiBhbmQgW0Vudm95XShodHRwczovL2dpdGh1Yi5jb20vZW52b3lwcm94eS9lbnZveSkgcHJveHkgc3VwcG9ydCB0aGlzIGZlYXR1cmUKIGFuZCB1c2UgaXQgZm9yIGxhcmdlIHNjYWxlIHByb2R1Y3Rpb24gc2VydmljZXMuCgogYEh0dHBSdWxlYCBkZWZpbmVzIHRoZSBzY2hlbWEgb2YgdGhlIGdSUEMvUkVTVCBtYXBwaW5nLiBUaGUgbWFwcGluZyBzcGVjaWZpZXMKIGhvdyBkaWZmZXJlbnQgcG9ydGlvbnMgb2YgdGhlIGdSUEMgcmVxdWVzdCBtZXNzYWdlIGFyZSBtYXBwZWQgdG8gdGhlIFVSTAogcGF0aCwgVVJMIHF1ZXJ5IHBhcmFtZXRlcnMsIGFuZCBIVFRQIHJlcXVlc3QgYm9keS4gSXQgYWxzbyBjb250cm9scyBob3cgdGhlCiBnUlBDIHJlc3BvbnNlIG1lc3NhZ2UgaXMgbWFwcGVkIHRvIHRoZSBIVFRQIHJlc3BvbnNlIGJvZHkuIGBIdHRwUnVsZWAgaXMKIHR5cGljYWxseSBzcGVjaWZpZWQgYXMgYW4gYGdvb2dsZS5hcGkuaHR0cGAgYW5ub3RhdGlvbiBvbiB0aGUgZ1JQQyBtZXRob2QuCgogRWFjaCBtYXBwaW5nIHNwZWNpZmllcyBhIFVSTCBwYXRoIHRlbXBsYXRlIGFuZCBhbiBIVFRQIG1ldGhvZC4gVGhlIHBhdGgKIHRlbXBsYXRlIG1heSByZWZlciB0byBvbmUgb3IgbW9yZSBmaWVsZHMgaW4gdGhlIGdSUEMgcmVxdWVzdCBtZXNzYWdlLCBhcyBsb25nCiBhcyBlYWNoIGZpZWxkIGlzIGEgbm9uLXJlcGVhdGVkIGZpZWxkIHdpdGggYSBwcmltaXRpdmUgKG5vbi1tZXNzYWdlKSB0eXBlLgogVGhlIHBhdGggdGVtcGxhdGUgY29udHJvbHMgaG93IGZpZWxkcyBvZiB0aGUgcmVxdWVzdCBtZXNzYWdlIGFyZSBtYXBwZWQgdG8KIHRoZSBVUkwgcGF0aC4KCiBFeGFtcGxlOgoKICAgICBzZXJ2aWNlIE1lc3NhZ2luZyB7CiAgICAgICBycGMgR2V0TWVzc2FnZShHZXRNZXNzYWdlUmVxdWVzdCkgcmV0dXJucyAoTWVzc2FnZSkgewogICAgICAgICBvcHRpb24gKGdvb2dsZS5hcGkuaHR0cCkgPSB7CiAgICAgICAgICAgICBnZXQ6ICIvdjEve25hbWU9bWVzc2FnZXMvKn0iCiAgICAgICAgIH07CiAgICAgICB9CiAgICAgfQogICAgIG1lc3NhZ2UgR2V0TWVzc2FnZVJlcXVlc3QgewogICAgICAgc3RyaW5nIG5hbWUgPSAxOyAvLyBNYXBwZWQgdG8gVVJMIHBhdGguCiAgICAgfQogICAgIG1lc3NhZ2UgTWVzc2FnZSB7CiAgICAgICBzdHJpbmcgdGV4dCA9IDE7IC8vIFRoZSByZXNvdXJjZSBjb250ZW50LgogICAgIH0KCiBUaGlzIGVuYWJsZXMgYW4gSFRUUCBSRVNUIHRvIGdSUEMgbWFwcGluZyBhcyBiZWxvdzoKCiBIVFRQIHwgZ1JQQwogLS0tLS18LS0tLS0KIGBHRVQgL3YxL21lc3NhZ2VzLzEyMzQ1NmAgIHwgYEdldE1lc3NhZ2UobmFtZTogIm1lc3NhZ2VzLzEyMzQ1NiIpYAoKIEFueSBmaWVsZHMgaW4gdGhlIHJlcXVlc3QgbWVzc2FnZSB3aGljaCBhcmUgbm90IGJvdW5kIGJ5IHRoZSBwYXRoIHRlbXBsYXRlCiBhdXRvbWF0aWNhbGx5IGJlY29tZSBIVFRQIHF1ZXJ5IHBhcmFtZXRlcnMgaWYgdGhlcmUgaXMgbm8gSFRUUCByZXF1ZXN0IGJvZHkuCiBGb3IgZXhhbXBsZToKCiAgICAgc2VydmljZSBNZXNzYWdpbmcgewogICAgICAgcnBjIEdldE1lc3NhZ2UoR2V0TWVzc2FnZVJlcXVlc3QpIHJldHVybnMgKE1lc3NhZ2UpIHsKICAgICAgICAgb3B0aW9uIChnb29nbGUuYXBpLmh0dHApID0gewogICAgICAgICAgICAgZ2V0OiIvdjEvbWVzc2FnZXMve21lc3NhZ2VfaWR9IgogICAgICAgICB9OwogICAgICAgfQogICAgIH0KICAgICBtZXNzYWdlIEdldE1lc3NhZ2VSZXF1ZXN0IHsKICAgICAgIG1lc3NhZ2UgU3ViTWVzc2FnZSB7CiAgICAgICAgIHN0cmluZyBzdWJmaWVsZCA9IDE7CiAgICAgICB9CiAgICAgICBzdHJpbmcgbWVzc2FnZV9pZCA9IDE7IC8vIE1hcHBlZCB0byBVUkwgcGF0aC4KICAgICAgIGludDY0IHJldmlzaW9uID0gMjsgICAgLy8gTWFwcGVkIHRvIFVSTCBxdWVyeSBwYXJhbWV0ZXIgYHJldmlzaW9uYC4KICAgICAgIFN1Yk1lc3NhZ2Ugc3ViID0gMzsgICAgLy8gTWFwcGVkIHRvIFVSTCBxdWVyeSBwYXJhbWV0ZXIgYHN1Yi5zdWJmaWVsZGAuCiAgICAgfQoKIFRoaXMgZW5hYmxlcyBhIEhUVFAgSlNPTiB0byBSUEMgbWFwcGluZyBhcyBiZWxvdzoKCiBIVFRQIHwgZ1JQQwogLS0tLS18LS0tLS0KIGBHRVQgL3YxL21lc3NhZ2VzLzEyMzQ1Nj9yZXZpc2lvbj0yJnN1Yi5zdWJmaWVsZD1mb29gIHwKIGBHZXRNZXNzYWdlKG1lc3NhZ2VfaWQ6ICIxMjM0NTYiIHJldmlzaW9uOiAyIHN1YjogU3ViTWVzc2FnZShzdWJmaWVsZDoKICJmb28iKSlgCgogTm90ZSB0aGF0IGZpZWxkcyB3aGljaCBhcmUgbWFwcGVkIHRvIFVSTCBxdWVyeSBwYXJhbWV0ZXJzIG11c3QgaGF2ZSBhCiBwcmltaXRpdmUgdHlwZSBvciBhIHJlcGVhdGVkIHByaW1pdGl2ZSB0eXBlIG9yIGEgbm9uLXJlcGVhdGVkIG1lc3NhZ2UgdHlwZS4KIEluIHRoZSBjYXNlIG9mIGEgcmVwZWF0ZWQgdHlwZSwgdGhlIHBhcmFtZXRlciBjYW4gYmUgcmVwZWF0ZWQgaW4gdGhlIFVSTAogYXMgYC4uLj9wYXJhbT1BJnBhcmFtPUJgLiBJbiB0aGUgY2FzZSBvZiBhIG1lc3NhZ2UgdHlwZSwgZWFjaCBmaWVsZCBvZiB0aGUKIG1lc3NhZ2UgaXMgbWFwcGVkIHRvIGEgc2VwYXJhdGUgcGFyYW1ldGVyLCBzdWNoIGFzCiBgLi4uP2Zvby5hPUEmZm9vLmI9QiZmb28uYz1DYC4KCiBGb3IgSFRUUCBtZXRob2RzIHRoYXQgYWxsb3cgYSByZXF1ZXN0IGJvZHksIHRoZSBgYm9keWAgZmllbGQKIHNwZWNpZmllcyB0aGUgbWFwcGluZy4gQ29uc2lkZXIgYSBSRVNUIHVwZGF0ZSBtZXRob2Qgb24gdGhlCiBtZXNzYWdlIHJlc291cmNlIGNvbGxlY3Rpb246CgogICAgIHNlcnZpY2UgTWVzc2FnaW5nIHsKICAgICAgIHJwYyBVcGRhdGVNZXNzYWdlKFVwZGF0ZU1lc3NhZ2VSZXF1ZXN0KSByZXR1cm5zIChNZXNzYWdlKSB7CiAgICAgICAgIG9wdGlvbiAoZ29vZ2xlLmFwaS5odHRwKSA9IHsKICAgICAgICAgICBwYXRjaDogIi92MS9tZXNzYWdlcy97bWVzc2FnZV9pZH0iCiAgICAgICAgICAgYm9keTogIm1lc3NhZ2UiCiAgICAgICAgIH07CiAgICAgICB9CiAgICAgfQogICAgIG1lc3NhZ2UgVXBkYXRlTWVzc2FnZVJlcXVlc3QgewogICAgICAgc3RyaW5nIG1lc3NhZ2VfaWQgPSAxOyAvLyBtYXBwZWQgdG8gdGhlIFVSTAogICAgICAgTWVzc2FnZSBtZXNzYWdlID0gMjsgICAvLyBtYXBwZWQgdG8gdGhlIGJvZHkKICAgICB9CgogVGhlIGZvbGxvd2luZyBIVFRQIEpTT04gdG8gUlBDIG1hcHBpbmcgaXMgZW5hYmxlZCwgd2hlcmUgdGhlCiByZXByZXNlbnRhdGlvbiBvZiB0aGUgSlNPTiBpbiB0aGUgcmVxdWVzdCBib2R5IGlzIGRldGVybWluZWQgYnkKIHByb3RvcyBKU09OIGVuY29kaW5nOgoKIEhUVFAgfCBnUlBDCiAtLS0tLXwtLS0tLQogYFBBVENIIC92MS9tZXNzYWdlcy8xMjM0NTYgeyAidGV4dCI6ICJIaSEiIH1gIHwgYFVwZGF0ZU1lc3NhZ2UobWVzc2FnZV9pZDoKICIxMjM0NTYiIG1lc3NhZ2UgeyB0ZXh0OiAiSGkhIiB9KWAKCiBUaGUgc3BlY2lhbCBuYW1lIGAqYCBjYW4gYmUgdXNlZCBpbiB0aGUgYm9keSBtYXBwaW5nIHRvIGRlZmluZSB0aGF0CiBldmVyeSBmaWVsZCBub3QgYm91bmQgYnkgdGhlIHBhdGggdGVtcGxhdGUgc2hvdWxkIGJlIG1hcHBlZCB0byB0aGUKIHJlcXVlc3QgYm9keS4gIFRoaXMgZW5hYmxlcyB0aGUgZm9sbG93aW5nIGFsdGVybmF0aXZlIGRlZmluaXRpb24gb2YKIHRoZSB1cGRhdGUgbWV0aG9kOgoKICAgICBzZXJ2aWNlIE1lc3NhZ2luZyB7CiAgICAgICBycGMgVXBkYXRlTWVzc2FnZShNZXNzYWdlKSByZXR1cm5zIChNZXNzYWdlKSB7CiAgICAgICAgIG9wdGlvbiAoZ29vZ2xlLmFwaS5odHRwKSA9IHsKICAgICAgICAgICBwYXRjaDogIi92MS9tZXNzYWdlcy97bWVzc2FnZV9pZH0iCiAgICAgICAgICAgYm9keTogIioiCiAgICAgICAgIH07CiAgICAgICB9CiAgICAgfQogICAgIG1lc3NhZ2UgTWVzc2FnZSB7CiAgICAgICBzdHJpbmcgbWVzc2FnZV9pZCA9IDE7CiAgICAgICBzdHJpbmcgdGV4dCA9IDI7CiAgICAgfQoKCiBUaGUgZm9sbG93aW5nIEhUVFAgSlNPTiB0byBSUEMgbWFwcGluZyBpcyBlbmFibGVkOgoKIEhUVFAgfCBnUlBDCiAtLS0tLXwtLS0tLQogYFBBVENIIC92MS9tZXNzYWdlcy8xMjM0NTYgeyAidGV4dCI6ICJIaSEiIH1gIHwgYFVwZGF0ZU1lc3NhZ2UobWVzc2FnZV9pZDoKICIxMjM0NTYiIHRleHQ6ICJIaSEiKWAKCiBOb3RlIHRoYXQgd2hlbiB1c2luZyBgKmAgaW4gdGhlIGJvZHkgbWFwcGluZywgaXQgaXMgbm90IHBvc3NpYmxlIHRvCiBoYXZlIEhUVFAgcGFyYW1ldGVycywgYXMgYWxsIGZpZWxkcyBub3QgYm91bmQgYnkgdGhlIHBhdGggZW5kIGluCiB0aGUgYm9keS4gVGhpcyBtYWtlcyB0aGlzIG9wdGlvbiBtb3JlIHJhcmVseSB1c2VkIGluIHByYWN0aWNlIHdoZW4KIGRlZmluaW5nIFJFU1QgQVBJcy4gVGhlIGNvbW1vbiB1c2FnZSBvZiBgKmAgaXMgaW4gY3VzdG9tIG1ldGhvZHMKIHdoaWNoIGRvbid0IHVzZSB0aGUgVVJMIGF0IGFsbCBmb3IgdHJhbnNmZXJyaW5nIGRhdGEuCgogSXQgaXMgcG9zc2libGUgdG8gZGVmaW5lIG11bHRpcGxlIEhUVFAgbWV0aG9kcyBmb3Igb25lIFJQQyBieSB1c2luZwogdGhlIGBhZGRpdGlvbmFsX2JpbmRpbmdzYCBvcHRpb24uIEV4YW1wbGU6CgogICAgIHNlcnZpY2UgTWVzc2FnaW5nIHsKICAgICAgIHJwYyBHZXRNZXNzYWdlKEdldE1lc3NhZ2VSZXF1ZXN0KSByZXR1cm5zIChNZXNzYWdlKSB7CiAgICAgICAgIG9wdGlvbiAoZ29vZ2xlLmFwaS5odHRwKSA9IHsKICAgICAgICAgICBnZXQ6ICIvdjEvbWVzc2FnZXMve21lc3NhZ2VfaWR9IgogICAgICAgICAgIGFkZGl0aW9uYWxfYmluZGluZ3MgewogICAgICAgICAgICAgZ2V0OiAiL3YxL3VzZXJzL3t1c2VyX2lkfS9tZXNzYWdlcy97bWVzc2FnZV9pZH0iCiAgICAgICAgICAgfQogICAgICAgICB9OwogICAgICAgfQogICAgIH0KICAgICBtZXNzYWdlIEdldE1lc3NhZ2VSZXF1ZXN0IHsKICAgICAgIHN0cmluZyBtZXNzYWdlX2lkID0gMTsKICAgICAgIHN0cmluZyB1c2VyX2lkID0gMjsKICAgICB9CgogVGhpcyBlbmFibGVzIHRoZSBmb2xsb3dpbmcgdHdvIGFsdGVybmF0aXZlIEhUVFAgSlNPTiB0byBSUEMgbWFwcGluZ3M6CgogSFRUUCB8IGdSUEMKIC0tLS0tfC0tLS0tCiBgR0VUIC92MS9tZXNzYWdlcy8xMjM0NTZgIHwgYEdldE1lc3NhZ2UobWVzc2FnZV9pZDogIjEyMzQ1NiIpYAogYEdFVCAvdjEvdXNlcnMvbWUvbWVzc2FnZXMvMTIzNDU2YCB8IGBHZXRNZXNzYWdlKHVzZXJfaWQ6ICJtZSIgbWVzc2FnZV9pZDoKICIxMjM0NTYiKWAKCiAjIyBSdWxlcyBmb3IgSFRUUCBtYXBwaW5nCgogMS4gTGVhZiByZXF1ZXN0IGZpZWxkcyAocmVjdXJzaXZlIGV4cGFuc2lvbiBuZXN0ZWQgbWVzc2FnZXMgaW4gdGhlIHJlcXVlc3QKICAgIG1lc3NhZ2UpIGFyZSBjbGFzc2lmaWVkIGludG8gdGhyZWUgY2F0ZWdvcmllczoKICAgIC0gRmllbGRzIHJlZmVycmVkIGJ5IHRoZSBwYXRoIHRlbXBsYXRlLiBUaGV5IGFyZSBwYXNzZWQgdmlhIHRoZSBVUkwgcGF0aC4KICAgIC0gRmllbGRzIHJlZmVycmVkIGJ5IHRoZSBbSHR0cFJ1bGUuYm9keV1bZ29vZ2xlLmFwaS5IdHRwUnVsZS5ib2R5XS4gVGhleQogICAgYXJlIHBhc3NlZCB2aWEgdGhlIEhUVFAKICAgICAgcmVxdWVzdCBib2R5LgogICAgLSBBbGwgb3RoZXIgZmllbGRzIGFyZSBwYXNzZWQgdmlhIHRoZSBVUkwgcXVlcnkgcGFyYW1ldGVycywgYW5kIHRoZQogICAgICBwYXJhbWV0ZXIgbmFtZSBpcyB0aGUgZmllbGQgcGF0aCBpbiB0aGUgcmVxdWVzdCBtZXNzYWdlLiBBIHJlcGVhdGVkCiAgICAgIGZpZWxkIGNhbiBiZSByZXByZXNlbnRlZCBhcyBtdWx0aXBsZSBxdWVyeSBwYXJhbWV0ZXJzIHVuZGVyIHRoZSBzYW1lCiAgICAgIG5hbWUuCiAgMi4gSWYgW0h0dHBSdWxlLmJvZHldW2dvb2dsZS5hcGkuSHR0cFJ1bGUuYm9keV0gaXMgIioiLCB0aGVyZSBpcyBubyBVUkwKICBxdWVyeSBwYXJhbWV0ZXIsIGFsbCBmaWVsZHMKICAgICBhcmUgcGFzc2VkIHZpYSBVUkwgcGF0aCBhbmQgSFRUUCByZXF1ZXN0IGJvZHkuCiAgMy4gSWYgW0h0dHBSdWxlLmJvZHldW2dvb2dsZS5hcGkuSHR0cFJ1bGUuYm9keV0gaXMgb21pdHRlZCwgdGhlcmUgaXMgbm8gSFRUUAogIHJlcXVlc3QgYm9keSwgYWxsCiAgICAgZmllbGRzIGFyZSBwYXNzZWQgdmlhIFVSTCBwYXRoIGFuZCBVUkwgcXVlcnkgcGFyYW1ldGVycy4KCiAjIyMgUGF0aCB0ZW1wbGF0ZSBzeW50YXgKCiAgICAgVGVtcGxhdGUgPSAiLyIgU2VnbWVudHMgWyBWZXJiIF0gOwogICAgIFNlZ21lbnRzID0gU2VnbWVudCB7ICIvIiBTZWdtZW50IH0gOwogICAgIFNlZ21lbnQgID0gIioiIHwgIioqIiB8IExJVEVSQUwgfCBWYXJpYWJsZSA7CiAgICAgVmFyaWFibGUgPSAieyIgRmllbGRQYXRoIFsgIj0iIFNlZ21lbnRzIF0gIn0iIDsKICAgICBGaWVsZFBhdGggPSBJREVOVCB7ICIuIiBJREVOVCB9IDsKICAgICBWZXJiICAgICA9ICI6IiBMSVRFUkFMIDsKCiBUaGUgc3ludGF4IGAqYCBtYXRjaGVzIGEgc2luZ2xlIFVSTCBwYXRoIHNlZ21lbnQuIFRoZSBzeW50YXggYCoqYCBtYXRjaGVzCiB6ZXJvIG9yIG1vcmUgVVJMIHBhdGggc2VnbWVudHMsIHdoaWNoIG11c3QgYmUgdGhlIGxhc3QgcGFydCBvZiB0aGUgVVJMIHBhdGgKIGV4Y2VwdCB0aGUgYFZlcmJgLgoKIFRoZSBzeW50YXggYFZhcmlhYmxlYCBtYXRjaGVzIHBhcnQgb2YgdGhlIFVSTCBwYXRoIGFzIHNwZWNpZmllZCBieSBpdHMKIHRlbXBsYXRlLiBBIHZhcmlhYmxlIHRlbXBsYXRlIG11c3Qgbm90IGNvbnRhaW4gb3RoZXIgdmFyaWFibGVzLiBJZiBhIHZhcmlhYmxlCiBtYXRjaGVzIGEgc2luZ2xlIHBhdGggc2VnbWVudCwgaXRzIHRlbXBsYXRlIG1heSBiZSBvbWl0dGVkLCBlLmcuIGB7dmFyfWAKIGlzIGVxdWl2YWxlbnQgdG8gYHt2YXI9Kn1gLgoKIFRoZSBzeW50YXggYExJVEVSQUxgIG1hdGNoZXMgbGl0ZXJhbCB0ZXh0IGluIHRoZSBVUkwgcGF0aC4gSWYgdGhlIGBMSVRFUkFMYAogY29udGFpbnMgYW55IHJlc2VydmVkIGNoYXJhY3Rlciwgc3VjaCBjaGFyYWN0ZXJzIHNob3VsZCBiZSBwZXJjZW50LWVuY29kZWQKIGJlZm9yZSB0aGUgbWF0Y2hpbmcuCgogSWYgYSB2YXJpYWJsZSBjb250YWlucyBleGFjdGx5IG9uZSBwYXRoIHNlZ21lbnQsIHN1Y2ggYXMgYCJ7dmFyfSJgIG9yCiBgInt2YXI9Kn0iYCwgd2hlbiBzdWNoIGEgdmFyaWFibGUgaXMgZXhwYW5kZWQgaW50byBhIFVSTCBwYXRoIG9uIHRoZSBjbGllbnQKIHNpZGUsIGFsbCBjaGFyYWN0ZXJzIGV4Y2VwdCBgWy1fLn4wLTlhLXpBLVpdYCBhcmUgcGVyY2VudC1lbmNvZGVkLiBUaGUKIHNlcnZlciBzaWRlIGRvZXMgdGhlIHJldmVyc2UgZGVjb2RpbmcuIFN1Y2ggdmFyaWFibGVzIHNob3cgdXAgaW4gdGhlCiBbRGlzY292ZXJ5CiBEb2N1bWVudF0oaHR0cHM6Ly9kZXZlbG9wZXJzLmdvb2dsZS5jb20vZGlzY292ZXJ5L3YxL3JlZmVyZW5jZS9hcGlzKSBhcwogYHt2YXJ9YC4KCiBJZiBhIHZhcmlhYmxlIGNvbnRhaW5zIG11bHRpcGxlIHBhdGggc2VnbWVudHMsIHN1Y2ggYXMgYCJ7dmFyPWZvby8qfSJgCiBvciBgInt2YXI9Kip9ImAsIHdoZW4gc3VjaCBhIHZhcmlhYmxlIGlzIGV4cGFuZGVkIGludG8gYSBVUkwgcGF0aCBvbiB0aGUKIGNsaWVudCBzaWRlLCBhbGwgY2hhcmFjdGVycyBleGNlcHQgYFstXy5+LzAtOWEtekEtWl1gIGFyZSBwZXJjZW50LWVuY29kZWQuCiBUaGUgc2VydmVyIHNpZGUgZG9lcyB0aGUgcmV2ZXJzZSBkZWNvZGluZywgZXhjZXB0ICIlMkYiIGFuZCAiJTJmIiBhcmUgbGVmdAogdW5jaGFuZ2VkLiBTdWNoIHZhcmlhYmxlcyBzaG93IHVwIGluIHRoZQogW0Rpc2NvdmVyeQogRG9jdW1lbnRdKGh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL2Rpc2NvdmVyeS92MS9yZWZlcmVuY2UvYXBpcykgYXMKIGB7K3Zhcn1gLgoKICMjIFVzaW5nIGdSUEMgQVBJIFNlcnZpY2UgQ29uZmlndXJhdGlvbgoKIGdSUEMgQVBJIFNlcnZpY2UgQ29uZmlndXJhdGlvbiAoc2VydmljZSBjb25maWcpIGlzIGEgY29uZmlndXJhdGlvbiBsYW5ndWFnZQogZm9yIGNvbmZpZ3VyaW5nIGEgZ1JQQyBzZXJ2aWNlIHRvIGJlY29tZSBhIHVzZXItZmFjaW5nIHByb2R1Y3QuIFRoZQogc2VydmljZSBjb25maWcgaXMgc2ltcGx5IHRoZSBZQU1MIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBgZ29vZ2xlLmFwaS5TZXJ2aWNlYAogcHJvdG8gbWVzc2FnZS4KCiBBcyBhbiBhbHRlcm5hdGl2ZSB0byBhbm5vdGF0aW5nIHlvdXIgcHJvdG8gZmlsZSwgeW91IGNhbiBjb25maWd1cmUgZ1JQQwogdHJhbnNjb2RpbmcgaW4geW91ciBzZXJ2aWNlIGNvbmZpZyBZQU1MIGZpbGVzLiBZb3UgZG8gdGhpcyBieSBzcGVjaWZ5aW5nIGEKIGBIdHRwUnVsZWAgdGhhdCBtYXBzIHRoZSBnUlBDIG1ldGhvZCB0byBhIFJFU1QgZW5kcG9pbnQsIGFjaGlldmluZyB0aGUgc2FtZQogZWZmZWN0IGFzIHRoZSBwcm90byBhbm5vdGF0aW9uLiBUaGlzIGNhbiBiZSBwYXJ0aWN1bGFybHkgdXNlZnVsIGlmIHlvdQogaGF2ZSBhIHByb3RvIHRoYXQgaXMgcmV1c2VkIGluIG11bHRpcGxlIHNlcnZpY2VzLiBOb3RlIHRoYXQgYW55IHRyYW5zY29kaW5nCiBzcGVjaWZpZWQgaW4gdGhlIHNlcnZpY2UgY29uZmlnIHdpbGwgb3ZlcnJpZGUgYW55IG1hdGNoaW5nIHRyYW5zY29kaW5nCiBjb25maWd1cmF0aW9uIGluIHRoZSBwcm90by4KCiBFeGFtcGxlOgoKICAgICBodHRwOgogICAgICAgcnVsZXM6CiAgICAgICAgICMgU2VsZWN0cyBhIGdSUEMgbWV0aG9kIGFuZCBhcHBsaWVzIEh0dHBSdWxlIHRvIGl0LgogICAgICAgICAtIHNlbGVjdG9yOiBleGFtcGxlLnYxLk1lc3NhZ2luZy5HZXRNZXNzYWdlCiAgICAgICAgICAgZ2V0OiAvdjEvbWVzc2FnZXMve21lc3NhZ2VfaWR9L3tzdWIuc3ViZmllbGR9CgogIyMgU3BlY2lhbCBub3RlcwoKIFdoZW4gZ1JQQyBUcmFuc2NvZGluZyBpcyB1c2VkIHRvIG1hcCBhIGdSUEMgdG8gSlNPTiBSRVNUIGVuZHBvaW50cywgdGhlCiBwcm90byB0byBKU09OIGNvbnZlcnNpb24gbXVzdCBmb2xsb3cgdGhlIFtwcm90bzMKIHNwZWNpZmljYXRpb25dKGh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL3Byb3RvY29sLWJ1ZmZlcnMvZG9jcy9wcm90bzMjanNvbikuCgogV2hpbGUgdGhlIHNpbmdsZSBzZWdtZW50IHZhcmlhYmxlIGZvbGxvd3MgdGhlIHNlbWFudGljcyBvZgogW1JGQyA2NTcwXShodHRwczovL3Rvb2xzLmlldGYub3JnL2h0bWwvcmZjNjU3MCkgU2VjdGlvbiAzLjIuMiBTaW1wbGUgU3RyaW5nCiBFeHBhbnNpb24sIHRoZSBtdWx0aSBzZWdtZW50IHZhcmlhYmxlICoqZG9lcyBub3QqKiBmb2xsb3cgUkZDIDY1NzAgU2VjdGlvbgogMy4yLjMgUmVzZXJ2ZWQgRXhwYW5zaW9uLiBUaGUgcmVhc29uIGlzIHRoYXQgdGhlIFJlc2VydmVkIEV4cGFuc2lvbgogZG9lcyBub3QgZXhwYW5kIHNwZWNpYWwgY2hhcmFjdGVycyBsaWtlIGA/YCBhbmQgYCNgLCB3aGljaCB3b3VsZCBsZWFkCiB0byBpbnZhbGlkIFVSTHMuIEFzIHRoZSByZXN1bHQsIGdSUEMgVHJhbnNjb2RpbmcgdXNlcyBhIGN1c3RvbSBlbmNvZGluZwogZm9yIG11bHRpIHNlZ21lbnQgdmFyaWFibGVzLgoKIFRoZSBwYXRoIHZhcmlhYmxlcyAqKm11c3Qgbm90KiogcmVmZXIgdG8gYW55IHJlcGVhdGVkIG9yIG1hcHBlZCBmaWVsZCwKIGJlY2F1c2UgY2xpZW50IGxpYnJhcmllcyBhcmUgbm90IGNhcGFibGUgb2YgaGFuZGxpbmcgc3VjaCB2YXJpYWJsZSBleHBhbnNpb24uCgogVGhlIHBhdGggdmFyaWFibGVzICoqbXVzdCBub3QqKiBjYXB0dXJlIHRoZSBsZWFkaW5nICIvIiBjaGFyYWN0ZXIuIFRoZSByZWFzb24KIGlzIHRoYXQgdGhlIG1vc3QgY29tbW9uIHVzZSBjYXNlICJ7dmFyfSIgZG9lcyBub3QgY2FwdHVyZSB0aGUgbGVhZGluZyAiLyIKIGNoYXJhY3Rlci4gRm9yIGNvbnNpc3RlbmN5LCBhbGwgcGF0aCB2YXJpYWJsZXMgbXVzdCBzaGFyZSB0aGUgc2FtZSBiZWhhdmlvci4KCiBSZXBlYXRlZCBtZXNzYWdlIGZpZWxkcyBtdXN0IG5vdCBiZSBtYXBwZWQgdG8gVVJMIHF1ZXJ5IHBhcmFtZXRlcnMsIGJlY2F1c2UKIG5vIGNsaWVudCBsaWJyYXJ5IGNhbiBzdXBwb3J0IHN1Y2ggY29tcGxpY2F0ZWQgbWFwcGluZy4KCiBJZiBhbiBBUEkgbmVlZHMgdG8gdXNlIGEgSlNPTiBhcnJheSBmb3IgcmVxdWVzdCBvciByZXNwb25zZSBib2R5LCBpdCBjYW4gbWFwCiB0aGUgcmVxdWVzdCBvciByZXNwb25zZSBib2R5IHRvIGEgcmVwZWF0ZWQgZmllbGQuIEhvd2V2ZXIsIHNvbWUgZ1JQQwogVHJhbnNjb2RpbmcgaW1wbGVtZW50YXRpb25zIG1heSBub3Qgc3VwcG9ydCB0aGlzIGZlYXR1cmUuCgoLCgMEAQESBLsCCBAKjwEKBAQBAgASBMACAhYagAEgU2VsZWN0cyBhIG1ldGhvZCB0byB3aGljaCB0aGlzIHJ1bGUgYXBwbGllcy4KCiBSZWZlciB0byBbc2VsZWN0b3JdW2dvb2dsZS5hcGkuRG9jdW1lbnRhdGlvblJ1bGUuc2VsZWN0b3JdIGZvciBzeW50YXgKIGRldGFpbHMuCgoNCgUEAQIABRIEwAICCAoNCgUEAQIAARIEwAIJEQoNCgUEAQIAAxIEwAIUFQrQAQoEBAEIABIGxQIC2wIDGr8BIERldGVybWluZXMgdGhlIFVSTCBwYXR0ZXJuIGlzIG1hdGNoZWQgYnkgdGhpcyBydWxlcy4gVGhpcyBwYXR0ZXJuIGNhbiBiZQogdXNlZCB3aXRoIGFueSBvZiB0aGUge2dldHxwdXR8cG9zdHxkZWxldGV8cGF0Y2h9IG1ldGhvZHMuIEEgY3VzdG9tIG1ldGhvZAogY2FuIGJlIGRlZmluZWQgdXNpbmcgdGhlICdjdXN0b20nIGZpZWxkLgoKDQoFBAEIAAESBMUCCA8KXAoEBAECARIEyAIEExpOIE1hcHMgdG8gSFRUUCBHRVQuIFVzZWQgZm9yIGxpc3RpbmcgYW5kIGdldHRpbmcgaW5mb3JtYXRpb24gYWJvdXQKIHJlc291cmNlcy4KCg0KBQQBAgEFEgTIAgQKCg0KBQQBAgEBEgTIAgsOCg0KBQQBAgEDEgTIAhESCkAKBAQBAgISBMsCBBMaMiBNYXBzIHRvIEhUVFAgUFVULiBVc2VkIGZvciByZXBsYWNpbmcgYSByZXNvdXJjZS4KCg0KBQQBAgIFEgTLAgQK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IHZlcmIuCgoNCgUEAgIABRIE9gICCAoNCgUEAgIAARIE9gIJDQoNCgUEAgIAAxIE9gIQEQo1CgQEAgIBEgT5AgISGicgVGhlIHBhdGggbWF0Y2hlZCBieSB0aGlzIGN1c3RvbSB2ZXJiLgoKDQoFBAICAQUSBPkCAggKDQoFBAICAQESBPkCCQ0KDQoFBAICAQMSBPkCEBFiBnByb3RvMwriwgMKIGdvb2dsZS9wcm90b2J1Zi9kZXNjcmlwdG9yLnByb3RvEg9nb29nbGUucHJvdG9idWYiTQoRRmlsZURlc2NyaXB0b3JTZXQSOAoEZmlsZRgBIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5GaWxlRGVzY3JpcHRvclByb3RvUgRmaWxlIv4EChNGaWxlRGVzY3JpcHRvclByb3RvEhIKBG5hbWUYASABKAlSBG5hbWUSGAoHcGFja2FnZRgCIAEoCVIHcGFja2FnZRIeCgpkZXBlbmRlbmN5GAMgAygJUgpkZXBlbmRlbmN5EisKEXB1YmxpY19kZXBlbmRlbmN5GAogAygFUhBwdWJsaWNEZXBlbmRlbmN5EicKD3dlYWtfZGVwZW5kZW5jeRgLIAMoBVIOd2Vha0RlcGVuZGVuY3kSQwoMbWVzc2FnZV90eXBlGAQgAygLMiAuZ29vZ2xlLnByb3RvYnVmLkRlc2NyaXB0b3JQcm90b1ILbWVzc2FnZVR5cGUSQQoJZW51bV90eXBlGAUgAygLMiQuZ29vZ2xlLnByb3RvYnVmLkVudW1EZXNjcmlwdG9yUHJvdG9SCGVudW1UeXBlEkEKB3NlcnZpY2UYBiADKAsyJy5nb29nbGUucHJvdG9idWYuU2VydmljZURlc2NyaXB0b3JQcm90b1IHc2VydmljZRJDCglleHRlbnNpb24YByADKAsyJS5nb29nbGUucHJvdG9idWYuRmllbGREZXNjcmlwdG9yUHJvdG9SCWV4dGVuc2lvbhI2CgdvcHRpb25zGAggASgLMhwuZ29vZ2xlLnByb3RvYnVmLkZpbGVPcHRpb25zUgdvcHRpb25zEkkKEHNvdXJjZV9jb2RlX2luZm8YCSABKAsyHy5nb29nbGUucHJvdG9idWYuU291cmNlQ29kZUluZm9SDnNvdXJjZUNvZGVJbmZvEhYKBnN5bnRheBgMIAEoCVIGc3ludGF4EhgKB2VkaXRpb24YDSABKAlSB2VkaXRpb24iuQYKD0Rlc2NyaXB0b3JQcm90bxISCgRuYW1lGAEgASgJUgRuYW1lEjsKBWZpZWxkGAIgAygLMiUuZ29vZ2xlLnByb3RvYnVmLkZpZWxkRGVzY3JpcHRvclByb3RvUgVmaWVsZBJDCglleHRlbnNpb24YBiADKAsyJS5nb29nbGUucHJvdG9idWYuRmllbGREZXNjcmlwdG9yUHJvdG9SCWV4dGVuc2lvbhJBCgtuZXN0ZWRfdHlwZRgDIAMoCzIgLmdvb2dsZS5wcm90b2J1Zi5EZXNjcmlwdG9yUHJvdG9SCm5lc3RlZFR5cGUSQQoJZW51bV90eXBlGAQgAygLMiQuZ29vZ2xlLnByb3RvYnVmLkVudW1EZXNjcmlwdG9yUHJvdG9SCGVudW1UeXBlElgKD2V4dGVuc2lvbl9yYW5nZRgFIAMoCzIvLmdvb2dsZS5wcm90b2J1Zi5EZXNjcmlwdG9yUHJvdG8uRXh0ZW5zaW9uUmFuZ2VSDmV4dGVuc2lvblJhbmdlEkQKCm9uZW9mX2RlY2wYCCADKAsyJS5nb29nbGUucHJvdG9idWYuT25lb2ZEZXNjcmlwdG9yUHJvdG9SCW9uZW9mRGVjbBI5CgdvcHRpb25zGAcgASgLMh8uZ29vZ2xlLnByb3RvYnVmLk1lc3NhZ2VPcHRpb25zUgdvcHRpb25zElUKDnJlc2VydmVkX3JhbmdlGAkgAygLMi4uZ29vZ2xlLnByb3RvYnVmLkRlc2NyaXB0b3JQcm90by5SZXNlcnZlZFJhbmdlUg1yZXNlcnZlZFJhbmdlEiMKDXJlc2VydmVkX25hbWUYCiADKAlSDHJlc2VydmVkTmFtZRp6Cg5FeHRlbnNpb25SYW5nZRIUCgVzdGFydBgBIAEoBVIFc3RhcnQSEAoDZW5kGAIgASgFUgNlbmQSQAoHb3B0aW9ucxgDIAEoCzImLmdvb2dsZS5wcm90b2J1Zi5FeHRlbnNpb25SYW5nZU9wdGlvbnNSB29wdGlvbnMaNwoNUmVzZXJ2ZWRSYW5nZRIUCgVzdGFydBgBIAEoBVIFc3RhcnQSEAoDZW5kGAIgASgFUgNlbmQirQQKFUV4dGVuc2lvblJhbmdlT3B0aW9ucxJYChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvblITdW5pbnRlcnByZXRlZE9wdGlvbhJZCgtkZWNsYXJhdGlvbhgCIAMoCzIyLmdvb2dsZS5wcm90b2J1Zi5FeHRlbnNpb25SYW5nZU9wdGlvbnMuRGVjbGFyYXRpb25CA4gBAlILZGVjbGFyYXRpb24SaAoMdmVyaWZpY2F0aW9uGAMgASgOMjguZ29vZ2xlLnByb3RvYnVmLkV4dGVuc2lvblJhbmdlT3B0aW9ucy5WZXJpZmljYXRpb25TdGF0ZToKVU5WRVJJRklFRFIMdmVyaWZpY2F0aW9uGrMBCgtEZWNsYXJhdGlvbhIWCgZudW1iZXIYASABKAVSBm51bWJlchIbCglmdWxsX25hbWUYAiABKAlSCGZ1bGxOYW1lEhIKBHR5cGUYAyABKAlSBHR5cGUSIwoLaXNfcmVwZWF0ZWQYBCABKAhCAhgBUgppc1JlcGVhdGVkEhoKCHJlc2VydmVkGAUgASgIUghyZXNlcnZlZBIaCghyZXBlYXRlZBgGIAEoCFIIcmVwZWF0ZWQiNAoRVmVyaWZpY2F0aW9uU3RhdGUSDwoLREVDTEFSQVRJT04QABIOCgpVTlZFUklGSUVEEAEqCQjoBxCAgICAAiLBBgoURmllbGREZXNjcmlwdG9yUHJvdG8SEgoEbmFtZRgBIAEoCVIEbmFtZRIWCgZudW1iZXIYAyABKAVSBm51bWJlchJBCgVsYWJlbBgEIAEoDjIrLmdvb2dsZS5wcm90b2J1Zi5GaWVsZERlc2NyaXB0b3JQcm90by5MYWJlbFIFbGFiZWwSPgoEdHlwZRgFIAEoDjIqLmdvb2dsZS5wcm90b2J1Zi5GaWVsZERlc2NyaXB0b3JQcm90by5UeXBlUgR0eXBlEhsKCXR5cGVfbmFtZRgGIAEoCVIIdHlwZU5hbWUSGgoIZXh0ZW5kZWUYAiABKAlSCGV4dGVuZGVlEiMKDWRlZmF1bHRfdmFsdWUYByABKAlSDGRlZmF1bHRWYWx1ZRIfCgtvbmVvZl9pbmRleBgJIAEoBVIKb25lb2ZJbmRleBIbCglqc29uX25hbWUYCiABKAlSCGpzb25OYW1lEjcKB29wdGlvbnMYCCABKAsyHS5nb29nbGUucHJvdG9idWYuRmllbGRPcHRpb25zUgdvcHRpb25zEicKD3Byb3RvM19vcHRpb25hbBgRIAEoCFIOcHJvdG8zT3B0aW9uYWwitgIKBFR5cGUSDwoLVFlQRV9ET1VCTEUQARIOCgpUWVBFX0ZMT0FUEAISDgoKVFlQRV9JTlQ2NBADEg8KC1RZUEVfVUlOVDY0EAQSDgoKVFlQRV9JTlQzMhAFEhAKDFRZUEVfRklYRUQ2NBAGEhAKDFRZUEVfRklYRUQzMhAHEg0KCVRZUEVfQk9PTBAIEg8KC1RZUEVfU1RSSU5HEAkSDgoKVFlQRV9HUk9VUBAKEhAKDFRZUEVfTUVTU0FHRRALEg4KClRZUEVfQllURVMQDBIPCgtUWVBFX1VJTlQzMhANEg0KCVRZUEVfRU5VTRAOEhEKDVRZUEVfU0ZJWEVEMzIQDxIRCg1UWVBFX1NGSVhFRDY0EBASDwoLVFlQRV9TSU5UMzIQERIPCgtUWVBFX1NJTlQ2NBASIkMKBUxhYmVsEhIKDkxBQkVMX09QVElPTkFMEAESEgoOTEFCRUxfUkVRVUlSRUQQAhISCg5MQUJFTF9SRVBFQVRFRBADImMKFE9uZW9mRGVzY3JpcHRvclByb3RvEhIKBG5hbWUYASABKAlSBG5hbWUSNwoHb3B0aW9ucxgCIAEoCzIdLmdvb2dsZS5wcm90b2J1Zi5PbmVvZk9wdGlvbnNSB29wdGlvbnMi4wIKE0VudW1EZXNjcmlwdG9yUHJvdG8SEgoEbmFtZRgBIAEoCVIEbmFtZRI/CgV2YWx1ZRgCIAMoCzIpLmdvb2dsZS5wcm90b2J1Zi5FbnVtVmFsdWVEZXNjcmlwdG9yUHJvdG9SBXZhbHVlEjYKB29wdGlvbnMYAyABKAsyHC5nb29nbGUucHJvdG9idWYuRW51bU9wdGlvbnNSB29wdGlvbnMSXQoOcmVzZXJ2ZWRfcmFuZ2UYBCADKAsyNi5nb29nbGUucHJvdG9idWYuRW51bURlc2NyaXB0b3JQcm90by5FbnVtUmVzZXJ2ZWRSYW5nZVINcmVzZXJ2ZWRSYW5nZRIjCg1yZXNlcnZlZF9uYW1lGAUgAygJUgxyZXNlcnZlZE5hbWUaOwoRRW51bVJlc2VydmVkUmFuZ2USFAoFc3RhcnQYASABKAVSBXN0YXJ0EhAKA2VuZBgCIAEoBVIDZW5kIoMBChhFbnVtVmFsdWVEZXNjcmlwdG9yUHJvdG8SEgoEbmFtZRgBIAEoCVIEbmFtZRIWCgZudW1iZXIYAiABKAVSBm51bWJlchI7CgdvcHRpb25zGAMgASgLMiEuZ29vZ2xlLnByb3RvYnVmLkVudW1WYWx1ZU9wdGlvbnNSB29wdGlvbnMipwEKFlNlcnZpY2VEZXNjcmlwdG9yUHJvdG8SEgoEbmFtZRgBIAEoCVIEbmFtZRI+CgZtZXRob2QYAiADKAsyJi5nb29nbGUucHJvdG9idWYuTWV0aG9kRGVzY3JpcHRvclByb3RvUgZtZXRob2QSOQoHb3B0aW9ucxgDIAEoCzIfLmdvb2dsZS5wcm90b2J1Zi5TZXJ2aWNlT3B0aW9uc1IHb3B0aW9ucyKJAgoVTWV0aG9kRGVzY3JpcHRvclByb3RvEhIKBG5hbWUYASABKAlSBG5hbWUSHQoKaW5wdXRfdHlwZRgCIAEoCVIJaW5wdXRUeXBlEh8KC291dHB1dF90eXBlGAMgASgJUgpvdXRwdXRUeXBlEjgKB29wdGlvbnMYBCABKAsyHi5nb29nbGUucHJvdG9idWYuTWV0aG9kT3B0aW9uc1IHb3B0aW9ucxIwChBjbGllbnRfc3RyZWFtaW5nGAUgASgIOgVmYWxzZVIPY2xpZW50U3RyZWFtaW5nEjAKEHNlcnZlcl9zdHJlYW1pbmcYBiABKAg6BWZhbHNlUg9zZXJ2ZXJTdHJlYW1pbmcikQkKC0ZpbGVPcHRpb25zEiEKDGphdmFfcGFja2FnZRgBIAEoCVILamF2YVBhY2thZ2USMAoUamF2YV9vdXRlcl9jbGFzc25hbWUYCCABKAlSEmphdmFPdXRlckNsYXNzbmFtZRI1ChNqYXZhX211bHRpcGxlX2ZpbGVzGAogASgIOgVmYWxzZVIRamF2YU11bHRpcGxlRmlsZXMSRAodamF2YV9nZW5lcmF0ZV9lcXVhbHNfYW5kX2hhc2gYFCABKAhCAhgBUhlqYXZhR2VuZXJhdGVFcXVhbHNBbmRIYXNoEjoKFmphdmFfc3RyaW5nX2NoZWNrX3V0ZjgYGyABKAg6BWZhbHNlUhNqYXZhU3RyaW5nQ2hlY2tVdGY4ElMKDG9wdGltaXplX2ZvchgJIAEoDjIpLmdvb2dsZS5wcm90b2J1Zi5GaWxlT3B0aW9ucy5PcHRpbWl6ZU1vZGU6BVNQRUVEUgtvcHRpbWl6ZUZvchIdCgpnb19wYWNrYWdlGAsgASgJUglnb1BhY2thZ2USNQoTY2NfZ2VuZXJpY19zZXJ2aWNlcxgQIAEoCDoFZmFsc2VSEWNjR2VuZXJpY1NlcnZpY2VzEjkKFWphdmFfZ2VuZXJpY19zZXJ2aWNlcxgRIAEoCDoFZmFsc2VSE2phdmFHZW5lcmljU2VydmljZXMSNQoTcHlfZ2VuZXJpY19zZXJ2aWNlcxgSIAEoCDoFZmFsc2VSEXB5R2VuZXJpY1NlcnZpY2VzEjcKFHBocF9nZW5lcmljX3NlcnZpY2VzGCogASgIOgVmYWxzZVIScGhwR2VuZXJpY1NlcnZpY2VzEiUKCmRlcHJlY2F0ZWQYFyABKAg6BWZhbHNlUgpkZXByZWNhdGVkEi4KEGNjX2VuYWJsZV9hcmVuYXMYHyABKAg6BHRydWVSDmNjRW5hYmxlQXJlbmFzEioKEW9iamNfY2xhc3NfcHJlZml4GCQgASgJUg9vYmpjQ2xhc3NQcmVmaXgSKQoQY3NoYXJwX25hbWVzcGFjZRglIAEoCVIPY3NoYXJwTmFtZXNwYWNlEiEKDHN3aWZ0X3ByZWZpeBgnIAEoCVILc3dpZnRQcmVmaXgSKAoQcGhwX2NsYXNzX3ByZWZpeBgoIAEoCVIOcGhwQ2xhc3NQcmVmaXgSIwoNcGhwX25hbWVzcGFjZRgpIAEoCVIMcGhwTmFtZXNwYWNlEjQKFnBocF9tZXRhZGF0YV9uYW1lc3BhY2UYLCABKAlSFHBocE1ldGFkYXRhTmFtZXNwYWNlEiEKDHJ1YnlfcGFja2FnZRgtIAEoCVILcnVieVBhY2thZ2USWAoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb25SE3VuaW50ZXJwcmV0ZWRPcHRpb24iOgoMT3B0aW1pemVNb2RlEgkKBVNQRUVEEAESDQoJQ09ERV9TSVpFEAISEAoMTElURV9SVU5USU1FEAMqCQjoBxCAgICAAkoECCYQJyK7AwoOTWVzc2FnZU9wdGlvbnMSPAoXbWVzc2FnZV9zZXRfd2lyZV9mb3JtYXQYASABKAg6BWZhbHNlUhRtZXNzYWdlU2V0V2lyZUZvcm1hdBJMCh9ub19zdGFuZGFyZF9kZXNjcmlwdG9yX2FjY2Vzc29yGAIgASgIOgVmYWxzZVIcbm9TdGFuZGFyZERlc2NyaXB0b3JBY2Nlc3NvchIlCgpkZXByZWNhdGVkGAMgASgIOgVmYWxzZVIKZGVwcmVjYXRlZBIbCgltYXBfZW50cnkYByABKAhSCG1hcEVudHJ5ElYKJmRlcHJlY2F0ZWRfbGVnYWN5X2pzb25fZmllbGRfY29uZmxpY3RzGAsgASgIQgIYAVIiZGVwcmVjYXRlZExlZ2FjeUpzb25GaWVsZENvbmZsaWN0cxJYChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvblITdW5pbnRlcnByZXRlZE9wdGlvbioJCOgHEICAgIACSgQIBBAFSgQIBRAGSgQIBhAHSgQICBAJSgQICRAKIoUJCgxGaWVsZE9wdGlvbnMSQQoFY3R5cGUYASABKA4yIy5nb29nbGUucHJvdG9idWYuRmllbGRPcHRpb25zLkNUeXBlOgZTVFJJTkdSBWN0eXBlEhYKBnBhY2tlZBgCIAEoCFIGcGFja2VkEkcKBmpzdHlwZRgGIAEoDjIkLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnMuSlNUeXBlOglKU19OT1JNQUxSBmpzdHlwZRIZCgRsYXp5GAUgASgIOgVmYWxzZVIEbGF6eRIuCg91bnZlcmlmaWVkX2xhenkYDyABKAg6BWZhbHNlUg51bnZlcmlmaWVkTGF6eRIlCgpkZXByZWNhdGVkGAMgASgIOgVmYWxzZVIKZGVwcmVjYXRlZBIZCgR3ZWFrGAogASgIOgVmYWxzZVIEd2VhaxIoCgxkZWJ1Z19yZWRhY3QYECABKAg6BWZhbHNlUgtkZWJ1Z1JlZGFjdBJLCglyZXRlbnRpb24YESABKA4yLS5nb29nbGUucHJvdG9idWYuRmllbGRPcHRpb25zLk9wdGlvblJldGVudGlvblIJcmV0ZW50aW9uEkoKBnRhcmdldBgSIAEoDjIuLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnMuT3B0aW9uVGFyZ2V0VHlwZUICGAFSBnRhcmdldBJICgd0YXJnZXRzGBMgAygOMi4uZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucy5PcHRpb25UYXJnZXRUeXBlUgd0YXJnZXRzElgKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uUhN1bmludGVycHJldGVkT3B0aW9uIi8KBUNUeXBlEgoKBlNUUklORxAAEggKBENPUkQQARIQCgxTVFJJTkdfUElFQ0UQAiI1CgZKU1R5cGUSDQoJSlNfTk9STUFMEAASDQoJSlNfU1RSSU5HEAESDQoJSlNfTlVNQkVSEAIiVQoPT3B0aW9uUmV0ZW50aW9uEhUKEVJFVEVOVElPTl9VTktOT1dOEAASFQoRUkVURU5USU9OX1JVTlRJTUUQARIUChBSRVRFTlRJT05fU09VUkNFEAIijAIKEE9wdGlvblRhcmdldFR5cGUSFwoTVEFSR0VUX1RZUEVfVU5LTk9XThAAEhQKEFRBUkdFVF9UWVBFX0ZJTEUQARIfChtUQVJHRVRfVFlQRV9FWFRFTlNJT05fUkFOR0UQAhIXChNUQVJHRVRfVFlQRV9NRVNTQUdFEAMSFQoRVEFSR0VUX1RZUEVfRklFTEQQBBIVChFUQVJHRVRfVFlQRV9PTkVPRhAFEhQKEFRBUkdFVF9UWVBFX0VOVU0QBhIaChZUQVJHRVRfVFlQRV9FTlVNX0VOVFJZEAcSFwoTVEFSR0VUX1RZUEVfU0VSVklDRRAIEhYKElRBUkdFVF9UWVBFX01FVEhPRBAJKgkI6AcQgICAgAJKBAgEEAUicwoMT25lb2ZPcHRpb25zElgKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uUhN1bmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIimAIKC0VudW1PcHRpb25zEh8KC2FsbG93X2FsaWFzGAIgASgIUgphbGxvd0FsaWFzEiUKCmRlcHJlY2F0ZWQYAyABKAg6BWZhbHNlUgpkZXByZWNhdGVkElYKJmRlcHJlY2F0ZWRfbGVnYWN5X2pzb25fZmllbGRfY29uZmxpY3RzGAYgASgIQgIYAVIiZGVwcmVjYXRlZExlZ2FjeUpzb25GaWVsZENvbmZsaWN0cxJYChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvblITdW5pbnRlcnByZXRlZE9wdGlvbioJCOgHEICAgIACSgQIBRAGIp4BChBFbnVtVmFsdWVPcHRpb25zEiUKCmRlcHJlY2F0ZWQYASABKAg6BWZhbHNlUgpkZXByZWNhdGVkElgKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uUhN1bmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIinAEKDlNlcnZpY2VPcHRpb25zEiUKCmRlcHJlY2F0ZWQYISABKAg6BWZhbHNlUgpkZXByZWNhdGVkElgKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uUhN1bmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIi4AIKDU1ldGhvZE9wdGlvbnMSJQoKZGVwcmVjYXRlZBghIAEoCDoFZmFsc2VSCmRlcHJlY2F0ZWQScQoRaWRlbXBvdGVuY3lfbGV2ZWwYIiABKA4yLy5nb29nbGUucHJvdG9idWYuTWV0aG9kT3B0aW9ucy5JZGVtcG90ZW5jeUxldmVsOhNJREVNUE9URU5DWV9VTktOT1dOUhBpZGVtcG90ZW5jeUxldmVsElgKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uUhN1bmludGVycHJldGVkT3B0aW9uIlAKEElkZW1wb3RlbmN5TGV2ZWwSFwoTSURFTVBPVEVOQ1lfVU5LTk9XThAAEhMKD05PX1NJREVfRUZGRUNUUxABEg4KCklERU1QT1RFTlQQAioJCOgHEICAgIACIpoDChNVbmludGVycHJldGVkT3B0aW9uEkEKBG5hbWUYAiADKAsyLS5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbi5OYW1lUGFydFIEbmFtZRIpChBpZGVudGlmaWVyX3ZhbHVlGAMgASgJUg9pZGVudGlmaWVyVmFsdWUSLAoScG9zaXRpdmVfaW50X3ZhbHVlGAQgASgEUhBwb3NpdGl2ZUludFZhbHVlEiwKEm5lZ2F0aXZlX2ludF92YWx1ZRgFIAEoA1IQbmVnYXRpdmVJbnRWYWx1ZRIhCgxkb3VibGVfdmFsdWUYBiABKAFSC2RvdWJsZVZhbHVlEiEKDHN0cmluZ192YWx1ZRgHIAEoDFILc3RyaW5nVmFsdWUSJwoPYWdncmVnYXRlX3ZhbHVlGAggASgJUg5hZ2dyZWdhdGVWYWx1ZRpKCghOYW1lUGFydBIbCgluYW1lX3BhcnQYASACKAlSCG5hbWVQYXJ0EiEKDGlzX2V4dGVuc2lvbhgCIAIoCFILaXNFeHRlbnNpb24ipwIKDlNvdXJjZUNvZGVJbmZvEkQKCGxvY2F0aW9uGAEgAygLMiguZ29vZ2xlLnByb3RvYnVmLlNvdXJjZUNvZGVJbmZvLkxvY2F0aW9uUghsb2NhdGlvbhrOAQoITG9jYXRpb24SFgoEcGF0aBgBIAMoBUICEAFSBHBhdGgSFgoEc3BhbhgCIAMoBUICEAFSBHNwYW4SKQoQbGVhZGluZ19jb21tZW50cxgDIAEoCVIPbGVhZGluZ0NvbW1lbnRzEisKEXRyYWlsaW5nX2NvbW1lbnRzGAQgASgJUhB0cmFpbGluZ0NvbW1lbnRzEjoKGWxlYWRpbmdfZGV0YWNoZWRfY29tbWVudHMYBiADKAlSF2xlYWRpbmdEZXRhY2hlZENvbW1lbnRzItACChFHZW5lcmF0ZWRDb2RlSW5mbxJNCgphbm5vdGF0aW9uGAEgAygLMi0uZ29vZ2xlLnByb3RvYnVmLkdlbmVyYXRlZENvZGVJbmZvLkFubm90YXRpb25SCmFubm90YXRpb24a6wEKCkFubm90YXRpb24SFgoEcGF0aBgBIAMoBUICEAFSBHBhdGgSHwoLc291cmNlX2ZpbGUYAiABKAlSCnNvdXJjZUZpbGUSFAoFYmVnaW4YAyABKAVSBWJlZ2luEhAKA2VuZBgEIAEoBVIDZW5kElIKCHNlbWFudGljGAUgASgOMjYuZ29vZ2xlLnByb3RvYnVmLkdlbmVyYXRlZENvZGVJbmZvLkFubm90YXRpb24uU2VtYW50aWNSCHNlbWFudGljIigKCFNlbWFudGljEggKBE5PTkUQABIHCgNTRVQQARIJCgVBTElBUxACQn4KE2NvbS5nb29nbGUucHJvdG9idWZCEERlc2NyaXB0b3JQcm90b3NIAVotZ29vZ2xlLmdvbGFuZy5vcmcvcHJvdG9idWYvdHlwZXMvZGVzY3JpcHRvcnBi+AEBogIDR1BCqgIaR29vZ2xlLlByb3RvYnVmLlJlZmxlY3Rpb25K/fsCCgcSBSYAgwgBCqoPCgEMEgMmABIywQwgUHJvdG9jb2wgQnVmZmVycyAtIEdvb2dsZSdzIGRhdGEgaW50ZXJjaGFuZ2UgZm9ybWF0CiBDb3B5cmlnaHQgMjAwOCBHb29nbGUgSW5jLiAgQWxsIHJpZ2h0cyByZXNlcnZlZC4KIGh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL3Byb3RvY29sLWJ1ZmZlcnMvCgogUmVkaXN0cmlidXRpb24gYW5kIHVzZSBpbiBzb3VyY2UgYW5kIGJpbmFyeSBmb3Jtcywgd2l0aCBvciB3aXRob3V0CiBtb2RpZmljYXRpb24sIGFyZSBwZXJtaXR0ZWQgcHJvdmlkZWQgdGhhdCB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnMgYXJlCiBtZXQ6CgogICAgICogUmVkaXN0cmlidXRpb25zIG9mIHNvdXJjZSBjb2RlIG11c3QgcmV0YWluIHRoZSBhYm92ZSBjb3B5cmlnaHQKIG5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lci4KICAgICAqIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUKIGNvcHlyaWdodCBub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIKIGluIHRoZSBkb2N1bWVudGF0aW9uIGFuZC9vciBvdGhlciBtYXRlcmlhbHMgcHJvdmlkZWQgd2l0aCB0aGUKIGRpc3RyaWJ1dGlvbi4KICAgICAqIE5laXRoZXIgdGhlIG5hbWUgb2YgR29vZ2xlIEluYy4gbm9yIHRoZSBuYW1lcyBvZiBpdHMKIGNvbnRyaWJ1dG9ycyBtYXkgYmUgdXNlZCB0byBlbmRvcnNlIG9yIHByb21vdGUgcHJvZHVjdHMgZGVyaXZlZCBmcm9tCiB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKIFRISVMgU09GVFdBUkUgSVMgUFJPVklERUQgQlkgVEhFIENPUFlSSUdIVCBIT0xERVJTIEFORCBDT05UUklCVVRPUlMKICJBUyBJUyIgQU5EIEFOWSBFWFBSRVNTIE9SIElNUExJRUQgV0FSUkFOVElFUywgSU5DTFVESU5HLCBCVVQgTk9UCiBMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUyBGT1IKIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFSRSBESVNDTEFJTUVELiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQ09QWVJJR0hUCiBPV05FUiBPUiBDT05UUklCVVRPUlMgQkUgTElBQkxFIEZPUiBBTlkgRElSRUNULCBJTkRJUkVDVCwgSU5DSURFTlRBTCwKIFNQRUNJQUwsIEVYRU1QTEFSWSwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTIChJTkNMVURJTkcsIEJVVCBOT1QKIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7IExPU1MgT0YgVVNFLAogREFUQSwgT1IgUFJPRklUUzsgT1IgQlVTSU5FU1MgSU5URVJSVVBUSU9OKSBIT1dFVkVSIENBVVNFRCBBTkQgT04gQU5ZCiBUSEVPUlkgT0YgTElBQklMSVRZLCBXSEVUSEVSIElOIENPTlRSQUNULCBTVFJJQ1QgTElBQklMSVRZLCBPUiBUT1JUCiAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOIEFOWSBXQVkgT1VUIE9GIFRIRSBVU0UKIE9GIFRISVMgU09GVFdBUkUsIEVW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cyBmaWVsZC4gVGhlIHZhbHVlIGlzIHNldCBieSBwcm90b2NvbCBjb21waWxlci4gSWYgdGhlCiB1c2VyIGhhcyBzZXQgYSAianNvbl9uYW1lIiBvcHRpb24gb24gdGhpcyBmaWVsZCwgdGhhdCBvcHRpb24ncyB2YWx1ZQogd2lsbCBiZSB1c2VkLiBPdGhlcndpc2UsIGl0J3MgZGVkdWNlZCBmcm9tIHRoZSBmaWVsZCdzIG5hbWUgYnkgY29udmVydGluZwogaXQgdG8gY2FtZWxDYXNlLgoKDQoFBAQCCAQSBIICAgoKDQoFBAQCCAUSBIICCxEKDQoFBAQCCAESBIICEhsKDQoFBAQCCAMSBIICHiAKDAoEBAQCCRIEhAICJAoNCgUEBAIJBBIEhAICCgoNCgUEBAIJBhIEhAILFwoNCgUEBAIJARIEhAIYHwoNCgUEBAIJAxIEhAIiIwqzCQoEBAQCChIEmwICJRqkCSBJZiB0cnVlLCB0aGlzIGlzIGEgcHJvdG8zICJvcHRpb25hbCIuIFdoZW4gYSBwcm90bzMgZmllbGQgaXMgb3B0aW9uYWwsIGl0CiB0cmFja3MgcHJlc2VuY2UgcmVnYXJkbGVzcyBvZiBmaWVsZCB0eXBlLgoKIFdoZW4gcHJvdG8zX29wdGlvbmFsIGlzIHRydWUsIHRoaXMgZmllbGQgbXVzdCBiZSBiZWxvbmcgdG8gYSBvbmVvZiB0bwogc2lnbmFsIHRvIG9sZCBwcm90bzMgY2xpZW50cyB0aGF0IHByZXNlbmNlIGlzIHRyYWNrZWQgZm9yIHRoaXMgZmllbGQuIFRoaXMKIG9uZW9mIGlzIGtub3duIGFzIGEgInN5bnRoZXRpYyIgb25lb2YsIGFuZCB0aGlzIGZpZWxkIG11c3QgYmUgaXRzIHNvbGUKIG1lbWJlciAoZWFjaCBwcm90bzMgb3B0aW9uYWwgZmllbGQgZ2V0cyBpdHMgb3duIHN5bnRoZXRpYyBvbmVvZikuIFN5bnRoZXRpYwogb25lb2ZzIGV4aXN0IGluIHRoZSBkZXNjcmlwdG9yIG9ubHksIGFuZCBkbyBub3QgZ2VuZXJhdGUgYW55IEFQSS4gU3ludGhldGljCiBvbmVvZnMgbXVzdCBiZSBvcmRlcmVkIGFmdGVyIGFsbCAicmVhbCIgb25lb2ZzLgoKIEZvciBtZXNzYWdlIGZpZWxkcywgcHJvdG8zX29wdGlvbmFsIGRvZXNuJ3QgY3JlYXRlIGFueSBzZW1hbnRpYyBjaGFuZ2UsCiBzaW5jZSBub24tcmVwZWF0ZWQgbWVzc2FnZSBmaWVsZHMgYWx3YXlzIHRyYWNrIHByZXNlbmNlLiBIb3dldmVyIGl0IHN0aWxsCiBpbmRpY2F0ZXMgdGhlIHNlbWFudGljIGRldGFpbCBvZiB3aGV0aGVyIHRoZSB1c2VyIHdyb3RlICJvcHRpb25hbCIgb3Igbm90LgogVGhpcyBjYW4gYmUgdXNlZnVsIGZvciByb3VuZC10cmlwcGluZyB0aGUgLnByb3RvIGZpbGUuIEZvciBjb25zaXN0ZW5jeSB3ZQogZ2l2ZSBtZXNzYWdlIGZpZWxkcyBhIHN5bnRoZXRpYyBvbmVvZiBhbHNvLCBldmVuIHRob3VnaCBpdCBpcyBub3QgcmVxdWlyZWQKIHRvIHRyYWNrIHByZXNlbmNlLiBUaGlzIGlzIGVzcGVjaWFsbHkgaW1wb3J0YW50IGJlY2F1c2UgdGhlIHBhcnNlciBjYW4ndAogdGVsbCBpZiBhIGZpZWxkIGlzIGEgbWVzc2FnZSBvciBhbiBlbnVtLCBzbyBpdCBtdXN0IGFsd2F5cyBjcmVhdGUgYQogc3ludGhldGljIG9uZW9mLgoKIFByb3RvMiBvcHRpb25hbCBmaWVsZHMgZG8gbm90IHNldCB0aGlzIGZsYWcsIGJlY2F1c2UgdGhleSBhbHJlYWR5IGluZGljYXRlCiBvcHRpb25hbCB3aXRoIGBMQUJFTF9PUFRJT05BTGAuCgoNCgUEBAIKBBIEmwICCgoNCgUEBAIKBRIEmwILDwoNCgUEBAIKARIEmwIQHwoNCgUEBAIKAxIEmwIiJAoiCgIEBRIGnwIAogIBGhQgRGVzY3JpYmVzIGEgb25lb2YuCgoLCgMEBQESBJ8CCBwKDAoEBAUCABIEoAICGwoNCgUEBQIABBIEoAICCgoNCgUEBQIABRIEoAILEQoNCgUEBQIAARIEoAISFgoNCgUEBQIAAxIEoAIZGgoMCgQEBQIBEgShAgIkCg0KBQQFAgEEEgShAgIKCg0KBQQFAgEGEgShAgsXCg0KBQQFAgEBEgShAhgf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ZSBkZWZpbml0aW9ucyBhYm92ZSBtYXkgaGF2ZSAib3B0aW9ucyIgYXR0YWNoZWQuICBUaGVzZSBhcmUKIGp1c3QgYW5ub3RhdGlvbnMgd2hpY2ggbWF5IGNhdXNlIGNvZGUgdG8gYmUgZ2VuZXJhdGVkIHNsaWdodGx5IGRpZmZlcmVudGx5CiBvciBtYXkgY29udGFpbiBoaW50cyBmb3IgY29kZSB0aGF0IG1hbmlwdWxhdGVzIHByb3RvY29sIG1lc3NhZ2VzLgoKIENsaWVudHMgbWF5IGRlZmluZSBjdXN0b20gb3B0aW9ucyBhcyBleHRlbnNpb25zIG9mIHRoZSAqT3B0aW9ucyBtZXNzYWdlcy4KIFRoZXNlIGV4dGVuc2lvbnMgbWF5IG5vdCB5ZXQgYmUga25vd24gYXQgcGFyc2luZyB0aW1lLCBzbyB0aGUgcGFyc2VyIGNhbm5vdAogc3RvcmUgdGhlIHZhbHVlcyBpbiB0aGVtLiAgSW5zdGVhZCBpdCBzdG9yZXMgdGhlbSBpbiBhIGZpZWxkIGluIHRoZSAqT3B0aW9ucwogbWVzc2FnZSBjYWxsZWQgdW5pbnRlcnByZXRlZF9vcHRpb24uIFRoaXMgZmllbGQgbXVzdCBoYXZlIHRoZSBzYW1lIG5hbWUKIGFjcm9zcyBhbGwgKk9wdGlvbnMgbWVzc2FnZXMuIFdlIHRoZW4gdXNlIHRoaXMgZmllbGQgdG8gcG9wdWxhdGUgdGhlCiBleHRlbnNpb25zIHdoZW4gd2UgYnVpbGQgYSBkZXNjcmlwdG9yLCBhdCB3aGljaCBwb2ludCBhbGwgcHJvdG9zIGhhdmUgYmVlbgogcGFyc2VkIGFuZCBzbyBhbGwgZXh0ZW5zaW9ucyBhcmUga25vd24uCgogRXh0ZW5zaW9uIG51bWJlcnMgZm9yIGN1c3RvbSBvcHRpb25zIG1heSBiZSBjaG9zZW4gYXMgZm9sbG93czoKICogRm9yIG9wdGlvbnMgd2hpY2ggd2lsbCBvbmx5IGJlIHVzZWQgd2l0aGluIGEgc2luZ2xlIGFwcGxpY2F0aW9uIG9yCiAgIG9yZ2FuaXphdGlvbiwgb3IgZm9yIGV4cGVyaW1lbnRhbCBvcHRpb25zLCB1c2UgZmllbGQgbnVtYmVycyA1MDAwMAogICB0aHJvdWdoIDk5OTk5LiAgSXQgaXMgdXAgdG8geW91IHRvIGVuc3VyZSB0aGF0IHlvdSBkbyBub3QgdXNlIHRoZQogICBzYW1lIG51bWJlciBmb3IgbXVsdGlwbGUgb3B0aW9ucy4KICogRm9yIG9wdGlvbnMgd2hpY2ggd2lsbCBiZSBwdWJsaXNoZWQgYW5kIHVzZWQgcHVibGljbHkgYnkgbXVsdGlwbGUKICAgaW5kZXBlbmRlbnQgZW50aXRpZXMsIGUtbWFpbCBwcm90b2J1Zi1nbG9iYWwtZXh0ZW5zaW9uLXJlZ2lzdHJ5QGdvb2dsZS5jb20KICAgdG8gcmVzZXJ2ZSBleHRlbnNpb24gbnVtYmVycy4gU2ltcGx5IHByb3ZpZGUgeW91ciBwcm9qZWN0IG5hbWUgKGUuZy4KICAgT2JqZWN0aXZlLUMgcGx1Z2luKSBhbmQgeW91ciBwcm9qZWN0IHdlYnNpdGUgKGlmIGF2YWlsYWJsZSkgLS0gdGhlcmUncyBubwogICBuZWVkIHRvIGV4cGxhaW4gaG93IHlvdSBpbnRlbmQgdG8gdXNlIHRoZW0uIFVzdWFsbHkgeW91IG9ubHkgbmVlZCBvbmUKICAgZXh0ZW5zaW9uIG51bWJlci4gWW91IGNhbiBkZWNsYXJlIG11bHRpcGxlIG9wdGlvbnMgd2l0aCBvbmx5IG9uZSBleHRlbnNpb24KICAgbnVtYmVyIGJ5IHB1dHRpbmcgdGhlbSBpbiBhIHN1Yi1tZXNzYWdlLiBTZWUgdGhlIEN1c3RvbSBPcHRpb25zIHNlY3Rpb24gb2YKICAgdGhlIGRvY3MgZm9yIGV4YW1wbGVzOgogICBodHRwczovL2RldmVsb3BlcnMuZ29vZ2xlLmNvbS9wcm90b2NvbC1idWZmZXJzL2RvY3MvcHJvdG8jb3B0aW9ucwogICBJZiB0aGlzIHR1cm5zIG91dCB0byBiZSBwb3B1bGFyLCBhIHdlYiBzZXJ2aWNlIHdpbGwgYmUgc2V0IHVwCiAgIHRvIGF1dG9tYXRpY2FsbHkgYXNzaWduIG9wdGlvbiBudW1iZXJzLgoKCwoDBAoBEgSCAwgTCvQBCgQECgIAEgSIAwIjGuUBIFNldHMgdGhlIEphdmEgcGFja2FnZSB3aGVyZSBjbGFzc2VzIGdlbmVyYXRlZCBmcm9tIHRoaXMgLnByb3RvIHdpbGwgYmUKIHBsYWNlZC4gIEJ5IGRlZmF1bHQsIHRoZSBwcm90byBwYWNrYWdlIGlzIHVzZWQsIGJ1dCB0aGlzIGlzIG9mdGVuCiBpbmFwcHJvcHJpYXRlIGJlY2F1c2UgcHJvdG8gcGFja2FnZXMgZG8gbm90IG5vcm1hbGx5IHN0YXJ0IHdpdGggYmFja3dhcmRzCiBkb21haW4gbmFtZXMuCgoNCgUECgIABBIEiAMCCgoNCgUECgIABRIEiAMLEQoNCgUECgIAARIEiAMSHgoNCgUECgIAAxIEiAMhIgrxAgoEBAoCARIEjwMCKxriAiBDb250cm9scyB0aGUgbmFtZSBvZiB0aGUgd3JhcHBlciBKYXZhIGNsYXNzIGdlbmVyYXRlZCBmb3IgdGhlIC5wcm90byBmaWxlLgogVGhhdCBjbGFzcyB3aWxsIGFsd2F5cyBjb250YWluIHRoZSAucHJvdG8gZmlsZSdzIGdldERlc2NyaXB0b3IoKSBtZXRob2QgYXMKIHdlbGwgYXMgYW55IHRvcC1sZXZlbCBleHRlbnNpb25zIGRlZmluZWQgaW4gdGhlIC5wcm90byBmaWxlLgogSWYgamF2YV9tdWx0aXBsZV9maWxlcyBpcyBkaXNhYmxlZCwgdGhlbiBhbGwgdGhlIG90aGVyIGNsYXNzZXMgZnJvbSB0aGUKIC5wcm90byBmaWxlIHdpbGwgYmUgbmVzdGVkIGluc2lkZSB0aGUgc2luZ2xlIHdyYXBwZXIgb3V0ZXIgY2xhc3MuCgoNCgUECgIBBBIEjwMCCgoNCgUECgIBBRIEjwMLEQoNCgUECgIBARIEjwMSJgoNCgUECgIBAxIEjwMpKgqmAwoEBAoCAhIElwMCOxqXAyBJZiBlbmFibGVkLCB0aGVuIHRoZSBKYXZhIGNvZGUgZ2VuZXJhdG9yIHdpbGwgZ2VuZXJhdGUgYSBzZXBhcmF0ZSAuamF2YQogZmlsZSBmb3IgZWFjaCB0b3AtbGV2ZWwgbWVzc2FnZSwgZW51bSwgYW5kIHNlcnZpY2UgZGVmaW5lZCBpbiB0aGUgLnByb3RvCiBmaWxlLiAgVGh1cywgdGhlc2UgdHlwZXMgd2lsbCAqbm90KiBiZSBuZXN0ZWQgaW5z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BAQKCQASBPUDCw0KDQoFBAoJAAESBPUDCw0KDQoFBAoJAAISBPUDCw0KDAoCBAsSBvgDAMUEAQoLCgMECwESBPgDCBYK2AUKBAQLAgASBIsEAj4ayQUgU2V0IHRydWUgdG8gdXNlIHRoZSBvbGQgcHJvdG8xIE1lc3NhZ2VTZXQgd2lyZSBmb3JtYXQgZm9yIGV4dGVuc2lvbnMuCiBUaGlzIGlzIHByb3ZpZGVkIGZvciBiYWNrd2FyZHMtY29tcGF0aWJpbGl0eSB3aXRoIHRoZSBNZXNzYWdlU2V0IHdpcmUKIGZvcm1hdC4gIFlvdSBzaG91bGQgbm90IHVzZSB0aGlzIGZvciBhbnkgb3RoZXIgcmVhc29uOiAgSXQncyBsZXNzCiBlZmZpY2llbnQsIGhhcyBmZXdlciBmZWF0dXJlcywgYW5kIGlzIG1vcmUgY29tcGxpY2F0ZWQuCgogVGhlIG1lc3NhZ2UgbXVzdCBiZSBkZWZpbmVkIGV4YWN0bHkgYXMgZm9sbG93czoKICAgbWVzc2FnZSBGb28gewogICAgIG9wdGlvbiBtZXNzYWdlX3NldF93aXJlX2Zvcm1hdCA9IHRydWU7CiAgICAgZXh0ZW5zaW9ucyA0IHRvIG1heDsKICAgfQogTm90ZSB0aGF0IHRoZSBtZXNzYWdlIGNhbm5vdCBoYXZlIGFueSBkZWZpbmVkIGZpZWxkczsgTWVzc2FnZVNldHMgb25seQogaGF2ZSBleHRlbnNpb25zLgoKIEFsbCBleHRlbnNpb25zIG9mIHlvdXIgdHlwZSBtdXN0IGJlIHNpbmd1bGFyIG1lc3NhZ2VzOyBlLmcuIHRoZXkgY2Fubm90CiBiZSBpbnQzMnMsIGVudW1zLCBvciByZXBlYXRlZCBtZXNzYWdlcy4KCiBCZWNhdXNlIHRoaXMgaXMgYW4gb3B0aW9uLCB0aGUgYWJvdmUgdHdvIHJlc3RyaWN0aW9ucyBhcmUgbm90IGVuZm9yY2VkIGJ5CiB0aGUgcHJvdG9jb2wgY29tcGlsZXIuCgoNCgUECwIABBIEiwQCCgoNCgUECwIABRIEiwQLDwoNCgUECwIAARIEiwQQJwoNCgUECwIAAxIEiwQqKwoNCgUECwIACBIEiwQsPQoNCgUECwIABxIEiwQ3PArrAQoEBAsCARIEkAQCRhrcASBEaXNhYmxlcyB0aGUgZ2VuZXJhdGlvbiBvZiB0aGUgc3RhbmRhcmQgImRlc2NyaXB0b3IoKSIgYWNjZXNzb3IsIHdoaWNoIGNhbgogY29uZmxpY3Qgd2l0aCBhIGZpZWxkIG9mIHRoZSBzYW1lIG5hbWUuICBUaGlzIGlzIG1lYW50IHRvIG1ha2UgbWlncmF0aW9uCiBmcm9tIHByb3RvMSBlYXNpZXI7IG5ldyBjb2RlIHNob3VsZCBhdm9pZCBmaWVsZHMgbmFtZWQgImRlc2NyaXB0b3IiLgoKDQoFBAsCAQQSBJAEAgoKDQoFBAsCAQUSBJAECw8KDQoFBAsCAQESBJAEEC8KDQoFBAsCAQMSBJAEMjMKDQoFBAsCAQgSBJAENEUKDQoFBAsCAQcSBJAEP0QK7gEKBAQLAgISBJYEAjEa3wEgSXMgdGhpcyBtZXNzYWdlIGRlcHJlY2F0ZWQ/CiBEZXBlbmRpbmcgb24gdGhlIHRhcmdldCBwbGF0Zm9ybSwgdGhpcyBjYW4gZW1pdCBEZXByZWNhdGVkIGFubm90YXRpb25zCiBmb3IgdGhlIG1lc3NhZ2UsIG9yIGl0IHdpbGwgYmUgY29tcGxldGVseSBpZ25vcmVkOyBpbiB0aGUgdmVyeSBsZWFzdCwKIHRoaXMgaXMgYSBmb3JtYWxpemF0aW9uIGZvciBkZXByZWNhdGluZyBtZXNzYWdlcy4KCg0KBQQLAgIEEgSWBAIKCg0KBQQLAgIFEgSWBAsPCg0KBQQLAgIBEgSWBBAaCg0KBQQLAgIDEgSWBB0eCg0KBQQLAgIIEgSWBB8wCg0KBQQLAgIHEgSWBCovCgsKAwQLCRIEmAQCEwoMCgQECwkAEgSYBAsMCg0KBQQLCQABEgSYBAsMCg0KBQQLCQACEgSYBAsMCgwKBAQLCQESBJgEDg8KDQoFBAsJAQESBJgEDg8KDQoFBAsJAQISBJgEDg8KDAoEBAsJAhIEmAQREgoNCgUECwkCARIEmAQREgoNCgUECwkCAhIEmAQREgqgBgoEBAsCAxIErwQCHhqRBiBOT1RFOiBEbyBub3Qgc2V0IHRoZSBvcHRpb24gaW4gLnByb3RvIGZpbGVzLiBBbHdheXMgdXNlIHRoZSBtYXBzIHN5bnRheAogaW5zdGVhZC4gVGhlIG9wdGlvbiBzaG91bGQgb25seSBiZSBpbXBsaWNpdGx5IHNldCBieSB0aGUgcHJvdG8gY29tcGlsZXIKIHBhcnNlci4KCiBXaGV0aGVyIHRoZSBtZXNzYWdlIGlzIGFuIGF1dG9tYXRpY2FsbHkgZ2VuZXJhdGVkIG1hcCBlbnRyeSB0eXBlIGZvciB0aGUKIG1hcHMgZmllbGQuCgogRm9yIG1hcHMgZmllbGRzOgogICAgIG1hcDxLZXlUeXBlLCBWYWx1ZVR5cGU+IG1hcF9maWVsZCA9IDE7CiBUaGUgcGFyc2VkIGRlc2NyaXB0b3IgbG9va3MgbGlrZToKICAgICBtZXNzYWdlIE1hcEZpZWxkRW50cnkgewogICAgICAgICBvcHRpb24gbWFwX2VudHJ5ID0gdHJ1ZTsKICAgICAgICAgb3B0aW9uYWwgS2V5VHlwZSBrZXkgPSAxOwogICAgICAgICBvcHRpb25hbCBWYWx1ZVR5cGUgdmFsdWUgPSAyOwogICAgIH0KICAgICByZXBlYXRlZCBNYXBGaWVsZEVudHJ5IG1hcF9maWVsZCA9IDE7CgogSW1wbGVtZW50YXRpb25zIG1heSBjaG9vc2Ugbm90IHRvIGdlbmVyYXRlIHRoZSBtYXBfZW50cnk9dHJ1ZSBtZXNzYWdlLCBidXQKIHVzZSBhIG5hdGl2ZSBtYXAgaW4gdGhlIHRhcmdldCBsYW5ndWFnZSB0byBob2xkIHRoZSBrZXlzIGFuZCB2YWx1ZXMuCiBUaGUgcmVmbGVjdGlvbiBBUElzIGluIHN1Y2ggaW1wbGVtZW50YXRpb25zIHN0aWxsIG5lZWQgdG8gd29yayBhcwogaWYgdGhlIGZpZWxkIGlzIGEgcmVwZWF0ZWQgbWVzc2FnZSBmaWVsZC4KCg0KBQQLAgMEEgSvBAIKCg0KBQQLAgMFEgSvBAsPCg0KBQQLAgMBEgSvBBAZCg0KBQQLAgMDEgSvBBwdCiQKAwQLCRIEsQQCDSIXIGphdmFsaXRlX3NlcmlhbGl6YWJsZQoKDAoEBAsJAxIEsQQLDAoNCgUECwkDARIEsQQLDAoNCgUECwkDAhIEsQQLDAofCgMECwkSBLIEAg0iEiBqYXZhbmFub19hc19saXRlCgoMCgQECwkEEgSyBAsMCg0KBQQLCQQBEgSyBAsM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IGEKIENvcmQsIG9yIHdoZW4gcGFyc2luZyB3aXRoIGFsaWFzaW5nIGVuYWJsZWQsIGFzIHRoZSBwYXJzZWQgQ29yZCBtYXkgdGhlbgogYWxpYXMgdGhlIG9yaWdpbmFsIGJ1ZmZlci4KCg8KBwQMBAACAQESBNkEBAgKDwoHBAwEAAIBAhIE2QQLDAoOCgYEDAQAAgISBNsEBBUKDwoHBAwEAAICARIE2wQEEAoPCgcEDAQAAgICEgTbBBMUCtoCCgQEDAIBEgTiBAIbGssCIFRoZSBwYWNrZWQgb3B0aW9uIGNhbiBiZSBlbmFibGVkIGZvciByZXBlYXRlZCBwcmltaXRpdmUgZmllbGRzIHRvIGVuYWJsZQogYSBtb3JlIGVmZmljaWVudCByZXByZXNlbnRhdGlvbiBvbiB0aGUgd2lyZS4gUmF0aGVyIHRoYW4gcmVwZWF0ZWRseQogd3JpdGluZyB0aGUgdGFnIGFuZCB0eXBlIGZvciBlYWNoIGVsZW1lbnQsIHRoZSBlbnRpcmUgYXJyYXkgaXMgZW5jb2RlZCBhcwogYSBzaW5nbGUgbGVuZ3RoLWRlbGltaXRlZCBibG9iLiBJbiBwcm90bzMsIG9ubHkgZXhwbGljaXQgc2V0dGluZyBpdCB0bwogZmFsc2Ugd2lsbCBhdm9pZCB1c2luZyBwYWNrZWQgZW5jb2RpbmcuCgoNCgUEDAIBBBIE4gQCCgoNCgUEDAIBBRIE4gQLDwoNCgUEDAIBARIE4gQQFgoNCgUEDAIBAxIE4gQZGgqaBQoEBAwCAhIE7wQCMxqLBSBUaGUganN0eXBlIG9wdGlvbiBkZXRlcm1pbmVzIHRoZSBKYXZhU2NyaXB0IHR5cGUgdXNlZCBmb3IgdmFsdWVzIG9mIHRoZQogZmllbGQuICBUaGUgb3B0aW9uIGlzIHBlcm1pdHRlZCBvbmx5IGZvciA2NCBiaXQgaW50ZWdyYWwgYW5kIGZpeGVkIHR5cGVzCiAoaW50NjQsIHVpbnQ2NCwgc2ludDY0LCBmaXhlZDY0LCBzZml4ZWQ2NCkuICBBIGZpZWxkIHdpdGgganN0eXBlIEpTX1NUUklORwogaXMgcmVwcmVzZW50ZWQgYXMgSmF2YVNjcmlwdCBzdHJpbmcsIHdoaWNoIGF2b2lkcyBsb3NzIG9mIHByZWNpc2lvbiB0aGF0CiBjYW4gaGFwcGVuIHdoZW4gYSBsYXJnZSB2YWx1ZSBpcyBjb252ZXJ0ZWQgdG8gYSBmbG9hdGluZyBwb2ludCBKYXZhU2NyaXB0LgogU3BlY2lmeWluZyBKU19OVU1CRVIgZm9yIHRoZSBqc3R5cGUgY2F1c2VzIHRoZSBnZW5lcmF0ZWQgSmF2YVNjcmlwdCBjb2RlIHRvCiB1c2UgdGhlIEphdmFTY3JpcHQgIm51bWJlciIgdHlwZS4gIFRoZSBiZWhhdmlvciBvZiB0aGUgZGVmYXVsdCBvcHRpb24KIEpTX05PUk1BTCBpcyBpbXBsZW1lbnRhdGlvbiBkZXBlbmRlbnQuCgogVGhpcyBvcHRpb24gaXMgYW4gZW51bSB0byBwZXJtaXQgYWRkaXRpb25hbCB0eXBlcyB0byBiZSBhZGRlZCwgZS5nLgogZ29vZy5tYXRoLkludGVnZXIuCgoNCgUEDAICBBIE7wQCCgoNCgUEDAICBhIE7wQLEQoNCgUEDAICARIE7wQSGAoNCgUEDAICAxIE7wQbHAoNCgUEDAICCBIE7wQdMgoNCgUEDAICBxIE7wQoMQoOCgQEDAQBEgbwBAL5BAMKDQoFBAwEAQESBPAEBw0KJwoGBAwEAQIAEgTyBAQSGhcgVXNlIHRoZSBkZWZhdWx0IHR5cGUuCgoPCgcEDAQBAgABEgTyBAQNCg8KBwQMBAECAAISBPIEEBEKKQoGBAwEAQIBEgT1BAQSGhkgVXNlIEphdmFTY3JpcHQgc3RyaW5ncy4KCg8KBwQMBAECAQESBPUEBA0KDwoHBAwEAQIBAhIE9QQQEQopCgYEDAQBAgISBPgEBBIaGSBVc2UgSmF2YVNjcmlwdCBudW1iZXJzLgoKDwoHBAwEAQICARIE+AQEDQoPCgcEDAQBAgICEgT4BBARCv8NCgQEDAIDEgSZBQIrGvANIFNob3VsZCB0aGlzIGZpZWxkIGJlIHBhcnNlZCBsYXppbHk/ICBMYXp5IGFwcGxpZXMgb25seSB0byBtZXNzYWdlLXR5cGUKIGZpZWxkcy4gIEl0IG1lYW5zIHRoYXQgd2hlbiB0aGUgb3V0ZXIgbWVzc2FnZSBpcyBpbml0aWFsbHkgcGFyc2VkLCB0aGUKIGlubmVyIG1lc3NhZ2UncyBjb250ZW50cyB3aWxsIG5vdCBiZSBwYXJzZWQgYnV0IGluc3RlYWQgc3RvcmVkIGluIGVuY29kZWQKIGZvcm0uICBUaGUgaW5uZXIgbWVzc2FnZSB3aWxsIGFjdHVhbGx5IGJlIHBhcnNlZCB3aGVuIGl0IGlzIGZpcnN0IGFjY2Vzc2VkLgoKIFRoaXMgaXMgb25seSBhIGhpbnQuICBJbXBsZW1lbnRhdGlvbnMgYXJlIGZyZWUgdG8gY2hvb3NlIHdoZXRoZXIgdG8gdXNlCiBlYWdlciBvciBsYXp5IHBhcnNpbmcgcmVnYXJkbGVzcyBvZiB0aGUgdmFsdWUgb2YgdGhpcyBvcHRpb24uICBIb3dldmVyLAogc2V0dGluZyB0aGlzIG9wdGlvbiB0cnVlIHN1Z2dlc3RzIHRoYXQgdGhlIHByb3RvY29sIGF1dGhvciBiZWxpZXZlcyB0aGF0CiB1c2luZyBsYXp5IHBhcnNpbmcgb24gdGhpcyBmaWVsZCBpcyB3b3J0aCB0aGUgYWRkaXRpb25hbCBib29ra2VlcGluZwogb3ZlcmhlYWQgdHlwaWNhbGx5IG5lZWRlZCB0byBpbXBsZW1lbnQgaXQuCgogVGhpcyBvcHRpb24gZG9lcyBub3QgYWZmZWN0IHRoZSBwdWJsaWMgaW50ZXJmYWNlIG9mIGFueSBnZW5lcmF0ZWQgY29kZTsKIGFsbCBtZXRob2Qgc2lnbmF0dXJlcyByZW1haW4gdGhlIHNhbWUuICBGdXJ0aGVybW9yZSwgdGhyZWFkLXNhZmV0eSBvZiB0aGUKIGludGVyZmFjZSBpcyBub3QgYWZmZWN0ZWQgYnkgdGhpcyBvcHRpb247IGNvbnN0IG1ldGhvZHMgcmVtYWluIHNhZmUgdG8KIGNhbGwgZnJvbSBtdWx0aXBsZSB0aHJlYWRzIGNvbmN1cnJlbnRseSwgd2hpbGUgbm9uLWNvbnN0IG1ldGhvZHMgY29udGludWUKIHRvIHJlcXVpcmUgZXhjbHVzaXZlIGFjY2Vzcy4KCiBOb3RlIHRoYXQgaW1wbGVtZW50YXRpb25zIG1heSBjaG9vc2Ugbm90IHRvIGNoZWNrIHJlcXVpcmVkIGZpZWxkcyB3aXRoaW4KIGEgbGF6eSBzdWItbWVzc2FnZS4gIFRoYXQgaXMsIGNhbGxpbmcgSXNJbml0aWFsaXplZCgpIG9uIHRoZSBvdXRlciBtZXNzYWdlCiBtYXkgcmV0dXJuIHRydWUgZXZlbiBpZiB0aGUgaW5uZXIgbWVzc2FnZSBoYXMgbWlzc2luZyByZXF1aXJlZCBmaWVsZHMuCiBUaGlzIGlzIG5lY2Vzc2FyeSBiZWNhdXNlIG90aGVyd2lzZSB0aGUgaW5uZXIgbWVzc2FnZSB3b3VsZCBoYXZlIHRvIGJlCiBwYXJzZWQgaW4gb3JkZXIgdG8gcGVyZm9ybSB0aGUgY2hlY2ssIGRlZmVhdGluZyB0aGUgcHVycG9zZSBvZiBsYXp5CiBwYXJzaW5nLiAgQW4gaW1wbGVtZW50YXRpb24gd2hpY2ggY2hvb3NlcyBub3QgdG8gY2hlY2sgcmVxdWlyZWQgZmllbGRzCiBtdXN0IGJlIGNvbnNpc3RlbnQgYWJvdXQgaXQuICBUaGF0IGlzLCBmb3IgYW55IHBhcnRpY3VsYXIgc3ViLW1lc3NhZ2UsIHRoZQogaW1wbGVtZW50YXRpb24gbXVzdCBlaXRoZXIgKmFsd2F5cyogY2hlY2sgaXRzIHJlcXVpcmVkIGZpZWxkcywgb3IgKm5ldmVyKgogY2hlY2sgaXRzIHJlcXVpcmVkIGZpZWxkcywgcmVnYXJkbGVzcyBvZiB3aGV0aGVyIG9yIG5vdCB0aGUgbWVzc2FnZSBoYXMKIGJlZW4gcGFyc2VkLgoKIEFzIG9mIE1heSAyMDIyLCBsYXp5IHZlcmlmaWVzIHRoZSBjb250ZW50cyBvZiB0aGUgYnl0ZSBzdHJlYW0gZHVyaW5nCiBwYXJzaW5nLiAgQW4gaW52YWxpZCBieXRlIHN0cmVhbSB3aWxsIGNhdXNlIHRoZSBvdmVyYWxsIHBhcnNpbmcgdG8gZmFpbC4KCg0KBQQMAgMEEgSZBQIKCg0KBQQMAgMFEgSZBQsPCg0KBQQMAgMBEgSZBRAUCg0KBQQMAgMDEgSZBRcYCg0KBQQMAgMIEgSZBRkqCg0KBQQMAgMHEgSZBSQpCq8BCgQEDAIEEgSeBQI3GqABIHVudmVyaWZpZWRfbGF6eSBkb2VzIG5vIGNvcnJlY3RuZXNzIGNoZWNrcyBvbiB0aGUgYnl0ZSBzdHJlYW0uIFRoaXMgc2hvdWxkCiBvbmx5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EQUAEgS4Bg0YCg0KBQQRBQABEgS4Bg0RCg0KBQQRBQACEgS4BhUYCosDCgIEEhIGwQYA1QYBGvwCIEEgbWVzc2FnZSByZXByZXNlbnRpbmcgYSBvcHRpb24gdGhlIHBhcnNlciBkb2VzIG5vdCByZWNvZ25pemUuIFRoaXMgb25seQogYXBwZWFycyBpbiBvcHRpb25zIHByb3RvcyBjcmVhdGVkIGJ5IHRoZSBjb21waWxlcjo6UGFyc2VyIGNsYXNzLgogRGVzY3JpcHRvclBvb2wgcmVzb2x2ZXMgdGhlc2Ugd2hlbiBidWlsZGluZyBEZXNjcmlwdG9yIG9iamVjdHMuIFRoZXJlZm9yZSwKIG9wdGlvbnMgcHJvdG9zIGluIGRlc2NyaXB0b3Igb2JqZWN0cyAoZS5nLiByZXR1cm5lZCBieSBEZXNjcmlwdG9yOjpvcHRpb25zKCksCiBvciBwcm9kdWNlZCBieSBEZXNjcmlwdG9yOjpDb3B5VG8oKSkgd2lsbCBuZXZlciBoYXZlIFVuaW50ZXJwcmV0ZWRPcHRpb25zCiBpbiB0aGVtLgoKCwoDBBIBEgTBBggbCssCCgQEEgMAEgbHBgLKBgMaugIgVGhlIG5hbWUgb2YgdGhlIHVuaW50ZXJwcmV0ZWQgb3B0aW9uLiAgRWFjaCBzdHJpbmcgcmVwcmVzZW50cyBhIHNlZ21lbnQgaW4KIGEgZG90LXNlcGFyYXRlZCBuYW1lLiAgaXNfZXh0ZW5zaW9uIGlzIHRydWUgaWZmIGEgc2VnbWVudCByZXByZXNlbnRzIGFuCiBleHRlbnNpb24gKGRlbm90ZWQgd2l0aCBwYXJlbnRoZXNlcyBpbiBvcHRpb25zIHNwZWNzIGluIC5wcm90byBmaWxlcykuCiBFLmcuLHsgWyJmb28iLCBmYWxzZV0sIFsiYmFyLmJheiIsIHRydWVdLCBbIm1vbyIsIGZhbHNlXSB9IHJlcHJlc2VudHMKICJmb28uKGJhci5iYXopLm1vbyIuCgoNCgUEEgMAARIExwYKEgoOCgYEEgMAAgASBMgGBCIKDwoHBBIDAAIABBIEyAYEDAoPCgcEEgMAAgAFEgTIBg0TCg8KBwQSAwACAAESBMgGFB0KDwoHBBIDAAIAAxIEyAYgIQoOCgYEEgMAAgESBMkGBCMK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dHNlbGYgKGkuZS4gbm90IHRvIGFueQogICBwYXJ0aWN1bGFyIGluZGV4IHdpdGhpbiBpdCkuICBUaGlzIGlzIHVzZWQgd2hlbmV2ZXIgYSBzZXQgb2YgZWxlbWVudHMgYXJlCiAgIGxvZ2ljYWxseSBlbmNsb3NlZCBpbiBhIHNpbmdsZSBjb2RlIHNlZ21lbnQuICBGb3IgZXhhbXBsZSwgYW4gZW50aXJlCiAgIGV4dGVuZCBibG9jayAocG9zc2libHkgY29udGFpbmluZyBtdWx0aXBsZSBleHRlbnNpb24gZGVmaW5pdGlvbnMpIHdpbGwKICAgaGF2ZSBhbiBvdXRlciBsb2NhdGlvbiB3aG9zZSBwYXRoIHJlZmVycyB0byB0aGUgImV4dGVuc2lvbnMiIHJlcGVhdGVkCiAgIGZpZWxkIHdpdGhvdXQgYW4gaW5kZXguCiAtIE11bHRpcGxlIGxvY2F0aW9ucyBtYXkgaGF2ZSB0aGUgc2FtZSBwYXRoLiAgVGhpcyBoYXBwZW5zIHdoZW4gYSBzaW5nbGUKICAgbG9naWNhbCBkZWNsYXJhdGlvbiBpcyBzcHJlYWQgb3V0IGFjcm9zcyBtdWx0aXBsZSBwbGFjZXMuICBUaGUgbW9zdAogICBvYnZpb3VzIGV4YW1wbGUgaXMgdGhlICJleHRlbmQiIGJsb2NrIGFnYWluIC0tIHRoZXJlIG1heSBiZSBtdWx0aXBsZQogICBleHRlbmQgYmxvY2tzIGluIHRoZSBzYW1lIHNjb3BlLCBlYWNoIG9mIHdoaWNoIHdpbGwgaGF2ZSB0aGUgc2FtZSBwYXRoLgogLSBBIGxvY2F0aW9uJ3Mgc3BhbiBpcyBub3QgYWx3YXlzIGEgc3Vic2V0IG9mIGl0cyBwYXJlbnQncyBzcGFuLiAgRm9yCiAgIGV4YW1wbGUsIHRoZSAiZXh0ZW5kZWUiIG9mIGFuIGV4dGVuc2lvbiBkZWNsYXJhdGlvbiBhcHBlYXJzIGF0IHRoZQogICBiZWdpbm5pbmcgb2YgdGhlICJleHRlbmQiIGJsb2NrIGFuZCBpcyBzaGFyZWQgYnkgYWxsIGV4dGVuc2lvbnMgd2l0aGluCiAgIHRoZSBibG9jay4KIC0gSnVzdCBiZWNhdXNlIGEgbG9jYXRpb24ncyBzcGFuIGlzIGEgc3Vic2V0IG9mIHNvbWUgb3RoZXIgbG9jYXRpb24ncyBzcGFuCiAgIGRvZXMgbm90IG1lYW4gdGhhdCBpdCBpcyBhIGRlc2NlbmRhbnQuICBGb3IgZXhhbXBsZSwgYSAiZ3JvdXAiIGRlZmluZXMKICAgYm90aCBhIHR5cGUgYW5kIGEgZmllbGQgaW4gYSBzaW5nbGUgZGVjbGFyYXRpb24uICBUaHVzLCB0aGUgbG9jYXRpb25zCiAgIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHR5cGUgYW5kIGZpZWxkIGFuZCB0aGVpciBjb21wb25lbnRzIHdpbGwgb3ZlcmxhcC4KIC0gQ29kZSB3aGljaCB0cmllcyB0byBpbnRlcnByZXQgbG9jYXRpb25zIHNob3VsZCBwcm9iYWJseSBiZSBkZXNpZ25lZCB0bwogICBpZ25vcmUgdGhvc2UgdGhhdCBpdCBkb2Vzbid0IHVuZGVyc3RhbmQsIGFzIG1vcmUgdHlwZXMgb2YgbG9jYXRpb25zIGNvdWxkCiAgIGJlIHJlY29yZGVkIGluIHRoZSBmdXR1cmUuCgoNCgUEEwIABBIEiAcCCgoNCgUEEwIABhIEiAcLEwoNCgUEEwIAARIEiAcUHAoNCgUEEwIAAxIEiAcfIAoOCgQEEwMAEgaJBwLcBwMKDQoFBBMDAAESBIkHChIKiQcKBgQTAwACABIEoQcELBr4BiBJZGVudGlmaWVzIHdoaWNoIHBhcnQgb2YgdGhlIEZpbGVEZXNjcmlwdG9yUHJvdG8gd2FzIGRlZmluZWQgYXQgdGhpcwogbG9jYXRpb24uCgogRWFjaCBlbGVtZW50IGlzIGEgZmllbGQgbnVtYmVyIG9yIGFuIGluZGV4LiAgVGhleSBmb3JtIGEgcGF0aCBmcm9tCiB0aGUgcm9vdCBGaWxlRGVzY3JpcHRvclByb3RvIHRvIHRoZSBwbGFjZSB3aGVyZSB0aGUgZGVmaW5pdGlvbiBvY2N1cnMuCiBGb3IgZXhhbXBsZSwgdGhpcyBwYXRoOgogICBbIDQsIDMsIDIsIDcsIDEgXQogcmVmZXJzIHRvOgogICBmaWxlLm1lc3NhZ2VfdHlwZSgzKSAgLy8gNCwgMwogICAgICAgLmZpZWxkKDcpICAgICAgICAgLy8gMiwgNwogICAgICAgLm5hbWUoKSAgICAgICAgICAgLy8gMQogVGhpcyBpcyBiZWNhdXNlIEZpbGVEZXNjcmlwdG9yUHJvdG8ubWVzc2FnZV90eXBlIGhhcyBmaWVsZCBudW1iZXIgNDoKICAgcmVwZWF0ZWQgRGVzY3JpcHRvclByb3RvIG1lc3NhZ2VfdHlwZSA9IDQ7CiBhbmQgRGVzY3JpcHRvclByb3RvLmZpZWxkIGhhcyBmaWVsZCBudW1iZXIgMjoKICAgcmVwZWF0ZWQgRmllbGREZXNjcmlwdG9yUHJvdG8gZmllbGQgPSAyOwogYW5kIEZpZWxkRGVzY3JpcHRvclByb3RvLm5hbWUgaGFzIGZpZWxkIG51bWJlciAxOgogICBvcHRpb25hbCBzdHJpbmcgbmFtZSA9IDE7CgogVGh1cywgdGhlIGFib3ZlIHBhdGggZ2l2ZXMgdGhlIGxvY2F0aW9uIG9mIGEgZmllbGQgbmFtZS4gIElmIHdlIHJlbW92ZWQKIHRoZSBsYXN0IGVsZW1lbnQ6CiAgIFsgNCwgMywgMiwgNyBdCiB0aGlzIHBhdGggcmVmZXJzIHRvIHRoZSB3aG9sZSBmaWVsZCBkZWNsYXJhdGlvbiAoZnJvbSB0aGUgYmVnaW5uaW5nCiBvZiB0aGUgbGFiZWwgdG8gdGhlIHRlcm1pbmF0aW5nIHNlbWljb2xvbikuCgoPCgcEEwMAAgAEEgShBwQMCg8KBwQTAwACAAUSBKEHDRIKDwoHBBMDAAIAARIEoQcTFwoPCgcEEwMAAgADEgShBxobCg8KBwQTAwACAAgSBKEHHCsKEAoIBBMDAAIACAISBKEHHSoK0gIKBgQTAwACARIEqAcELBrBAiBBbHdheXMgaGFzIGV4YWN0bHkgdGhyZWUgb3IgZm91ciBlbGVtZW50czogc3RhcnQgbGluZSwgc3RhcnQgY29sdW1uLAogZW5kIGxpbmUgKG9wdGlvbmFsLCBvdGhlcndpc2UgYXNzdW1lZCBzYW1lIGFzIHN0YXJ0IGxpbmUpLCBlbmQgY29sdW1uLgogVGhlc2UgYXJlIHBhY2tlZCBpbnRvIGEgc2luZ2xlIGZpZWxkIGZvciBlZmZpY2llbmN5LiAgTm90ZSB0aGF0IGxpbmUKIGFuZCBjb2x1bW4gbnVtYmVycyBhcmUgemVyby1iYXNlZCAtLSB0eXBpY2FsbHkgeW91IHdpbGwgd2FudCB0byBhZGQKIDEgdG8gZWFjaCBiZWZvcmUgZGlzcGxheWluZyB0byBhIHVzZXIuCgoPCgcEEwMAAgEEEgSoBwQMCg8KBwQTAwACAQUSBKgHDRIKDwoHBBMDAAIBARIEqAcTFwoPCgcEEwMAAgEDEgSoBxobCg8KBwQTAwACAQgSBKgHHCsKEAoIBBMDAAIBCAISBKgHHSoKpQwKBgQTAwACAhIE2QcEKRqUDCBJZiB0aGlzIFNvdXJjZUNvZGVJbmZvIHJlcHJlc2VudHMgYSBjb21wbGV0ZSBkZWNsYXJhdGlvbiwgdGhlc2UgYXJlIGFueQogY29tbWVudHMgYXBwZWFyaW5nIGJlZm9yZSBhbmQgYWZ0ZXIgdGhlIGRlY2xhcmF0aW9uIHdoaWNoIGFwcGVhciB0byBiZQogYXR0YWNoZWQgdG8gdGhlIGRlY2xhcmF0aW9uLgoKIEEgc2VyaWVzIG9mIGxpbmUgY29tbWVudHMgYXBwZWFyaW5nIG9uIGNvbnNlY3V0aXZlIGxpbmVzLCB3aXRoIG5vIG90aGVyCiB0b2tlbnMgYXBwZWFyaW5nIG9uIHRob3NlIGxpbmVzLCB3aWxsIGJlIHRyZWF0ZWQgYXMgYSBzaW5nbGUgY29tbWVudC4KCiBsZWFkaW5nX2RldGFjaGVkX2NvbW1lbnRzIHdpbGwga2VlcCBwYXJhZ3JhcGhzIG9mIGNvbW1lbnRzIHRoYXQgYXBwZWFyCiBiZWZvcmUgKGJ1dCBub3QgY29ubmVjdGVkIHRvKSB0aGUgY3VycmVudCBlbGVtZW50LiBFYWNoIHBhcmFncmFwaCwKIHNlcGFyYXRlZCBieSBlbXB0eSBsaW5lcywgd2lsbCBiZSBvbmUgY29tbWVudCBlbGVtZW50IGluIHRoZSByZXBlYXRlZAogZmllbGQuCgogT25seSB0aGUgY29tbWVudCBjb250ZW50IGlzIHByb3ZpZGVkOyBjb21tZW50IG1hcmtlcnMgKGUuZy4gLy8pIGFyZQogc3RyaXBwZWQgb3V0LiAgRm9yIGJsb2NrIGNvbW1lbnRzLCBsZWFkaW5nIHdoaXRlc3BhY2UgYW5kIGFuIGFzdGVyaXNrCiB3aWxsIGJlIHN0cmlwcGVkIGZyb20gdGhlIGJlZ2lubmluZyBvZiBlYWNoIGxpbmUgb3RoZXIgdGhhbiB0aGUgZmlyc3QuCiBOZXdsaW5lcyBhcmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dC4KCiBFeGFtcGxlczoKCiAgIG9wdGlvbmFsIGludDMyIGZvbyA9IDE7ICAvLyBDb21tZW50IGF0dGFjaGVkIHRvIGZvby4KICAgLy8gQ29tbWVudCBhdHRhY2hlZCB0byBiYXIuCiAgIG9wdGlvbmFsIGludDMyIGJhciA9IDI7CgogICBvcHRpb25hbCBzdHJpbmcgYmF6ID0gMzsKICAgLy8gQ29tbWVudCBhdHRhY2hlZCB0byBiYXouCiAgIC8vIEFub3RoZXIgbGluZSBhdHRhY2hlZCB0byBiYXouCgogICAvLyBDb21tZW50IGF0dGFjaGVkIHRvIG1vby4KICAgLy8KICAgLy8gQW5vdGhlciBsaW5lIGF0dGFjaGVkIHRvIG1vby4KICAgb3B0aW9uYWwgZG91YmxlIG1vbyA9IDQ7CgogICAvLyBEZXRhY2hlZCBjb21tZW50IGZvciBjb3JnZS4gVGhpcyBpcyBub3QgbGVhZGluZyBvciB0cmFpbGluZyBjb21tZW50cwogICAvLyB0byBtb28gb3IgY29yZ2UgYmVjYXVzZSB0aGVyZSBhcmUgYmxhbmsgbGluZXMgc2VwYXJhdGluZyBpdCBmcm9tCiAgIC8vIGJvdGguCgogICAvLyBEZXRhY2hlZCBjb21tZW50IGZvciBjb3JnZSBwYXJhZ3JhcGggMi4KCiAgIG9wdGlvbmFsIHN0cmluZyBjb3JnZSA9IDU7CiAgIC8qIEJsb2NrIGNvbW1lbnQgYXR0YWNoZWQKICAgICogdG8gY29yZ2UuICBMZWFkaW5nIGFzdGVyaXNrcwogICAgKiB3aWxsIGJlIHJlbW92ZWQuICovCiAgIC8qIEJsb2NrIGNvbW1lbnQgYXR0YWNoZWQgdG8KICAgICogZ3JhdWx0LiAqLwogICBvcHRpb25hbCBpbnQzMiBncmF1bHQgPSA2OwoKICAgLy8gaWdub3JlZCBkZXRhY2hlZCBjb21tZW50cy4KCg8KBwQTAwACAgQSBNkHBAwKDwoHBBMDAAICBRIE2QcN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GiogVGhlIGVsZW1lbnQgaXMgc2V0IG9yIG90aGVyd2lzZSBtdXRhdGVkLgoKEQoJBBQDAAQAAgEBEgT9BwYJChEKCQQUAwAEAAIBAhIE/QcMDQo4CggEFAMABAACAhIE/wcGEBomIEFuIGFsaWFzIHRvIHRoZSBlbGVtZW50IGlzIHJldHVybmVkLgoKEQoJBBQDAAQAAgIBEgT/BwYLChEKCQQUAwAEAAICAhIE/wcODwoOCgYEFAMAAgQSBIEIBCMKDwoHBBQDAAIEBBIEgQgEDAoPCgcEFAMAAgQGEgSBCA0VCg8KBwQUAwACBAESBIEIFh4KDwoHBBQDAAIEAxIEgQghIgrUCAocZ29vZ2xlL2FwaS9hbm5vdGF0aW9ucy5wcm90bxIKZ29vZ2xlLmFwaRoVZ29vZ2xlL2FwaS9odHRwLnByb3RvGiBnb29nbGUvcHJvdG9idWYvZGVzY3JpcHRvci5wcm90bzpLCgRodHRwEh4uZ29vZ2xlLnByb3RvYnVmLk1ldGhvZE9wdGlvbnMYsMq8IiABKAsyFC5nb29nbGUuYXBpLkh0dHBSdWxlUgRodHRwQm4KDmNvbS5nb29nbGUuYXBpQhBBbm5vdGF0aW9uc1Byb3RvUAFaQWdvb2dsZS5nb2xhbmcub3JnL2dlbnByb3RvL2dvb2dsZWFwaXMvYXBpL2Fubm90YXRpb25zO2Fubm90YXRpb25zogIER0FQSUqpBgoGEgQOAB4BCrwECgEMEgMOABIysQQgQ29weXJpZ2h0IDIwMTUgR29vZ2xlIExMQwoKIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOwogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLgogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0CgogICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMAoKIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmUKIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuICJBUyBJUyIgQkFTSVMsCiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC4KIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmQKIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLgoKCAoBAhIDEAATCgkKAgMAEgMSAB8KCQoCAwESAxMAKgoICgEIEgMVAFgKCQoCCAsSAxUAWAoICgEIEgMWACIKCQoCCAoSAxYAIgoICgEIEgMXADEKCQoCCAgSAxcAMQoICgEIEgMYACcKCQoCCAESAxgAJwoICgEIEgMZACIKCQoCCCQSAxkAIgoJCgEHEgQbAB4BChwKAgcAEgMdAhsaESBTZWUgYEh0dHBSdWxlYC4KCgoKAwcAAhIDGwckCgoKAwcABhIDHQIKCgoKAwcAARIDHQsPCgoKAwcAAxIDHRIaYgZwcm90bzMKyA4KEGhlbGxvd29ybGQucHJvdG8SCmhlbGxvd29ybGQaHGdvb2dsZS9hcGkvYW5ub3RhdGlvbnMucHJvdG8iIgoMSGVsbG9SZXF1ZXN0EhIKBG5hbWUYASABKAlSBG5hbWUiJgoKSGVsbG9SZXBseRIYCgdtZXNzYWdlGAEgASgJUgdtZXNzYWdlMroCCgdHcmVldGVyEk8KCFNheUhlbGxvEhguaGVsbG93b3JsZC5IZWxsb1JlcXVlc3QaFi5oZWxsb3dvcmxkLkhlbGxvUmVwbHkiEYLT5JMCCxIJL3YxL2hlbGxvEkMKDVNheUhlbGxvQWdhaW4SGC5oZWxsb3dvcmxkLkhlbGxvUmVxdWVzdBoWLmhlbGxvd29ybGQuSGVsbG9SZXBseSIAEksKE1NheUhlbGxvU3RyZWFtUmVwbHkSGC5oZWxsb3dvcmxkLkhlbGxvUmVxdWVzdBoWLmhlbGxvd29ybGQuSGVsbG9SZXBseSIAMAESTAoSU2F5SGVsbG9CaWRpU3RyZWFtEhguaGVsbG93b3JsZC5IZWxsb1JlcXVlc3QaFi5oZWxsb3dvcmxkLkhlbGxvUmVwbHkiACgBMAFCNgobaW8uZ3JwYy5leGFtcGxlcy5oZWxsb3dvcmxkQg9IZWxsb1dvcmxkUHJvdG9QAaICA0hMV0rACgoGEgQOADIBCr8ECgEMEgMOABIytAQgQ29weXJpZ2h0IDIwMTUgZ1JQQyBhdXRob3JzLgoKIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOwogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLgogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0CgogICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMAoKIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmUKIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuICJBUyBJUyIgQkFTSVMsCiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC4KIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmQKIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLgoKCAoBCBIDEAAiCgkKAggKEgMQACIKCAoBCBIDEQA0CgkKAggBEgMRADQKCAoBCBIDEgAwCgkKAggIEgMSADAKCAoBCBIDEwAhCgkKAggkEgMTACEKCAoBAhIDFQATCgkKAgMAEgMXACYKLgoCBgASBBoAKAEaIiBUaGUgZ3JlZXRpbmcgc2VydmljZSBkZWZpbml0aW9uLgoKCgoDBgABEgMaCA8KIAoEBgACABIEHAIgAxoSIFNlbmRzIGEgZ3JlZXRpbmcKCgwKBQYAAgABEgMcBg4KDAoFBgACAAISAxwQHAoMCgUGAAIAAxIDHCcxCg0KBQYAAgAEEgQdBB8GChEKCQYAAgAEsMq8IhIEHQQfBgodCgQGAAIBEgMjAjoaECBBbm90aGVyIE1ldGhvZAoKDAoFBgACAQESAyMGEwoMCgUGAAIBAhIDIxUhCgwKBQYAAgEDEgMjLDYKCwoEBgACAhIDJQJHCgwKBQYAAgIBEgMlBhkKDAoFBgACAgISAyUbJwoMCgUGAAICBhIDJTI4CgwKBQYAAgIDEgMlOUMKCwoEBgACAxIDJwJNCgwKBQYAAgMBEgMnBhgKDAoFBgACAwUSAycaIAoMCgUGAAIDAhIDJyEtCgwKBQYAAgMGEgMnOD4KDAoFBgACAwMSAyc/SQo9CgIEABIEKwAtARoxIFRoZSByZXF1ZXN0IG1lc3NhZ2UgY29udGFpbmluZyB0aGUgdXNlcidzIG5hbWUuCgoKCgMEAAESAysIFAoLCgQEAAIAEgMsAhIKDAoFBAACAAUSAywCCAoMCgUEAAIAARIDLAkNCgwKBQQAAgADEgMsEBEKOwoCBAESBDAAMgEaLyBUaGUgcmVzcG9uc2UgbWVzc2FnZSBjb250YWluaW5nIHRoZSBncmVldGluZ3MKCgoKAwQBARIDMAgSCgsKBAQBAgASAzECFQoMCgUEAQIABRIDMQIICgwKBQQBAgABEgMxCRAKDAoFBAECAAMSAzETFGIGcHJvdG8z

위와 같은 EnvoyFilter 를 사용해서 HTTP + gRPC 를 동시에 사용가능한 gRPC 서버를 구축 가능합니다. 일단 위 예제에서는 애플리케이션 파드 의 사이드카에 이 필터를 삽입해서 처리를 하는 예제입니다. (context 가 SIDECAR_INBOUND 이기 때문에 .. 필요에 따라 ingress gateway 쪽에 이 필터를 삽입 할 수도 있습니다.)  

여기서 주의깊게 봐야하는것은 listener.portNumberpatch.value.services , patch.value.proto_descriptor_bin 부분입니다.

listener.portNumber 의 경우 gRPC 서버 이미지를 띄운 애플리케이션의 gRPC 포트번호를 지정해야 합니다.  

patch.value.services 에는 어떤 서비스들에 대해서 이 규칙을 사용해 gRPC 서버에 http json transcoder 를 적용해서 요청할지에 대한 부분이고.

patch.value.proto_descriptor_bin 의 경우 protobuf descriptor 를 base64 인코딩한 값을 넣어주었습니다.

당연하게도, 위 EnvoyFilter 를 적용하려면 gRPC 서버 애플리케이션 파드에 Istio Proxy(Enovy) 사이드카가 떠있어야 합니다!

아래는 gRPC 서버에 대한 deployment.yaml 예시입니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: grpc-python
spec:
  replicas: 1
  selector:
    matchLabels:
      app: grpc-python
  template:
    metadata:
      labels:
        app: grpc-python
        sidecar.istio.io/inject: 'true'
      annotations:
         sidecar.istio.io/rewriteAppHTTPProbers: "false"
      # 위 어노테이션을 달지 않으면 istio-sidecar-injector 가 mutatingWebhook 에서 강제로 http-get 으로 수정한다.
      # 글로벌하게 설정하고 싶으면 istio-sidecar-injector 의 configmap 을 수정하면 된다.
      # https://preliminary.istio.io/latest/docs/ops/configuration/mesh/app-health-check/#disable-the-http-probe-rewrite-for-a-pod
    spec:
      containers:
        - name: grpc-python
          image: kimsehwan96/python-grpc
          imagePullPolicy: Always
          ports:
            - containerPort: 7777
              name: grpc
          resources:
            requests:
              cpu: 500m
            limits:
              cpu: 500m
          readinessProbe:
            grpc:
              port: 7777

위와 같이 istio sidecar 를 label 을 통해 inject 하도록 명시하였고, annotationssidecar.istio.io/rewriteAppHTTPProbers: "false" 은 주석에도 달려있지만, gRPC HealthCheck 프로브를 istio가 mutating Webhook 에서 강제로 덮어쓰지 않도록 방지하는 내용입니다.

(yaml 파일 예제는 https://github.com/kimsehwan96/istio-example/tree/master/grpc-python 여기에 있습니다.)

위 내용을 $ kubectl kustomize --enable-helm . | kubectl apply -f - --server-side --force-conflicts 로 배포해봅니다.

kustomize 를 통해 위 yaml 들을 하나의 yaml로 묶어서 배포하기 위해 위와 같은 명령어를 사용하였습니다. (그래서 Deployment 든, EnvoyFilter 든 네임스페이스 레이블이 빠져있는데, 실제로는 kustomization.yaml 항목에 지정되어있습니다.)

Deployment 의 배포 결과
EnvoyFilter 가 grpc-python 네임스페이스에 적용됨

테스트

GitHub - kimsehwan96/gRPC-python-example: 테스트용
테스트용. Contribute to kimsehwan96/gRPC-python-example development by creating an account on GitHub.

 테스트를 위한 grpc 서버는 위 링크에 구현되어있고, 편의를 위해 gRPC reflection 등을 통해 어떤 메서드들이 노출되는지 볼 수 있게 세팅하고, + 기본적으로 제공되는 health check 기능을 넣어두었습니다.

(health check 기능 추가 라인 : https://github.com/kimsehwan96/gRPC-python-example/blob/266ec547524d7afb73f10ad596e8b0eb96759eea/greeter_server.py#L54-L58)

당연하게도 gRPC 요청은 잘 됩니다.

gRPC 가 아닌 일반 HTTP 호출

https://github.com/kimsehwan96/gRPC-python-example/blob/266ec547524d7afb73f10ad596e8b0eb96759eea/proto/helloworld.proto#L27-L33

위 코드에서 정의했듯이 /v1/hello 에 대해서 rpc SayHello (HelloRequest) returns (HelloReply) 를 호출하도록 하였는데, 정상적으로 HTTP 요청으로 호출되는것을 볼 수 있습니다. 이 때 Content-Type 헤더를 application/json 으로 명시적으로 헤더에 담아서 요청해야 처리됩니다.

EnvoyFilter 를 제거하고 호출해보면

이렇게 처리할 수 없는 헤더로 요청이 들어왔기때문에 요청이 처리되지 않습니다.

gRPC HealthCheck in K8s

 

k8s v1.27 부터 stable 하게 들어온 기능

Configure Liveness, Readiness and Startup Probes  

이것을 사용하기 위해서는 gRPC 서버 측에 Health Check 기능이 GRPC Health Checking Protocol 에 맞게 구현되어있으면 되고 (프로토콜 : https://github.com/grpc/grpc/blob/master/doc/health-checking.md )

각 언어별로 위 프로토콜 문서를 따른 gRPC HealthCheck 구현체가 있기때문에, 단순하게 서버에 삽입하기만 하면 사용 가능합니다.

(https://github.com/kimsehwan96/gRPC-python-example/blob/266ec547524d7afb73f10ad596e8b0eb96759eea/greeter_server.py#L54-L58)

apiVersion: apps/v1
kind: Deployment
metadata:
  ...
spec:
  ...
  template:
    ...
    spec:
      containers:
        - name: grpc-python
          ...
          readinessProbe:
            grpc:
              port: 7777

위와 같이 readinessProbe / livenessProbe 등에 grpc.portgrpc.service 를 지정하면 사용 가능하고. service 를 지정하지 않으면 GRPC Health Checking Protocol 에서 사용하는 기본적인 서비스.메서드로 프로브를 호출하게 됩니다. (매우 간편)

 

Proto Descriptor 를 base64 로 직접 envoy filter 에 넣지않고 ConfigMap 기반으로 경로를 지정해 세팅하는 방법

 

kustomizeconfigMapGenerator 를 사용합니다. (템플리팅 툴으로 helm 을 쓴다면 helm 에도 비슷한 기능이 있습니다. kustomize 든 helm 이든 yaml 관리를 위한 툴을 사용하지 않고서는 사용하기 어려움)

https://github.com/kimsehwan96/istio-example/blob/73f993ffead22376edbd74f5905091b575c30679/grpc-python/helloworld.pb

위 파일처럼 kustomization.yaml 이 있는 디렉터리에 바이너리로 된 proto descriptor (pb) 파일을 넣어둡니다.

namespace: grpc-python

resources:
  - ./namespace.yaml
  - ./deployment.yaml
  - ./service.yaml
  - ./mtls.yaml
  - ./grpc-virtualservice.yaml
  - ./grpc-gateway.yaml
  - ./envoy-filter.yaml

configMapGenerator:
  - name: grpc-python-proto-descriptor-configmap
    files:
      - ./helloworld.pb

generatorOptions:
  disableNameSuffixHash: true

kustomization.yamlconfigMapGenerator 를 이용해 바이너리 파일을 configMap 으로 생성합니다.

위 사진처럼 로컬에 있는 파일이 base64 인코딩 되어서 configMap 으로 알아서 잘 들어갑니다. 이때의 configMap binary Data 의 키는 파일명과 동일(helloworld.pb)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: grpc-python
spec:
  replicas: 1
  selector:
    matchLabels:
      app: grpc-python
  template:
    metadata:
      labels:
        app: grpc-python
        sidecar.istio.io/inject: 'true'
      annotations:
        sidecar.istio.io/rewriteAppHTTPProbers: "false"
      # 위 어노테이션을 달지 않으면 istio-sidecar-injector 가 mutatingWebhook 에서 강제로 http-get 으로 수정한다.
      # 글로벌하게 설정하고 싶으면 istio-sidecar-injector 의 configmap 을 수정하면 된다.
      # https://preliminary.istio.io/latest/docs/ops/configuration/mesh/app-health-check/#disable-the-http-probe-rewrite-for-a-pod
        sidecar.istio.io/userVolumeMount: '[{"name":"proto-descriptor","mountPath":"/etc/envoy","readOnly":true}]'
        sidecar.istio.io/userVolume: '[{"name":"proto-descriptor","configMap":{"name":"grpc-python-proto-descriptor-configmap","items":[{"key":"helloworld.pb","path":"helloworld.pb"}]}}]'
...
...

Deployment 오브젝트에 istio 어노테이션을 사용해서 configMapsidecar 컨테이너에 마운트 하도록 합니다. sidecar.istio.io/userVolumeMount , sidecar.istio.io/userVolume 어노테이션을 이용합니다.

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: grpc-transcoder
spec:
  workloadSelector:
    labels:
      app: grpc-python
  configPatches:
    - applyTo: HTTP_FILTER
      match:
        context: SIDECAR_INBOUND
        listener:
          portNumber: 7777 # 5xxxx 번대 포트를 지정하면 반영이 안된다. 이건 왜그런지;
          filterChain:
            filter:
              name: "envoy.filters.network.http_connection_manager"
              subFilter:
                name: "envoy.filters.http.router"
      patch:
        operation: INSERT_BEFORE
        value:
          name: envoy.filters.http.grpc_json_transcoder
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
            services:
              - helloworld.Greeter
            print_options:
              add_whitespace: true
              always_print_enums_as_ints: false
              always_print_primitive_fields: true
              preserve_proto_field_names: false
            convert_grpc_status: true
            proto_descriptor: /etc/envoy/helloworld.pb

EnvoyFilter 오브젝트에서는 proto_descriptor 를 사용해서 사이드카 내에 proto descriptor 가 있는 경로를 지정해서 사용합니다.

만약 kustomize 가 아닌 helm 으로만 네이티브하게 구현한다면

# configMap.yml
kind: ConfigMap
apiVersion: v1
data:
binaryData:
  {{- $binaryFiles := $.Files }}  
  {{- range .binaryFiles }}
  {{ base .}}: |-
    {{- $binaryFiles.Get (printf "%s/%s" "configs" . ) | b64enc | nindent 6 -}}
  {{- end}}
# values.yaml
configs:
  - name: proto-descriptor
    binaryFiles:
      - api_descriptor.pb

위와 같이 helm 에서 사용 가능한 템플리팅 함수들이나 기능을 이용해서 구현하면 됩니다. 다만 kustomizehelm 이든 proto descriptor 파일이 helm / kustomize 명령어들을 수행하는 디렉터리와 같은 위치에 있어야 한다는게 단점? 일수도 있습니다.

위 상황에서, proto descriptor 파일이 바뀌었을 때 언제 반영이 되는가?

 

테스트 결과, configMap 의 바이너리( proto descriptor )를 바꾸더라도 기존에 떠있는 파드 에는 영향이 가지 않습니다. 파드를 내렸다가 올리는 경우에만 반영이 됩니다. 따라서 실제 프로덕션 환경에서는 proto descriptor 업데이트 이후 모든 Deployment / StatefulSet 의 파드를 rolloing update 하거나, canary 배포 하는등 아무튼 모든 파드가 내려갔다가 올라갈 필요가 있습니다.

참고 : https://github.com/grpc-ecosystem/grpc-gateway