forked from LaconicNetwork/kompose
Fix PVC handling for StatefulSets
This commit is contained in:
parent
e6b323eedb
commit
f2688ca2cf
@ -606,7 +606,13 @@ func dockerComposeToKomposeMapping(composeObject *types.Project) (kobject.Kompos
|
||||
komposeObject.ServiceConfigs[normalizeServiceNames(name)] = serviceConfig
|
||||
}
|
||||
|
||||
handleVolume(&komposeObject, &composeObject.Volumes)
|
||||
// Normalize volume names in the volumes map so lookups work with normalized names
|
||||
normalizedVolumes := make(types.Volumes)
|
||||
for volName, volConfig := range composeObject.Volumes {
|
||||
normalizedVolumes[normalizeVolumes(volName)] = volConfig
|
||||
}
|
||||
|
||||
handleVolume(&komposeObject, &normalizedVolumes)
|
||||
return komposeObject, nil
|
||||
}
|
||||
|
||||
|
||||
@ -89,7 +89,8 @@ type ServiceValues struct {
|
||||
Tag string
|
||||
PullPolicy string
|
||||
}
|
||||
Env map[string]string
|
||||
Env map[string]string
|
||||
Replicas int `yaml:"replicas,omitempty"`
|
||||
}
|
||||
|
||||
type PersistenceValues struct {
|
||||
@ -134,62 +135,79 @@ func unquoteHelmTemplates(yamlBytes []byte) []byte {
|
||||
}
|
||||
|
||||
// templatePVCStorage replaces PVC storage values with Helm templates
|
||||
// TODO: This post-processes YAML because resource.Quantity validates input and rejects template strings
|
||||
// This post-processes YAML because resource.Quantity validates input and rejects template strings
|
||||
func templatePVCStorage(yamlBytes []byte, persistenceValues map[string]PersistenceValues) []byte {
|
||||
yamlStr := string(yamlBytes)
|
||||
|
||||
// Check if this is a PersistentVolumeClaim
|
||||
if !strings.Contains(yamlStr, "kind: PersistentVolumeClaim") {
|
||||
// Only process PersistentVolumeClaim or StatefulSet with volumeClaimTemplates
|
||||
if !strings.Contains(yamlStr, "kind: PersistentVolumeClaim") &&
|
||||
!(strings.Contains(yamlStr, "kind: StatefulSet") && strings.Contains(yamlStr, "volumeClaimTemplates:")) {
|
||||
return yamlBytes
|
||||
}
|
||||
|
||||
// Extract PVC name from metadata
|
||||
var pvcName string
|
||||
lines := strings.Split(yamlStr, "\n")
|
||||
var pvcName string
|
||||
inVolumeClaimTemplates := false
|
||||
inMetadata := false
|
||||
|
||||
// Extract PVC name from metadata
|
||||
for _, line := range lines {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if trimmed == "metadata:" {
|
||||
|
||||
if strings.Contains(trimmed, "volumeClaimTemplates:") {
|
||||
inVolumeClaimTemplates = true
|
||||
} else if (trimmed == "metadata:" || trimmed == "- metadata:") && (inVolumeClaimTemplates || !strings.Contains(yamlStr, "volumeClaimTemplates:")) {
|
||||
inMetadata = true
|
||||
continue
|
||||
}
|
||||
if inMetadata && strings.HasPrefix(trimmed, "name:") {
|
||||
parts := strings.SplitN(trimmed, ":", 2)
|
||||
if len(parts) == 2 {
|
||||
} else if inMetadata && strings.HasPrefix(trimmed, "name:") {
|
||||
if parts := strings.SplitN(trimmed, ":", 2); len(parts) == 2 {
|
||||
pvcName = strings.TrimSpace(parts[1])
|
||||
break
|
||||
}
|
||||
}
|
||||
// Exit metadata section if we hit another top-level key
|
||||
if inMetadata && len(line) > 0 && line[0] != ' ' && line[0] != '\t' && !strings.HasPrefix(trimmed, "name:") {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If we found the PVC name and it's in our persistence values, template it
|
||||
// Template the storage value if PVC name is in persistence values
|
||||
if pvcName != "" {
|
||||
if _, exists := persistenceValues[pvcName]; exists {
|
||||
templateStr := "{{ index .Values \"persistence\" \"" + pvcName + "\" \"size\" }}"
|
||||
|
||||
// Replace storage value with template
|
||||
for i, line := range lines {
|
||||
if strings.Contains(line, "storage:") {
|
||||
log.Debugf("Found storage line: %q", line)
|
||||
prefix := line[:strings.Index(line, ":")+2]
|
||||
log.Debugf("Prefix: %q, Template: %q", prefix, templateStr)
|
||||
lines[i] = prefix + templateStr
|
||||
log.Debugf("New line: %q", lines[i])
|
||||
break
|
||||
}
|
||||
}
|
||||
yamlStr = strings.Join(lines, "\n")
|
||||
log.Debugf("Final templated YAML:\n%s", yamlStr)
|
||||
}
|
||||
}
|
||||
|
||||
return []byte(yamlStr)
|
||||
}
|
||||
|
||||
// templateReplicas replaces hardcoded replica values with Helm templates
|
||||
// This post-processes YAML because Replicas is *int32 and rejects template strings
|
||||
func templateReplicas(yamlBytes []byte, serviceName string) []byte {
|
||||
yamlStr := string(yamlBytes)
|
||||
|
||||
// Only process Deployment or StatefulSet (DaemonSet doesn't use replicas)
|
||||
if !strings.Contains(yamlStr, "kind: Deployment") && !strings.Contains(yamlStr, "kind: StatefulSet") {
|
||||
return yamlBytes
|
||||
}
|
||||
|
||||
lines := strings.Split(yamlStr, "\n")
|
||||
templateStr := "{{ " + helmValuesPath(serviceName, "replicas") + " | default 1 }}"
|
||||
|
||||
for i, line := range lines {
|
||||
if strings.HasPrefix(strings.TrimSpace(line), "replicas:") {
|
||||
indentEnd := strings.Index(line, "replicas:")
|
||||
lines[i] = line[:indentEnd] + "replicas: " + templateStr
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return []byte(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
// extractValuesFromKomposeObject extracts service values from KomposeObject for values.yaml
|
||||
// Note: PVC values are extracted during transformation and stored in Kubernetes.PersistenceValues
|
||||
func extractValuesFromKomposeObject(komposeObject kobject.KomposeObject) (map[string]ServiceValues, map[string]PersistenceValues) {
|
||||
@ -216,6 +234,13 @@ func extractValuesFromKomposeObject(komposeObject kobject.KomposeObject) (map[st
|
||||
svcValues.Env[envVar.Name] = envVar.Value
|
||||
}
|
||||
|
||||
// Extract replicas (default to 1 if not set)
|
||||
if service.Replicas > 0 {
|
||||
svcValues.Replicas = service.Replicas
|
||||
} else {
|
||||
svcValues.Replicas = 1
|
||||
}
|
||||
|
||||
serviceValues[serviceName] = svcValues
|
||||
}
|
||||
|
||||
@ -324,6 +349,9 @@ func generateValuesYAML(serviceValues map[string]ServiceValues, persistenceValue
|
||||
serviceMap["env"] = svcValues.Env
|
||||
}
|
||||
|
||||
// Add replicas
|
||||
serviceMap["replicas"] = svcValues.Replicas
|
||||
|
||||
valuesMap[serviceName] = serviceMap
|
||||
}
|
||||
|
||||
@ -517,6 +545,7 @@ func PrintList(objects []runtime.Object, opt kobject.ConvertOptions, komposeObje
|
||||
if opt.CreateChart {
|
||||
data = unquoteHelmTemplates(data)
|
||||
data = templatePVCStorage(data, persistenceValues)
|
||||
data = templateReplicas(data, objectMeta.Name)
|
||||
}
|
||||
|
||||
file, err = transformer.Print(objectMeta.Name, finalDirName, strings.ToLower(typeMeta.Kind), data, opt.ToStdout, opt.GenerateJSON, f, opt.Provider)
|
||||
@ -828,7 +857,15 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
|
||||
volumesMount = append(volumesMount, TmpVolumesMount...)
|
||||
}
|
||||
|
||||
if pvc != nil && opt.Controller != StatefulStateController {
|
||||
// Check if service uses StatefulSet controller (either from opt or from label)
|
||||
isStatefulSet := opt.Controller == StatefulStateController
|
||||
if !isStatefulSet {
|
||||
if controllerType, ok := service.Labels[compose.LabelControllerType]; ok {
|
||||
isStatefulSet = (controllerType == StatefulStateController)
|
||||
}
|
||||
}
|
||||
|
||||
if pvc != nil && !isStatefulSet {
|
||||
// Looping on the slice pvc instead of `*objects = append(*objects, pvc...)`
|
||||
// because the type of objects and pvc is different, but when doing append
|
||||
// one element at a time it gets converted to runtime.Object for objects slice
|
||||
@ -904,7 +941,8 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
|
||||
}
|
||||
}
|
||||
|
||||
if initPvc != nil {
|
||||
// Skip PVCs for init containers if parent service is StatefulSet
|
||||
if initPvc != nil && !isStatefulSet {
|
||||
for _, p := range initPvc {
|
||||
if !existingObjectNames[p.Name] {
|
||||
*objects = append(*objects, p)
|
||||
@ -1115,8 +1153,26 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
|
||||
persistentVolumeClaims[i] = *persistentVolumeClaim
|
||||
persistentVolumeClaims[i].APIVersion = ""
|
||||
persistentVolumeClaims[i].Kind = ""
|
||||
|
||||
// Store PVC size for helm chart values (for volumeClaimTemplates)
|
||||
if opt.CreateChart {
|
||||
pvcName := persistentVolumeClaim.Name
|
||||
if storageQty, ok := persistentVolumeClaim.Spec.Resources.Requests[api.ResourceStorage]; ok {
|
||||
k.PersistenceValues[pvcName] = storageQty.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
objType.Spec.VolumeClaimTemplates = persistentVolumeClaims
|
||||
|
||||
// Remove PVC volumes from pod spec since they come from volumeClaimTemplates
|
||||
// Keep only non-PVC volumes (ConfigMaps, Secrets, EmptyDir, HostPath, etc.)
|
||||
var filteredVolumes []api.Volume
|
||||
for _, vol := range objType.Spec.Template.Spec.Volumes {
|
||||
if vol.PersistentVolumeClaim == nil {
|
||||
filteredVolumes = append(filteredVolumes, vol)
|
||||
}
|
||||
}
|
||||
objType.Spec.Template.Spec.Volumes = filteredVolumes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user