Skip to content

Commit

Permalink
Support for enable/disable pod lifecycle events (#1453)
Browse files Browse the repository at this point in the history
Signed-off-by: Pablo Chico de Guzman <pchico83@gmail.com>
  • Loading branch information
pchico83 committed Apr 27, 2021
1 parent d25c0d5 commit edaf99a
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 36 deletions.
8 changes: 7 additions & 1 deletion pkg/cmd/init/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ import (
"context"
"fmt"

"github.com/okteto/okteto/pkg/k8s/client"
k8Client "github.com/okteto/okteto/pkg/k8s/client"
okLabels "github.com/okteto/okteto/pkg/k8s/labels"
"github.com/okteto/okteto/pkg/k8s/pods"
"github.com/okteto/okteto/pkg/k8s/replicasets"
"github.com/okteto/okteto/pkg/k8s/services"
"github.com/okteto/okteto/pkg/model"
"github.com/okteto/okteto/pkg/okteto"
appsv1 "k8s.io/api/apps/v1"
apiv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
Expand All @@ -47,7 +49,11 @@ func SetDevDefaultsFromDeployment(ctx context.Context, dev *model.Dev, d *appsv1
if err != nil {
return err
}
setResourcesFromPod(dev, pod, container)

if okteto.GetClusterContext() != client.GetSessionContext("") {
setResourcesFromPod(dev, pod, container)
}

return setForwardsFromPod(ctx, dev, pod, c)
}

Expand Down
29 changes: 23 additions & 6 deletions pkg/k8s/deployments/translate.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,29 +210,46 @@ func TranslateDevContainer(c *apiv1.Container, rule *model.TranslationRule) {
}

TranslateProbes(c, rule.Probes)
TranslateLifecycle(c, rule.Lifecycle)

TranslateResources(c, rule.Resources)
TranslateEnvVars(c, rule)
TranslateVolumeMounts(c, rule)
TranslateContainerSecurityContext(c, rule.SecurityContext)
}

//TranslateProbes translates the healthchecks attached to a container
func TranslateProbes(c *apiv1.Container, h *model.Probes) {
if h == nil {
//TranslateProbes translates the probes attached to a container
func TranslateProbes(c *apiv1.Container, p *model.Probes) {
if p == nil {
return
}
if !h.Liveness {
if !p.Liveness {
c.LivenessProbe = nil
}
if !h.Readiness {
if !p.Readiness {
c.ReadinessProbe = nil
}
if !h.Startup {
if !p.Startup {
c.StartupProbe = nil
}
}

//TranslateLifecycle translates the lifecycle events attached to a container
func TranslateLifecycle(c *apiv1.Container, l *model.Lifecycle) {
if l == nil {
return
}
if c.Lifecycle == nil {
return
}
if !l.PostStart {
c.Lifecycle.PostStart = nil
}
if !l.PostStart {
c.Lifecycle.PostStart = nil
}
}

func TranslateInitContainer(initContainer *model.InitContainer) {
if initContainer.Resources.Limits == nil {
initContainer.Resources.Limits = make(map[apiv1.ResourceName]resource.Quantity)
Expand Down
21 changes: 18 additions & 3 deletions pkg/model/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ type Dev struct {
Command Command `json:"command,omitempty" yaml:"command,omitempty"`
Healthchecks bool `json:"healthchecks,omitempty" yaml:"healthchecks,omitempty"`
Probes *Probes `json:"probes,omitempty" yaml:"probes,omitempty"`
Lifecycle *Lifecycle `json:"lifecycle,omitempty" yaml:"lifecycle,omitempty"`
WorkDir string `json:"workdir,omitempty" yaml:"workdir,omitempty"`
MountPath string `json:"mountpath,omitempty" yaml:"mountpath,omitempty"`
SubPath string `json:"subpath,omitempty" yaml:"subpath,omitempty"`
Expand Down Expand Up @@ -265,6 +266,12 @@ type Probes struct {
Startup bool `json:"startup,omitempty" yaml:"startup,omitempty"`
}

// Lifecycle defines the lifecycle for containers
type Lifecycle struct {
PostStart bool `json:"postStart,omitempty" yaml:"postStart,omitempty"`
PostStop bool `json:"postStop,omitempty" yaml:"postStop,omitempty"`
}

// ResourceList is a set of (resource name, quantity) pairs.
type ResourceList map[apiv1.ResourceName]resource.Quantity

Expand Down Expand Up @@ -321,6 +328,7 @@ func Read(bytes []byte) (*Dev, error) {
Services: make([]*Dev, 0),
PersistentVolumeInfo: &PersistentVolumeInfo{Enabled: true},
Probes: &Probes{},
Lifecycle: &Lifecycle{},
}

if bytes != nil {
Expand Down Expand Up @@ -511,6 +519,9 @@ func (dev *Dev) setDefaults() error {
if dev.Probes == nil {
dev.Probes = &Probes{}
}
if dev.Lifecycle == nil {
dev.Lifecycle = &Lifecycle{}
}
if dev.Interface == "" {
dev.Interface = Localhost
}
Expand Down Expand Up @@ -557,6 +568,9 @@ func (dev *Dev) setDefaults() error {
if s.Probes == nil {
s.Probes = &Probes{}
}
if s.Lifecycle == nil {
s.Lifecycle = &Lifecycle{}
}
}

return nil
Expand Down Expand Up @@ -794,6 +808,7 @@ func (dev *Dev) ToTranslationRule(main *Dev, reset bool) *TranslationRule {
Healthchecks: dev.Healthchecks,
InitContainer: dev.InitContainer,
Probes: dev.Probes,
Lifecycle: dev.Lifecycle,
}

if !dev.EmptyImage {
Expand All @@ -804,7 +819,7 @@ func (dev *Dev) ToTranslationRule(main *Dev, reset bool) *TranslationRule {
rule.Probes = &Probes{Liveness: true, Startup: true, Readiness: true}
}

if areHealthchecksEnabled(rule.Probes) {
if areProbesEnabled(rule.Probes) {
rule.Healthchecks = true
}
if main == dev {
Expand Down Expand Up @@ -905,14 +920,14 @@ func (dev *Dev) ToTranslationRule(main *Dev, reset bool) *TranslationRule {
return rule
}

func areHealthchecksEnabled(probes *Probes) bool {
func areProbesEnabled(probes *Probes) bool {
if probes != nil {
return probes.Liveness || probes.Readiness || probes.Startup
}
return false
}

func areAllHealthchecksEnabled(probes *Probes) bool {
func areAllProbesEnabled(probes *Probes) bool {
if probes != nil {
return probes.Liveness && probes.Readiness && probes.Startup
}
Expand Down
63 changes: 49 additions & 14 deletions pkg/model/serializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,19 @@ type storageResourceRaw struct {
Class string `json:"class,omitempty" yaml:"class,omitempty"`
}

// healthCheckProbesRaw represents the healthchecks info for serialization
type healthCheckProbesRaw struct {
// probesRaw represents the healthchecks info for serialization
type probesRaw struct {
Liveness bool `json:"liveness,omitempty" yaml:"liveness,omitempty"`
Readiness bool `json:"readiness,omitempty" yaml:"readiness,omitempty"`
Startup bool `json:"startup,omitempty" yaml:"startup,omitempty"`
}

// lifecycleRaw represents the lifecycle info for serialization
type lifecycleRaw struct {
PostStart bool `json:"postStart,omitempty" yaml:"postStart,omitempty"`
PostStop bool `json:"postStop,omitempty" yaml:"postStop,omitempty"`
}

// UnmarshalYAML Implements the Unmarshaler interface of the yaml pkg.
func (e *EnvVar) UnmarshalYAML(unmarshal func(interface{}) error) error {
var raw string
Expand Down Expand Up @@ -478,34 +484,63 @@ func (v ExternalVolume) MarshalYAML() (interface{}, error) {
}

// UnmarshalYAML Implements the Unmarshaler interface of the yaml pkg.
func (healthcheckProbes *Probes) UnmarshalYAML(unmarshal func(interface{}) error) error {
func (p *Probes) UnmarshalYAML(unmarshal func(interface{}) error) error {
var rawBool bool
err := unmarshal(&rawBool)
if err == nil {
healthcheckProbes.Liveness = rawBool
healthcheckProbes.Startup = rawBool
healthcheckProbes.Readiness = rawBool
p.Liveness = rawBool
p.Startup = rawBool
p.Readiness = rawBool
return nil
}

var healthCheckProbesRaw healthCheckProbesRaw
var healthCheckProbesRaw probesRaw
err = unmarshal(&healthCheckProbesRaw)
if err != nil {
return err
}

healthcheckProbes.Liveness = healthCheckProbesRaw.Liveness
healthcheckProbes.Startup = healthCheckProbesRaw.Startup
healthcheckProbes.Readiness = healthCheckProbesRaw.Readiness
p.Liveness = healthCheckProbesRaw.Liveness
p.Startup = healthCheckProbesRaw.Startup
p.Readiness = healthCheckProbesRaw.Readiness
return nil
}

// MarshalYAML Implements the marshaler interface of the yaml pkg.
func (p Probes) MarshalYAML() (interface{}, error) {
if p.Liveness && p.Readiness && p.Startup {
return true, nil
}
return probesRaw(p), nil
}

// UnmarshalYAML Implements the Unmarshaler interface of the yaml pkg.
func (l *Lifecycle) UnmarshalYAML(unmarshal func(interface{}) error) error {
var rawBool bool
err := unmarshal(&rawBool)
if err == nil {
l.PostStart = rawBool
l.PostStop = rawBool
return nil
}

var lifecycleRaw lifecycleRaw
err = unmarshal(&lifecycleRaw)
if err != nil {
return err
}

l.PostStart = lifecycleRaw.PostStart
l.PostStop = lifecycleRaw.PostStop
return nil
}

// MarshalYAML Implements the marshaler interface of the yaml pkg.
func (healthcheckProbes Probes) MarshalYAML() (interface{}, error) {
if healthcheckProbes.Liveness && healthcheckProbes.Readiness && healthcheckProbes.Startup {
func (l Lifecycle) MarshalYAML() (interface{}, error) {
if l.PostStart && l.PostStop {
return true, nil
}
return healthCheckProbesRaw(healthcheckProbes), nil
return lifecycleRaw(l), nil
}

func checkFileAndNotDirectory(path string) error {
Expand All @@ -525,7 +560,7 @@ func (d Dev) MarshalYAML() (interface{}, error) {
if isDefaultProbes(&d) {
toMarshall.Probes = nil
}
if areAllHealthchecksEnabled(d.Probes) {
if areAllProbesEnabled(d.Probes) {
toMarshall.Probes = nil
toMarshall.Healthchecks = true
}
Expand Down
54 changes: 43 additions & 11 deletions pkg/model/serializer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,27 +277,59 @@ func TestImageMashalling(t *testing.T) {
}
}

func TestHealthcheckMashalling(t *testing.T) {
func TestProbesMashalling(t *testing.T) {
tests := []struct {
name string
healthchecks Probes
expected string
name string
probes Probes
expected string
}{
{
name: "liveness-true-and-defaults",
probes: Probes{Liveness: true},
expected: "liveness: true\n",
},
{
name: "all-probes-true",
probes: Probes{Liveness: true, Readiness: true, Startup: true},
expected: "true\n",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
marshalled, err := yaml.Marshal(tt.probes)
if err != nil {
t.Fatal(err)
}

if string(marshalled) != tt.expected {
t.Errorf("didn't marshal correctly. Actual '%s', Expected '%s'", marshalled, tt.expected)
}
})
}
}

func TestLifecycleMashalling(t *testing.T) {
tests := []struct {
name string
lifecycle Lifecycle
expected string
}{
{
name: "liveness-true-and-defaults",
healthchecks: Probes{Liveness: true},
expected: "liveness: true\n",
name: "true-and-false",
lifecycle: Lifecycle{PostStart: true},
expected: "postStart: true\n",
},
{
name: "all-healthchecks-true",
healthchecks: Probes{Liveness: true, Readiness: true, Startup: true},
expected: "true\n",
name: "all-lifecycle-true",
lifecycle: Lifecycle{PostStart: true, PostStop: true},
expected: "true\n",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
marshalled, err := yaml.Marshal(tt.healthchecks)
marshalled, err := yaml.Marshal(tt.lifecycle)
if err != nil {
t.Fatal(err)
}
Expand Down
1 change: 1 addition & 0 deletions pkg/model/translation.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type TranslationRule struct {
Resources ResourceRequirements `json:"resources,omitempty"`
InitContainer InitContainer `json:"initContainers,omitempty"`
Probes *Probes `json:"probes" yaml:"probes"`
Lifecycle *Lifecycle `json:"lifecycle" yaml:"lifecycle"`
}

//IsMainDevContainer returns true if the translation rule applies to the main dev container of the okteto manifest
Expand Down
6 changes: 5 additions & 1 deletion pkg/model/translation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ services:
imagePullPolicy: IfNotPresent
sync:
- worker:/src
healthchecks: true`)
healthchecks: true
lifecycle:
postStart: true`)

dev, err := Read(manifest)
if err != nil {
Expand All @@ -63,6 +65,7 @@ services:
Command: []string{"/var/okteto/bin/start.sh"},
Args: []string{"-r"},
Probes: &Probes{},
Lifecycle: &Lifecycle{},
Environment: Environment{
{
Name: "OKTETO_NAMESPACE",
Expand Down Expand Up @@ -131,6 +134,7 @@ services:
Args: nil,
Healthchecks: true,
Probes: &Probes{Readiness: true, Liveness: true, Startup: true},
Lifecycle: &Lifecycle{PostStart: true, PostStop: false},
Environment: make(Environment, 0),
SecurityContext: &SecurityContext{
RunAsUser: &rootUser,
Expand Down

0 comments on commit edaf99a

Please sign in to comment.