diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go index d225def56..6fb9a835b 100644 --- a/pkg/spec/spec.go +++ b/pkg/spec/spec.go @@ -72,6 +72,7 @@ type ConfigMapMod struct { type PodSpecMod struct { Containers []Container `json:"containers,omitempty"` + InitContainers []Container `json:"initContainers,omitempty"` api_v1.PodSpec `json:",inline"` } diff --git a/pkg/transform/kubernetes/kubernetes.go b/pkg/transform/kubernetes/kubernetes.go index b6036310c..c004f43ff 100644 --- a/pkg/transform/kubernetes/kubernetes.go +++ b/pkg/transform/kubernetes/kubernetes.go @@ -265,75 +265,6 @@ 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 fmt.Errorf("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 populateEnvFrom(app *spec.App) error { - // iterate on the containers so that we can extract envFrom - // that we have custom defined - for ci, c := range app.Containers { - for ei, e := range c.EnvFrom { - cmName := e.ConfigMapRef.Name - - // we also need to check if the configMap specified if it exists - var cmFound bool - // to populate the envs we also need to know the data - // from configmaps defined in the app - for _, cm := range app.ConfigMaps { - // we will only populate the configMap that is specified - // not every configMap out there - if cm.Name != cmName { - continue - } - var envs []api_v1.EnvVar - // start populating - for k := range cm.Data { - // here we are directly referring to the containers - // from app.PodSpec.Containers because that is where data - // is parsed into so populating that is more valid thing to do - envs = append(envs, api_v1.EnvVar{ - Name: k, - ValueFrom: &api_v1.EnvVarSource{ - ConfigMapKeyRef: &api_v1.ConfigMapKeySelector{ - LocalObjectReference: api_v1.LocalObjectReference{ - Name: cmName, - }, - Key: k, - }, - }, - }) - } - // we collect all the envs from configMap before - // envs provided inside the container - envs = append(envs, app.PodSpec.Containers[ci].Env...) - app.PodSpec.Containers[ci].Env = envs - - cmFound = true - // once the population is done we exit out of the loop - // we don't need to check other configMaps - break - } - if !cmFound { - return fmt.Errorf("undefined configMap in app.containers[%d].envFrom[%d].configMapRef.name", ci, ei) - } - } - } - return nil -} - func CreateK8sObjects(app *spec.App) ([]runtime.Object, error) { var objects []runtime.Object @@ -351,16 +282,17 @@ func CreateK8sObjects(app *spec.App) ([]runtime.Object, error) { return nil, errors.Wrap(err, "Unable to create Kubernetes Ingresses") } - // withdraw the health and populate actual pod spec - if err := populateContainerHealth(app); err != nil { + app.PodSpec.Containers, err = populateContainers(app.Containers, app.ConfigMaps) + if err != nil { return nil, errors.Wrapf(err, "app %q", app.Name) } log.Debugf("object after population: %#v\n", app) - // withdraw the envFrom and populate actual containers - if err := populateEnvFrom(app); err != nil { + app.PodSpec.InitContainers, err = populateContainers(app.InitContainers, app.ConfigMaps) + if err != nil { return nil, errors.Wrapf(err, "app %q", app.Name) } + log.Debugf("object after population: %#v\n", app) // create pvc for each root level persistent volume var pvcs []runtime.Object diff --git a/pkg/transform/kubernetes/populators.go b/pkg/transform/kubernetes/populators.go new file mode 100644 index 000000000..e4520ccd7 --- /dev/null +++ b/pkg/transform/kubernetes/populators.go @@ -0,0 +1,101 @@ +package kubernetes + +import ( + "encoding/json" + "fmt" + + "github.com/kedgeproject/kedge/pkg/spec" + + log "github.com/Sirupsen/logrus" + "github.com/pkg/errors" + api_v1 "k8s.io/client-go/pkg/api/v1" +) + +func populateProbes(c spec.Container) (spec.Container, error) { + // check if health and liveness given together + if c.Health != nil && (c.ReadinessProbe != nil || c.LivenessProbe != nil) { + return c, fmt.Errorf("cannot define field 'health' and " + + "'livnessProbe' or 'readinessProbe' together") + } + if c.Health != nil { + c.LivenessProbe = c.Health + c.ReadinessProbe = c.Health + } + return c, nil +} + +func searchConfigMap(cms []spec.ConfigMapMod, name string) (spec.ConfigMapMod, error) { + for _, cm := range cms { + if cm.Name == name { + return cm, nil + } + } + return spec.ConfigMapMod{}, fmt.Errorf("configMap %q not found", name) +} + +func convertEnvFromToEnvs(envFrom []spec.EnvFromSource, cms []spec.ConfigMapMod) ([]api_v1.EnvVar, error) { + var envs []api_v1.EnvVar + + // we will iterate on all envFroms + for ei, e := range envFrom { + cmName := e.ConfigMapRef.Name + + // see if the configMap name which is given actually exists + cm, err := searchConfigMap(cms, cmName) + if err != nil { + return nil, errors.Wrapf(err, "envFrom[%d].configMapRef.name", ei) + } + // once that configMap is found extract all data from it and create a env out of it + for k := range cm.Data { + envs = append(envs, api_v1.EnvVar{ + Name: k, + ValueFrom: &api_v1.EnvVarSource{ + ConfigMapKeyRef: &api_v1.ConfigMapKeySelector{ + LocalObjectReference: api_v1.LocalObjectReference{ + Name: cmName, + }, + Key: k, + }, + }, + }) + } + } + return envs, nil +} + +func populateEnvFrom(c spec.Container, cms []spec.ConfigMapMod) (spec.Container, error) { + // now do the env from + envs, err := convertEnvFromToEnvs(c.EnvFrom, cms) + if err != nil { + return c, err + } + // we collect all the envs from configMap before + // envs provided inside the container + envs = append(envs, c.Env...) + c.Env = envs + return c, nil +} + +func populateContainers(containers []spec.Container, cms []spec.ConfigMapMod) ([]api_v1.Container, error) { + var cnts []api_v1.Container + + for cn, c := range containers { + // process health field + c, err := populateProbes(c) + if err != nil { + return cnts, errors.Wrapf(err, "error converting 'health' to 'probes', app.containers[%d]", cn) + } + + // process envFrom field + c, err = populateEnvFrom(c, cms) + if err != nil { + return cnts, fmt.Errorf("error converting 'envFrom' to 'envs', app.containers[%d].%s", cn, err.Error()) + } + + cnts = append(cnts, c.Container) + } + + b, _ := json.MarshalIndent(cnts, "", " ") + log.Debugf("containers after populating health: %s", string(b)) + return cnts, nil +}