Add CEL validation to SE and WG (#3302)

* Add CEL validation to SE and WG

* fix bad validation
This commit is contained in:
John Howard 2024-10-02 11:37:29 -07:00 committed by GitHub
parent 6a5c0db3b5
commit eb7df4d4b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 600 additions and 10 deletions

View File

@ -6152,9 +6152,14 @@ spec:
properties:
labels:
additionalProperties:
maxLength: 63
type: string
x-kubernetes-validations:
- message: wildcard is not supported in selector
rule: '!self.contains(''*'')'
description: One or more labels that indicate a specific set of
pods/VMs on which the configuration should be applied.
maxProperties: 256
type: object
type: object
type: object
@ -7157,7 +7162,9 @@ spec:
addresses:
description: The virtual IP addresses associated with the service.
items:
maxLength: 64
type: string
maxItems: 256
type: array
endpoints:
description: One or more endpoints associated with the service.
@ -7232,6 +7239,11 @@ spec:
description: The hosts associated with the ServiceEntry.
items:
type: string
x-kubernetes-validations:
- message: hostname cannot be wildcard
rule: self != '*'
maxItems: 256
minItems: 1
type: array
location:
description: |-
@ -7248,14 +7260,19 @@ spec:
properties:
name:
description: Label assigned to the port.
maxLength: 256
type: string
number:
description: A valid non-negative integer port number.
maximum: 4294967295
minimum: 0
type: integer
x-kubernetes-validations:
- message: port must be between 1-65535
rule: 0 < self && self <= 65535
protocol:
description: The protocol exposed on the port.
maxLength: 256
type: string
targetPort:
description: The port number on the endpoint where the traffic
@ -7263,11 +7280,21 @@ spec:
maximum: 4294967295
minimum: 0
type: integer
x-kubernetes-validations:
- message: port must be between 1-65535
rule: 0 < self && self <= 65535
required:
- number
- name
type: object
maxItems: 256
type: array
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
x-kubernetes-validations:
- message: port number cannot be duplicated
rule: self.all(l1, self.exists_one(l2, l1.number == l2.number))
resolution:
description: |-
Service resolution mode for the hosts.
@ -7290,14 +7317,32 @@ spec:
properties:
labels:
additionalProperties:
maxLength: 63
type: string
x-kubernetes-validations:
- message: wildcard is not supported in selector
rule: '!self.contains(''*'')'
description: One or more labels that indicate a specific set of
pods/VMs on which the configuration should be applied.
maxProperties: 256
type: object
type: object
required:
- hosts
type: object
x-kubernetes-validations:
- message: only one of WorkloadSelector or Endpoints can be set
rule: (has(self.workloadSelector)?1:0)+(has(self.endpoints)?1:0)<=1
- message: CIDR addresses are allowed only for NONE/STATIC resolution
types
rule: '!(has(self.addresses) && self.addresses.exists(k, k.contains(''/''))
&& (self.resolution != ''STATIC'' && self.resolution != ''NONE''))'
- message: NONE mode cannot set endpoints
rule: '(!has(self.resolution) || self.resolution == ''NONE'') ? !has(self.endpoints)
: true'
- message: DNS_ROUND_ROBIN mode cannot have multiple endpoints
rule: '(has(self.resolution) && self.resolution == ''DNS_ROUND_ROBIN'')
? (!has(self.endpoints) || size(self.endpoints) == 1) : true'
status:
properties:
conditions:
@ -7369,6 +7414,10 @@ spec:
type: array
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- spec
- spec
- spec
type: object
served: true
storage: false
@ -7407,7 +7456,9 @@ spec:
addresses:
description: The virtual IP addresses associated with the service.
items:
maxLength: 64
type: string
maxItems: 256
type: array
endpoints:
description: One or more endpoints associated with the service.
@ -7482,6 +7533,11 @@ spec:
description: The hosts associated with the ServiceEntry.
items:
type: string
x-kubernetes-validations:
- message: hostname cannot be wildcard
rule: self != '*'
maxItems: 256
minItems: 1
type: array
location:
description: |-
@ -7498,14 +7554,19 @@ spec:
properties:
name:
description: Label assigned to the port.
maxLength: 256
type: string
number:
description: A valid non-negative integer port number.
maximum: 4294967295
minimum: 0
type: integer
x-kubernetes-validations:
- message: port must be between 1-65535
rule: 0 < self && self <= 65535
protocol:
description: The protocol exposed on the port.
maxLength: 256
type: string
targetPort:
description: The port number on the endpoint where the traffic
@ -7513,11 +7574,21 @@ spec:
maximum: 4294967295
minimum: 0
type: integer
x-kubernetes-validations:
- message: port must be between 1-65535
rule: 0 < self && self <= 65535
required:
- number
- name
type: object
maxItems: 256
type: array
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
x-kubernetes-validations:
- message: port number cannot be duplicated
rule: self.all(l1, self.exists_one(l2, l1.number == l2.number))
resolution:
description: |-
Service resolution mode for the hosts.
@ -7540,14 +7611,32 @@ spec:
properties:
labels:
additionalProperties:
maxLength: 63
type: string
x-kubernetes-validations:
- message: wildcard is not supported in selector
rule: '!self.contains(''*'')'
description: One or more labels that indicate a specific set of
pods/VMs on which the configuration should be applied.
maxProperties: 256
type: object
type: object
required:
- hosts
type: object
x-kubernetes-validations:
- message: only one of WorkloadSelector or Endpoints can be set
rule: (has(self.workloadSelector)?1:0)+(has(self.endpoints)?1:0)<=1
- message: CIDR addresses are allowed only for NONE/STATIC resolution
types
rule: '!(has(self.addresses) && self.addresses.exists(k, k.contains(''/''))
&& (self.resolution != ''STATIC'' && self.resolution != ''NONE''))'
- message: NONE mode cannot set endpoints
rule: '(!has(self.resolution) || self.resolution == ''NONE'') ? !has(self.endpoints)
: true'
- message: DNS_ROUND_ROBIN mode cannot have multiple endpoints
rule: '(has(self.resolution) && self.resolution == ''DNS_ROUND_ROBIN'')
? (!has(self.endpoints) || size(self.endpoints) == 1) : true'
status:
properties:
conditions:
@ -7619,6 +7708,10 @@ spec:
type: array
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- spec
- spec
- spec
type: object
served: true
storage: false
@ -7657,7 +7750,9 @@ spec:
addresses:
description: The virtual IP addresses associated with the service.
items:
maxLength: 64
type: string
maxItems: 256
type: array
endpoints:
description: One or more endpoints associated with the service.
@ -7732,6 +7827,11 @@ spec:
description: The hosts associated with the ServiceEntry.
items:
type: string
x-kubernetes-validations:
- message: hostname cannot be wildcard
rule: self != '*'
maxItems: 256
minItems: 1
type: array
location:
description: |-
@ -7748,14 +7848,19 @@ spec:
properties:
name:
description: Label assigned to the port.
maxLength: 256
type: string
number:
description: A valid non-negative integer port number.
maximum: 4294967295
minimum: 0
type: integer
x-kubernetes-validations:
- message: port must be between 1-65535
rule: 0 < self && self <= 65535
protocol:
description: The protocol exposed on the port.
maxLength: 256
type: string
targetPort:
description: The port number on the endpoint where the traffic
@ -7763,11 +7868,21 @@ spec:
maximum: 4294967295
minimum: 0
type: integer
x-kubernetes-validations:
- message: port must be between 1-65535
rule: 0 < self && self <= 65535
required:
- number
- name
type: object
maxItems: 256
type: array
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
x-kubernetes-validations:
- message: port number cannot be duplicated
rule: self.all(l1, self.exists_one(l2, l1.number == l2.number))
resolution:
description: |-
Service resolution mode for the hosts.
@ -7790,14 +7905,32 @@ spec:
properties:
labels:
additionalProperties:
maxLength: 63
type: string
x-kubernetes-validations:
- message: wildcard is not supported in selector
rule: '!self.contains(''*'')'
description: One or more labels that indicate a specific set of
pods/VMs on which the configuration should be applied.
maxProperties: 256
type: object
type: object
required:
- hosts
type: object
x-kubernetes-validations:
- message: only one of WorkloadSelector or Endpoints can be set
rule: (has(self.workloadSelector)?1:0)+(has(self.endpoints)?1:0)<=1
- message: CIDR addresses are allowed only for NONE/STATIC resolution
types
rule: '!(has(self.addresses) && self.addresses.exists(k, k.contains(''/''))
&& (self.resolution != ''STATIC'' && self.resolution != ''NONE''))'
- message: NONE mode cannot set endpoints
rule: '(!has(self.resolution) || self.resolution == ''NONE'') ? !has(self.endpoints)
: true'
- message: DNS_ROUND_ROBIN mode cannot have multiple endpoints
rule: '(has(self.resolution) && self.resolution == ''DNS_ROUND_ROBIN'')
? (!has(self.endpoints) || size(self.endpoints) == 1) : true'
status:
properties:
conditions:
@ -7869,6 +8002,10 @@ spec:
type: array
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- spec
- spec
- spec
type: object
served: true
storage: true
@ -8349,9 +8486,14 @@ spec:
properties:
labels:
additionalProperties:
maxLength: 63
type: string
x-kubernetes-validations:
- message: wildcard is not supported in selector
rule: '!self.contains(''*'')'
description: One or more labels that indicate a specific set of
pods/VMs on which the configuration should be applied.
maxProperties: 256
type: object
type: object
type: object
@ -8882,9 +9024,14 @@ spec:
properties:
labels:
additionalProperties:
maxLength: 63
type: string
x-kubernetes-validations:
- message: wildcard is not supported in selector
rule: '!self.contains(''*'')'
description: One or more labels that indicate a specific set of
pods/VMs on which the configuration should be applied.
maxProperties: 256
type: object
type: object
type: object
@ -9415,9 +9562,14 @@ spec:
properties:
labels:
additionalProperties:
maxLength: 63
type: string
x-kubernetes-validations:
- message: wildcard is not supported in selector
rule: '!self.contains(''*'')'
description: One or more labels that indicate a specific set of
pods/VMs on which the configuration should be applied.
maxProperties: 256
type: object
type: object
type: object
@ -13178,10 +13330,12 @@ spec:
annotations:
additionalProperties:
type: string
maxProperties: 256
type: object
labels:
additionalProperties:
type: string
maxProperties: 256
type: object
type: object
probe:
@ -13210,13 +13364,17 @@ spec:
command:
description: Command to run.
items:
minLength: 1
type: string
type: array
required:
- command
type: object
failureThreshold:
description: Minimum consecutive failures for the probe to be
considered failed after having succeeded.
format: int32
minimum: 0
type: integer
httpGet:
description: '`httpGet` is performed to a given endpoint and the
@ -13231,6 +13389,7 @@ spec:
items:
properties:
name:
pattern: ^[-_A-Za-z0-9]+$
type: string
value:
type: string
@ -13244,8 +13403,14 @@ spec:
maximum: 4294967295
minimum: 0
type: integer
x-kubernetes-validations:
- message: port must be between 1-65535
rule: 0 < self && self <= 65535
scheme:
type: string
x-kubernetes-validations:
- message: scheme must be one of [HTTP, HTTPS]
rule: self in ['', 'HTTP', 'HTTPS']
required:
- port
type: object
@ -13253,15 +13418,18 @@ spec:
description: Number of seconds after the container has started
before readiness probes are initiated.
format: int32
minimum: 0
type: integer
periodSeconds:
description: How often (in seconds) to perform the probe.
format: int32
minimum: 0
type: integer
successThreshold:
description: Minimum consecutive successes for the probe to be
considered successful after having failed.
format: int32
minimum: 0
type: integer
tcpSocket:
description: Health is determined by if the proxy is able to connect.
@ -13272,12 +13440,16 @@ spec:
maximum: 4294967295
minimum: 0
type: integer
x-kubernetes-validations:
- message: port must be between 1-65535
rule: 0 < self && self <= 65535
required:
- port
type: object
timeoutSeconds:
description: Number of seconds after which the probe times out.
format: int32
minimum: 0
type: integer
type: object
template:
@ -13414,6 +13586,10 @@ spec:
type: array
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- spec
- spec
- spec
type: object
served: true
storage: false
@ -13442,10 +13618,12 @@ spec:
annotations:
additionalProperties:
type: string
maxProperties: 256
type: object
labels:
additionalProperties:
type: string
maxProperties: 256
type: object
type: object
probe:
@ -13474,13 +13652,17 @@ spec:
command:
description: Command to run.
items:
minLength: 1
type: string
type: array
required:
- command
type: object
failureThreshold:
description: Minimum consecutive failures for the probe to be
considered failed after having succeeded.
format: int32
minimum: 0
type: integer
httpGet:
description: '`httpGet` is performed to a given endpoint and the
@ -13495,6 +13677,7 @@ spec:
items:
properties:
name:
pattern: ^[-_A-Za-z0-9]+$
type: string
value:
type: string
@ -13508,8 +13691,14 @@ spec:
maximum: 4294967295
minimum: 0
type: integer
x-kubernetes-validations:
- message: port must be between 1-65535
rule: 0 < self && self <= 65535
scheme:
type: string
x-kubernetes-validations:
- message: scheme must be one of [HTTP, HTTPS]
rule: self in ['', 'HTTP', 'HTTPS']
required:
- port
type: object
@ -13517,15 +13706,18 @@ spec:
description: Number of seconds after the container has started
before readiness probes are initiated.
format: int32
minimum: 0
type: integer
periodSeconds:
description: How often (in seconds) to perform the probe.
format: int32
minimum: 0
type: integer
successThreshold:
description: Minimum consecutive successes for the probe to be
considered successful after having failed.
format: int32
minimum: 0
type: integer
tcpSocket:
description: Health is determined by if the proxy is able to connect.
@ -13536,12 +13728,16 @@ spec:
maximum: 4294967295
minimum: 0
type: integer
x-kubernetes-validations:
- message: port must be between 1-65535
rule: 0 < self && self <= 65535
required:
- port
type: object
timeoutSeconds:
description: Number of seconds after which the probe times out.
format: int32
minimum: 0
type: integer
type: object
template:
@ -13678,6 +13874,10 @@ spec:
type: array
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- spec
- spec
- spec
type: object
served: true
storage: false
@ -13706,10 +13906,12 @@ spec:
annotations:
additionalProperties:
type: string
maxProperties: 256
type: object
labels:
additionalProperties:
type: string
maxProperties: 256
type: object
type: object
probe:
@ -13738,13 +13940,17 @@ spec:
command:
description: Command to run.
items:
minLength: 1
type: string
type: array
required:
- command
type: object
failureThreshold:
description: Minimum consecutive failures for the probe to be
considered failed after having succeeded.
format: int32
minimum: 0
type: integer
httpGet:
description: '`httpGet` is performed to a given endpoint and the
@ -13759,6 +13965,7 @@ spec:
items:
properties:
name:
pattern: ^[-_A-Za-z0-9]+$
type: string
value:
type: string
@ -13772,8 +13979,14 @@ spec:
maximum: 4294967295
minimum: 0
type: integer
x-kubernetes-validations:
- message: port must be between 1-65535
rule: 0 < self && self <= 65535
scheme:
type: string
x-kubernetes-validations:
- message: scheme must be one of [HTTP, HTTPS]
rule: self in ['', 'HTTP', 'HTTPS']
required:
- port
type: object
@ -13781,15 +13994,18 @@ spec:
description: Number of seconds after the container has started
before readiness probes are initiated.
format: int32
minimum: 0
type: integer
periodSeconds:
description: How often (in seconds) to perform the probe.
format: int32
minimum: 0
type: integer
successThreshold:
description: Minimum consecutive successes for the probe to be
considered successful after having failed.
format: int32
minimum: 0
type: integer
tcpSocket:
description: Health is determined by if the proxy is able to connect.
@ -13800,12 +14016,16 @@ spec:
maximum: 4294967295
minimum: 0
type: integer
x-kubernetes-validations:
- message: port must be between 1-65535
rule: 0 < self && self <= 65535
required:
- port
type: object
timeoutSeconds:
description: Number of seconds after which the probe times out.
format: int32
minimum: 0
type: integer
type: object
template:
@ -13942,6 +14162,10 @@ spec:
type: array
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- spec
- spec
- spec
type: object
served: true
storage: true

View File

@ -24,6 +24,7 @@ import "istio.io/api/networking/v1alpha3"
// Clients may not set this value. It is represented in RFC3339 form and is in UTC.
// Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
// +cue-gen:ServiceEntry:preserveUnknownFields:false
// +cue-gen:ServiceEntry:spec:required
// -->
//
// <!-- go code generation tags
@ -33,6 +34,10 @@ import "istio.io/api/networking/v1alpha3"
// +k8s:deepcopy-gen=true
// istiostatus-override: ServiceEntryStatus: istio.io/api/networking/v1alpha3
// -->
// +kubebuilder:validation:XValidation:message="only one of WorkloadSelector or Endpoints can be set",rule="(has(self.workloadSelector)?1:0)+(has(self.endpoints)?1:0)<=1"
// +kubebuilder:validation:XValidation:message="CIDR addresses are allowed only for NONE/STATIC resolution types",rule="!(has(self.addresses) && self.addresses.exists(k, k.contains('/')) && (self.resolution != 'STATIC' && self.resolution != 'NONE'))"
// +kubebuilder:validation:XValidation:message="NONE mode cannot set endpoints",rule="(!has(self.resolution) || self.resolution == 'NONE') ? !has(self.endpoints) : true"
// +kubebuilder:validation:XValidation:message="DNS_ROUND_ROBIN mode cannot have multiple endpoints",rule="(has(self.resolution) && self.resolution == 'DNS_ROUND_ROBIN') ? (!has(self.endpoints) || size(self.endpoints) == 1) : true"
type ServiceEntry = v1alpha3.ServiceEntry
// Location specifies whether the service is part of Istio mesh or

View File

@ -21,6 +21,7 @@ import "istio.io/api/networking/v1alpha3"
// Clients may not set this value. It is represented in RFC3339 form and is in UTC.
// Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
// +cue-gen:WorkloadGroup:preserveUnknownFields:false
// +cue-gen:WorkloadGroup:spec:required
// -->
//
// <!-- go code generation tags

View File

@ -584,6 +584,7 @@ func (ServiceEntry_Resolution) EnumDescriptor() ([]byte, []int) {
// Clients may not set this value. It is represented in RFC3339 form and is in UTC.
// Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
// +cue-gen:ServiceEntry:preserveUnknownFields:false
// +cue-gen:ServiceEntry:spec:required
// -->
//
// <!-- go code generation tags
@ -593,6 +594,10 @@ func (ServiceEntry_Resolution) EnumDescriptor() ([]byte, []int) {
// +k8s:deepcopy-gen=true
// istiostatus-override: ServiceEntryStatus: istio.io/api/networking/v1alpha3
// -->
// +kubebuilder:validation:XValidation:message="only one of WorkloadSelector or Endpoints can be set",rule="(has(self.workloadSelector)?1:0)+(has(self.endpoints)?1:0)<=1"
// +kubebuilder:validation:XValidation:message="CIDR addresses are allowed only for NONE/STATIC resolution types",rule="!(has(self.addresses) && self.addresses.exists(k, k.contains('/')) && (self.resolution != 'STATIC' && self.resolution != 'NONE'))"
// +kubebuilder:validation:XValidation:message="NONE mode cannot set endpoints",rule="(!has(self.resolution) || self.resolution == 'NONE') ? !has(self.endpoints) : true"
// +kubebuilder:validation:XValidation:message="DNS_ROUND_ROBIN mode cannot have multiple endpoints",rule="(has(self.resolution) && self.resolution == 'DNS_ROUND_ROBIN') ? (!has(self.endpoints) || size(self.endpoints) == 1) : true"
type ServiceEntry struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -621,6 +626,10 @@ type ServiceEntry struct {
// 1. subjectAltNames: In addition to verifying the SANs of the
// service accounts associated with the pods of the service, the
// SANs specified here will also be verified.
//
// +kubebuilder:validation:MinItems=1
// +kubebuilder:validation:MaxItems=256
// +protoc-gen-crd:list-value-validation:XValidation:message="hostname cannot be wildcard",rule="self != '*'"
Hosts []string `protobuf:"bytes,1,rep,name=hosts,proto3" json:"hosts,omitempty"`
// The virtual IP addresses associated with the service. Could be CIDR
// prefix. For HTTP traffic, generated route configurations will include http route
@ -636,10 +645,16 @@ type ServiceEntry struct {
// simple TCP proxy, forwarding incoming traffic on a specified port to
// the specified destination endpoint IP/host. Unix domain socket
// addresses are not supported in this field.
// +kubebuilder:validation:MaxItems=256
// +protoc-gen-crd:list-value-validation:MaxLength=64
Addresses []string `protobuf:"bytes,2,rep,name=addresses,proto3" json:"addresses,omitempty"`
// The ports associated with the external service. If the
// Endpoints are Unix domain socket addresses, there must be exactly one
// port.
// +listType=map
// +listMapKey=name
// +kubebuilder:validation:MaxItems=256
// +kubebuilder:validation:XValidation:message="port number cannot be duplicated",rule="self.all(l1, self.exists_one(l2, l1.number == l2.number))"
Ports []*ServicePort `protobuf:"bytes,3,rep,name=ports,proto3" json:"ports,omitempty"`
// Specify whether the service should be considered external to the mesh
// or part of the mesh.
@ -789,16 +804,20 @@ type ServicePort struct {
unknownFields protoimpl.UnknownFields
// A valid non-negative integer port number.
// +kubebuilder:validation:XValidation:message="port must be between 1-65535",rule="0 < self && self <= 65535"
Number uint32 `protobuf:"varint,1,opt,name=number,proto3" json:"number,omitempty"`
// The protocol exposed on the port.
// MUST BE one of HTTP|HTTPS|GRPC|HTTP2|MONGO|TCP|TLS.
// TLS implies the connection will be routed based on the SNI header to
// the destination without terminating the TLS connection.
// +kubebuilder:validation:MaxLength=256
Protocol string `protobuf:"bytes,2,opt,name=protocol,proto3" json:"protocol,omitempty"`
// Label assigned to the port.
// +kubebuilder:validation:MaxLength=256
Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
// The port number on the endpoint where the traffic will be
// received. If unset, default to `number`.
// +kubebuilder:validation:XValidation:message="port must be between 1-65535",rule="0 < self && self <= 65535"
TargetPort uint32 `protobuf:"varint,4,opt,name=target_port,json=targetPort,proto3" json:"target_port,omitempty"`
}

View File

@ -427,6 +427,7 @@ option go_package = "istio.io/api/networking/v1alpha3";
// Clients may not set this value. It is represented in RFC3339 form and is in UTC.
// Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
// +cue-gen:ServiceEntry:preserveUnknownFields:false
// +cue-gen:ServiceEntry:spec:required
// -->
//
// <!-- go code generation tags
@ -436,6 +437,10 @@ option go_package = "istio.io/api/networking/v1alpha3";
// +k8s:deepcopy-gen=true
// istiostatus-override: ServiceEntryStatus: istio.io/api/networking/v1alpha3
// -->
// +kubebuilder:validation:XValidation:message="only one of WorkloadSelector or Endpoints can be set",rule="(has(self.workloadSelector)?1:0)+(has(self.endpoints)?1:0)<=1"
// +kubebuilder:validation:XValidation:message="CIDR addresses are allowed only for NONE/STATIC resolution types",rule="!(has(self.addresses) && self.addresses.exists(k, k.contains('/')) && (self.resolution != 'STATIC' && self.resolution != 'NONE'))"
// +kubebuilder:validation:XValidation:message="NONE mode cannot set endpoints",rule="(!has(self.resolution) || self.resolution == 'NONE') ? !has(self.endpoints) : true"
// +kubebuilder:validation:XValidation:message="DNS_ROUND_ROBIN mode cannot have multiple endpoints",rule="(has(self.resolution) && self.resolution == 'DNS_ROUND_ROBIN') ? (!has(self.endpoints) || size(self.endpoints) == 1) : true"
message ServiceEntry {
// The hosts associated with the ServiceEntry. Could be a DNS
// name with wildcard prefix.
@ -460,7 +465,9 @@ message ServiceEntry {
// 1. subjectAltNames: In addition to verifying the SANs of the
// service accounts associated with the pods of the service, the
// SANs specified here will also be verified.
//
// +kubebuilder:validation:MinItems=1
// +kubebuilder:validation:MaxItems=256
// +protoc-gen-crd:list-value-validation:XValidation:message="hostname cannot be wildcard",rule="self != '*'"
repeated string hosts = 1 [(google.api.field_behavior) = REQUIRED];
// The virtual IP addresses associated with the service. Could be CIDR
@ -477,11 +484,17 @@ message ServiceEntry {
// simple TCP proxy, forwarding incoming traffic on a specified port to
// the specified destination endpoint IP/host. Unix domain socket
// addresses are not supported in this field.
// +kubebuilder:validation:MaxItems=256
// +protoc-gen-crd:list-value-validation:MaxLength=64
repeated string addresses = 2;
// The ports associated with the external service. If the
// Endpoints are Unix domain socket addresses, there must be exactly one
// port.
// +listType=map
// +listMapKey=name
// +kubebuilder:validation:MaxItems=256
// +kubebuilder:validation:XValidation:message="port number cannot be duplicated",rule="self.all(l1, self.exists_one(l2, l1.number == l2.number))"
repeated ServicePort ports = 3;
// Location specifies whether the service is part of Istio mesh or
@ -600,19 +613,23 @@ message ServiceEntry {
// ServicePort describes the properties of a specific port of a service.
message ServicePort {
// A valid non-negative integer port number.
// +kubebuilder:validation:XValidation:message="port must be between 1-65535",rule="0 < self && self <= 65535"
uint32 number = 1 [(google.api.field_behavior) = REQUIRED];
// The protocol exposed on the port.
// MUST BE one of HTTP|HTTPS|GRPC|HTTP2|MONGO|TCP|TLS.
// TLS implies the connection will be routed based on the SNI header to
// the destination without terminating the TLS connection.
// +kubebuilder:validation:MaxLength=256
string protocol = 2;
// Label assigned to the port.
// +kubebuilder:validation:MaxLength=256
string name = 3 [(google.api.field_behavior) = REQUIRED];
// The port number on the endpoint where the traffic will be
// received. If unset, default to `number`.
// +kubebuilder:validation:XValidation:message="port must be between 1-65535",rule="0 < self && self <= 65535"
uint32 target_port = 4;
}

View File

@ -875,6 +875,9 @@ type WorkloadSelector struct {
// on which the configuration should be applied. The scope of
// label search is restricted to the configuration namespace in which the
// the resource is present.
// +kubebuilder:validation:MaxProperties=256
// +protoc-gen-crd:map-value-validation:MaxLength=63
// +protoc-gen-crd:map-value-validation:XValidation:message="wildcard is not supported in selector",rule="!self.contains('*')"
Labels map[string]string `protobuf:"bytes,1,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}

View File

@ -552,6 +552,9 @@ message WorkloadSelector {
// on which the configuration should be applied. The scope of
// label search is restricted to the configuration namespace in which the
// the resource is present.
// +kubebuilder:validation:MaxProperties=256
// +protoc-gen-crd:map-value-validation:MaxLength=63
// +protoc-gen-crd:map-value-validation:XValidation:message="wildcard is not supported in selector",rule="!self.contains('*')"
map<string, string> labels = 1;
// $hide_from_docs

View File

@ -106,6 +106,7 @@ const (
// Clients may not set this value. It is represented in RFC3339 form and is in UTC.
// Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
// +cue-gen:WorkloadGroup:preserveUnknownFields:false
// +cue-gen:WorkloadGroup:spec:required
// -->
//
// <!-- go code generation tags
@ -193,18 +194,23 @@ type ReadinessProbe struct {
unknownFields protoimpl.UnknownFields
// Number of seconds after the container has started before readiness probes are initiated.
// +kubebuilder:validation:Minimum=0
InitialDelaySeconds int32 `protobuf:"varint,2,opt,name=initial_delay_seconds,json=initialDelaySeconds,proto3" json:"initial_delay_seconds,omitempty"`
// Number of seconds after which the probe times out.
// Defaults to 1 second. Minimum value is 1 second.
// +kubebuilder:validation:Minimum=0
TimeoutSeconds int32 `protobuf:"varint,3,opt,name=timeout_seconds,json=timeoutSeconds,proto3" json:"timeout_seconds,omitempty"`
// How often (in seconds) to perform the probe.
// Default to 10 seconds. Minimum value is 1 second.
// +kubebuilder:validation:Minimum=0
PeriodSeconds int32 `protobuf:"varint,4,opt,name=period_seconds,json=periodSeconds,proto3" json:"period_seconds,omitempty"`
// Minimum consecutive successes for the probe to be considered successful after having failed.
// Defaults to 1 second.
// +kubebuilder:validation:Minimum=0
SuccessThreshold int32 `protobuf:"varint,5,opt,name=success_threshold,json=successThreshold,proto3" json:"success_threshold,omitempty"`
// Minimum consecutive failures for the probe to be considered failed after having succeeded.
// Defaults to 3 seconds.
// +kubebuilder:validation:Minimum=0
FailureThreshold int32 `protobuf:"varint,6,opt,name=failure_threshold,json=failureThreshold,proto3" json:"failure_threshold,omitempty"`
// Users can only provide one configuration for healthchecks (tcp, http, exec),
// and this is expressed as a oneof. All of the other configuration values
@ -347,11 +353,13 @@ type HTTPHealthCheckConfig struct {
// Path to access on the HTTP server.
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
// Port on which the endpoint lives.
// +kubebuilder:validation:XValidation:message="port must be between 1-65535",rule="0 < self && self <= 65535"
Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"`
// Host name to connect to, defaults to the pod IP. You probably want to set
// "Host" in httpHeaders instead.
Host string `protobuf:"bytes,3,opt,name=host,proto3" json:"host,omitempty"`
// HTTP or HTTPS, defaults to HTTP
// +kubebuilder:validation:XValidation:message="scheme must be one of [HTTP, HTTPS]",rule="self in [”, 'HTTP', 'HTTPS']"
Scheme string `protobuf:"bytes,4,opt,name=scheme,proto3" json:"scheme,omitempty"`
// Headers the proxy will pass on to make the request.
// Allows repeated headers.
@ -431,6 +439,7 @@ type HTTPHeader struct {
unknownFields protoimpl.UnknownFields
// The header field name
// +kubebuilder:validation:Pattern=^[-_A-Za-z0-9]+$
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// The header field value
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
@ -490,6 +499,7 @@ type TCPHealthCheckConfig struct {
// Host to connect to, defaults to localhost
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
// Port of host
// +kubebuilder:validation:XValidation:message="port must be between 1-65535",rule="0 < self && self <= 65535"
Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"`
}
@ -545,6 +555,7 @@ type ExecHealthCheckConfig struct {
unknownFields protoimpl.UnknownFields
// Command to run. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.
// +protoc-gen-crd:list-value-validation:MinLength=1
Command []string `protobuf:"bytes,1,rep,name=command,proto3" json:"command,omitempty"`
}
@ -595,8 +606,10 @@ type WorkloadGroup_ObjectMeta struct {
unknownFields protoimpl.UnknownFields
// Labels to attach
// +kubebuilder:validation:MaxProperties=256
Labels map[string]string `protobuf:"bytes,1,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
// Annotations to attach
// +kubebuilder:validation:MaxProperties=256
Annotations map[string]string `protobuf:"bytes,2,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
@ -744,13 +757,13 @@ var file_networking_v1alpha3_workload_group_proto_rawDesc = []byte{
0x68, 0x65, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f,
0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x18,
0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x04, 0xe2, 0x41,
0x01, 0x02, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x31, 0x0a, 0x15, 0x45, 0x78, 0x65, 0x63,
0x01, 0x02, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x37, 0x0a, 0x15, 0x45, 0x78, 0x65, 0x63,
0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x03,
0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x42, 0x22, 0x5a, 0x20, 0x69,
0x73, 0x74, 0x69, 0x6f, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6e, 0x65, 0x74, 0x77,
0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x33, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x67, 0x12, 0x1e, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x03,
0x28, 0x09, 0x42, 0x04, 0xe2, 0x41, 0x01, 0x02, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
0x64, 0x42, 0x22, 0x5a, 0x20, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70,
0x69, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x2f, 0x76, 0x31, 0x61,
0x6c, 0x70, 0x68, 0x61, 0x33, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -385,7 +385,7 @@ Yes
</td>
<td>
No
Yes
</td>
</tr>
</tbody>

View File

@ -91,6 +91,7 @@ option go_package = "istio.io/api/networking/v1alpha3";
// Clients may not set this value. It is represented in RFC3339 form and is in UTC.
// Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
// +cue-gen:WorkloadGroup:preserveUnknownFields:false
// +cue-gen:WorkloadGroup:spec:required
// -->
//
// <!-- go code generation tags
@ -116,9 +117,11 @@ message WorkloadGroup {
// It is a subset of the supported Kubernetes metadata.
message ObjectMeta {
// Labels to attach
// +kubebuilder:validation:MaxProperties=256
map<string, string> labels = 1;
// Annotations to attach
// +kubebuilder:validation:MaxProperties=256
map<string, string> annotations = 2;
}
@ -128,24 +131,28 @@ message WorkloadGroup {
}
message ReadinessProbe {
// Number of seconds after the container has started before readiness probes are initiated.
// +kubebuilder:validation:Minimum=0
int32 initial_delay_seconds = 2;
// Number of seconds after which the probe times out.
// Defaults to 1 second. Minimum value is 1 second.
// +kubebuilder:validation:Minimum=0
int32 timeout_seconds = 3;
// How often (in seconds) to perform the probe.
// Default to 10 seconds. Minimum value is 1 second.
// +kubebuilder:validation:Minimum=0
int32 period_seconds = 4;
// Minimum consecutive successes for the probe to be considered successful after having failed.
// Defaults to 1 second.
// +kubebuilder:validation:Minimum=0
int32 success_threshold = 5;
// Minimum consecutive failures for the probe to be considered failed after having succeeded.
// Defaults to 3 seconds.
// +kubebuilder:validation:Minimum=0
int32 failure_threshold = 6;
// Users can only provide one configuration for healthchecks (tcp, http, exec),
@ -167,6 +174,7 @@ message HTTPHealthCheckConfig {
string path = 1;
// Port on which the endpoint lives.
// +kubebuilder:validation:XValidation:message="port must be between 1-65535",rule="0 < self && self <= 65535"
uint32 port = 2 [(google.api.field_behavior) = REQUIRED];
// Host name to connect to, defaults to the pod IP. You probably want to set
@ -174,6 +182,7 @@ message HTTPHealthCheckConfig {
string host = 3;
// HTTP or HTTPS, defaults to HTTP
// +kubebuilder:validation:XValidation:message="scheme must be one of [HTTP, HTTPS]",rule="self in ['', 'HTTP', 'HTTPS']"
string scheme = 4;
// Headers the proxy will pass on to make the request.
@ -183,6 +192,7 @@ message HTTPHealthCheckConfig {
message HTTPHeader {
// The header field name
// +kubebuilder:validation:Pattern=^[-_A-Za-z0-9]+$
string name = 1;
// The header field value
@ -193,11 +203,13 @@ message TCPHealthCheckConfig {
// Host to connect to, defaults to localhost
string host = 1;
// Port of host
// +kubebuilder:validation:XValidation:message="port must be between 1-65535",rule="0 < self && self <= 65535"
uint32 port = 2 [(google.api.field_behavior) = REQUIRED];
}
message ExecHealthCheckConfig {
// Command to run. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.
repeated string command = 1;
// +protoc-gen-crd:list-value-validation:MinLength=1
repeated string command = 1 [(google.api.field_behavior) = REQUIRED];
}

View File

@ -24,6 +24,7 @@ import "istio.io/api/networking/v1alpha3"
// Clients may not set this value. It is represented in RFC3339 form and is in UTC.
// Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
// +cue-gen:ServiceEntry:preserveUnknownFields:false
// +cue-gen:ServiceEntry:spec:required
// -->
//
// <!-- go code generation tags
@ -33,6 +34,10 @@ import "istio.io/api/networking/v1alpha3"
// +k8s:deepcopy-gen=true
// istiostatus-override: ServiceEntryStatus: istio.io/api/networking/v1alpha3
// -->
// +kubebuilder:validation:XValidation:message="only one of WorkloadSelector or Endpoints can be set",rule="(has(self.workloadSelector)?1:0)+(has(self.endpoints)?1:0)<=1"
// +kubebuilder:validation:XValidation:message="CIDR addresses are allowed only for NONE/STATIC resolution types",rule="!(has(self.addresses) && self.addresses.exists(k, k.contains('/')) && (self.resolution != 'STATIC' && self.resolution != 'NONE'))"
// +kubebuilder:validation:XValidation:message="NONE mode cannot set endpoints",rule="(!has(self.resolution) || self.resolution == 'NONE') ? !has(self.endpoints) : true"
// +kubebuilder:validation:XValidation:message="DNS_ROUND_ROBIN mode cannot have multiple endpoints",rule="(has(self.resolution) && self.resolution == 'DNS_ROUND_ROBIN') ? (!has(self.endpoints) || size(self.endpoints) == 1) : true"
type ServiceEntry = v1alpha3.ServiceEntry
// Location specifies whether the service is part of Istio mesh or

View File

@ -21,6 +21,7 @@ import "istio.io/api/networking/v1alpha3"
// Clients may not set this value. It is represented in RFC3339 form and is in UTC.
// Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata"
// +cue-gen:WorkloadGroup:preserveUnknownFields:false
// +cue-gen:WorkloadGroup:spec:required
// -->
//
// <!-- go code generation tags

159
tests/testdata/serviceentry-invalid.yaml vendored Normal file
View File

@ -0,0 +1,159 @@
_err: "spec: Required value"
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
name: no-spec
---
_err: "spec.hosts: Required value"
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
name: missing-host
spec: {}
---
#_err: "TODO"
#apiVersion: networking.istio.io/v1alpha3
#kind: ServiceEntry
#metadata:
# name: bad-selector-key
#spec:
# hosts: ["example.com"]
# resolution: STATIC
# workloadSelector:
# labels:
# "*": val
#---
_err: "wildcard is not supported in selector"
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: bad-selector-value
spec:
hosts: ["example.com"]
workloadSelector:
labels:
"val": "*"
---
_err: "only one of WorkloadSelector or Endpoints can be set"
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: selector-and-endpoints
spec:
hosts: ["example.com"]
workloadSelector: {}
endpoints:
- address: "1.2.3.4"
---
_err: "hostname cannot be wildcard"
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: bad-host-wildcard
spec:
hosts: ["*"]
#---
#_err: "TODO"
#apiVersion: networking.istio.io/v1alpha3
#kind: ServiceEntry
#metadata:
# name: bad-host-wildcard-suffix
#spec:
# hosts: ["foo*"]
#---
#_err: "TODO"
#apiVersion: networking.istio.io/v1alpha3
#kind: ServiceEntry
#metadata:
# name: bad-cidr
#spec:
# hosts: ["example.com"]
# addresses: [1.2.3.4/99]
---
_err: "CIDR addresses are allowed only for NONE/STATIC resolution types"
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: bad-cidr-resolution
spec:
hosts: ["example.com"]
addresses: [1.2.3.4/16]
resolution: DNS
---
_err: "Duplicate value"
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: duplicate-ports-name
spec:
hosts: ["example.com"]
ports:
- name: a
number: 1
- name: a
number: 12
---
_err: "port number cannot be duplicated"
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: duplicate-ports-number
spec:
hosts: ["example.com"]
ports:
- name: a
number: 1
- name: b
number: 1
---
_err: "port must be between 1-65535"
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: bad-port
spec:
hosts: ["example.com"]
ports:
- name: a
number: 99999
---
_err: "port must be between 1-65535"
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: bad-port-target
spec:
hosts: ["example.com"]
ports:
- name: a
number: 1
targetPort: 99999
---
_err: "NONE mode cannot set endpoints"
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: none-with-endpoints
spec:
hosts: ["example.com"]
endpoints:
- address: "1.2.3.4"
resolution: NONE
---
_err: "DNS_ROUND_ROBIN mode cannot have multiple endpoints"
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: dns-rr-with-multiple-endpoints
spec:
hosts: ["example.com"]
resolution: DNS_ROUND_ROBIN
endpoints:
- address: "sub1.example.com"
- address: "sub2.example.com"
# TODO:
# validate cidr
# validate port name
# validate protocol parsing
# Validation of DNS/DNS_RR endpoints
# Validate exportTo duplicates (with logic about . and current namespace)

52
tests/testdata/serviceentry-valid.yaml vendored Normal file
View File

@ -0,0 +1,52 @@
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
name: full
namespace: default
spec:
hosts: ["example.com"]
workloadSelector:
labels:
foo: bar
addresses: [1.2.3.4, 001:db8::]
exportTo: [foo, .]
location: MESH_EXTERNAL
ports:
- name: foo
protocol: HTTP
number: 1234
targetPort: 2345
subjectAltNames: [sa]
resolution: DNS
---
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
name: static-endpoints
spec:
hosts: ["example.com"]
endpoints:
- address: "sub.example.com"
ports:
- name: foo
protocol: HTTP
number: 1234
targetPort: 2345
resolution: DNS
---
# Weird case but we allow it
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: empty-selector
spec:
workloadSelector: {}
hosts: ["example.com"]
---
# Weird case but we allow it
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: partial-wildcard
spec:
hosts: ["*x"]

View File

@ -0,0 +1,31 @@
_err: 'spec: Required value'
apiVersion: networking.istio.io/v1
kind: WorkloadGroup
metadata:
name: no-spec
---
_err: port must be between 1-65535
apiVersion: networking.istio.io/v1alpha3
kind: WorkloadGroup
metadata:
name: tcp-probe-invalid
spec:
probe:
tcpSocket:
port: 65536
template:
serviceAccount: sa
network: net
---
_err: 'spec.probe.httpGet.httpHeaders[0].name in body should match'
apiVersion: networking.istio.io/v1alpha3
kind: WorkloadGroup
metadata:
name: http-probe-invalid
spec:
probe:
httpGet:
httpHeaders:
- name: "**"
port: 80
template: {}

45
tests/testdata/workloadgroup-valid.yaml vendored Normal file
View File

@ -0,0 +1,45 @@
apiVersion: networking.istio.io/v1alpha3
kind: WorkloadGroup
metadata:
name: full
spec:
metadata:
labels:
foo: bar
annotations:
foo: bar
probe:
failureThreshold: 2
initialDelaySeconds: 1
periodSeconds: 1
successThreshold: 1
timeoutSeconds: 1
httpGet:
port: 8080
host: "bar"
scheme: HTTPS
path: /bar
httpHeaders:
- name: foo
value: bar
template:
serviceAccount: sa
network: net
labels:
app: app
ports:
"http": 1
weight: 2
locality: foo/bar/baz
---
apiVersion: networking.istio.io/v1alpha3
kind: WorkloadGroup
metadata:
name: tcp-probe
spec:
probe:
tcpSocket:
port: 8080
template:
serviceAccount: sa
network: net