Add support for Config, endpoint_mode and 3.3 support (#994)

* Add support for Config

* Add deployment endpoint_mode support

* Add docker compose 3.3 support

* Add compose v3.3 support for openshift
This commit is contained in:
Xianlu Bird authored and Charlie Drage committed 2018-08-01 15:09:00 -04:00
1 parent 0c01309fe8
commit 0252213efb
24 files changed
+1117 -16

No files matched your search

+6
View File
@@ -17,6 +17,7 @@ limitations under the License.
package kobject
import (
dockerCliTypes "github.com/docker/cli/cli/compose/types"
"github.com/docker/libcompose/yaml"
"k8s.io/kubernetes/pkg/api"
)
@@ -94,6 +95,7 @@ type ServiceConfig struct {
Build string `compose:"build"`
BuildArgs map[string]*string `compose:"build-args"`
ExposeService string `compose:"kompose.service.expose"`
BuildLabels map[string]string `compose:"build-labels"`
ExposeServiceTLS string `compose:"kompose.service.expose.tls-secret"`
Stdin bool `compose:"stdin_open"`
Tty bool `compose:"tty"`
@@ -107,6 +109,10 @@ type ServiceConfig struct {
Volumes []Volumes `compose:""`
HealthChecks HealthCheck `compose:""`
Placement map[string]string `compose:""`
//This is for long LONG SYNTAX link(https://docs.docker.com/compose/compose-file/#long-syntax)
Configs []dockerCliTypes.ServiceConfigObjConfig `compose:""`
//This is for SHORT SYNTAX link(https://docs.docker.com/compose/compose-file/#configs)
ConfigsMetaData map[string]dockerCliTypes.ConfigObjConfig `compose:""`
}
// HealthCheck the healthcheck configuration for a service
+1 -1
View File
@@ -183,7 +183,7 @@ func (c *Compose) LoadFile(files []string) (kobject.KomposeObject, error) {
}
return komposeObject, nil
// Use docker/cli for 3
case "3", "3.0", "3.1", "3.2":
case "3", "3.0", "3.1", "3.2", "3.3":
komposeObject, err := parseV3(files)
if err != nil {
return kobject.KomposeObject{}, err
+49 -2
View File
@@ -119,8 +119,10 @@ func parseV3(files []string) (kobject.KomposeObject, error) {
}
}
// TODO: Check all "unsupported" keys and output details
// Specifically, keys such as "volumes_from" are not supported in V3.
noSupKeys := checkUnsupportedKeyForV3(config)
for _, keyName := range noSupKeys {
log.Warningf("Unsupported %s key - ignoring", keyName)
}
// Finally, we convert the object from docker/cli's ServiceConfig to our appropriate one
komposeObject, err := dockerComposeToKomposeMapping(config)
@@ -350,6 +352,7 @@ func dockerComposeToKomposeMapping(composeObject *types.Config) (kobject.Kompose
serviceConfig.Build = composeServiceConfig.Build.Context
serviceConfig.Dockerfile = composeServiceConfig.Build.Dockerfile
serviceConfig.BuildArgs = composeServiceConfig.Build.Args
serviceConfig.BuildLabels = composeServiceConfig.Build.Labels
// Gather the environment values
// DockerCompose uses map[string]*string while we use []string
@@ -411,9 +414,15 @@ func dockerComposeToKomposeMapping(composeObject *types.Config) (kobject.Kompose
log.Infof("Service name in docker-compose has been changed from %q to %q", name, normalizeServiceNames(name))
}
serviceConfig.Configs = composeServiceConfig.Configs
serviceConfig.ConfigsMetaData = composeObject.Configs
if composeServiceConfig.Deploy.EndpointMode == "vip" {
serviceConfig.ServiceType = string(api.ServiceTypeNodePort)
}
// Final step, add to the array!
komposeObject.ServiceConfigs[normalizeServiceNames(name)] = serviceConfig
}
handleVolume(&komposeObject)
return komposeObject, nil
@@ -580,3 +589,41 @@ func mergeComposeObject(oldCompose *types.Config, newCompose *types.Config) (*ty
return oldCompose, nil
}
func checkUnsupportedKeyForV3(composeObject *types.Config) []string {
if composeObject == nil {
return []string{}
}
var keysFound []string
for _, service := range composeObject.Services {
//For short syntax, volume mount path must be /, but this will cause pod create fail in kubernetes
//So we ignore this attribute
for _, tmpConfig := range service.Configs {
if tmpConfig.Mode == nil {
keysFound = append(keysFound, "short syntax config")
} else {
if tmpConfig.GID != "" {
keysFound = append(keysFound, "long syntax config gid")
}
if tmpConfig.UID != "" {
keysFound = append(keysFound, "long syntax config uid")
}
}
}
if service.CredentialSpec.Registry != "" || service.CredentialSpec.File != "" {
keysFound = append(keysFound, "credential_spec")
}
}
for _, config := range composeObject.Configs {
if config.External.External {
keysFound = append(keysFound, "external config")
}
}
return keysFound
}
+24 -2
View File
@@ -386,10 +386,10 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
template.Spec.Containers[0].Command = service.Command
template.Spec.Containers[0].Args = service.Args
template.Spec.Containers[0].WorkingDir = service.WorkingDir
template.Spec.Containers[0].VolumeMounts = volumesMount
template.Spec.Containers[0].VolumeMounts = append(template.Spec.Containers[0].VolumeMounts, volumesMount...)
template.Spec.Containers[0].Stdin = service.Stdin
template.Spec.Containers[0].TTY = service.Tty
template.Spec.Volumes = volumes
template.Spec.Volumes = append(template.Spec.Volumes, volumes...)
template.Spec.NodeSelector = service.Placement
// Configure the HealthCheck
// We check to see if it's blank
@@ -608,6 +608,21 @@ func GetEnvsFromFile(file string, opt kobject.ConvertOptions) (map[string]string
return envLoad, nil
}
// GetContentFromFile get content from file
func GetContentFromFile(file string, opt kobject.ConvertOptions) (string, error) {
// Get the correct file context / directory
composeDir, err := transformer.GetComposeFileDir(opt.InputFiles)
if err != nil {
return "", errors.Wrap(err, "Unable to load file context")
}
fileLocation := path.Join(composeDir, file)
fileBytes, err := ioutil.ReadFile(fileLocation)
if err != nil {
return "", errors.Wrap(err, "Unable to read file")
}
return string(fileBytes), nil
}
// FormatEnvName format env name
func FormatEnvName(name string) string {
envName := strings.Trim(name, "./")
@@ -615,3 +630,10 @@ func FormatEnvName(name string) string {
envName = strings.Replace(envName, "/", "-", -1)
return envName
}
// FormatFileName format file name
func FormatFileName(name string) string {
envName := strings.Trim(name, "./")
envName = strings.Replace(envName, "_", "-", -1)
return envName
}
+107 -4
View File
@@ -127,6 +127,52 @@ func (k *Kubernetes) InitPodSpec(name string, image string) api.PodSpec {
return pod
}
//InitPodSpecWithConfigMap creates the pod specification
func (k *Kubernetes) InitPodSpecWithConfigMap(name string, image string, service kobject.ServiceConfig) api.PodSpec {
var volumeMounts []api.VolumeMount
var volumes []api.Volume
if len(service.Configs) > 0 && service.Configs[0].Mode != nil {
//This is for LONG SYNTAX
for _, value := range service.Configs {
if value.Target == "/" {
log.Warnf("Long syntax config, target path can not be /")
continue
}
tmpKey := FormatFileName(value.Source)
volumeMounts = append(volumeMounts,
api.VolumeMount{
Name: tmpKey,
MountPath: "/" + FormatFileName(value.Target),
})
tmpVolume := api.Volume{
Name: tmpKey,
}
tmpVolume.ConfigMap = &api.ConfigMapVolumeSource{}
tmpVolume.ConfigMap.Name = tmpKey
var tmpMode int32
tmpMode = int32(*value.Mode)
tmpVolume.ConfigMap.DefaultMode = &tmpMode
volumes = append(volumes, tmpVolume)
}
} else {
//This is for SHORT SYNTAX, unsupported
}
pod := api.PodSpec{
Containers: []api.Container{
{
Name: name,
Image: image,
VolumeMounts: volumeMounts,
},
},
Volumes: volumes,
}
return pod
}
// InitRC initializes Kubernetes ReplicationController object
func (k *Kubernetes) InitRC(name string, service kobject.ServiceConfig, replicas int) *api.ReplicationController {
rc := &api.ReplicationController{
@@ -169,8 +215,8 @@ func (k *Kubernetes) InitSvc(name string, service kobject.ServiceConfig) *api.Se
return svc
}
// InitConfigMap initializes a ConfigMap object
func (k *Kubernetes) InitConfigMap(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions, envFile string) *api.ConfigMap {
// InitConfigMapForEnv initializes a ConfigMap object
func (k *Kubernetes) InitConfigMapForEnv(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions, envFile string) *api.ConfigMap {
envs, err := GetEnvsFromFile(envFile, opt)
if err != nil {
@@ -197,8 +243,46 @@ func (k *Kubernetes) InitConfigMap(name string, service kobject.ServiceConfig, o
return configMap
}
//InitConfigMapFromFile initializes a ConfigMap object
func (k *Kubernetes) InitConfigMapFromFile(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions, fileName string) *api.ConfigMap {
content, err := GetContentFromFile(fileName, opt)
if err != nil {
log.Fatalf("Unable to retrieve file: %s", err)
}
originFileName := FormatFileName(fileName)
dataMap := make(map[string]string)
dataMap[originFileName] = content
configMapName := ""
for key, tmpConfig := range service.ConfigsMetaData {
if tmpConfig.File == fileName {
configMapName = key
}
}
configMap := &api.ConfigMap{
TypeMeta: unversioned.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: api.ObjectMeta{
Name: FormatFileName(configMapName),
Labels: transformer.ConfigLabels(name),
},
Data: dataMap,
}
return configMap
}
// InitD initializes Kubernetes Deployment object
func (k *Kubernetes) InitD(name string, service kobject.ServiceConfig, replicas int) *extensions.Deployment {
var podSpec api.PodSpec
if len(service.Configs) > 0 {
podSpec = k.InitPodSpecWithConfigMap(name, service.Image, service)
} else {
podSpec = k.InitPodSpec(name, service.Image)
}
dc := &extensions.Deployment{
TypeMeta: unversioned.TypeMeta{
Kind: "Deployment",
@@ -211,7 +295,7 @@ func (k *Kubernetes) InitD(name string, service kobject.ServiceConfig, replicas
Spec: extensions.DeploymentSpec{
Replicas: int32(replicas),
Template: api.PodTemplateSpec{
Spec: k.InitPodSpec(name, service.Image),
Spec: podSpec,
},
},
}
@@ -660,9 +744,14 @@ func (k *Kubernetes) CreateKubernetesObjects(name string, service kobject.Servic
opt.Controller = val
}
if len(service.Configs) > 0 {
objects = k.createConfigMapFromComposeConfig(name, opt, service, objects)
}
if opt.CreateD || opt.Controller == DeploymentController {
objects = append(objects, k.InitD(name, service, replica))
}
if opt.CreateDS || opt.Controller == DaemonSetController {
objects = append(objects, k.InitDS(name, service))
}
@@ -672,7 +761,7 @@ func (k *Kubernetes) CreateKubernetesObjects(name string, service kobject.Servic
if len(service.EnvFile) > 0 {
for _, envFile := range service.EnvFile {
configMap := k.InitConfigMap(name, service, opt, envFile)
configMap := k.InitConfigMapForEnv(name, service, opt, envFile)
objects = append(objects, configMap)
}
}
@@ -680,6 +769,20 @@ func (k *Kubernetes) CreateKubernetesObjects(name string, service kobject.Servic
return objects
}
func (k *Kubernetes) createConfigMapFromComposeConfig(name string, opt kobject.ConvertOptions, service kobject.ServiceConfig, objects []runtime.Object) []runtime.Object {
for _, config := range service.Configs {
currentConfigName := config.Source
currentConfigObj := service.ConfigsMetaData[currentConfigName]
if currentConfigObj.External.External {
continue
}
currentFileName := currentConfigObj.File
configMap := k.InitConfigMapFromFile(name, service, opt, currentFileName)
objects = append(objects, configMap)
}
return objects
}
// InitPod initializes Kubernetes Pod object
func (k *Kubernetes) InitPod(name string, service kobject.ServiceConfig) *api.Pod {
pod := api.Pod{
+8 -1
View File
@@ -183,6 +183,13 @@ func (o *OpenShift) initDeploymentConfig(name string, service kobject.ServiceCon
containerName = []string{service.ContainerName}
}
var podSpec kapi.PodSpec
if len(service.Configs) > 0 {
podSpec = o.InitPodSpecWithConfigMap(name, " ", service)
} else {
podSpec = o.InitPodSpec(name, " ")
}
dc := &deployapi.DeploymentConfig{
TypeMeta: unversioned.TypeMeta{
Kind: "DeploymentConfig",
@@ -200,7 +207,7 @@ func (o *OpenShift) initDeploymentConfig(name string, service kobject.ServiceCon
ObjectMeta: kapi.ObjectMeta{
Labels: transformer.ConfigLabels(name),
},
Spec: o.InitPodSpec(name, " "),
Spec: podSpec,
},
Triggers: []deployapi.DeploymentTriggerPolicy{
// Trigger new deploy when DeploymentConfig is created (config change)