Skip to content
This repository has been archived by the owner on Nov 15, 2022. It is now read-only.

Add health field to the container #27

Merged
merged 1 commit into from
Jun 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions examples/health/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Health

At container level instead of defining `livenessProbe` and
`readinessProbe` you can define a field called `helath`.
And then that gets replicated in `livenessProbe` and
`readinessProbe`.

See the snippet below from [web.yaml](web.yaml):

```yaml
containers:
...
health:
httpGet:
path: /
port: 80
initialDelaySeconds: 20
timeoutSeconds: 5
...
```

When this is converted the same content is replicated in both
fields:

```yaml
$ opencomposition convert -f web.yaml
...
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 20
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 20
timeoutSeconds: 5
...
```

But if `health` is defined with `livenessProbe` or `readinessProbe`
the tool will error out, so define only one.
23 changes: 23 additions & 0 deletions examples/health/db.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: database
containers:
- image: mariadb:10
env:
- name: MYSQL_ROOT_PASSWORD
value: rootpasswd
- name: MYSQL_DATABASE
value: wordpress
- name: MYSQL_USER
value: wordpress
- name: MYSQL_PASSWORD
value: wordpress
readinessProbe:
exec:
command:
- mysqladmin
- ping
initialDelaySeconds: 300
timeoutSeconds: 50
services:
- name: database
ports:
- port: 3306
25 changes: 25 additions & 0 deletions examples/health/web.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: web
replicas: 1
containers:
- image: wordpress:4
env:
- name: WORDPRESS_DB_HOST
value: database:3306
- name: WORDPRESS_DB_PASSWORD
value: wordpress
- name: WORDPRESS_DB_USER
value: wordpress
- name: WORDPRESS_DB_NAME
value: wordpress
health:
httpGet:
path: /
port: 80
initialDelaySeconds: 20
timeoutSeconds: 5
services:
- name: wordpress
type: NodePort
ports:
- port: 8080
targetPort: 80
32 changes: 19 additions & 13 deletions pkg/spec/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,29 @@ import (
)

type PersistentVolume struct {
api_v1.PersistentVolumeClaimSpec `yaml:",inline"`
Name string `yaml:"name"`
Size string `yaml:"size"`
api_v1.PersistentVolumeClaimSpec `json:",inline"`
Name string `json:"name"`
Size string `json:"size"`
}

type Service struct {
Name string `yaml:"name,omitempty"`
api_v1.ServiceSpec `yaml:",inline"`
ext_v1beta1.IngressSpec `yaml:",inline"`
Name string `json:"name,omitempty"`
api_v1.ServiceSpec `json:",inline"`
ext_v1beta1.IngressSpec `json:",inline"`
}

type Container struct {
Health *api_v1.Probe `json:"health,omitempty"`
api_v1.Container `json:",inline"`
}

type App struct {
Name string `yaml:"name"`
Replicas *int32 `yaml:"replicas,omitempty"`
Labels map[string]string `yaml:"labels,omitempty"`
PersistentVolumes []PersistentVolume `yaml:"persistentVolumes,omitempty"`
ConfigData map[string]string `yaml:"configData,omitempty"`
Services []Service `yaml:"services,omitempty"`
api_v1.PodSpec `yaml:",inline"`
Name string `json:"name"`
Replicas *int32 `json:"replicas,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
PersistentVolumes []PersistentVolume `json:"persistentVolumes,omitempty"`
ConfigData map[string]string `json:"configData,omitempty"`
Services []Service `json:"services,omitempty"`
Containers []Container `json:"containers,omitempty"`
api_v1.PodSpec `json:",inline"`
}
36 changes: 28 additions & 8 deletions pkg/transform/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func isVolumeDefined(app *spec.App, name string) bool {
}

func isAnyConfigMapRef(app *spec.App) bool {
for _, c := range app.Containers {
for _, c := range app.PodSpec.Containers {
for _, env := range c.Env {
if env.ValueFrom != nil && env.ValueFrom.ConfigMapKeyRef != nil && env.ValueFrom.ConfigMapKeyRef.Name == app.Name {
return true
Expand All @@ -199,7 +199,7 @@ func isAnyConfigMapRef(app *spec.App) bool {
// root level persistent volume and entry in the container
// volume mount, we alse need to update the pod's volume field
func populateVolumes(app *spec.App) error {
for cn, c := range app.Containers {
for cn, c := range app.PodSpec.Containers {
for vn, vm := range c.VolumeMounts {
if isPVCDefined(app, vm.Name) {
app.Volumes = append(app.Volumes, api_v1.Volume{
Expand All @@ -223,15 +223,35 @@ func populateVolumes(app *spec.App) error {
return nil
}

func populateContainerHealth(app *spec.App) error {
for cn, c := range app.Containers {
// check if health and liveness given together
if c.Health != nil && (c.ReadinessProbe != nil || c.LivenessProbe != nil) {
return errors.New(fmt.Sprintf("cannot define field health and livnessProbe"+
" or readinessProbe together in app.containers[%d]", cn))
}
if c.Health != nil {
c.LivenessProbe = c.Health
c.ReadinessProbe = c.Health
}
app.PodSpec.Containers = append(app.PodSpec.Containers, c.Container)
}
return nil
}

func CreateK8sObjects(app *spec.App) ([]runtime.Object, error) {
var objects []runtime.Object

if app.Labels == nil {
app.Labels = getLabels(app)
}

svcs := createServices(app)

// withdraw the health and populate actual pod spec
if err := populateContainerHealth(app); err != nil {
return nil, errors.Wrapf(err, "app %q", app.Name)
}

// create pvc for each root level persistent volume
var pvcs []runtime.Object
for _, v := range app.PersistentVolumes {
Expand All @@ -246,8 +266,8 @@ func CreateK8sObjects(app *spec.App) ([]runtime.Object, error) {
}

// if only one container set name of it as app name
if len(app.Containers) == 1 && app.Containers[0].Name == "" {
app.Containers[0].Name = app.Name
if len(app.PodSpec.Containers) == 1 && app.PodSpec.Containers[0].Name == "" {
app.PodSpec.Containers[0].Name = app.Name
}

var configMap *api_v1.ConfigMap
Expand All @@ -262,10 +282,10 @@ func CreateK8sObjects(app *spec.App) ([]runtime.Object, error) {
// add it to the envs if there is no configMapRef
// we cannot re-create the entries for configMap
// because there is no way we will know which container wants to use it
if len(app.Containers) == 1 && !isAnyConfigMapRef(app) {
if len(app.PodSpec.Containers) == 1 && !isAnyConfigMapRef(app) {
// iterate over the data in the configMap
for k, _ := range app.ConfigData {
app.Containers[0].Env = append(app.Containers[0].Env,
app.PodSpec.Containers[0].Env = append(app.PodSpec.Containers[0].Env,
api_v1.EnvVar{
Name: k,
ValueFrom: &api_v1.EnvVarSource{
Expand All @@ -278,7 +298,7 @@ func CreateK8sObjects(app *spec.App) ([]runtime.Object, error) {
},
})
}
} else if len(app.Containers) > 1 && !isAnyConfigMapRef(app) {
} else if len(app.PodSpec.Containers) > 1 && !isAnyConfigMapRef(app) {
log.Warnf("You have defined a configMap but you have not mentioned where you gonna consume it!")
}

Expand Down