Fix environment variables interpolation (#1524)
This commit is contained in:
@@ -91,6 +91,7 @@ func (opt *ConvertOptions) IsPodController() bool {
|
||||
return opt.IsDeploymentFlag || opt.IsDaemonSetFlag || opt.IsReplicationControllerFlag || opt.Controller != ""
|
||||
}
|
||||
|
||||
// ServiceConfigGroup holds an array of a ServiceConfig objects.
|
||||
type ServiceConfigGroup []ServiceConfig
|
||||
|
||||
// ServiceConfig holds the basic struct of a container
|
||||
|
||||
@@ -105,7 +105,9 @@ func parseV3(files []string) (kobject.KomposeObject, error) {
|
||||
// We load it in order to retrieve the parsed output configuration!
|
||||
// This will output a github.com/docker/cli ServiceConfig
|
||||
// Which is similar to our version of ServiceConfig
|
||||
currentConfig, err := loader.Load(configDetails)
|
||||
currentConfig, err := loader.Load(configDetails, func(options *loader.Options) {
|
||||
options.SkipInterpolation = true
|
||||
})
|
||||
if err != nil {
|
||||
return kobject.KomposeObject{}, err
|
||||
}
|
||||
@@ -129,7 +131,6 @@ func parseV3(files []string) (kobject.KomposeObject, error) {
|
||||
if err != nil {
|
||||
return kobject.KomposeObject{}, err
|
||||
}
|
||||
|
||||
return komposeObject, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -382,6 +383,7 @@ func (k *Kubernetes) initSvcObject(name string, service kobject.ServiceConfig, p
|
||||
return svc
|
||||
}
|
||||
|
||||
// CreateLBService creates a k8s Load Balancer Service
|
||||
func (k *Kubernetes) CreateLBService(name string, service kobject.ServiceConfig) []*api.Service {
|
||||
var svcs []*api.Service
|
||||
tcpPorts, udpPorts := k.ConfigLBServicePorts(service)
|
||||
@@ -442,6 +444,8 @@ func (k *Kubernetes) CreateHeadlessService(name string, service kobject.ServiceC
|
||||
|
||||
return svc
|
||||
}
|
||||
|
||||
// UpdateKubernetesObjectsMultipleContainers method updates the kubernetes objects with the necessary data
|
||||
func (k *Kubernetes) UpdateKubernetesObjectsMultipleContainers(name string, service kobject.ServiceConfig, objects *[]runtime.Object, podSpec PodSpec) error {
|
||||
// Configure annotations
|
||||
annotations := transformer.ConfigAnnotations(service)
|
||||
@@ -524,7 +528,7 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
|
||||
template.Spec.Containers[0].Name = GetContainerName(service)
|
||||
template.Spec.Containers[0].Env = envs
|
||||
template.Spec.Containers[0].Command = service.Command
|
||||
template.Spec.Containers[0].Args = service.Args
|
||||
template.Spec.Containers[0].Args = GetContainerArgs(service)
|
||||
template.Spec.Containers[0].WorkingDir = service.WorkingDir
|
||||
template.Spec.Containers[0].VolumeMounts = append(template.Spec.Containers[0].VolumeMounts, volumesMount...)
|
||||
template.Spec.Containers[0].Stdin = service.Stdin
|
||||
@@ -594,18 +598,18 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
|
||||
template.ObjectMeta.Labels = transformer.ConfigLabelsWithNetwork(name, service.Network)
|
||||
|
||||
// Configure the image pull policy
|
||||
if policy, err := GetImagePullPolicy(name, service.ImagePullPolicy); err != nil {
|
||||
policy, err := GetImagePullPolicy(name, service.ImagePullPolicy)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
template.Spec.Containers[0].ImagePullPolicy = policy
|
||||
}
|
||||
template.Spec.Containers[0].ImagePullPolicy = policy
|
||||
|
||||
// Configure the container restart policy.
|
||||
if restart, err := GetRestartPolicy(name, service.Restart); err != nil {
|
||||
restart, err := GetRestartPolicy(name, service.Restart)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
template.Spec.RestartPolicy = restart
|
||||
}
|
||||
template.Spec.RestartPolicy = restart
|
||||
|
||||
// Configure hostname/domain_name settings
|
||||
if service.HostName != "" {
|
||||
@@ -887,6 +891,7 @@ func FormatContainerName(name string) string {
|
||||
return name
|
||||
}
|
||||
|
||||
// GetContainerName returns the name of the container, from the service config object
|
||||
func GetContainerName(service kobject.ServiceConfig) string {
|
||||
name := service.Name
|
||||
if len(service.ContainerName) > 0 {
|
||||
@@ -899,3 +904,15 @@ func GetContainerName(service kobject.ServiceConfig) string {
|
||||
func FormatResourceName(name string) string {
|
||||
return strings.ToLower(strings.Replace(name, "_", "-", -1))
|
||||
}
|
||||
|
||||
// GetContainerArgs update the interpolation of env variables if exists.
|
||||
// example: [curl, $PROTOCOL://$DOMAIN] => [curl, $(PROTOCOL)://$(DOMAIN)]
|
||||
func GetContainerArgs(service kobject.ServiceConfig) []string {
|
||||
var args []string
|
||||
re := regexp.MustCompile(`\$([a-zA-Z0-9]*)`)
|
||||
for _, arg := range service.Args {
|
||||
arg = re.ReplaceAllString(arg, `$($1)`)
|
||||
args = append(args, arg)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
@@ -597,3 +597,35 @@ func TestCreateServiceWithSpecialName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestArgsInterpolation(t *testing.T) {
|
||||
// An example service
|
||||
service := kobject.ServiceConfig{
|
||||
ContainerName: "name",
|
||||
Image: "image",
|
||||
Environment: []kobject.EnvVar{{Name: "PROTOCOL", Value: "https"}, {Name: "DOMAIN", Value: "google.com"}},
|
||||
Port: []kobject.Ports{{HostPort: 123, ContainerPort: 456, Protocol: string(corev1.ProtocolTCP)}},
|
||||
Command: []string{"curl"},
|
||||
Args: []string{"$PROTOCOL://$DOMAIN/"},
|
||||
}
|
||||
|
||||
// An example object generated via k8s runtime.Objects()
|
||||
komposeObject := kobject.KomposeObject{
|
||||
ServiceConfigs: map[string]kobject.ServiceConfig{"app": service},
|
||||
}
|
||||
k := Kubernetes{}
|
||||
objects, err := k.Transform(komposeObject, kobject.ConvertOptions{CreateD: true, Replicas: 3})
|
||||
if err != nil {
|
||||
t.Error(errors.Wrap(err, "k.Transform failed"))
|
||||
}
|
||||
|
||||
expectedArgs := []string{"$(PROTOCOL)://$(DOMAIN)/"}
|
||||
for _, obj := range objects {
|
||||
if deployment, ok := obj.(*appsv1.Deployment); ok {
|
||||
args := deployment.Spec.Template.Spec.Containers[0].Args[0]
|
||||
if args != expectedArgs[0] {
|
||||
t.Errorf("Expected args %v upon conversion, actual %v", expectedArgs, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ type Kubernetes struct {
|
||||
// PVCRequestSize (Persistent Volume Claim) has default size
|
||||
const PVCRequestSize = "100Mi"
|
||||
|
||||
// ValidVolumeSet has the different types of valid volumes
|
||||
var ValidVolumeSet = map[string]struct{}{"emptyDir": {}, "hostPath": {}, "configMap": {}, "persistentVolumeClaim": {}}
|
||||
|
||||
const (
|
||||
@@ -432,6 +433,7 @@ func (k *Kubernetes) InitDS(name string, service kobject.ServiceConfig) *appsv1.
|
||||
return ds
|
||||
}
|
||||
|
||||
// InitSS method initialize a stateful set
|
||||
func (k *Kubernetes) InitSS(name string, service kobject.ServiceConfig, replicas int) *appsv1.StatefulSet {
|
||||
var podSpec api.PodSpec
|
||||
if len(service.Configs) > 0 {
|
||||
@@ -634,6 +636,7 @@ func ConfigPorts(service kobject.ServiceConfig) []api.ContainerPort {
|
||||
return ports
|
||||
}
|
||||
|
||||
// ConfigLBServicePorts method configure the ports of the k8s Load Balancer Service
|
||||
func (k *Kubernetes) ConfigLBServicePorts(service kobject.ServiceConfig) ([]api.ServicePort, []api.ServicePort) {
|
||||
var tcpPorts []api.ServicePort
|
||||
var udpPorts []api.ServicePort
|
||||
@@ -954,16 +957,15 @@ func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) (
|
||||
volsource = source
|
||||
} else if useConfigMap {
|
||||
log.Debugf("Use configmap volume")
|
||||
|
||||
if cm, err := k.IntiConfigMapFromFileOrDir(name, volumeName, volume.Host, service); err != nil {
|
||||
cm, err := k.IntiConfigMapFromFileOrDir(name, volumeName, volume.Host, service)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
} else {
|
||||
cms = append(cms, cm)
|
||||
volsource = k.ConfigConfigMapVolumeSource(volumeName, volume.Container, cm)
|
||||
}
|
||||
cms = append(cms, cm)
|
||||
volsource = k.ConfigConfigMapVolumeSource(volumeName, volume.Container, cm)
|
||||
|
||||
if useSubPathMount(cm) {
|
||||
volMount.SubPath = volsource.ConfigMap.Items[0].Path
|
||||
}
|
||||
if useSubPathMount(cm) {
|
||||
volMount.SubPath = volsource.ConfigMap.Items[0].Path
|
||||
}
|
||||
} else {
|
||||
volsource = k.ConfigPVCVolumeSource(volumeName, readonly)
|
||||
|
||||
@@ -13,12 +13,15 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
// PodSpec holds the spec of k8s pod.
|
||||
type PodSpec struct {
|
||||
api.PodSpec
|
||||
}
|
||||
|
||||
// PodSpecOption holds the function to apply on a PodSpec
|
||||
type PodSpecOption func(*PodSpec)
|
||||
|
||||
// AddContainer method is responsible for adding a new container to a k8s Pod.
|
||||
func AddContainer(service kobject.ServiceConfig, opt kobject.ConvertOptions) PodSpecOption {
|
||||
return func(podSpec *PodSpec) {
|
||||
name := GetContainerName(service)
|
||||
@@ -50,6 +53,7 @@ func AddContainer(service kobject.ServiceConfig, opt kobject.ConvertOptions) Pod
|
||||
}
|
||||
}
|
||||
|
||||
// TerminationGracePeriodSeconds method is responsible for attributing the grace period seconds option to a pod
|
||||
func TerminationGracePeriodSeconds(name string, service kobject.ServiceConfig) PodSpecOption {
|
||||
return func(podSpec *PodSpec) {
|
||||
var err error
|
||||
@@ -156,6 +160,7 @@ func SecurityContext(name string, service kobject.ServiceConfig) PodSpecOption {
|
||||
}
|
||||
}
|
||||
|
||||
// SetVolumeNames method return a set of volume names
|
||||
func SetVolumeNames(volumes []api.Volume) mapset.Set {
|
||||
set := mapset.NewSet()
|
||||
for _, volume := range volumes {
|
||||
@@ -164,6 +169,7 @@ func SetVolumeNames(volumes []api.Volume) mapset.Set {
|
||||
return set
|
||||
}
|
||||
|
||||
// SetVolumes method returns a method that adds the volumes to the pod spec
|
||||
func SetVolumes(volumes []api.Volume) PodSpecOption {
|
||||
return func(podSpec *PodSpec) {
|
||||
volumesSet := SetVolumeNames(volumes)
|
||||
@@ -179,6 +185,7 @@ func SetVolumes(volumes []api.Volume) PodSpecOption {
|
||||
}
|
||||
}
|
||||
|
||||
// SetVolumeMountPaths method returns a set of volumes mount path
|
||||
func SetVolumeMountPaths(volumesMount []api.VolumeMount) mapset.Set {
|
||||
set := mapset.NewSet()
|
||||
for _, volumeMount := range volumesMount {
|
||||
@@ -188,6 +195,7 @@ func SetVolumeMountPaths(volumesMount []api.VolumeMount) mapset.Set {
|
||||
return set
|
||||
}
|
||||
|
||||
// SetVolumeMounts returns a function which adds the volume mounts option to the pod spec
|
||||
func SetVolumeMounts(volumesMount []api.VolumeMount) PodSpecOption {
|
||||
return func(podSpec *PodSpec) {
|
||||
volumesMountSet := SetVolumeMountPaths(volumesMount)
|
||||
@@ -242,6 +250,7 @@ func RestartPolicy(name string, service kobject.ServiceConfig) PodSpecOption {
|
||||
}
|
||||
}
|
||||
|
||||
// HostName configure the host name of a pod
|
||||
func HostName(service kobject.ServiceConfig) PodSpecOption {
|
||||
return func(podSpec *PodSpec) {
|
||||
// Configure hostname/domain_name settings
|
||||
@@ -251,6 +260,7 @@ func HostName(service kobject.ServiceConfig) PodSpecOption {
|
||||
}
|
||||
}
|
||||
|
||||
// DomainName configure the domain name of a pod
|
||||
func DomainName(service kobject.ServiceConfig) PodSpecOption {
|
||||
return func(podSpec *PodSpec) {
|
||||
if service.DomainName != "" {
|
||||
@@ -299,18 +309,21 @@ func configProbe(healthCheck kobject.HealthCheck) *api.Probe {
|
||||
return &probe
|
||||
}
|
||||
|
||||
// ServiceAccountName is responsible for setting the service account name to the pod spec
|
||||
func ServiceAccountName(serviceAccountName string) PodSpecOption {
|
||||
return func(podSpec *PodSpec) {
|
||||
podSpec.ServiceAccountName = serviceAccountName
|
||||
}
|
||||
}
|
||||
|
||||
// TopologySpreadConstraints is responsible for setting the topology spread constraints to the pod spec
|
||||
func TopologySpreadConstraints(service kobject.ServiceConfig) PodSpecOption {
|
||||
return func(podSpec *PodSpec) {
|
||||
podSpec.TopologySpreadConstraints = ConfigTopologySpreadConstraints(service)
|
||||
}
|
||||
}
|
||||
|
||||
// Append is responsible for adding the pod spec options to the particular pod
|
||||
func (podSpec *PodSpec) Append(ops ...PodSpecOption) *PodSpec {
|
||||
for _, option := range ops {
|
||||
option(podSpec)
|
||||
@@ -318,6 +331,7 @@ func (podSpec *PodSpec) Append(ops ...PodSpecOption) *PodSpec {
|
||||
return podSpec
|
||||
}
|
||||
|
||||
// Get is responsible for returning the pod spec of a particular pod
|
||||
func (podSpec *PodSpec) Get() api.PodSpec {
|
||||
return podSpec.PodSpec
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ type Image struct {
|
||||
Remote string // the image's remote identifier. (ie: registry/name[:tag])
|
||||
}
|
||||
|
||||
// NewImageFromParsed method returns the docker image from the docker parser reference
|
||||
func NewImageFromParsed(parsed *dockerparser.Reference) Image {
|
||||
return Image{
|
||||
Name: parsed.Name(),
|
||||
|
||||
@@ -27,6 +27,7 @@ type Tag struct {
|
||||
Client dockerlib.Client
|
||||
}
|
||||
|
||||
// TagImage function is responsible for tagging the docker image
|
||||
func (c *Tag) TagImage(image Image) error {
|
||||
options := dockerlib.TagImageOptions{
|
||||
Tag: image.Tag,
|
||||
|
||||
Reference in New Issue
Block a user