Skip to content

Commit

Permalink
feat(region,host): startup probe of container (#21011)
Browse files Browse the repository at this point in the history
  • Loading branch information
zexi authored Aug 15, 2024
1 parent 42e2108 commit addbcbe
Show file tree
Hide file tree
Showing 34 changed files with 2,068 additions and 19 deletions.
5 changes: 5 additions & 0 deletions cmd/climc/shell/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ func init() {
return doComputeEventList(s, &nargs)
})

R(&TypeEventListOptions{}, "container-event", "Show operation event logs of container", func(s *mcclient.ClientSession, args *TypeEventListOptions) error {
nargs := EventListOptions{BaseEventListOptions: args.BaseEventListOptions, Id: args.ID, Type: []string{"container"}}
return doComputeEventList(s, &nargs)
})

R(&TypeEventListOptions{}, "disk-event", "Show operation event logs of disk", func(s *mcclient.ClientSession, args *TypeEventListOptions) error {
nargs := EventListOptions{BaseEventListOptions: args.BaseEventListOptions, Id: args.ID, Type: []string{"disk"}}
return doComputeEventList(s, &nargs)
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/compute/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ const (
CONTAINER_STATUS_RUNNING = "running"
CONTAINER_STATUS_DELETING = "deleting"
CONTAINER_STATUS_DELETE_FAILED = "delete_failed"
// for health check
CONTAINER_STATUS_PROBING = "probing"
CONTAINER_STATUS_PROBE_FAILED = "probe_failed"
)

const (
Expand Down
7 changes: 4 additions & 3 deletions pkg/apis/compute/guests.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,10 @@ type BackupInfo struct {
}

type PodContainerDesc struct {
Id string `json:"id"`
Name string `json:"name"`
Image string `json:"image"`
Id string `json:"id"`
Name string `json:"name"`
Image string `json:"image"`
Status string `json:"status"`
}

type Floppy struct {
Expand Down
17 changes: 17 additions & 0 deletions pkg/apis/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,23 @@ type ContainerSpec struct {
SimulateCpu bool `json:"simulate_cpu"`
ShmSizeMB int `json:"shm_size_mb"`
SecurityContext *ContainerSecurityContext `json:"security_context,omitempty"`
// Periodic probe of container liveness.
// Container will be restarted if the probe fails.
// Cannot be updated.
//LivenessProbe *ContainerProbe `json:"liveness_probe,omitempty"`
// StartupProbe indicates that the Pod has successfully initialized.
// If specified, no other probes are executed until this completes successfully.
StartupProbe *ContainerProbe `json:"startup_probe,omitempty"`
}

func (c *ContainerSpec) NeedProbe() bool {
//if c.LivenessProbe != nil {
// return true
//}
if c.StartupProbe != nil {
return true
}
return false
}

type ContainerCapability struct {
Expand Down
116 changes: 116 additions & 0 deletions pkg/apis/container_probe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apis

// ContainerProbeHandlerExecAction describes a "run in container" action.
type ContainerProbeHandlerExecAction struct {
// Command is the command line to execute inside the container, the working directory for the
// command is root ('/') in the container's filesystem. The command is simply exec'd, it is
// not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use
// a shell, you need to explicitly call out to that shell.
// Exit status of 0 is treated as live/healthy and non-zero is unhealthy.
// +optional
Command []string `json:"command,omitempty"`
}

// URIScheme identifies the scheme used for connection to a host for Get actions
type URIScheme string

const (
// URISchemeHTTP means that the scheme used will be http://
URISchemeHTTP URIScheme = "HTTP"
// URISchemeHTTPS means that the scheme used will be https://
URISchemeHTTPS URIScheme = "HTTPS"
)

// HTTPHeader describes a custom header to be used in HTTP probes
type HTTPHeader struct {
// The header field name
Name string `json:"name"`
// The header field value
Value string `json:"value"`
}

// ContainerProbeHTTPGetAction describes an action based on HTTP Get requests.
type ContainerProbeHTTPGetAction struct {
// Path to access on the HTTP server.
// +optional
Path string `json:"path,omitempty"`
// Name or number of the port to access on the container.
// Number must be in the range 1 to 65535.
// Name must be an IANA_SVC_NAME.
Port int `json:"port"`
// Host name to connect to, defaults to the pod IP. You probably want to set
// "Host" in httpHeaders instead.
// +optional
Host string `json:"host,omitempty"`
// Scheme to use for connecting to the host.
// Defaults to HTTP.
// +optional
Scheme URIScheme `json:"scheme,omitempty"`
// Custom headers to set in the request. HTTP allows repeated headers.
// +optional
HTTPHeaders []HTTPHeader `json:"httpHeaders,omitempty"`
}

// ContainerProbeTCPSocketAction describes an action based on opening a socket
type ContainerProbeTCPSocketAction struct {
// Number or name of the port to access on the container.
// Number must be in the range 1 to 65535.
// Name must be an IANA_SVC_NAME.
Port int `json:"port"`
// Optional: Host name to connect to, defaults to the pod IP.
// +optional
Host string `json:"host,omitempty"`
}

type ContainerProbeType string

const (
ContainerProbeTypeLiveness ContainerProbeType = "Liveness"
ContainerProbeTypeReadiness ContainerProbeType = "Readiness"
ContainerProbeTypeStartup ContainerProbeType = "Startup"
)

// ContainerProbeHandler defines a specific action that should be taken
type ContainerProbeHandler struct {
// One and only one of the following should be specified.
// Exec specifies the action to take.
Exec *ContainerProbeHandlerExecAction `json:"exec,omitempty"`
// HTTPGet specifies the http request to perform.
HTTPGet *ContainerProbeHTTPGetAction `json:"http_get,omitempty"`
// TCPSocket specifies an action involving a TCP port.
TCPSocket *ContainerProbeTCPSocketAction `json:"tcp_socket,omitempty"`
}

// ContainerProbe describes a health check to be performed against a container to determine whether it is
// alive or ready to receive traffic.
type ContainerProbe struct {
// The action taken to determine the health of a container
ContainerProbeHandler `json:",inline"`
// Number of seconds after the container has started before liveness probes are initiated.
// InitialDelaySeconds int32 `json:"initial_delay_seconds,omitempty"`
// Number of seconds after which the probe times out.
TimeoutSeconds int32 `json:"timeout_seconds,omitempty"`
// How often (in seconds) to perform the probe.
// Default to 10 seconds. Minimum value is 1.
PeriodSeconds int32 `json:"period_seconds,omitempty"`
// Minimum consecutive successes for the probe to be considered successful after having failed.
// Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.
SuccessThreshold int32 `json:"success_threshold,omitempty"`
// Minimum consecutive failures for the probe to be considered failed after having succeeded.
// Defaults to 3. Minimum value is 1.
FailureThreshold int32 `json:"failure_threshold,omitempty"`
}
89 changes: 84 additions & 5 deletions pkg/compute/models/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ func (m *SContainerManager) ValidateSpec(ctx context.Context, userCred mcclient.
return httperrors.NewInputParameterError("/dev/shm size is small than 64MB")
}

if err := m.ValidateSpecProbe(ctx, userCred, spec); err != nil {
return errors.Wrap(err, "validate probe configuration")
}

return nil
}

Expand Down Expand Up @@ -248,6 +252,81 @@ func (c *SContainer) CustomizeCreate(ctx context.Context, userCred mcclient.Toke
return nil
}*/

func (m *SContainerManager) ValidateSpecProbe(ctx context.Context, userCred mcclient.TokenCredential, spec *api.ContainerSpec) error {
//if err := m.validateSpecProbe(ctx, userCred, spec.LivenessProbe); err != nil {
// return errors.Wrap(err, "validate liveness probe")
//}
if err := m.validateSpecProbe(ctx, userCred, spec.StartupProbe); err != nil {
return errors.Wrap(err, "validate startup probe")
}
return nil
}

func (m *SContainerManager) validateSpecProbe(ctx context.Context, userCred mcclient.TokenCredential, probe *apis.ContainerProbe) error {
if probe == nil {
return nil
}
if err := m.validateSpecProbeHandler(probe.ContainerProbeHandler); err != nil {
return errors.Wrap(err, "validate container probe handler")
}
for key, val := range map[string]int32{
//"initial_delay_seconds": probe.InitialDelaySeconds,
"timeout_seconds": probe.TimeoutSeconds,
"period_seconds": probe.PeriodSeconds,
"success_threshold": probe.SuccessThreshold,
"failure_threshold": probe.FailureThreshold,
} {
if val < 0 {
return httperrors.NewInputParameterError(key + " is negative")
}
}

//if probe.InitialDelaySeconds == 0 {
// probe.InitialDelaySeconds = 5
//}
if probe.TimeoutSeconds == 0 {
probe.TimeoutSeconds = 3
}
if probe.PeriodSeconds == 0 {
probe.PeriodSeconds = 10
}
if probe.SuccessThreshold == 0 {
probe.SuccessThreshold = 1
}
if probe.FailureThreshold == 0 {
probe.FailureThreshold = 3
}
return nil
}

func (m *SContainerManager) validateSpecProbeHandler(probe apis.ContainerProbeHandler) error {
isAllNil := true
if probe.Exec != nil {
isAllNil = false
if len(probe.Exec.Command) == 0 {
return httperrors.NewInputParameterError("exec command is required")
}
}
if probe.TCPSocket != nil {
isAllNil = false
port := probe.TCPSocket.Port
if port < 1 || port > 65535 {
return httperrors.NewInputParameterError("invalid tcp socket port: %d, must between [1,65535]", port)
}
}
if probe.HTTPGet != nil {
isAllNil = false
port := probe.HTTPGet.Port
if port < 1 || port > 65535 {
return httperrors.NewInputParameterError("invalid http port: %d, must between [1,65535]", port)
}
}
if isAllNil {
return httperrors.NewInputParameterError("one of [exec, http_get, tcp_socket] is required")
}
return nil
}

func (c *SContainer) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
c.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
if !jsonutils.QueryBoolean(data, "skip_task", false) {
Expand Down Expand Up @@ -581,16 +660,16 @@ func NewContainerReleasedDevice(device *api.ContainerDevice, devType, devModel s
}
}

func (s *SContainer) SaveReleasedDevices(ctx context.Context, userCred mcclient.TokenCredential, devs map[string]ContainerReleasedDevice) error {
return s.SetMetadata(ctx, api.CONTAINER_METADATA_RELEASED_DEVICES, devs, userCred)
func (c *SContainer) SaveReleasedDevices(ctx context.Context, userCred mcclient.TokenCredential, devs map[string]ContainerReleasedDevice) error {
return c.SetMetadata(ctx, api.CONTAINER_METADATA_RELEASED_DEVICES, devs, userCred)
}

func (s *SContainer) GetReleasedDevices(ctx context.Context, userCred mcclient.TokenCredential) (map[string]ContainerReleasedDevice, error) {
func (c *SContainer) GetReleasedDevices(ctx context.Context, userCred mcclient.TokenCredential) (map[string]ContainerReleasedDevice, error) {
out := make(map[string]ContainerReleasedDevice, 0)
if ret := s.GetMetadata(ctx, api.CONTAINER_METADATA_RELEASED_DEVICES, userCred); ret == "" {
if ret := c.GetMetadata(ctx, api.CONTAINER_METADATA_RELEASED_DEVICES, userCred); ret == "" {
return out, nil
}
obj := s.GetMetadataJson(ctx, api.CONTAINER_METADATA_RELEASED_DEVICES, userCred)
obj := c.GetMetadataJson(ctx, api.CONTAINER_METADATA_RELEASED_DEVICES, userCred)
if obj == nil {
return nil, errors.Error("get metadata released devices")
}
Expand Down
6 changes: 5 additions & 1 deletion pkg/compute/models/guest_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,11 @@ func fetchContainers(guestIds []string) (map[string][]*api.PodContainerDesc, err
if !ok {
ret[container.GuestId] = []*api.PodContainerDesc{}
}
desc := &api.PodContainerDesc{Id: container.GetId(), Name: container.GetName()}
desc := &api.PodContainerDesc{
Id: container.GetId(),
Name: container.GetName(),
Status: container.Status,
}
if container.Spec != nil {
desc.Image = container.Spec.Image
}
Expand Down
1 change: 1 addition & 0 deletions pkg/hostman/container/prober/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package prober // import "yunion.io/x/onecloud/pkg/hostman/container/prober"
Loading

0 comments on commit addbcbe

Please sign in to comment.