Skip to content

Commit

Permalink
Feat: Add startup probe trait (kubevela#5093)
Browse files Browse the repository at this point in the history
* Feat: Add startup probe trait

Signed-off-by: Jerome Guionnet <jguionnet@guidewire.com>

* Feat: Implemented review comments in startup probe trait

Signed-off-by: Jerome Guionnet <jguionnet@guidewire.com>

Signed-off-by: Jerome Guionnet <jguionnet@guidewire.com>
  • Loading branch information
jguionnet authored and zhaohuiweixiao committed Mar 7, 2023
1 parent 0d90fc6 commit 7b548f8
Show file tree
Hide file tree
Showing 5 changed files with 579 additions and 0 deletions.
168 changes: 168 additions & 0 deletions charts/vela-core/templates/defwithtemplate/startup-probe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
# Definition source cue file: vela-templates/definitions/internal/startup-probe.cue
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: Add startup probe hooks for the specified container of K8s pod for your workload which follows the pod spec in path 'spec.template'.
name: startup-probe
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
appliesToWorkloads:
- deployments.apps
- statefulsets.apps
- daemonsets.apps
- jobs.batch
podDisruptive: true
schematic:
cue:
template: |
#StartupProbeParams: {
// +usage=Specify the name of the target container, if not set, use the component name
containerName: *"" | string
// +usage=Number of seconds after the container has started before liveness probes are initiated. Minimum value is 0.
initialDelaySeconds: *0 | int
// +usage=How often, in seconds, to execute the probe. Minimum value is 1.
periodSeconds: *10 | int
// +usage=Number of seconds after which the probe times out. Minimum value is 1.
timeoutSeconds: *1 | int
// +usage=Minimum consecutive successes for the probe to be considered successful after having failed. Minimum value is 1.
successThreshold: *1 | int
// +usage=Minimum consecutive failures for the probe to be considered failed after having succeeded. Minimum value is 1.
failureThreshold: *3 | int
// +usage=Optional duration in seconds the pod needs to terminate gracefully upon probe failure. Set this value longer than the expected cleanup time for your process.
terminationGracePeriodSeconds?: int
// +usage=Instructions for assessing container startup status by executing a command. Either this attribute or the httpGet attribute or the grpc attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with the httpGet attribute and the tcpSocket attribute and the gRPC attribute.
exec?: {
// +usage=A command to be executed inside the container to assess its health. Each space delimited token of the command is a separate array element. Commands exiting 0 are considered to be successful probes, whilst all other exit codes are considered failures.
command: [...string]
}
// +usage=Instructions for assessing container startup status by executing an HTTP GET request. Either this attribute or the exec attribute or the grpc attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with the exec attribute and the tcpSocket attribute and the gRPC attribute.
httpGet?: {
// +usage=The endpoint, relative to the port, to which the HTTP GET request should be directed.
path?: string
// +usage=The port numer to access on the host or container.
port: int
// +usage=The hostname to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
host?: string
// +usage=The Scheme to use for connecting to the host.
scheme?: *"HTTP" | "HTTPS"
// +usage=Custom headers to set in the request. HTTP allows repeated headers.
httpHeaders?: [...{
// +usage=The header field name
name: string
//+usage=The header field value
value: string
}]
}
// +usage=Instructions for assessing container startup status by probing a gRPC service. Either this attribute or the exec attribute or the grpc attribute or the httpGet attribute MUST be specified. This attribute is mutually exclusive with the exec attribute and the httpGet attribute and the tcpSocket attribute.
grpc?: {
// +usage=The port number of the gRPC service.
port: int
// +usage=The name of the service to place in the gRPC HealthCheckRequest
service?: string
}
// +usage=Instructions for assessing container startup status by probing a TCP socket. Either this attribute or the exec attribute or the tcpSocket attribute or the httpGet attribute MUST be specified. This attribute is mutually exclusive with the exec attribute and the httpGet attribute and the gRPC attribute.
tcpSocket?: {
// +usage=Number or name of the port to access on the container.
port: string
// +usage=Host name to connect to, defaults to the pod IP.
host?: string
}
}
PatchContainer: {
_params: #StartupProbeParams
name: _params.containerName
_baseContainers: context.output.spec.template.spec.containers
_matchContainers_: [ for _container_ in _baseContainers if _container_.name == name {_container_}]
if len(_matchContainers_) == 0 {
err: "container \(name) not found"
}
if len(_matchContainers_) > 0 {
startupProbe: {
if _params.exec != _|_ {
exec: _params.exec
}
if _params.httpGet != _|_ {
httpGet: _params.httpGet
}
if _params.grpc != _|_ {
grpc: _params.grpc
}
if _params.tcpSocket != _|_ {
tcpSocket: _params.tcpSocket
}
if _params.initialDelaySeconds != _|_ {
initialDelaySeconds: _params.initialDelaySeconds
}
if _params.periodSeconds != _|_ {
periodSeconds: _params.periodSeconds
}
if _params.tcpSocket != _|_ {
tcpSocket: _params.tcpSocket
}
if _params.timeoutSeconds != _|_ {
timeoutSeconds: _params.timeoutSeconds
}
if _params.successThreshold != _|_ {
successThreshold: _params.successThreshold
}
if _params.failureThreshold != _|_ {
failureThreshold: _params.failureThreshold
}
if _params.terminationGracePeriodSeconds != _|_ {
terminationGracePeriodSeconds: _params.terminationGracePeriodSeconds
}
}
}
}
patch: spec: template: spec: {
if parameter.probes == _|_ {
// +patchKey=name
containers: [{
PatchContainer & {_params: {
if parameter.containerName == "" {
containerName: context.name
}
if parameter.containerName != "" {
containerName: parameter.containerName
}
periodSeconds: parameter.periodSeconds
initialDelaySeconds: parameter.initialDelaySeconds
timeoutSeconds: parameter.timeoutSeconds
successThreshold: parameter.successThreshold
failureThreshold: parameter.failureThreshold
terminationGracePeriodSeconds: parameter.terminationGracePeriodSeconds
if parameter.exec != _|_ {
exec: parameter.exec
}
if parameter.httpGet != _|_ {
httpGet: parameter.httpGet
}
if parameter.grpc != _|_ {
grpc: parameter.grpc
}
if parameter.tcpSocket != _|_ {
tcpSocket: parameter.grtcpSocketpc
}
}}
}]
}
if parameter.probes != _|_ {
// +patchKey=name
containers: [ for c in parameter.probes {
if c.name == "" {
err: "containerName must be set when specifying startup probe for multiple containers"
}
if c.name != "" {
PatchContainer & {_params: c}
}
}]
}
}
parameter: *#StartupProbeParams | close({
// +usage=Specify the startup probe for multiple containers
probes: [...#StartupProbeParams]
})
errs: [ for c in patch.spec.template.spec.containers if c.err != _|_ {c.err}]
168 changes: 168 additions & 0 deletions charts/vela-minimal/templates/defwithtemplate/startup-probe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
# Definition source cue file: vela-templates/definitions/internal/startup-probe.cue
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: Add startup probe hooks for the specified container of K8s pod for your workload which follows the pod spec in path 'spec.template'.
name: startup-probe
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
appliesToWorkloads:
- deployments.apps
- statefulsets.apps
- daemonsets.apps
- jobs.batch
podDisruptive: true
schematic:
cue:
template: |
#StartupProbeParams: {
// +usage=Specify the name of the target container, if not set, use the component name
containerName: *"" | string
// +usage=Number of seconds after the container has started before liveness probes are initiated. Minimum value is 0.
initialDelaySeconds: *0 | int
// +usage=How often, in seconds, to execute the probe. Minimum value is 1.
periodSeconds: *10 | int
// +usage=Number of seconds after which the probe times out. Minimum value is 1.
timeoutSeconds: *1 | int
// +usage=Minimum consecutive successes for the probe to be considered successful after having failed. Minimum value is 1.
successThreshold: *1 | int
// +usage=Minimum consecutive failures for the probe to be considered failed after having succeeded. Minimum value is 1.
failureThreshold: *3 | int
// +usage=Optional duration in seconds the pod needs to terminate gracefully upon probe failure. Set this value longer than the expected cleanup time for your process.
terminationGracePeriodSeconds?: int
// +usage=Instructions for assessing container startup status by executing a command. Either this attribute or the httpGet attribute or the grpc attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with the httpGet attribute and the tcpSocket attribute and the gRPC attribute.
exec?: {
// +usage=A command to be executed inside the container to assess its health. Each space delimited token of the command is a separate array element. Commands exiting 0 are considered to be successful probes, whilst all other exit codes are considered failures.
command: [...string]
}
// +usage=Instructions for assessing container startup status by executing an HTTP GET request. Either this attribute or the exec attribute or the grpc attribute or the tcpSocket attribute MUST be specified. This attribute is mutually exclusive with the exec attribute and the tcpSocket attribute and the gRPC attribute.
httpGet?: {
// +usage=The endpoint, relative to the port, to which the HTTP GET request should be directed.
path?: string
// +usage=The port numer to access on the host or container.
port: int
// +usage=The hostname to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
host?: string
// +usage=The Scheme to use for connecting to the host.
scheme?: *"HTTP" | "HTTPS"
// +usage=Custom headers to set in the request. HTTP allows repeated headers.
httpHeaders?: [...{
// +usage=The header field name
name: string
//+usage=The header field value
value: string
}]
}
// +usage=Instructions for assessing container startup status by probing a gRPC service. Either this attribute or the exec attribute or the grpc attribute or the httpGet attribute MUST be specified. This attribute is mutually exclusive with the exec attribute and the httpGet attribute and the tcpSocket attribute.
grpc?: {
// +usage=The port number of the gRPC service.
port: int
// +usage=The name of the service to place in the gRPC HealthCheckRequest
service?: string
}
// +usage=Instructions for assessing container startup status by probing a TCP socket. Either this attribute or the exec attribute or the tcpSocket attribute or the httpGet attribute MUST be specified. This attribute is mutually exclusive with the exec attribute and the httpGet attribute and the gRPC attribute.
tcpSocket?: {
// +usage=Number or name of the port to access on the container.
port: string
// +usage=Host name to connect to, defaults to the pod IP.
host?: string
}
}
PatchContainer: {
_params: #StartupProbeParams
name: _params.containerName
_baseContainers: context.output.spec.template.spec.containers
_matchContainers_: [ for _container_ in _baseContainers if _container_.name == name {_container_}]
if len(_matchContainers_) == 0 {
err: "container \(name) not found"
}
if len(_matchContainers_) > 0 {
startupProbe: {
if _params.exec != _|_ {
exec: _params.exec
}
if _params.httpGet != _|_ {
httpGet: _params.httpGet
}
if _params.grpc != _|_ {
grpc: _params.grpc
}
if _params.tcpSocket != _|_ {
tcpSocket: _params.tcpSocket
}
if _params.initialDelaySeconds != _|_ {
initialDelaySeconds: _params.initialDelaySeconds
}
if _params.periodSeconds != _|_ {
periodSeconds: _params.periodSeconds
}
if _params.tcpSocket != _|_ {
tcpSocket: _params.tcpSocket
}
if _params.timeoutSeconds != _|_ {
timeoutSeconds: _params.timeoutSeconds
}
if _params.successThreshold != _|_ {
successThreshold: _params.successThreshold
}
if _params.failureThreshold != _|_ {
failureThreshold: _params.failureThreshold
}
if _params.terminationGracePeriodSeconds != _|_ {
terminationGracePeriodSeconds: _params.terminationGracePeriodSeconds
}
}
}
}
patch: spec: template: spec: {
if parameter.probes == _|_ {
// +patchKey=name
containers: [{
PatchContainer & {_params: {
if parameter.containerName == "" {
containerName: context.name
}
if parameter.containerName != "" {
containerName: parameter.containerName
}
periodSeconds: parameter.periodSeconds
initialDelaySeconds: parameter.initialDelaySeconds
timeoutSeconds: parameter.timeoutSeconds
successThreshold: parameter.successThreshold
failureThreshold: parameter.failureThreshold
terminationGracePeriodSeconds: parameter.terminationGracePeriodSeconds
if parameter.exec != _|_ {
exec: parameter.exec
}
if parameter.httpGet != _|_ {
httpGet: parameter.httpGet
}
if parameter.grpc != _|_ {
grpc: parameter.grpc
}
if parameter.tcpSocket != _|_ {
tcpSocket: parameter.grtcpSocketpc
}
}}
}]
}
if parameter.probes != _|_ {
// +patchKey=name
containers: [ for c in parameter.probes {
if c.name == "" {
err: "containerName must be set when specifying startup probe for multiple containers"
}
if c.name != "" {
PatchContainer & {_params: c}
}
}]
}
}
parameter: *#StartupProbeParams | close({
// +usage=Specify the startup probe for multiple containers
probes: [...#StartupProbeParams]
})
errs: [ for c in patch.spec.template.spec.containers if c.err != _|_ {c.err}]
41 changes: 41 additions & 0 deletions references/docgen/def-doc/trait/startup-probe.eg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: application-with-startup-probe
spec:
components:
- name: busybox-runner
type: worker
properties:
image: busybox
cmd:
- sleep
- '1000'
traits:
- type: sidecar
properties:
name: nginx
image: nginx
# This startup-probe is blocking the startup of the main container
# as the URL has a typo '.comm' vs '.com'
- type: startup-probe
properties:
containerName: "busybox-runner"
httpGet:
host: "www.guidewire.comm"
scheme: "HTTPS"
port: 443
periodSeconds: 4
failureThreshold: 4
# This startup probe targets the nginx sidecar
- type: startup-probe
properties:
containerName: nginx
httpGet:
host: "www.guidewire.com"
scheme: "HTTPS"
port: 443
periodSeconds: 5
failureThreshold: 5
```
Loading

0 comments on commit 7b548f8

Please sign in to comment.