Skip to content

Commit

Permalink
feat: add lifecycle type and encoding/decoding support
Browse files Browse the repository at this point in the history
  • Loading branch information
adohe committed Aug 24, 2023
1 parent 93647b2 commit 355b7ee
Show file tree
Hide file tree
Showing 2 changed files with 471 additions and 37 deletions.
188 changes: 165 additions & 23 deletions pkg/models/appconfiguration/workload/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
)

// Container describes how the Application's tasks are expected to be run.
type Container struct {
// Image to run for this container
Image string `yaml:"image" json:"image"`
Expand All @@ -19,12 +20,36 @@ type Container struct {
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
// The current working directory of the running process defined in entrypoint.
WorkingDir string `yaml:"workingDir,omitempty" json:"workingDir,omitempty"`
// Resource requirements for this container.
Resources map[string]string `yaml:"resources,omitempty" json:"resources,omitempty"`
// Files configures one or more files to be created in the container.
Files map[string]FileSpec `yaml:"files,omitempty" json:"files,omitempty"`
// Dirs configures one or more volumes to be mounted to the specified folder.
Dirs map[string]string `yaml:"dirs,omitempty" json:"dirs,omitempty"`
// Periodic probe of container liveness.
LivenessProbe *Probe `yaml:"livenessProbe,omitempty" json:"livenessProbe,omitempty"`
// Periodic probe of container service readiness.
ReadinessProbe *Probe `yaml:"readinessProbe,omitempty" json:"readinessProbe,omitempty"`
// StartupProbe indicates that the Pod has successfully initialized.
StartupProbe *Probe `yaml:"startupProbe,omitempty" json:"startupProbe,omitempty"`
// Actions that the management system should take in response to container lifecycle events.
Lifecycle *Lifecycle `yaml:"lifecycle,omitempty" json:"lifecycle,omitempty"`
}

// FileSpec defines the target file in a Container
type FileSpec struct {
// The content of target file in plain text.
Content string `yaml:"content,omitempty" json:"content,omitempty"`
// Source for the file content, might be a reference to a secret value.
ContentFrom string `yaml:"contentFrom,omitempty" json:"contentFrom,omitempty"`
// Mode bits used to set permissions on this file.
Mode string `yaml:"mode" json:"mode"`
}

// TypeWrapper is a thin wrapper to make YAML decoder happy.
type TypeWrapper struct {
// Type of action to be taken.
Type string `yaml:"_type" json:"_type"`
}

// Probe describes a health check to be performed against a container to determine whether it is
Expand All @@ -44,16 +69,11 @@ type Probe struct {
FailureThreshold int32 `yaml:"failureThreshold,omitempty" json:"failureThreshold,omitempty"`
}

type ProbeType struct {
// Type of action to be taken.
Type string `yaml:"_type" json:"_type"`
}

// ProbeHandler defines a specific action that should be taken in a probe.
// One and only one of the fields must be specified.
type ProbeHandler struct {
// Type of action to be taken.
ProbeType `yaml:"_type" json:"_type"`
TypeWrapper `yaml:"_type" json:"_type"`
// Exec specifies the action to take.
// +optional
*ExecAction `yaml:",inline" json:",inline"`
Expand Down Expand Up @@ -87,31 +107,55 @@ type TCPSocketAction struct {
URL string `yaml:"url,omitempty" json:"url,omitempty"`
}

// Lifecycle describes actions that the management system should take in response
// to container lifecycle events.
type Lifecycle struct {
// PreStop is called immediately before a container is terminated due to an
// API request or management event such as liveness/startup probe failure,
// preemption, resource contention, etc.
PreStop *LifecycleHandler `yaml:"preStop,omitempty" json:"preStop,omitempty"`
// PostStart is called immediately after a container is created.
PostStart *LifecycleHandler `yaml:"postStart,omitempty" json:"postStart,omitempty"`
}

// LifecycleHandler defines a specific action that should be taken in a lifecycle
// hook. One and only one of the fields, except TCPSocket must be specified.
type LifecycleHandler struct {
// Type of action to be taken.
TypeWrapper `yaml:"_type" json:"_type"`
// Exec specifies the action to take.
// +optional
*ExecAction `yaml:",inline" json:",inline"`
// HTTPGet specifies the http request to perform.
// +optional
*HTTPGetAction `yaml:",inline" json:",inline"`
}

// MarshalJSON implements the json.Marshaler interface for ProbeHandler.
func (p *ProbeHandler) MarshalJSON() ([]byte, error) {
switch p.Type {
case "Http":
return json.Marshal(struct {
ProbeType `json:",inline"`
TypeWrapper `json:",inline"`
*HTTPGetAction `json:",inline"`
}{
ProbeType: ProbeType{p.Type},
TypeWrapper: TypeWrapper{p.Type},
HTTPGetAction: p.HTTPGetAction,
})
case "Exec":
return json.Marshal(struct {
ProbeType `json:",inline"`
TypeWrapper `json:",inline"`
*ExecAction `json:",inline"`
}{
ProbeType: ProbeType{p.Type},
ExecAction: p.ExecAction,
TypeWrapper: TypeWrapper{p.Type},
ExecAction: p.ExecAction,
})
case "Tcp":
return json.Marshal(struct {
ProbeType `json:",inline"`
TypeWrapper `json:",inline"`
*TCPSocketAction `json:",inline"`
}{
ProbeType: ProbeType{p.Type},
TypeWrapper: TypeWrapper{p.Type},
TCPSocketAction: p.TCPSocketAction,
})
default:
Expand All @@ -121,7 +165,7 @@ func (p *ProbeHandler) MarshalJSON() ([]byte, error) {

// UnmarshalJSON implements the json.Unmarshaller interface for ProbeHandler.
func (p *ProbeHandler) UnmarshalJSON(data []byte) error {
var probeType ProbeType
var probeType TypeWrapper
err := json.Unmarshal(data, &probeType)
if err != nil {
return err
Expand Down Expand Up @@ -153,26 +197,26 @@ func (p *ProbeHandler) MarshalYAML() (interface{}, error) {
switch p.Type {
case "Http":
return struct {
ProbeType `yaml:",inline" json:",inline"`
TypeWrapper `yaml:",inline" json:",inline"`
HTTPGetAction `yaml:",inline" json:",inline"`
}{
ProbeType: ProbeType{Type: "Http"},
TypeWrapper: TypeWrapper{Type: p.Type},
HTTPGetAction: *p.HTTPGetAction,
}, nil
case "Exec":
return struct {
ProbeType `yaml:",inline" json:",inline"`
ExecAction `yaml:",inline" json:",inline"`
TypeWrapper `yaml:",inline" json:",inline"`
ExecAction `yaml:",inline" json:",inline"`
}{
ProbeType: ProbeType{Type: "Exec"},
ExecAction: *p.ExecAction,
TypeWrapper: TypeWrapper{Type: p.Type},
ExecAction: *p.ExecAction,
}, nil
case "Tcp":
return struct {
ProbeType `yaml:",inline" json:",inline"`
TypeWrapper `yaml:",inline" json:",inline"`
TCPSocketAction `yaml:",inline" json:",inline"`
}{
ProbeType: ProbeType{Type: "Tcp"},
TypeWrapper: TypeWrapper{Type: p.Type},
TCPSocketAction: *p.TCPSocketAction,
}, nil
}
Expand All @@ -182,7 +226,7 @@ func (p *ProbeHandler) MarshalYAML() (interface{}, error) {

// UnmarshalYAML implements the yaml.Unmarshaler interface for ProbeHandler.
func (p *ProbeHandler) UnmarshalYAML(unmarshal func(interface{}) error) error {
var probeType ProbeType
var probeType TypeWrapper
err := unmarshal(&probeType)
if err != nil {
return err
Expand All @@ -208,3 +252,101 @@ func (p *ProbeHandler) UnmarshalYAML(unmarshal func(interface{}) error) error {

return err
}

// MarshalJSON implements the json.Marshaler interface for LifecycleHandler.
func (l *LifecycleHandler) MarshalJSON() ([]byte, error) {
switch l.Type {
case "Http":
return json.Marshal(struct {
TypeWrapper `json:",inline"`
*HTTPGetAction `json:",inline"`
}{
TypeWrapper: TypeWrapper{l.Type},
HTTPGetAction: l.HTTPGetAction,
})
case "Exec":
return json.Marshal(struct {
TypeWrapper `json:",inline"`
*ExecAction `json:",inline"`
}{
TypeWrapper: TypeWrapper{l.Type},
ExecAction: l.ExecAction,
})
default:
return nil, errors.New("unrecognized lifecycle handler type")
}
}

// UnmarshalJSON implements the json.Unmarshaller interface for LifecycleHandler.
func (l *LifecycleHandler) UnmarshalJSON(data []byte) error {
var handlerType TypeWrapper
err := json.Unmarshal(data, &handlerType)
if err != nil {
return err
}

l.Type = handlerType.Type
switch l.Type {
case "Http":
handler := &HTTPGetAction{}
err = json.Unmarshal(data, handler)
l.HTTPGetAction = handler
case "Exec":
handler := &ExecAction{}
err = json.Unmarshal(data, handler)
l.ExecAction = handler
default:
return errors.New("unrecognized lifecycle handler type")
}

return err
}

// MarshalYAML implements the yaml.Marshaler interface for LifecycleHandler.
func (l *LifecycleHandler) MarshalYAML() (interface{}, error) {
switch l.Type {
case "Http":
return struct {
TypeWrapper `yaml:",inline" json:",inline"`
HTTPGetAction `yaml:",inline" json:",inline"`
}{
TypeWrapper: TypeWrapper{Type: l.Type},
HTTPGetAction: *l.HTTPGetAction,
}, nil
case "Exec":
return struct {
TypeWrapper `yaml:",inline" json:",inline"`
ExecAction `yaml:",inline" json:",inline"`
}{
TypeWrapper: TypeWrapper{Type: l.Type},
ExecAction: *l.ExecAction,
}, nil
default:
return nil, errors.New("unrecognized lifecycle handler type")
}
}

// UnmarshalYAML implements the yaml.Unmarshaler interface for LifecycleHandler.
func (l *LifecycleHandler) UnmarshalYAML(unmarshal func(interface{}) error) error {
var handlerType TypeWrapper
err := unmarshal(&handlerType)
if err != nil {
return err
}

l.Type = handlerType.Type
switch l.Type {
case "Http":
handler := &HTTPGetAction{}
err = unmarshal(handler)
l.HTTPGetAction = handler
case "Exec":
handler := &ExecAction{}
err = unmarshal(handler)
l.ExecAction = handler
default:
return errors.New("unrecognized lifecycle handler type")
}

return err
}
Loading

0 comments on commit 355b7ee

Please sign in to comment.