diff --git a/cilium.io/ciliumbgppeeringpolicy_v2alpha1.json b/cilium.io/ciliumbgppeeringpolicy_v2alpha1.json index 9deaa6bc..58871751 100644 --- a/cilium.io/ciliumbgppeeringpolicy_v2alpha1.json +++ b/cilium.io/ciliumbgppeeringpolicy_v2alpha1.json @@ -1,12 +1,12 @@ { - "description": "CiliumBGPPeeringPolicy is a Kubernetes third-party resource for instructing Cilium's BGP control plane to create virtual BGP routers.", + "description": "CiliumBGPPeeringPolicy is a Kubernetes third-party resource for instructing\nCilium's BGP control plane to create virtual BGP routers.", "properties": { "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "description": "APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", "type": "string" }, "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "description": "Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", "type": "string" }, "metadata": { @@ -16,19 +16,19 @@ "description": "Spec is a human readable description of a BGP peering policy", "properties": { "nodeSelector": { - "description": "NodeSelector selects a group of nodes where this BGP Peering Policy applies. \n If empty / nil this policy applies to all nodes.", + "description": "NodeSelector selects a group of nodes where this BGP Peering\nPolicy applies.\n\nIf empty / nil this policy applies to all nodes.", "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.", + "description": "A label selector requirement is a selector that contains values, a key, and an operator that\nrelates 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.", + "description": "operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist.", "enum": [ "In", "NotIn", @@ -38,21 +38,22 @@ "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.", + "description": "values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch.", "items": { "type": "string" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" } }, "required": [ "key", "operator" ], - "type": "object", - "additionalProperties": false + "type": "object" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" }, "matchLabels": { "additionalProperties": { @@ -61,25 +62,25 @@ "pattern": "^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$", "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.", + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is \"key\", the\noperator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", "type": "object" } }, "type": "object", - "additionalProperties": false + "x-kubernetes-map-type": "atomic" }, "virtualRouters": { - "description": "A list of CiliumBGPVirtualRouter(s) which instructs the BGP control plane how to instantiate virtual BGP routers.", + "description": "A list of CiliumBGPVirtualRouter(s) which instructs\nthe BGP control plane how to instantiate virtual BGP routers.", "items": { "description": "CiliumBGPVirtualRouter defines a discrete BGP virtual router configuration.", "properties": { "exportPodCIDR": { "default": false, - "description": "ExportPodCIDR determines whether to export the Node's private CIDR block to the configured neighbors.", + "description": "ExportPodCIDR determines whether to export the Node's private CIDR block\nto the configured neighbors.", "type": "boolean" }, "localASN": { - "description": "LocalASN is the ASN of this virtual router. Supports extended 32bit ASNs", + "description": "LocalASN is the ASN of this virtual router.\nSupports extended 32bit ASNs", "format": "int64", "maximum": 4294967295, "minimum": 0, @@ -88,8 +89,143 @@ "neighbors": { "description": "Neighbors is a list of neighboring BGP peers for this virtual router", "items": { - "description": "CiliumBGPNeighbor is a neighboring peer for use in a CiliumBGPVirtualRouter configuration.", + "description": "CiliumBGPNeighbor is a neighboring peer for use in a\nCiliumBGPVirtualRouter configuration.", "properties": { + "advertisedPathAttributes": { + "description": "AdvertisedPathAttributes can be used to apply additional path attributes\nto selected routes when advertising them to the peer.\nIf empty / nil, no additional path attributes are advertised.", + "items": { + "description": "CiliumBGPPathAttributes can be used to apply additional path attributes\nto matched routes when advertising them to a BGP peer.", + "properties": { + "communities": { + "description": "Communities defines a set of community values advertised in the supported BGP Communities path attributes.\nIf nil / not set, no BGP Communities path attribute will be advertised.", + "properties": { + "large": { + "description": "Large holds a list of the BGP Large Communities Attribute (RFC 8092) values.", + "items": { + "description": "BGPLargeCommunity type represents a value of the BGP Large Communities Attribute (RFC 8092),\nas three 4-byte decimal numbers separated by colons.", + "pattern": "^([0-9]|[1-9][0-9]{1,8}|[1-3][0-9]{9}|4[01][0-9]{8}|42[0-8][0-9]{7}|429[0-3][0-9]{6}|4294[0-8][0-9]{5}|42949[0-5][0-9]{4}|429496[0-6][0-9]{3}|4294967[01][0-9]{2}|42949672[0-8][0-9]|429496729[0-5]):([0-9]|[1-9][0-9]{1,8}|[1-3][0-9]{9}|4[01][0-9]{8}|42[0-8][0-9]{7}|429[0-3][0-9]{6}|4294[0-8][0-9]{5}|42949[0-5][0-9]{4}|429496[0-6][0-9]{3}|4294967[01][0-9]{2}|42949672[0-8][0-9]|429496729[0-5]):([0-9]|[1-9][0-9]{1,8}|[1-3][0-9]{9}|4[01][0-9]{8}|42[0-8][0-9]{7}|429[0-3][0-9]{6}|4294[0-8][0-9]{5}|42949[0-5][0-9]{4}|429496[0-6][0-9]{3}|4294967[01][0-9]{2}|42949672[0-8][0-9]|429496729[0-5])$", + "type": "string" + }, + "type": "array" + }, + "standard": { + "description": "Standard holds a list of \"standard\" 32-bit BGP Communities Attribute (RFC 1997) values defined as numeric values.", + "items": { + "description": "BGPStandardCommunity type represents a value of the \"standard\" 32-bit BGP Communities Attribute (RFC 1997)\nas a 4-byte decimal number or two 2-byte decimal numbers separated by a colon (<0-65535>:<0-65535>).\nFor example, no-export community value is 65553:65281.", + "pattern": "^([0-9]|[1-9][0-9]{1,8}|[1-3][0-9]{9}|4[01][0-9]{8}|42[0-8][0-9]{7}|429[0-3][0-9]{6}|4294[0-8][0-9]{5}|42949[0-5][0-9]{4}|429496[0-6][0-9]{3}|4294967[01][0-9]{2}|42949672[0-8][0-9]|429496729[0-5])$|^([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]):([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$", + "type": "string" + }, + "type": "array" + }, + "wellKnown": { + "description": "WellKnown holds a list \"standard\" 32-bit BGP Communities Attribute (RFC 1997) values defined as\nwell-known string aliases to their numeric values.", + "items": { + "description": "BGPWellKnownCommunity type represents a value of the \"standard\" 32-bit BGP Communities Attribute (RFC 1997)\nas a well-known string alias to its numeric value. Allowed values and their mapping to the numeric values:\n\n\tinternet = 0x00000000 (0:0)\n\tplanned-shut = 0xffff0000 (65535:0)\n\taccept-own = 0xffff0001 (65535:1)\n\troute-filter-translated-v4 = 0xffff0002 (65535:2)\n\troute-filter-v4 = 0xffff0003 (65535:3)\n\troute-filter-translated-v6 = 0xffff0004 (65535:4)\n\troute-filter-v6 = 0xffff0005 (65535:5)\n\tllgr-stale = 0xffff0006 (65535:6)\n\tno-llgr = 0xffff0007 (65535:7)\n\tblackhole = 0xffff029a (65535:666)\n\tno-export = 0xffffff01\t(65535:65281)\n\tno-advertise = 0xffffff02 (65535:65282)\n\tno-export-subconfed = 0xffffff03 (65535:65283)\n\tno-peer = 0xffffff04 (65535:65284)", + "enum": [ + "internet", + "planned-shut", + "accept-own", + "route-filter-translated-v4", + "route-filter-v4", + "route-filter-translated-v6", + "route-filter-v6", + "llgr-stale", + "no-llgr", + "blackhole", + "no-export", + "no-advertise", + "no-export-subconfed", + "no-peer" + ], + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "localPreference": { + "description": "LocalPreference defines the preference value advertised in the BGP Local Preference path attribute.\nAs Local Preference is only valid for iBGP peers, this value will be ignored for eBGP peers\n(no Local Preference path attribute will be advertised).\nIf nil / not set, the default Local Preference of 100 will be advertised in\nthe Local Preference path attribute for iBGP peers.", + "format": "int64", + "maximum": 4294967295, + "minimum": 0, + "type": "integer" + }, + "selector": { + "description": "Selector selects a group of objects of the SelectorType\nresulting into routes that will be announced with the configured Attributes.\nIf nil / not set, all objects of the SelectorType are selected.", + "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\nrelates 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.\nValid operators are In, NotIn, Exists and DoesNotExist.", + "enum": [ + "In", + "NotIn", + "Exists", + "DoesNotExist" + ], + "type": "string" + }, + "values": { + "description": "values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge 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": { + "description": "MatchLabelsValue represents the value from the MatchLabels {key,value} pair.", + "maxLength": 63, + "pattern": "^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$", + "type": "string" + }, + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is \"key\", the\noperator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + "type": "object" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "selectorType": { + "description": "SelectorType defines the object type on which the Selector applies:\n- For \"PodCIDR\" the Selector matches k8s CiliumNode resources\n (path attributes apply to routes announced for PodCIDRs of selected CiliumNodes.\n Only affects routes of cluster scope / Kubernetes IPAM CIDRs, not Multi-Pool IPAM CIDRs.\n- For \"CiliumLoadBalancerIPPool\" the Selector matches CiliumLoadBalancerIPPool custom resources\n (path attributes apply to routes announced for selected CiliumLoadBalancerIPPools).\n- For \"CiliumPodIPPool\" the Selector matches CiliumPodIPPool custom resources\n (path attributes apply to routes announced for allocated CIDRs of selected CiliumPodIPPools).", + "enum": [ + "PodCIDR", + "CiliumLoadBalancerIPPool", + "CiliumPodIPPool" + ], + "type": "string" + } + }, + "required": [ + "selectorType" + ], + "type": "object" + }, + "type": "array" + }, + "authSecretRef": { + "description": "AuthSecretRef is the name of the secret to use to fetch a TCP\nauthentication password for this peer.", + "type": "string" + }, "connectRetryTimeSeconds": { "default": 120, "description": "ConnectRetryTimeSeconds defines the initial value for the BGP ConnectRetryTimer (RFC 4271, Section 8).", @@ -100,14 +236,60 @@ }, "eBGPMultihopTTL": { "default": 1, - "description": "EBGPMultihopTTL controls the multi-hop feature for eBGP peers. Its value defines the Time To Live (TTL) value used in BGP packets sent to the neighbor. The value 1 implies that eBGP multi-hop feature is disabled (only a single hop is allowed). This field is ignored for iBGP peers.", + "description": "EBGPMultihopTTL controls the multi-hop feature for eBGP peers.\nIts value defines the Time To Live (TTL) value used in BGP packets sent to the neighbor.\nThe value 1 implies that eBGP multi-hop feature is disabled (only a single hop is allowed).\nThis field is ignored for iBGP peers.", "format": "int32", "maximum": 255, "minimum": 1, "type": "integer" }, + "families": { + "description": "Families, if provided, defines a set of AFI/SAFIs the speaker will\nnegotiate with it's peer.\n\nIf this slice is not provided the default families of IPv6 and IPv4 will\nbe provided.", + "items": { + "description": "CiliumBGPFamily represents a AFI/SAFI address family pair.", + "properties": { + "afi": { + "description": "Afi is the Address Family Identifier (AFI) of the family.", + "enum": [ + "ipv4", + "ipv6", + "l2vpn", + "ls", + "opaque" + ], + "type": "string" + }, + "safi": { + "description": "Safi is the Subsequent Address Family Identifier (SAFI) of the family.", + "enum": [ + "unicast", + "multicast", + "mpls_label", + "encapsulation", + "vpls", + "evpn", + "ls", + "sr_policy", + "mup", + "mpls_vpn", + "mpls_vpn_multicast", + "route_target_constraints", + "flowspec_unicast", + "flowspec_vpn", + "key_value" + ], + "type": "string" + } + }, + "required": [ + "afi", + "safi" + ], + "type": "object" + }, + "type": "array" + }, "gracefulRestart": { - "description": "GracefulRestart defines graceful restart parameters which are negotiated with this neighbor. If empty / nil, the graceful restart capability is disabled.", + "description": "GracefulRestart defines graceful restart parameters which are negotiated\nwith this neighbor. If empty / nil, the graceful restart capability is disabled.", "properties": { "enabled": { "description": "Enabled flag, when set enables graceful restart capability.", @@ -115,7 +297,7 @@ }, "restartTimeSeconds": { "default": 120, - "description": "RestartTimeSeconds is the estimated time it will take for the BGP session to be re-established with peer after a restart. After this period, peer will remove stale routes. This is described RFC 4724 section 4.2.", + "description": "RestartTimeSeconds is the estimated time it will take for the BGP\nsession to be re-established with peer after a restart.\nAfter this period, peer will remove stale routes. This is\ndescribed RFC 4724 section 4.2.", "format": "int32", "maximum": 4095, "minimum": 1, @@ -125,12 +307,11 @@ "required": [ "enabled" ], - "type": "object", - "additionalProperties": false + "type": "object" }, "holdTimeSeconds": { "default": 90, - "description": "HoldTimeSeconds defines the initial value for the BGP HoldTimer (RFC 4271, Section 4.2). Updating this value will cause a session reset.", + "description": "HoldTimeSeconds defines the initial value for the BGP HoldTimer (RFC 4271, Section 4.2).\nUpdating this value will cause a session reset.", "format": "int32", "maximum": 65535, "minimum": 3, @@ -138,27 +319,27 @@ }, "keepAliveTimeSeconds": { "default": 30, - "description": "KeepaliveTimeSeconds defines the initial value for the BGP KeepaliveTimer (RFC 4271, Section 8). It can not be larger than HoldTimeSeconds. Updating this value will cause a session reset.", + "description": "KeepaliveTimeSeconds defines the initial value for the BGP KeepaliveTimer (RFC 4271, Section 8).\nIt can not be larger than HoldTimeSeconds. Updating this value will cause a session reset.", "format": "int32", "maximum": 65535, "minimum": 1, "type": "integer" }, "peerASN": { - "description": "PeerASN is the ASN of the peer BGP router. Supports extended 32bit ASNs", + "description": "PeerASN is the ASN of the peer BGP router.\nSupports extended 32bit ASNs", "format": "int64", "maximum": 4294967295, "minimum": 0, "type": "integer" }, "peerAddress": { - "description": "PeerAddress is the IP address of the peer. This must be in CIDR notation and use a /32 to express a single host.", + "description": "PeerAddress is the IP address of the peer.\nThis must be in CIDR notation and use a /32 to express\na single host.", "format": "cidr", "type": "string" }, "peerPort": { "default": 179, - "description": "PeerPort is the TCP port of the peer. 1-65535 is the range of valid port numbers that can be specified. If unset, defaults to 179.", + "description": "PeerPort is the TCP port of the peer. 1-65535 is the range of\nvalid port numbers that can be specified. If unset, defaults to 179.", "format": "int32", "maximum": 65535, "minimum": 1, @@ -169,26 +350,95 @@ "peerASN", "peerAddress" ], - "type": "object", - "additionalProperties": false + "type": "object" }, "minItems": 1, "type": "array" }, + "podIPPoolSelector": { + "description": "PodIPPoolSelector selects CiliumPodIPPools based on labels. The virtual\nrouter will announce allocated CIDRs of matching CiliumPodIPPools.\n\nIf empty / nil no CiliumPodIPPools will be announced.", + "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\nrelates 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.\nValid operators are In, NotIn, Exists and DoesNotExist.", + "enum": [ + "In", + "NotIn", + "Exists", + "DoesNotExist" + ], + "type": "string" + }, + "values": { + "description": "values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge 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": { + "description": "MatchLabelsValue represents the value from the MatchLabels {key,value} pair.", + "maxLength": 63, + "pattern": "^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$", + "type": "string" + }, + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is \"key\", the\noperator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + "type": "object" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "serviceAdvertisements": { + "default": [ + "LoadBalancerIP" + ], + "description": "ServiceAdvertisements selects a group of BGP Advertisement(s) to advertise\nfor the selected services.", + "items": { + "description": "BGPServiceAddressType defines type of service address to be advertised.\n\nNote list of supported service addresses is not exhaustive and can be extended in the future.\nConsumer of this API should be able to handle unknown values.", + "enum": [ + "LoadBalancerIP", + "ClusterIP", + "ExternalIP" + ], + "type": "string" + }, + "type": "array" + }, "serviceSelector": { - "description": "ServiceSelector selects a group of load balancer services which this virtual router will announce. \n If empty / nil no services will be announced.", + "description": "ServiceSelector selects a group of load balancer services which this\nvirtual router will announce. The loadBalancerClass for a service must\nbe nil or specify a class supported by Cilium, e.g. \"io.cilium/bgp-control-plane\".\nRefer to the following document for additional details regarding load balancer\nclasses:\n\n https://kubernetes.io/docs/concepts/services-networking/service/#load-balancer-class\n\nIf empty / nil no services will be announced.", "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.", + "description": "A label selector requirement is a selector that contains values, a key, and an operator that\nrelates 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.", + "description": "operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist.", "enum": [ "In", "NotIn", @@ -198,21 +448,22 @@ "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.", + "description": "values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch.", "items": { "type": "string" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" } }, "required": [ "key", "operator" ], - "type": "object", - "additionalProperties": false + "type": "object" }, - "type": "array" + "type": "array", + "x-kubernetes-list-type": "atomic" }, "matchLabels": { "additionalProperties": { @@ -221,20 +472,19 @@ "pattern": "^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$", "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.", + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is \"key\", the\noperator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", "type": "object" } }, "type": "object", - "additionalProperties": false + "x-kubernetes-map-type": "atomic" } }, "required": [ "localASN", "neighbors" ], - "type": "object", - "additionalProperties": false + "type": "object" }, "minItems": 1, "type": "array" @@ -243,8 +493,7 @@ "required": [ "virtualRouters" ], - "type": "object", - "additionalProperties": false + "type": "object" } }, "required": [