From 1165a8cd932e8d2dbf10f1f1d3f5068559674ff9 Mon Sep 17 00:00:00 2001 From: Andre Fredette Date: Fri, 6 Dec 2024 09:54:15 -0500 Subject: [PATCH] Add support for attaching TCX, TC and XDP programs inside containers. (#345) * Add ContainerSelector to tc, tcx, and xdp programs * Add netns support for TCX, TC and XDP programs. GetPrimaryNodeInterface() only finds the primary "node" interface, which may or may not be the name of the primary interface inside of a container. Signed-off-by: Andre Fredette --- apis/v1alpha1/tcProgram_types.go | 6 + apis/v1alpha1/tcxProgram_types.go | 6 + apis/v1alpha1/xdpProgram_types.go | 6 + apis/v1alpha1/zz_generated.deepcopy.go | 15 + ...bpfman-operator.clusterserviceversion.yaml | 2 +- .../manifests/bpfman.io_bpfapplications.yaml | 204 ++++++++ bundle/manifests/bpfman.io_tcprograms.yaml | 68 +++ bundle/manifests/bpfman.io_tcxprograms.yaml | 68 +++ bundle/manifests/bpfman.io_xdpprograms.yaml | 68 +++ .../crd/bases/bpfman.io_bpfapplications.yaml | 204 ++++++++ config/crd/bases/bpfman.io_tcprograms.yaml | 68 +++ config/crd/bases/bpfman.io_tcxprograms.yaml | 68 +++ config/crd/bases/bpfman.io_xdpprograms.yaml | 68 +++ .../bpfman.io_v1alpha1_bpfapp-containers.yaml | 57 +++ ...n.io_v1alpha1_tc_containers_tcprogram.yaml | 34 ++ ...io_v1alpha1_tcx_containers_tcxprogram.yaml | 34 ++ ...fman.io_v1alpha1_tcx_pass_tcxprogram.yaml} | 0 ...io_v1alpha1_xdp_containers_xdpprogram.yaml | 33 ++ controllers/bpfman-agent/containers.go | 30 ++ controllers/bpfman-agent/tc-program.go | 99 +++- controllers/bpfman-agent/tcx-program.go | 98 +++- controllers/bpfman-agent/uprobe-program.go | 40 +- controllers/bpfman-agent/xdp-program.go | 94 +++- go.mod | 4 +- go.sum | 4 +- internal/constants.go | 6 + .../bpfman/clients/gobpfman/v1/bpfman.pb.go | 460 ++++++++++-------- vendor/modules.txt | 2 +- 28 files changed, 1561 insertions(+), 285 deletions(-) create mode 100644 config/samples/bpfman.io_v1alpha1_bpfapp-containers.yaml create mode 100644 config/samples/bpfman.io_v1alpha1_tc_containers_tcprogram.yaml create mode 100644 config/samples/bpfman.io_v1alpha1_tcx_containers_tcxprogram.yaml rename config/samples/{bpfman.io_v1alpha1_tcx_pass_tcprogram.yaml => bpfman.io_v1alpha1_tcx_pass_tcxprogram.yaml} (100%) create mode 100644 config/samples/bpfman.io_v1alpha1_xdp_containers_xdpprogram.yaml diff --git a/apis/v1alpha1/tcProgram_types.go b/apis/v1alpha1/tcProgram_types.go index 24ed566ab..6db1481b7 100644 --- a/apis/v1alpha1/tcProgram_types.go +++ b/apis/v1alpha1/tcProgram_types.go @@ -61,6 +61,12 @@ type TcProgramInfo struct { // Selector to determine the network interface (or interfaces) InterfaceSelector InterfaceSelector `json:"interfaceselector"` + // Containers identifes the set of containers in which to attach the eBPF + // program. If Containers is not specified, the BPF program will be attached + // in the root network namespace. + // +optional + Containers *ContainerSelector `json:"containers"` + // Priority specifies the priority of the tc program in relation to // other programs of the same type with the same attach point. It is a value // from 0 to 1000 where lower values have higher precedence. diff --git a/apis/v1alpha1/tcxProgram_types.go b/apis/v1alpha1/tcxProgram_types.go index 08648ae87..6036ca8a4 100644 --- a/apis/v1alpha1/tcxProgram_types.go +++ b/apis/v1alpha1/tcxProgram_types.go @@ -58,6 +58,12 @@ type TcxProgramInfo struct { // Selector to determine the network interface (or interfaces) InterfaceSelector InterfaceSelector `json:"interfaceselector"` + // Containers identifes the set of containers in which to attach the eBPF + // program. If Containers is not specified, the BPF program will be attached + // in the root network namespace. + // +optional + Containers *ContainerSelector `json:"containers"` + // Direction specifies the direction of traffic the tcx program should // attach to for a given network device. // +kubebuilder:validation:Enum=ingress;egress diff --git a/apis/v1alpha1/xdpProgram_types.go b/apis/v1alpha1/xdpProgram_types.go index 8ec1ac072..83f023989 100644 --- a/apis/v1alpha1/xdpProgram_types.go +++ b/apis/v1alpha1/xdpProgram_types.go @@ -59,6 +59,12 @@ type XdpProgramInfo struct { // Selector to determine the network interface (or interfaces) InterfaceSelector InterfaceSelector `json:"interfaceselector"` + // Containers identifes the set of containers in which to attach the eBPF + // program. If Containers is not specified, the BPF program will be attached + // in the root network namespace. + // +optional + Containers *ContainerSelector `json:"containers"` + // Priority specifies the priority of the bpf program in relation to // other programs of the same type with the same attach point. It is a value // from 0 to 1000 where lower values have higher precedence. diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 80f3b52cd..3954623d5 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -825,6 +825,11 @@ func (in *TcProgramInfo) DeepCopyInto(out *TcProgramInfo) { *out = *in in.BpfProgramCommon.DeepCopyInto(&out.BpfProgramCommon) in.InterfaceSelector.DeepCopyInto(&out.InterfaceSelector) + if in.Containers != nil { + in, out := &in.Containers, &out.Containers + *out = new(ContainerSelector) + (*in).DeepCopyInto(*out) + } if in.ProceedOn != nil { in, out := &in.ProceedOn, &out.ProceedOn *out = make([]TcProceedOnValue, len(*in)) @@ -939,6 +944,11 @@ func (in *TcxProgramInfo) DeepCopyInto(out *TcxProgramInfo) { *out = *in in.BpfProgramCommon.DeepCopyInto(&out.BpfProgramCommon) in.InterfaceSelector.DeepCopyInto(&out.InterfaceSelector) + if in.Containers != nil { + in, out := &in.Containers, &out.Containers + *out = new(ContainerSelector) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TcxProgramInfo. @@ -1274,6 +1284,11 @@ func (in *XdpProgramInfo) DeepCopyInto(out *XdpProgramInfo) { *out = *in in.BpfProgramCommon.DeepCopyInto(&out.BpfProgramCommon) in.InterfaceSelector.DeepCopyInto(&out.InterfaceSelector) + if in.Containers != nil { + in, out := &in.Containers, &out.Containers + *out = new(ContainerSelector) + (*in).DeepCopyInto(*out) + } if in.ProceedOn != nil { in, out := &in.ProceedOn, &out.ProceedOn *out = make([]XdpProceedOnValue, len(*in)) diff --git a/bundle/manifests/bpfman-operator.clusterserviceversion.yaml b/bundle/manifests/bpfman-operator.clusterserviceversion.yaml index 4e5708825..5e79d542d 100644 --- a/bundle/manifests/bpfman-operator.clusterserviceversion.yaml +++ b/bundle/manifests/bpfman-operator.clusterserviceversion.yaml @@ -307,7 +307,7 @@ metadata: capabilities: Basic Install categories: OpenShift Optional containerImage: quay.io/bpfman/bpfman-operator:latest - createdAt: "2024-11-01T20:38:03Z" + createdAt: "2024-12-06T14:27:05Z" features.operators.openshift.io/cnf: "false" features.operators.openshift.io/cni: "false" features.operators.openshift.io/csi: "true" diff --git a/bundle/manifests/bpfman.io_bpfapplications.yaml b/bundle/manifests/bpfman.io_bpfapplications.yaml index 4f6a53fdd..cd8cc45cd 100644 --- a/bundle/manifests/bpfman.io_bpfapplications.yaml +++ b/bundle/manifests/bpfman.io_bpfapplications.yaml @@ -459,6 +459,74 @@ spec: BpfFunctionName is the name of the function that is the entry point for the BPF program type: string + containers: + description: |- + Containers identifes the set of containers in which to attach the eBPF + program. If Containers is not specified, the BPF program will be attached + in the root network namespace. + properties: + containernames: + description: |- + Name(s) of container(s). If none are specified, all containers in the + pod are selected. + items: + type: string + type: array + namespace: + default: "" + description: Target namespaces. + type: string + pods: + description: |- + Target pods. This field must be specified, to select all pods use + standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - pods + type: object direction: description: |- Direction specifies the direction of traffic the tc program should @@ -582,6 +650,74 @@ spec: BpfFunctionName is the name of the function that is the entry point for the BPF program type: string + containers: + description: |- + Containers identifes the set of containers in which to attach the eBPF + program. If Containers is not specified, the BPF program will be attached + in the root network namespace. + properties: + containernames: + description: |- + Name(s) of container(s). If none are specified, all containers in the + pod are selected. + items: + type: string + type: array + namespace: + default: "" + description: Target namespaces. + type: string + pods: + description: |- + Target pods. This field must be specified, to select all pods use + standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - pods + type: object direction: description: |- Direction specifies the direction of traffic the tcx program should @@ -1082,6 +1218,74 @@ spec: BpfFunctionName is the name of the function that is the entry point for the BPF program type: string + containers: + description: |- + Containers identifes the set of containers in which to attach the eBPF + program. If Containers is not specified, the BPF program will be attached + in the root network namespace. + properties: + containernames: + description: |- + Name(s) of container(s). If none are specified, all containers in the + pod are selected. + items: + type: string + type: array + namespace: + default: "" + description: Target namespaces. + type: string + pods: + description: |- + Target pods. This field must be specified, to select all pods use + standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - pods + type: object interfaceselector: description: Selector to determine the network interface (or interfaces) diff --git a/bundle/manifests/bpfman.io_tcprograms.yaml b/bundle/manifests/bpfman.io_tcprograms.yaml index ca3dec73e..db4073a6c 100644 --- a/bundle/manifests/bpfman.io_tcprograms.yaml +++ b/bundle/manifests/bpfman.io_tcprograms.yaml @@ -115,6 +115,74 @@ spec: description: Path is used to specify a bytecode object via filepath. type: string type: object + containers: + description: |- + Containers identifes the set of containers in which to attach the eBPF + program. If Containers is not specified, the BPF program will be attached + in the root network namespace. + properties: + containernames: + description: |- + Name(s) of container(s). If none are specified, all containers in the + pod are selected. + items: + type: string + type: array + namespace: + default: "" + description: Target namespaces. + type: string + pods: + description: |- + Target pods. This field must be specified, to select all pods use + standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - pods + type: object direction: description: |- Direction specifies the direction of traffic the tc program should diff --git a/bundle/manifests/bpfman.io_tcxprograms.yaml b/bundle/manifests/bpfman.io_tcxprograms.yaml index e230c0dac..f176c2f80 100644 --- a/bundle/manifests/bpfman.io_tcxprograms.yaml +++ b/bundle/manifests/bpfman.io_tcxprograms.yaml @@ -115,6 +115,74 @@ spec: description: Path is used to specify a bytecode object via filepath. type: string type: object + containers: + description: |- + Containers identifes the set of containers in which to attach the eBPF + program. If Containers is not specified, the BPF program will be attached + in the root network namespace. + properties: + containernames: + description: |- + Name(s) of container(s). If none are specified, all containers in the + pod are selected. + items: + type: string + type: array + namespace: + default: "" + description: Target namespaces. + type: string + pods: + description: |- + Target pods. This field must be specified, to select all pods use + standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - pods + type: object direction: description: |- Direction specifies the direction of traffic the tcx program should diff --git a/bundle/manifests/bpfman.io_xdpprograms.yaml b/bundle/manifests/bpfman.io_xdpprograms.yaml index 94ce6b7c4..31791544f 100644 --- a/bundle/manifests/bpfman.io_xdpprograms.yaml +++ b/bundle/manifests/bpfman.io_xdpprograms.yaml @@ -111,6 +111,74 @@ spec: description: Path is used to specify a bytecode object via filepath. type: string type: object + containers: + description: |- + Containers identifes the set of containers in which to attach the eBPF + program. If Containers is not specified, the BPF program will be attached + in the root network namespace. + properties: + containernames: + description: |- + Name(s) of container(s). If none are specified, all containers in the + pod are selected. + items: + type: string + type: array + namespace: + default: "" + description: Target namespaces. + type: string + pods: + description: |- + Target pods. This field must be specified, to select all pods use + standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - pods + type: object globaldata: additionalProperties: format: byte diff --git a/config/crd/bases/bpfman.io_bpfapplications.yaml b/config/crd/bases/bpfman.io_bpfapplications.yaml index 9bc282ef1..cc775ec1a 100644 --- a/config/crd/bases/bpfman.io_bpfapplications.yaml +++ b/config/crd/bases/bpfman.io_bpfapplications.yaml @@ -459,6 +459,74 @@ spec: BpfFunctionName is the name of the function that is the entry point for the BPF program type: string + containers: + description: |- + Containers identifes the set of containers in which to attach the eBPF + program. If Containers is not specified, the BPF program will be attached + in the root network namespace. + properties: + containernames: + description: |- + Name(s) of container(s). If none are specified, all containers in the + pod are selected. + items: + type: string + type: array + namespace: + default: "" + description: Target namespaces. + type: string + pods: + description: |- + Target pods. This field must be specified, to select all pods use + standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - pods + type: object direction: description: |- Direction specifies the direction of traffic the tc program should @@ -582,6 +650,74 @@ spec: BpfFunctionName is the name of the function that is the entry point for the BPF program type: string + containers: + description: |- + Containers identifes the set of containers in which to attach the eBPF + program. If Containers is not specified, the BPF program will be attached + in the root network namespace. + properties: + containernames: + description: |- + Name(s) of container(s). If none are specified, all containers in the + pod are selected. + items: + type: string + type: array + namespace: + default: "" + description: Target namespaces. + type: string + pods: + description: |- + Target pods. This field must be specified, to select all pods use + standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - pods + type: object direction: description: |- Direction specifies the direction of traffic the tcx program should @@ -1082,6 +1218,74 @@ spec: BpfFunctionName is the name of the function that is the entry point for the BPF program type: string + containers: + description: |- + Containers identifes the set of containers in which to attach the eBPF + program. If Containers is not specified, the BPF program will be attached + in the root network namespace. + properties: + containernames: + description: |- + Name(s) of container(s). If none are specified, all containers in the + pod are selected. + items: + type: string + type: array + namespace: + default: "" + description: Target namespaces. + type: string + pods: + description: |- + Target pods. This field must be specified, to select all pods use + standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - pods + type: object interfaceselector: description: Selector to determine the network interface (or interfaces) diff --git a/config/crd/bases/bpfman.io_tcprograms.yaml b/config/crd/bases/bpfman.io_tcprograms.yaml index 29df476c7..713629be2 100644 --- a/config/crd/bases/bpfman.io_tcprograms.yaml +++ b/config/crd/bases/bpfman.io_tcprograms.yaml @@ -115,6 +115,74 @@ spec: description: Path is used to specify a bytecode object via filepath. type: string type: object + containers: + description: |- + Containers identifes the set of containers in which to attach the eBPF + program. If Containers is not specified, the BPF program will be attached + in the root network namespace. + properties: + containernames: + description: |- + Name(s) of container(s). If none are specified, all containers in the + pod are selected. + items: + type: string + type: array + namespace: + default: "" + description: Target namespaces. + type: string + pods: + description: |- + Target pods. This field must be specified, to select all pods use + standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - pods + type: object direction: description: |- Direction specifies the direction of traffic the tc program should diff --git a/config/crd/bases/bpfman.io_tcxprograms.yaml b/config/crd/bases/bpfman.io_tcxprograms.yaml index 5b813f761..d0eca6137 100644 --- a/config/crd/bases/bpfman.io_tcxprograms.yaml +++ b/config/crd/bases/bpfman.io_tcxprograms.yaml @@ -115,6 +115,74 @@ spec: description: Path is used to specify a bytecode object via filepath. type: string type: object + containers: + description: |- + Containers identifes the set of containers in which to attach the eBPF + program. If Containers is not specified, the BPF program will be attached + in the root network namespace. + properties: + containernames: + description: |- + Name(s) of container(s). If none are specified, all containers in the + pod are selected. + items: + type: string + type: array + namespace: + default: "" + description: Target namespaces. + type: string + pods: + description: |- + Target pods. This field must be specified, to select all pods use + standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - pods + type: object direction: description: |- Direction specifies the direction of traffic the tcx program should diff --git a/config/crd/bases/bpfman.io_xdpprograms.yaml b/config/crd/bases/bpfman.io_xdpprograms.yaml index 131fd0435..3e15190b0 100644 --- a/config/crd/bases/bpfman.io_xdpprograms.yaml +++ b/config/crd/bases/bpfman.io_xdpprograms.yaml @@ -111,6 +111,74 @@ spec: description: Path is used to specify a bytecode object via filepath. type: string type: object + containers: + description: |- + Containers identifes the set of containers in which to attach the eBPF + program. If Containers is not specified, the BPF program will be attached + in the root network namespace. + properties: + containernames: + description: |- + Name(s) of container(s). If none are specified, all containers in the + pod are selected. + items: + type: string + type: array + namespace: + default: "" + description: Target namespaces. + type: string + pods: + description: |- + Target pods. This field must be specified, to select all pods use + standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - pods + type: object globaldata: additionalProperties: format: byte diff --git a/config/samples/bpfman.io_v1alpha1_bpfapp-containers.yaml b/config/samples/bpfman.io_v1alpha1_bpfapp-containers.yaml new file mode 100644 index 000000000..db2acc4d2 --- /dev/null +++ b/config/samples/bpfman.io_v1alpha1_bpfapp-containers.yaml @@ -0,0 +1,57 @@ +apiVersion: bpfman.io/v1alpha1 +kind: BpfApplication +metadata: + labels: + app.kubernetes.io/name: bpfapplication + name: bpfapp-containers +spec: + # Select all nodes + nodeselector: {} + bytecode: + image: + url: quay.io/bpfman-bytecode/go-app-counter:latest + programs: + - type: TC + tc: + bpffunctionname: stats + interfaceselector: + primarynodeinterface: true + priority: 55 + direction: ingress + containers: + namespace: bpfman + pods: + matchLabels: + name: bpfman-daemon + containernames: + - bpfman + - bpfman-agent + - type: TCX + tcx: + bpffunctionname: tcx_stats + interfaceselector: + primarynodeinterface: true + priority: 500 + direction: ingress + containers: + namespace: bpfman + pods: + matchLabels: + name: bpfman-daemon + containernames: + - bpfman + - bpfman-agent + - type: XDP + xdp: + bpffunctionname: xdp_stats + interfaceselector: + primarynodeinterface: true + priority: 55 + containers: + namespace: bpfman + pods: + matchLabels: + name: bpfman-daemon + containernames: + - bpfman + - bpfman-agent diff --git a/config/samples/bpfman.io_v1alpha1_tc_containers_tcprogram.yaml b/config/samples/bpfman.io_v1alpha1_tc_containers_tcprogram.yaml new file mode 100644 index 000000000..723b2b478 --- /dev/null +++ b/config/samples/bpfman.io_v1alpha1_tc_containers_tcprogram.yaml @@ -0,0 +1,34 @@ +apiVersion: bpfman.io/v1alpha1 +kind: TcProgram +metadata: + labels: + app.kubernetes.io/name: tcprogram + name: tc-containers +spec: + bpffunctionname: pass + # Select all nodes + nodeselector: {} + interfaceselector: + interfaces: + - eth0 + priority: 0 + direction: ingress + bytecode: + image: + url: quay.io/bpfman-bytecode/tc_pass:latest + globaldata: + GLOBAL_u8: + - 0x01 + GLOBAL_u32: + - 0x0D + - 0x0C + - 0x0B + - 0x0A + containers: + namespace: bpfman + pods: + matchLabels: + name: bpfman-daemon + containernames: + - bpfman + - bpfman-agent \ No newline at end of file diff --git a/config/samples/bpfman.io_v1alpha1_tcx_containers_tcxprogram.yaml b/config/samples/bpfman.io_v1alpha1_tcx_containers_tcxprogram.yaml new file mode 100644 index 000000000..3c40b8ce7 --- /dev/null +++ b/config/samples/bpfman.io_v1alpha1_tcx_containers_tcxprogram.yaml @@ -0,0 +1,34 @@ +apiVersion: bpfman.io/v1alpha1 +kind: TcxProgram +metadata: + labels: + app.kubernetes.io/name: tcxprogram + name: tcx-containers +spec: + bpffunctionname: tcx_next + # Select all nodes + nodeselector: {} + interfaceselector: + interfaces: + - eth0 + priority: 0 + direction: ingress + bytecode: + image: + url: quay.io/bpfman-bytecode/tcx_test:latest + globaldata: + GLOBAL_u8: + - 0x01 + GLOBAL_u32: + - 0x0D + - 0x0C + - 0x0B + - 0x0A + containers: + namespace: bpfman + pods: + matchLabels: + name: bpfman-daemon + containernames: + - bpfman + - bpfman-agent diff --git a/config/samples/bpfman.io_v1alpha1_tcx_pass_tcprogram.yaml b/config/samples/bpfman.io_v1alpha1_tcx_pass_tcxprogram.yaml similarity index 100% rename from config/samples/bpfman.io_v1alpha1_tcx_pass_tcprogram.yaml rename to config/samples/bpfman.io_v1alpha1_tcx_pass_tcxprogram.yaml diff --git a/config/samples/bpfman.io_v1alpha1_xdp_containers_xdpprogram.yaml b/config/samples/bpfman.io_v1alpha1_xdp_containers_xdpprogram.yaml new file mode 100644 index 000000000..3432aeb40 --- /dev/null +++ b/config/samples/bpfman.io_v1alpha1_xdp_containers_xdpprogram.yaml @@ -0,0 +1,33 @@ +apiVersion: bpfman.io/v1alpha1 +kind: XdpProgram +metadata: + labels: + app.kubernetes.io/name: xdpprogram + name: xdp-containers +spec: + bpffunctionname: pass + # Select all nodes + nodeselector: {} + interfaceselector: + interfaces: + - eth0 + priority: 0 + bytecode: + image: + url: quay.io/bpfman-bytecode/xdp_pass:latest + globaldata: + GLOBAL_u8: + - 0x01 + GLOBAL_u32: + - 0x0D + - 0x0C + - 0x0B + - 0x0A + containers: + namespace: bpfman + pods: + matchLabels: + name: bpfman-daemon + containernames: + - bpfman + - bpfman-agent diff --git a/controllers/bpfman-agent/containers.go b/controllers/bpfman-agent/containers.go index 4edec2df1..54775bc49 100644 --- a/controllers/bpfman-agent/containers.go +++ b/controllers/bpfman-agent/containers.go @@ -32,6 +32,36 @@ import ( "github.com/go-logr/logr" ) +// Figure out the list of container pids in which the program should be +// attached. +func getContainers( + ctx context.Context, + containerSelector *bpfmaniov1alpha1.ContainerSelector, + nodeName string, + logger logr.Logger) (*[]containerInfo, error) { + + clientSet, err := getClientset() + if err != nil { + return nil, fmt.Errorf("failed to get clientset: %v", err) + } + + // Get the list of pods that match the selector. + podList, err := getPodsForNode(ctx, clientSet, containerSelector, nodeName) + if err != nil { + return nil, fmt.Errorf("failed to get pod list: %v", err) + } + + // Get the list of containers in the list of pods that match the selector. + containerList, err := getContainerInfo(podList, containerSelector.ContainerNames, logger) + if err != nil { + return nil, fmt.Errorf("failed to get container info: %v", err) + } + + logger.V(1).Info("from getContainerInfo", "containers", containerList) + + return containerList, nil +} + // getPodsForNode returns a list of pods on the given node that match the given // container selector. func getPodsForNode(ctx context.Context, clientset kubernetes.Interface, diff --git a/controllers/bpfman-agent/tc-program.go b/controllers/bpfman-agent/tc-program.go index 6c585c751..45b478e74 100644 --- a/controllers/bpfman-agent/tc-program.go +++ b/controllers/bpfman-agent/tc-program.go @@ -19,6 +19,7 @@ package bpfmanagent import ( "context" "fmt" + "strconv" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" @@ -166,21 +167,87 @@ func (r *TcProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { &handler.EnqueueRequestForObject{}, builder.WithPredicates(predicate.And(predicate.LabelChangedPredicate{}, nodePredicate(r.NodeName))), ). + // Watch for changes in Pod resources in case we are using a container selector. + Watches( + &v1.Pod{}, + &handler.EnqueueRequestForObject{}, + builder.WithPredicates(podOnNodePredicate(r.NodeName)), + ). Complete(r) } func (r *TcProgramReconciler) getExpectedBpfPrograms(ctx context.Context) (*bpfmaniov1alpha1.BpfProgramList, error) { progs := &bpfmaniov1alpha1.BpfProgramList{} - for _, iface := range r.interfaces { - attachPoint := iface + "-" + r.currentTcProgram.Spec.Direction - annotations := map[string]string{internal.TcProgramInterface: iface} - prog, err := r.createBpfProgram(attachPoint, r, annotations) + if r.currentTcProgram.Spec.Containers != nil { + + // There is a container selector, so see if there are any matching + // containers on this node. + containerInfo, err := getContainers(ctx, r.currentTcProgram.Spec.Containers, r.NodeName, r.Logger) if err != nil { - return nil, fmt.Errorf("failed to create BpfProgram %s: %v", attachPoint, err) + return nil, fmt.Errorf("failed to get container pids: %v", err) } - progs.Items = append(progs.Items, *prog) + if containerInfo == nil || len(*containerInfo) == 0 { + // There were no errors, but the container selector didn't + // select any containers on this node. + for _, iface := range r.interfaces { + attachPoint := fmt.Sprintf("%s-%s-%s", + iface, + r.currentTcProgram.Spec.Direction, + "no-containers-on-node", + ) + + annotations := map[string]string{ + internal.TcProgramInterface: iface, + internal.TcNoContainersOnNode: "true", + } + + prog, err := r.createBpfProgram(attachPoint, r, annotations) + if err != nil { + return nil, fmt.Errorf("failed to create BpfProgram %s: %v", attachPoint, err) + } + + progs.Items = append(progs.Items, *prog) + } + } else { + // Containers were found, so create bpfPrograms. + for i := range *containerInfo { + container := (*containerInfo)[i] + for _, iface := range r.interfaces { + attachPoint := fmt.Sprintf("%s-%s-%s-%s", + iface, + r.currentTcProgram.Spec.Direction, + container.podName, + container.containerName, + ) + + annotations := map[string]string{ + internal.TcProgramInterface: iface, + internal.TcContainerPid: strconv.FormatInt(container.pid, 10), + } + + prog, err := r.createBpfProgram(attachPoint, r, annotations) + if err != nil { + return nil, fmt.Errorf("failed to create BpfProgram %s: %v", attachPoint, err) + } + + progs.Items = append(progs.Items, *prog) + } + } + } + } else { + for _, iface := range r.interfaces { + attachPoint := iface + "-" + r.currentTcProgram.Spec.Direction + annotations := map[string]string{internal.TcProgramInterface: iface} + + prog, err := r.createBpfProgram(attachPoint, r, annotations) + if err != nil { + return nil, fmt.Errorf("failed to create BpfProgram %s: %v", attachPoint, err) + } + + progs.Items = append(progs.Items, *prog) + } } return progs, nil @@ -233,18 +300,26 @@ func (r *TcProgramReconciler) getLoadRequest(bpfProgram *bpfmaniov1alpha1.BpfPro return nil, fmt.Errorf("failed to process bytecode selector: %v", err) } + attachInfo := &gobpfman.TCAttachInfo{ + Priority: r.currentTcProgram.Spec.Priority, + Iface: bpfProgram.Annotations[internal.TcProgramInterface], + Direction: r.currentTcProgram.Spec.Direction, + ProceedOn: tcProceedOnToInt(r.currentTcProgram.Spec.ProceedOn), + } + + containerPidStr, ok := bpfProgram.Annotations[internal.TcContainerPid] + if ok { + netns := fmt.Sprintf("/host/proc/%s/ns/net", containerPidStr) + attachInfo.Netns = &netns + } + loadRequest := gobpfman.LoadRequest{ Bytecode: bytecode, Name: r.currentTcProgram.Spec.BpfFunctionName, ProgramType: uint32(internal.Tc), Attach: &gobpfman.AttachInfo{ Info: &gobpfman.AttachInfo_TcAttachInfo{ - TcAttachInfo: &gobpfman.TCAttachInfo{ - Priority: r.currentTcProgram.Spec.Priority, - Iface: bpfProgram.Annotations[internal.TcProgramInterface], - Direction: r.currentTcProgram.Spec.Direction, - ProceedOn: tcProceedOnToInt(r.currentTcProgram.Spec.ProceedOn), - }, + TcAttachInfo: attachInfo, }, }, Metadata: map[string]string{internal.UuidMetadataKey: string(bpfProgram.UID), internal.ProgramNameKey: r.getOwner().GetName()}, diff --git a/controllers/bpfman-agent/tcx-program.go b/controllers/bpfman-agent/tcx-program.go index 3c918b186..e377c61d1 100644 --- a/controllers/bpfman-agent/tcx-program.go +++ b/controllers/bpfman-agent/tcx-program.go @@ -19,6 +19,7 @@ package bpfmanagent import ( "context" "fmt" + "strconv" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" @@ -132,21 +133,87 @@ func (r *TcxProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { &handler.EnqueueRequestForObject{}, builder.WithPredicates(predicate.And(predicate.LabelChangedPredicate{}, nodePredicate(r.NodeName))), ). + // Watch for changes in Pod resources in case we are using a container selector. + Watches( + &v1.Pod{}, + &handler.EnqueueRequestForObject{}, + builder.WithPredicates(podOnNodePredicate(r.NodeName)), + ). Complete(r) } func (r *TcxProgramReconciler) getExpectedBpfPrograms(ctx context.Context) (*bpfmaniov1alpha1.BpfProgramList, error) { progs := &bpfmaniov1alpha1.BpfProgramList{} - for _, iface := range r.interfaces { - attachPoint := iface + "-" + r.currentTcxProgram.Spec.Direction - annotations := map[string]string{internal.TcxProgramInterface: iface} - prog, err := r.createBpfProgram(attachPoint, r, annotations) + if r.currentTcxProgram.Spec.Containers != nil { + + // There is a container selector, so see if there are any matching + // containers on this node. + containerInfo, err := getContainers(ctx, r.currentTcxProgram.Spec.Containers, r.NodeName, r.Logger) if err != nil { - return nil, fmt.Errorf("failed to create BpfProgram %s: %v", attachPoint, err) + return nil, fmt.Errorf("failed to get container pids: %v", err) } - progs.Items = append(progs.Items, *prog) + if containerInfo == nil || len(*containerInfo) == 0 { + // There were no errors, but the container selector didn't + // select any containers on this node. + for _, iface := range r.interfaces { + attachPoint := fmt.Sprintf("%s-%s-%s", + iface, + r.currentTcxProgram.Spec.Direction, + "no-containers-on-node", + ) + + annotations := map[string]string{ + internal.TcxProgramInterface: iface, + internal.TcxNoContainersOnNode: "true", + } + + prog, err := r.createBpfProgram(attachPoint, r, annotations) + if err != nil { + return nil, fmt.Errorf("failed to create BpfProgram %s: %v", attachPoint, err) + } + + progs.Items = append(progs.Items, *prog) + } + } else { + // Containers were found, so create bpfPrograms. + for i := range *containerInfo { + container := (*containerInfo)[i] + for _, iface := range r.interfaces { + attachPoint := fmt.Sprintf("%s-%s-%s-%s", + iface, + r.currentTcxProgram.Spec.Direction, + container.podName, + container.containerName, + ) + + annotations := map[string]string{ + internal.TcxProgramInterface: iface, + internal.TcxContainerPid: strconv.FormatInt(container.pid, 10), + } + + prog, err := r.createBpfProgram(attachPoint, r, annotations) + if err != nil { + return nil, fmt.Errorf("failed to create BpfProgram %s: %v", attachPoint, err) + } + + progs.Items = append(progs.Items, *prog) + } + } + } + } else { + for _, iface := range r.interfaces { + attachPoint := iface + "-" + r.currentTcxProgram.Spec.Direction + annotations := map[string]string{internal.TcxProgramInterface: iface} + + prog, err := r.createBpfProgram(attachPoint, r, annotations) + if err != nil { + return nil, fmt.Errorf("failed to create BpfProgram %s: %v", attachPoint, err) + } + + progs.Items = append(progs.Items, *prog) + } } return progs, nil @@ -198,17 +265,26 @@ func (r *TcxProgramReconciler) getLoadRequest(bpfProgram *bpfmaniov1alpha1.BpfPr if err != nil { return nil, fmt.Errorf("failed to process bytecode selector: %v", err) } + + attachInfo := &gobpfman.TCXAttachInfo{ + Priority: r.currentTcxProgram.Spec.Priority, + Iface: bpfProgram.Annotations[internal.TcxProgramInterface], + Direction: r.currentTcxProgram.Spec.Direction, + } + + containerPidStr, ok := bpfProgram.Annotations[internal.TcxContainerPid] + if ok { + netns := fmt.Sprintf("/host/proc/%s/ns/net", containerPidStr) + attachInfo.Netns = &netns + } + loadRequest := gobpfman.LoadRequest{ Bytecode: bytecode, Name: r.currentTcxProgram.Spec.BpfFunctionName, ProgramType: uint32(internal.Tc), Attach: &gobpfman.AttachInfo{ Info: &gobpfman.AttachInfo_TcxAttachInfo{ - TcxAttachInfo: &gobpfman.TCXAttachInfo{ - Priority: r.currentTcxProgram.Spec.Priority, - Iface: bpfProgram.Annotations[internal.TcxProgramInterface], - Direction: r.currentTcxProgram.Spec.Direction, - }, + TcxAttachInfo: attachInfo, }, }, Metadata: map[string]string{internal.UuidMetadataKey: string(bpfProgram.UID), internal.ProgramNameKey: r.getOwner().GetName()}, diff --git a/controllers/bpfman-agent/uprobe-program.go b/controllers/bpfman-agent/uprobe-program.go index 09838073f..a4a609b83 100644 --- a/controllers/bpfman-agent/uprobe-program.go +++ b/controllers/bpfman-agent/uprobe-program.go @@ -131,40 +131,16 @@ func (r *UprobeProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -// Figure out the list of container pids the uProbe needs to be attached to. -func (r *UprobeProgramReconciler) getUprobeContainerInfo(ctx context.Context) (*[]containerInfo, error) { - - clientSet, err := getClientset() - if err != nil { - return nil, fmt.Errorf("failed to get clientset: %v", err) - } - - // Get the list of pods that match the selector. - podList, err := getPodsForNode(ctx, clientSet, r.currentUprobeProgram.Spec.Containers, r.NodeName) - if err != nil { - return nil, fmt.Errorf("failed to get pod list: %v", err) - } - - // Get the list of containers in the list of pods that match the selector. - containers, err := getContainerInfo(podList, r.currentUprobeProgram.Spec.Containers.ContainerNames, r.Logger) - if err != nil { - return nil, fmt.Errorf("failed to get container info: %v", err) - } - - r.Logger.V(1).Info("from getContainerInfo", "containers", containers) - - return containers, nil -} - func (r *UprobeProgramReconciler) getExpectedBpfPrograms(ctx context.Context) (*bpfmaniov1alpha1.BpfProgramList, error) { progs := &bpfmaniov1alpha1.BpfProgramList{} - sanatizedUprobe := sanitize(r.currentUprobeProgram.Spec.Target) + "-" + sanitize(r.currentUprobeProgram.Spec.FunctionName) + sanitizedUprobe := sanitize(r.currentUprobeProgram.Spec.Target) + "-" + sanitize(r.currentUprobeProgram.Spec.FunctionName) if r.currentUprobeProgram.Spec.Containers != nil { - // Some containers were specified, so we need to get the containers - containerInfo, err := r.getUprobeContainerInfo(ctx) + // There is a container selector, so see if there are any matching + // containers on this node. + containerInfo, err := getContainers(ctx, r.currentUprobeProgram.Spec.Containers, r.NodeName, r.Logger) if err != nil { return nil, fmt.Errorf("failed to get container pids: %v", err) } @@ -177,7 +153,7 @@ func (r *UprobeProgramReconciler) getExpectedBpfPrograms(ctx context.Context) (* internal.UprobeNoContainersOnNode: "true", } - attachPoint := sanatizedUprobe + "-no-containers-on-node" + attachPoint := sanitizedUprobe + "-no-containers-on-node" prog, err := r.createBpfProgram(attachPoint, r, annotations) if err != nil { @@ -187,7 +163,7 @@ func (r *UprobeProgramReconciler) getExpectedBpfPrograms(ctx context.Context) (* progs.Items = append(progs.Items, *prog) } else { - // We got some containers, so create the bpfPrograms for each one. + // Containers were found, so create bpfPrograms. for i := range *containerInfo { container := (*containerInfo)[i] @@ -195,7 +171,7 @@ func (r *UprobeProgramReconciler) getExpectedBpfPrograms(ctx context.Context) (* annotations[internal.UprobeContainerPid] = strconv.FormatInt(container.pid, 10) attachPoint := fmt.Sprintf("%s-%s-%s", - sanatizedUprobe, + sanitizedUprobe, container.podName, container.containerName, ) @@ -211,7 +187,7 @@ func (r *UprobeProgramReconciler) getExpectedBpfPrograms(ctx context.Context) (* } else { annotations := map[string]string{internal.UprobeProgramTarget: r.currentUprobeProgram.Spec.Target} - attachPoint := sanatizedUprobe + attachPoint := sanitizedUprobe prog, err := r.createBpfProgram(attachPoint, r, annotations) if err != nil { diff --git a/controllers/bpfman-agent/xdp-program.go b/controllers/bpfman-agent/xdp-program.go index 7b8b0dac9..ab1d51c20 100644 --- a/controllers/bpfman-agent/xdp-program.go +++ b/controllers/bpfman-agent/xdp-program.go @@ -19,6 +19,7 @@ package bpfmanagent import ( "context" "fmt" + "strconv" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" @@ -151,22 +152,85 @@ func (r *XdpProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { &handler.EnqueueRequestForObject{}, builder.WithPredicates(predicate.And(predicate.LabelChangedPredicate{}, nodePredicate(r.NodeName))), ). + // Watch for changes in Pod resources in case we are using a container selector. + Watches( + &v1.Pod{}, + &handler.EnqueueRequestForObject{}, + builder.WithPredicates(podOnNodePredicate(r.NodeName)), + ). Complete(r) } func (r *XdpProgramReconciler) getExpectedBpfPrograms(ctx context.Context) (*bpfmaniov1alpha1.BpfProgramList, error) { progs := &bpfmaniov1alpha1.BpfProgramList{} - for _, iface := range r.interfaces { - attachPoint := iface - annotations := map[string]string{internal.XdpProgramInterface: iface} + if r.currentXdpProgram.Spec.Containers != nil { - prog, err := r.createBpfProgram(attachPoint, r, annotations) + // There is a container selector, so see if there are any matching + // containers on this node. + containerInfo, err := getContainers(ctx, r.currentXdpProgram.Spec.Containers, r.NodeName, r.Logger) if err != nil { - return nil, fmt.Errorf("failed to create BpfProgram %s: %v", attachPoint, err) + return nil, fmt.Errorf("failed to get container pids: %v", err) } - progs.Items = append(progs.Items, *prog) + if containerInfo == nil || len(*containerInfo) == 0 { + // There were no errors, but the container selector didn't + // select any containers on this node. + for _, iface := range r.interfaces { + attachPoint := fmt.Sprintf("%s-%s", + iface, + "no-containers-on-node", + ) + + annotations := map[string]string{ + internal.XdpProgramInterface: iface, + internal.XdpNoContainersOnNode: "true", + } + + prog, err := r.createBpfProgram(attachPoint, r, annotations) + if err != nil { + return nil, fmt.Errorf("failed to create BpfProgram %s: %v", attachPoint, err) + } + + progs.Items = append(progs.Items, *prog) + } + } else { + // Containers were found, so create bpfPrograms. + for i := range *containerInfo { + container := (*containerInfo)[i] + for _, iface := range r.interfaces { + attachPoint := fmt.Sprintf("%s-%s-%s", + iface, + container.podName, + container.containerName, + ) + + annotations := map[string]string{ + internal.XdpProgramInterface: iface, + internal.XdpContainerPid: strconv.FormatInt(container.pid, 10), + } + + prog, err := r.createBpfProgram(attachPoint, r, annotations) + if err != nil { + return nil, fmt.Errorf("failed to create BpfProgram %s: %v", attachPoint, err) + } + + progs.Items = append(progs.Items, *prog) + } + } + } + } else { + for _, iface := range r.interfaces { + attachPoint := iface + annotations := map[string]string{internal.XdpProgramInterface: iface} + + prog, err := r.createBpfProgram(attachPoint, r, annotations) + if err != nil { + return nil, fmt.Errorf("failed to create BpfProgram %s: %v", attachPoint, err) + } + + progs.Items = append(progs.Items, *prog) + } } return progs, nil @@ -218,17 +282,25 @@ func (r *XdpProgramReconciler) getLoadRequest(bpfProgram *bpfmaniov1alpha1.BpfPr return nil, fmt.Errorf("failed to process bytecode selector: %v", err) } + attachInfo := &gobpfman.XDPAttachInfo{ + Priority: r.currentXdpProgram.Spec.Priority, + Iface: bpfProgram.Annotations[internal.XdpProgramInterface], + ProceedOn: xdpProceedOnToInt(r.currentXdpProgram.Spec.ProceedOn), + } + + containerPidStr, ok := bpfProgram.Annotations[internal.XdpContainerPid] + if ok { + netns := fmt.Sprintf("/host/proc/%s/ns/net", containerPidStr) + attachInfo.Netns = &netns + } + loadRequest := gobpfman.LoadRequest{ Bytecode: bytecode, Name: r.currentXdpProgram.Spec.BpfFunctionName, ProgramType: uint32(internal.Xdp), Attach: &gobpfman.AttachInfo{ Info: &gobpfman.AttachInfo_XdpAttachInfo{ - XdpAttachInfo: &gobpfman.XDPAttachInfo{ - Priority: r.currentXdpProgram.Spec.Priority, - Iface: bpfProgram.Annotations[internal.XdpProgramInterface], - ProceedOn: xdpProceedOnToInt(r.currentXdpProgram.Spec.ProceedOn), - }, + XdpAttachInfo: attachInfo, }, }, Metadata: map[string]string{internal.UuidMetadataKey: string(bpfProgram.UID), internal.ProgramNameKey: r.getOwner().GetName()}, diff --git a/go.mod b/go.mod index abbed1e28..b20c736dc 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.0 toolchain go1.22.2 require ( - github.com/bpfman/bpfman v0.5.5-0.20241022174147-497c1ed71c81 + github.com/bpfman/bpfman v0.5.5-0.20241203131329-c0f90bcfe0c7 github.com/containers/image/v5 v5.32.2 github.com/go-logr/logr v1.4.2 github.com/google/go-cmp v0.6.0 @@ -85,7 +85,7 @@ require ( k8s.io/apiextensions-apiserver v0.31.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kind v0.24.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect diff --git a/go.sum b/go.sum index ccb949d5a..dfa88f75b 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/bpfman/bpfman v0.5.5-0.20241022174147-497c1ed71c81 h1:cDjf1+aZhWsLc9FsJ7Li/R4NHvm4pUPIfXU+6AL90aQ= -github.com/bpfman/bpfman v0.5.5-0.20241022174147-497c1ed71c81/go.mod h1:ZBZc5wo+lEQA+w/89Sbjaw5VhuvuLTDS3a3yDjOl81o= +github.com/bpfman/bpfman v0.5.5-0.20241203131329-c0f90bcfe0c7 h1:YMdqM5QNwZcNPGKAEgqHeciM07+CFXMSkEB6T4FCCqY= +github.com/bpfman/bpfman v0.5.5-0.20241203131329-c0f90bcfe0c7/go.mod h1:ZBZc5wo+lEQA+w/89Sbjaw5VhuvuLTDS3a3yDjOl81o= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= diff --git a/internal/constants.go b/internal/constants.go index 2fc58f303..3c324dde8 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -20,8 +20,14 @@ import "fmt" const ( XdpProgramInterface = "bpfman.io.xdpprogramcontroller/interface" + XdpContainerPid = "bpfman.io.xdpprogramcontroller/containerpid" + XdpNoContainersOnNode = "bpfman.io.xdpprogramcontroller/nocontainersonnode" TcProgramInterface = "bpfman.io.tcprogramcontroller/interface" + TcContainerPid = "bpfman.io.tcprogramcontroller/containerpid" + TcNoContainersOnNode = "bpfman.io.tcprogramcontroller/nocontainersonnode" TcxProgramInterface = "bpfman.io.tcxprogramcontroller/interface" + TcxContainerPid = "bpfman.io.tcxprogramcontroller/containerpid" + TcxNoContainersOnNode = "bpfman.io.tcxprogramcontroller/nocontainersonnode" TracepointProgramTracepoint = "bpfman.io.tracepointprogramcontroller/tracepoint" KprobeProgramFunction = "bpfman.io.kprobeprogramcontroller/function" UprobeProgramTarget = "bpfman.io.uprobeprogramcontroller/target" diff --git a/vendor/github.com/bpfman/bpfman/clients/gobpfman/v1/bpfman.pb.go b/vendor/github.com/bpfman/bpfman/clients/gobpfman/v1/bpfman.pb.go index d61732ae4..cacc823a7 100644 --- a/vendor/github.com/bpfman/bpfman/clients/gobpfman/v1/bpfman.pb.go +++ b/vendor/github.com/bpfman/bpfman/clients/gobpfman/v1/bpfman.pb.go @@ -432,6 +432,7 @@ type XDPAttachInfo struct { Iface string `protobuf:"bytes,2,opt,name=iface,proto3" json:"iface,omitempty"` Position int32 `protobuf:"varint,3,opt,name=position,proto3" json:"position,omitempty"` ProceedOn []int32 `protobuf:"varint,4,rep,packed,name=proceed_on,json=proceedOn,proto3" json:"proceed_on,omitempty"` + Netns *string `protobuf:"bytes,5,opt,name=netns,proto3,oneof" json:"netns,omitempty"` } func (x *XDPAttachInfo) Reset() { @@ -494,6 +495,13 @@ func (x *XDPAttachInfo) GetProceedOn() []int32 { return nil } +func (x *XDPAttachInfo) GetNetns() string { + if x != nil && x.Netns != nil { + return *x.Netns + } + return "" +} + type TCAttachInfo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -504,6 +512,7 @@ type TCAttachInfo struct { Position int32 `protobuf:"varint,3,opt,name=position,proto3" json:"position,omitempty"` Direction string `protobuf:"bytes,4,opt,name=direction,proto3" json:"direction,omitempty"` ProceedOn []int32 `protobuf:"varint,5,rep,packed,name=proceed_on,json=proceedOn,proto3" json:"proceed_on,omitempty"` + Netns *string `protobuf:"bytes,6,opt,name=netns,proto3,oneof" json:"netns,omitempty"` } func (x *TCAttachInfo) Reset() { @@ -573,15 +582,23 @@ func (x *TCAttachInfo) GetProceedOn() []int32 { return nil } +func (x *TCAttachInfo) GetNetns() string { + if x != nil && x.Netns != nil { + return *x.Netns + } + return "" +} + type TCXAttachInfo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Priority int32 `protobuf:"varint,1,opt,name=priority,proto3" json:"priority,omitempty"` - Iface string `protobuf:"bytes,2,opt,name=iface,proto3" json:"iface,omitempty"` - Position int32 `protobuf:"varint,3,opt,name=position,proto3" json:"position,omitempty"` - Direction string `protobuf:"bytes,4,opt,name=direction,proto3" json:"direction,omitempty"` + Priority int32 `protobuf:"varint,1,opt,name=priority,proto3" json:"priority,omitempty"` + Iface string `protobuf:"bytes,2,opt,name=iface,proto3" json:"iface,omitempty"` + Position int32 `protobuf:"varint,3,opt,name=position,proto3" json:"position,omitempty"` + Direction string `protobuf:"bytes,4,opt,name=direction,proto3" json:"direction,omitempty"` + Netns *string `protobuf:"bytes,5,opt,name=netns,proto3,oneof" json:"netns,omitempty"` } func (x *TCXAttachInfo) Reset() { @@ -644,6 +661,13 @@ func (x *TCXAttachInfo) GetDirection() string { return "" } +func (x *TCXAttachInfo) GetNetns() string { + if x != nil && x.Netns != nil { + return *x.Netns + } + return "" +} + type TracepointAttachInfo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1782,220 +1806,227 @@ var file_bpfman_proto_rawDesc = []byte{ 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x6d, 0x61, 0x70, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x22, 0x7c, 0x0a, 0x0d, 0x58, 0x44, 0x50, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x69, 0x66, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x69, 0x66, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x65, 0x64, 0x5f, 0x6f, 0x6e, - 0x18, 0x04, 0x20, 0x03, 0x28, 0x05, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x65, 0x64, 0x4f, - 0x6e, 0x22, 0x99, 0x01, 0x0a, 0x0c, 0x54, 0x43, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x69, 0x66, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, - 0x66, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, - 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x05, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x65, 0x64, 0x4f, 0x6e, 0x22, 0x7b, 0x0a, - 0x0d, 0x54, 0x43, 0x58, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, - 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x66, - 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x66, 0x61, 0x63, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, - 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x36, 0x0a, 0x14, 0x54, 0x72, - 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x22, 0x9b, 0x01, 0x0a, 0x10, 0x4b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x41, 0x74, 0x74, + 0x5f, 0x69, 0x64, 0x22, 0xa1, 0x01, 0x0a, 0x0d, 0x58, 0x44, 0x50, 0x41, 0x74, 0x74, 0x61, 0x63, + 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x66, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x69, 0x66, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x65, 0x64, 0x5f, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x05, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x65, 0x64, + 0x4f, 0x6e, 0x12, 0x19, 0x0a, 0x05, 0x6e, 0x65, 0x74, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, + 0x06, 0x5f, 0x6e, 0x65, 0x74, 0x6e, 0x73, 0x22, 0xbe, 0x01, 0x0a, 0x0c, 0x54, 0x43, 0x41, 0x74, + 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x66, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x66, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x65, 0x64, 0x5f, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x05, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x65, + 0x64, 0x4f, 0x6e, 0x12, 0x19, 0x0a, 0x05, 0x6e, 0x65, 0x74, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x42, 0x08, + 0x0a, 0x06, 0x5f, 0x6e, 0x65, 0x74, 0x6e, 0x73, 0x22, 0xa0, 0x01, 0x0a, 0x0d, 0x54, 0x43, 0x58, + 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, + 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, + 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x66, 0x61, 0x63, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x66, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x05, 0x6e, 0x65, 0x74, 0x6e, 0x73, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x6e, 0x73, 0x88, 0x01, + 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6e, 0x65, 0x74, 0x6e, 0x73, 0x22, 0x36, 0x0a, 0x14, 0x54, + 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x22, 0x9b, 0x01, 0x0a, 0x10, 0x4b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x41, 0x74, + 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x6e, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6e, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x74, + 0x70, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x74, + 0x70, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x28, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x5f, 0x70, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x0c, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x69, 0x64, 0x88, 0x01, 0x01, 0x42, + 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x69, + 0x64, 0x22, 0xe3, 0x01, 0x0a, 0x10, 0x55, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x41, 0x74, 0x74, 0x61, + 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x07, 0x66, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x66, 0x6e, 0x4e, 0x61, 0x6d, + 0x65, 0x88, 0x01, 0x01, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x62, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x62, 0x65, + 0x12, 0x15, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, + 0x03, 0x70, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, + 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x69, 0x64, 0x88, 0x01, + 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x66, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x06, 0x0a, + 0x04, 0x5f, 0x70, 0x69, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x69, 0x64, 0x22, 0x2b, 0x0a, 0x10, 0x46, 0x65, 0x6e, 0x74, 0x72, + 0x79, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x66, + 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6e, + 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x2a, 0x0a, 0x0f, 0x46, 0x65, 0x78, 0x69, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6e, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x74, 0x70, - 0x72, 0x6f, 0x62, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x74, 0x70, - 0x72, 0x6f, 0x62, 0x65, 0x12, 0x28, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x5f, 0x70, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x0c, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x69, 0x64, 0x88, 0x01, 0x01, 0x42, 0x10, - 0x0a, 0x0e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x69, 0x64, - 0x22, 0xe3, 0x01, 0x0a, 0x10, 0x55, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x41, 0x74, 0x74, 0x61, 0x63, - 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x07, 0x66, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x66, 0x6e, 0x4e, 0x61, 0x6d, 0x65, - 0x88, 0x01, 0x01, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x12, - 0x15, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x03, - 0x70, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x28, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, - 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x69, 0x64, 0x88, 0x01, 0x01, - 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x66, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x06, 0x0a, 0x04, - 0x5f, 0x70, 0x69, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x5f, 0x70, 0x69, 0x64, 0x22, 0x2b, 0x0a, 0x10, 0x46, 0x65, 0x6e, 0x74, 0x72, 0x79, - 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x6e, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6e, 0x4e, - 0x61, 0x6d, 0x65, 0x22, 0x2a, 0x0a, 0x0f, 0x46, 0x65, 0x78, 0x69, 0x74, 0x41, 0x74, 0x74, 0x61, - 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x22, - 0xe7, 0x04, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x42, - 0x0a, 0x0f, 0x78, 0x64, 0x70, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x58, 0x44, 0x50, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, - 0x6f, 0x48, 0x00, 0x52, 0x0d, 0x78, 0x64, 0x70, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x0e, 0x74, 0x63, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x5f, - 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x70, 0x66, - 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x43, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, - 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x0c, 0x74, 0x63, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x57, 0x0a, 0x16, 0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, - 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x14, 0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4b, 0x0a, 0x12, - 0x6b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x41, 0x74, 0x74, 0x61, 0x63, - 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x10, 0x6b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x41, - 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4b, 0x0a, 0x12, 0x75, 0x70, 0x72, - 0x6f, 0x62, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x55, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, - 0x66, 0x6f, 0x48, 0x00, 0x52, 0x10, 0x75, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x41, 0x74, 0x74, 0x61, - 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4b, 0x0a, 0x12, 0x66, 0x65, 0x6e, 0x74, 0x72, 0x79, - 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x46, - 0x65, 0x6e, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x48, - 0x00, 0x52, 0x10, 0x66, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x11, 0x66, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x61, 0x74, 0x74, - 0x61, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x65, 0x78, 0x69, 0x74, - 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x0f, 0x66, 0x65, - 0x78, 0x69, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x42, 0x0a, - 0x0f, 0x74, 0x63, 0x78, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x43, 0x58, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, - 0x48, 0x00, 0x52, 0x0d, 0x74, 0x63, 0x78, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, - 0x6f, 0x42, 0x06, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x8d, 0x04, 0x0a, 0x0b, 0x4c, 0x6f, - 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x08, 0x62, 0x79, 0x74, - 0x65, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x62, 0x70, - 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, - 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x62, 0x79, 0x74, 0x65, 0x63, 0x6f, - 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, - 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x72, - 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x61, 0x74, 0x74, - 0x61, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x70, 0x66, 0x6d, - 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x06, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x12, 0x40, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x62, 0x70, 0x66, - 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x47, 0x0a, 0x0b, 0x67, 0x6c, - 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x26, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x61, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x61, - 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, - 0x61, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x00, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0c, - 0x6d, 0x61, 0x70, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0d, 0x48, 0x01, 0x52, 0x0a, 0x6d, 0x61, 0x70, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, - 0x88, 0x01, 0x01, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, + 0x22, 0xe7, 0x04, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x42, 0x0a, 0x0f, 0x78, 0x64, 0x70, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x58, 0x44, 0x50, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, + 0x66, 0x6f, 0x48, 0x00, 0x52, 0x0d, 0x78, 0x64, 0x70, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x0e, 0x74, 0x63, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x70, + 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x43, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, + 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x0c, 0x74, 0x63, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x57, 0x0a, 0x16, 0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, + 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x14, 0x74, 0x72, 0x61, 0x63, 0x65, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4b, 0x0a, + 0x12, 0x6b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x62, 0x70, 0x66, 0x6d, + 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x41, 0x74, 0x74, 0x61, + 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x10, 0x6b, 0x70, 0x72, 0x6f, 0x62, 0x65, + 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4b, 0x0a, 0x12, 0x75, 0x70, + 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x55, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, + 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x10, 0x75, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x41, 0x74, 0x74, + 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4b, 0x0a, 0x12, 0x66, 0x65, 0x6e, 0x74, 0x72, + 0x79, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, + 0x48, 0x00, 0x52, 0x10, 0x66, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x11, 0x66, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x61, 0x74, + 0x74, 0x61, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x65, 0x78, 0x69, + 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x0f, 0x66, + 0x65, 0x78, 0x69, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x42, + 0x0a, 0x0f, 0x74, 0x63, 0x78, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x66, + 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x43, 0x58, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, + 0x6f, 0x48, 0x00, 0x52, 0x0d, 0x74, 0x63, 0x78, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, + 0x66, 0x6f, 0x42, 0x06, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x8d, 0x04, 0x0a, 0x0b, 0x4c, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x08, 0x62, 0x79, + 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x62, + 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x63, 0x6f, 0x64, + 0x65, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x62, 0x79, 0x74, 0x65, 0x63, + 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x67, 0x72, + 0x61, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, + 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x61, 0x74, + 0x74, 0x61, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x70, 0x66, + 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x06, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x12, 0x40, 0x0a, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x62, 0x70, + 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x47, 0x0a, 0x0b, 0x67, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x26, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x61, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, + 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, + 0x0c, 0x6d, 0x61, 0x70, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x0a, 0x6d, 0x61, 0x70, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x49, + 0x64, 0x88, 0x01, 0x01, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x1a, 0x3d, 0x0a, 0x0f, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, - 0x07, 0x0a, 0x05, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x6d, 0x61, 0x70, - 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x22, 0x79, 0x0a, 0x0c, 0x4c, 0x6f, 0x61, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x04, 0x69, 0x6e, 0x66, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x6d, 0x61, + 0x70, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x22, 0x79, 0x0a, 0x0c, 0x4c, 0x6f, + 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x04, 0x69, 0x6e, + 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x0b, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x70, + 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x50, 0x72, + 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x6b, 0x65, 0x72, 0x6e, 0x65, + 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x1f, 0x0a, 0x0d, 0x55, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x55, 0x6e, 0x6c, 0x6f, 0x61, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xaa, 0x02, 0x0a, 0x0b, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x67, + 0x72, 0x61, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, + 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, + 0x12, 0x35, 0x0a, 0x14, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, + 0x61, 0x6d, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, + 0x52, 0x12, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x73, + 0x4f, 0x6e, 0x6c, 0x79, 0x88, 0x01, 0x01, 0x12, 0x50, 0x0a, 0x0e, 0x6d, 0x61, 0x74, 0x63, 0x68, + 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x29, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x6d, 0x61, 0x74, 0x63, + 0x68, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x40, 0x0a, 0x12, 0x4d, 0x61, 0x74, + 0x63, 0x68, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, + 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x17, 0x0a, 0x15, + 0x5f, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x73, + 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x22, 0xd4, 0x01, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x73, 0x1a, 0x85, 0x01, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x12, 0x2f, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, + 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x66, + 0x6f, 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x0b, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x70, 0x66, 0x6d, + 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x67, + 0x72, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, + 0x6e, 0x66, 0x6f, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x45, 0x0a, 0x13, + 0x50, 0x75, 0x6c, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x42, + 0x79, 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x05, 0x69, 0x6d, + 0x61, 0x67, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x50, 0x75, 0x6c, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x63, + 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x0a, 0x0a, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22, 0x86, 0x01, 0x0a, 0x0b, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x0b, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, - 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x70, 0x66, - 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, - 0x67, 0x72, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, - 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x1f, 0x0a, 0x0d, 0x55, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x55, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xaa, 0x02, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x67, 0x72, - 0x61, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, - 0x0b, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, - 0x35, 0x0a, 0x14, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, - 0x6d, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, - 0x12, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x73, 0x4f, - 0x6e, 0x6c, 0x79, 0x88, 0x01, 0x01, 0x12, 0x50, 0x0a, 0x0e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, - 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x6d, 0x61, 0x74, 0x63, 0x68, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x40, 0x0a, 0x12, 0x4d, 0x61, 0x74, 0x63, - 0x68, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x70, - 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x17, 0x0a, 0x15, 0x5f, - 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x73, 0x5f, - 0x6f, 0x6e, 0x6c, 0x79, 0x22, 0xd4, 0x01, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x73, 0x1a, 0x85, 0x01, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x12, 0x2f, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, - 0x67, 0x72, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, - 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x0b, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x6e, - 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, - 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x67, 0x72, - 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, - 0x66, 0x6f, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x45, 0x0a, 0x13, 0x50, - 0x75, 0x6c, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x79, - 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x05, 0x69, 0x6d, 0x61, - 0x67, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x50, 0x75, 0x6c, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x63, 0x6f, - 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x0a, 0x0a, 0x47, 0x65, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22, 0x86, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, - 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x0b, 0x6b, 0x65, 0x72, - 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x72, 0x6e, 0x65, - 0x6c, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x6b, 0x65, - 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x32, 0xc0, 0x02, 0x0a, 0x06, 0x42, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x12, 0x37, 0x0a, 0x04, - 0x4c, 0x6f, 0x61, 0x64, 0x12, 0x16, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x62, - 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x06, 0x55, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x12, - 0x18, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x6c, 0x6f, - 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x62, 0x70, 0x66, 0x6d, - 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x2e, 0x62, - 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, - 0x0c, 0x50, 0x75, 0x6c, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1e, 0x2e, - 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x42, 0x79, - 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, - 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x42, 0x79, - 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, - 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x15, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x62, - 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x73, 0x2f, 0x67, 0x6f, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x48, + 0x00, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x0b, 0x6b, 0x65, + 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x72, 0x6e, + 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x6b, + 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x69, 0x6e, + 0x66, 0x6f, 0x32, 0xc0, 0x02, 0x0a, 0x06, 0x42, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x12, 0x37, 0x0a, + 0x04, 0x4c, 0x6f, 0x61, 0x64, 0x12, 0x16, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x06, 0x55, 0x6e, 0x6c, 0x6f, 0x61, 0x64, + 0x12, 0x18, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x62, 0x70, 0x66, + 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x2e, + 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, + 0x0a, 0x0c, 0x50, 0x75, 0x6c, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1e, + 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x42, + 0x79, 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x42, + 0x79, 0x74, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x34, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x15, 0x2e, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x73, 0x2f, 0x67, 0x6f, 0x62, 0x70, 0x66, 0x6d, 0x61, 0x6e, 0x2f, 0x76, 0x31, 0x3b, 0x76, + 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2387,6 +2418,9 @@ func file_bpfman_proto_init() { (*BytecodeLocation_File)(nil), } file_bpfman_proto_msgTypes[3].OneofWrappers = []interface{}{} + file_bpfman_proto_msgTypes[4].OneofWrappers = []interface{}{} + file_bpfman_proto_msgTypes[5].OneofWrappers = []interface{}{} + file_bpfman_proto_msgTypes[6].OneofWrappers = []interface{}{} file_bpfman_proto_msgTypes[8].OneofWrappers = []interface{}{} file_bpfman_proto_msgTypes[9].OneofWrappers = []interface{}{} file_bpfman_proto_msgTypes[12].OneofWrappers = []interface{}{ diff --git a/vendor/modules.txt b/vendor/modules.txt index d98dd5057..080e9f70e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -4,7 +4,7 @@ github.com/beorn7/perks/quantile # github.com/blang/semver/v4 v4.0.0 ## explicit; go 1.14 github.com/blang/semver/v4 -# github.com/bpfman/bpfman v0.5.5-0.20241022174147-497c1ed71c81 +# github.com/bpfman/bpfman v0.5.5-0.20241203131329-c0f90bcfe0c7 ## explicit; go 1.22.0 github.com/bpfman/bpfman/clients/gobpfman/v1 # github.com/buger/jsonparser v1.1.1