Merge Develop to prepare V3 #77

Merged
metal3d merged 98 commits from develop into master 2024-10-29 16:45:05 +00:00
26 changed files with 582 additions and 513 deletions
Showing only changes of commit 4367a01769 - Show all commits

View File

@@ -9,11 +9,11 @@ import (
"os"
"strings"
"katenary/generator"
"katenary/utils"
"github.com/compose-spec/compose-go/cli"
"github.com/spf13/cobra"
"katenary/generator"
"katenary/utils"
)
const longHelp = `Katenary is a tool to convert compose files to Helm Charts.

View File

@@ -1,12 +0,0 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->
# katenary
```go
import "katenary/cmd/katenary"
```
Katenary CLI, main package.
This package is not intended to be imported. It contains the main function that build the command line with \`cobra\` package.

View File

@@ -35,7 +35,7 @@ var Version = "master" // changed at compile time
```
<a name="Convert"></a>
## func [Convert](<https://github.com/metal3d/katenary/blob/develop/generator/converter.go#L38>)
## func [Convert](<https://github.com/metal3d/katenary/blob/develop/generator/converter.go#L37>)
```go
func Convert(config ConvertOptions, dockerComposeFile ...string)
@@ -116,16 +116,14 @@ func Prefix() string
<a name="ChartTemplate"></a>
## type [ChartTemplate](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L9-L12>)
## type [ChartTemplate](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L25-L28>)
ChartTemplate is a template of a chart. It contains the content of the template and the name of the service. This is used internally to generate the templates.
TODO: maybe we can set it private.
```go
type ChartTemplate struct {
Content []byte
Servicename string
Content []byte
}
```
@@ -151,25 +149,25 @@ func NewConfigMap(service types.ServiceConfig, appName string) *ConfigMap
NewConfigMap creates a new ConfigMap from a compose service. The appName is the name of the application taken from the project name. The ConfigMap is filled by environment variables and labels "map\-env".
<a name="NewConfigMapFromDirectory"></a>
### func [NewConfigMapFromDirectory](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L130>)
### func [NewConfigMapFromDirectory](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L128>)
```go
func NewConfigMapFromDirectory(service types.ServiceConfig, appName string, path string) *ConfigMap
func NewConfigMapFromDirectory(service types.ServiceConfig, appName, path string) *ConfigMap
```
NewConfigMapFromDirectory creates a new ConfigMap from a compose service. This path is the path to the file or directory. If the path is a directory, all files in the directory are added to the ConfigMap. Each subdirectory are ignored. Note that the Generate\(\) function will create the subdirectories ConfigMaps.
<a name="ConfigMap.AddData"></a>
### func \(\*ConfigMap\) [AddData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L166>)
### func \(\*ConfigMap\) [AddData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L164>)
```go
func (c *ConfigMap) AddData(key string, value string)
func (c *ConfigMap) AddData(key, value string)
```
AddData adds a key value pair to the configmap. Append or overwrite the value if the key already exists.
<a name="ConfigMap.AppendDir"></a>
### func \(\*ConfigMap\) [AppendDir](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L172>)
### func \(\*ConfigMap\) [AppendDir](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L170>)
```go
func (c *ConfigMap) AppendDir(path string)
@@ -178,7 +176,7 @@ func (c *ConfigMap) AppendDir(path string)
AddFile adds files from given path to the configmap. It is not recursive, to add all files in a directory, you need to call this function for each subdirectory.
<a name="ConfigMap.AppendFile"></a>
### func \(\*ConfigMap\) [AppendFile](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L208>)
### func \(\*ConfigMap\) [AppendFile](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L206>)
```go
func (c *ConfigMap) AppendFile(path string)
@@ -187,7 +185,7 @@ func (c *ConfigMap) AppendFile(path string)
<a name="ConfigMap.Filename"></a>
### func \(\*ConfigMap\) [Filename](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L226>)
### func \(\*ConfigMap\) [Filename](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L224>)
```go
func (c *ConfigMap) Filename() string
@@ -196,7 +194,7 @@ func (c *ConfigMap) Filename() string
Filename returns the filename of the configmap. If the configmap is used for files, the filename contains the path.
<a name="ConfigMap.SetData"></a>
### func \(\*ConfigMap\) [SetData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L161>)
### func \(\*ConfigMap\) [SetData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L159>)
```go
func (c *ConfigMap) SetData(data map[string]string)
@@ -205,7 +203,7 @@ func (c *ConfigMap) SetData(data map[string]string)
SetData sets the data of the configmap. It replaces the entire data.
<a name="ConfigMap.Yaml"></a>
### func \(\*ConfigMap\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L236>)
### func \(\*ConfigMap\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L234>)
```go
func (c *ConfigMap) Yaml() ([]byte, error)
@@ -225,18 +223,18 @@ type ConfigMapMount struct {
```
<a name="ConvertOptions"></a>
## type [ConvertOptions](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L46-L53>)
## type [ConvertOptions](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L14-L21>)
ConvertOptions are the options to convert a compose project to a helm chart.
```go
type ConvertOptions struct {
Force bool // Force the chart directory deletion if it already exists.
OutputDir string // The output directory of the chart.
Profiles []string // Profile to use for the conversion.
HelmUpdate bool // If true, the "helm dep update" command will be run after the chart generation.
AppVersion *string // Set the chart "appVersion" field. If nil, the version will be set to 0.1.0.
ChartVersion string // Set the chart "version" field.
AppVersion *string
OutputDir string
ChartVersion string
Profiles []string
Force bool
HelmUpdate bool
}
```
@@ -275,7 +273,7 @@ Yaml returns the yaml representation of the cronjob.
Implements the Yaml interface.
<a name="CronJobValue"></a>
## type [CronJobValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L50-L55>)
## type [CronJobValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L47-L52>)
CronJobValue is a cronjob configuration that will be saved in values.yaml.
@@ -304,7 +302,7 @@ type DataMap interface {
### func [NewFileMap](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L26>)
```go
func NewFileMap(service types.ServiceConfig, appName string, kind string) DataMap
func NewFileMap(service types.ServiceConfig, appName, kind string) DataMap
```
NewFileMap creates a new DataMap from a compose service. The appName is the name of the application taken from the project name.
@@ -340,7 +338,7 @@ func (d *Deployment) AddContainer(service types.ServiceConfig)
AddContainer adds a container to the deployment.
<a name="Deployment.AddHealthCheck"></a>
### func \(\*Deployment\) [AddHealthCheck](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L440>)
### func \(\*Deployment\) [AddHealthCheck](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L450>)
```go
func (d *Deployment) AddHealthCheck(service types.ServiceConfig, container *corev1.Container)
@@ -367,7 +365,7 @@ func (d *Deployment) AddVolumes(service types.ServiceConfig, appName string)
AddVolumes adds a volume to the deployment. It does not create the PVC, it only adds the volumes to the deployment. If the volume is a bind volume it will warn the user that it is not supported yet.
<a name="Deployment.BindFrom"></a>
### func \(\*Deployment\) [BindFrom](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L320>)
### func \(\*Deployment\) [BindFrom](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L330>)
```go
func (d *Deployment) BindFrom(service types.ServiceConfig, binded *Deployment)
@@ -385,7 +383,7 @@ func (d *Deployment) DependsOn(to *Deployment, servicename string) error
DependsOn adds a initContainer to the deployment that will wait for the service to be up.
<a name="Deployment.Filename"></a>
### func \(\*Deployment\) [Filename](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L618>)
### func \(\*Deployment\) [Filename](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L628>)
```go
func (d *Deployment) Filename() string
@@ -394,7 +392,7 @@ func (d *Deployment) Filename() string
Filename returns the filename of the deployment.
<a name="Deployment.SetEnvFrom"></a>
### func \(\*Deployment\) [SetEnvFrom](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L348>)
### func \(\*Deployment\) [SetEnvFrom](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L358>)
```go
func (d *Deployment) SetEnvFrom(service types.ServiceConfig, appName string)
@@ -403,7 +401,7 @@ func (d *Deployment) SetEnvFrom(service types.ServiceConfig, appName string)
SetEnvFrom sets the environment variables to a configmap. The configmap is created.
<a name="Deployment.Yaml"></a>
### func \(\*Deployment\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L469>)
### func \(\*Deployment\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L479>)
```go
func (d *Deployment) Yaml() ([]byte, error)
@@ -430,28 +428,29 @@ const (
```
<a name="HelmChart"></a>
## type [HelmChart](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L16-L28>)
## type [HelmChart](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L32-L44>)
HelmChart is a Helm Chart representation. It contains all the tempaltes, values, versions, helpers...
```go
type HelmChart struct {
Templates map[string]*ChartTemplate `yaml:"-"`
Values map[string]any `yaml:"-"`
VolumeMounts map[string]any `yaml:"-"`
Name string `yaml:"name"`
ApiVersion string `yaml:"apiVersion"`
Version string `yaml:"version"`
AppVersion string `yaml:"appVersion"`
Description string `yaml:"description"`
Helper string `yaml:"-"`
Dependencies []labelStructs.Dependency `yaml:"dependencies,omitempty"`
Templates map[string]*ChartTemplate `yaml:"-"` // do not export to yaml
Helper string `yaml:"-"` // do not export to yaml
Values map[string]any `yaml:"-"` // do not export to yaml
VolumeMounts map[string]any `yaml:"-"` // do not export to yaml
// contains filtered or unexported fields
}
```
<a name="Generate"></a>
### func [Generate](<https://github.com/metal3d/katenary/blob/develop/generator/generator.go#L33>)
### func [Generate](<https://github.com/metal3d/katenary/blob/develop/generator/generator.go#L31>)
```go
func Generate(project *types.Project) (*HelmChart, error)
@@ -471,7 +470,7 @@ The Generate function will create the HelmChart object this way:
- Merge the same\-pod services.
<a name="NewChart"></a>
### func [NewChart](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L31>)
### func [NewChart](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L47>)
```go
func NewChart(name string) *HelmChart
@@ -479,6 +478,15 @@ func NewChart(name string) *HelmChart
NewChart creates a new empty chart with the given name.
<a name="HelmChart.SaveTemplates"></a>
### func \(\*HelmChart\) [SaveTemplates](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L62>)
```go
func (chart *HelmChart) SaveTemplates(templateDir string)
```
SaveTemplates the templates of the chart to the given directory.
<a name="Help"></a>
## type [Help](<https://github.com/metal3d/katenary/blob/develop/generator/katenaryLabels.go#L32-L37>)
@@ -533,17 +541,17 @@ func (ingress *Ingress) Yaml() ([]byte, error)
<a name="IngressValue"></a>
## type [IngressValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L27-L33>)
## type [IngressValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L24-L30>)
IngressValue is a ingress configuration that will be saved in values.yaml.
```go
type IngressValue struct {
Enabled bool `yaml:"enabled"`
Annotations map[string]string `yaml:"annotations"`
Host string `yaml:"host"`
Path string `yaml:"path"`
Class string `yaml:"class"`
Annotations map[string]string `yaml:"annotations"`
Enabled bool `yaml:"enabled"`
}
```
@@ -578,16 +586,16 @@ const (
```
<a name="PersistenceValue"></a>
## type [PersistenceValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L19-L24>)
## type [PersistenceValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L16-L21>)
PersistenceValue is a persistence configuration that will be saved in values.yaml.
```go
type PersistenceValue struct {
Enabled bool `yaml:"enabled"`
StorageClass string `yaml:"storageClass"`
Size string `yaml:"size"`
AccessMode []string `yaml:"accessMode"`
Enabled bool `yaml:"enabled"`
}
```
@@ -614,7 +622,7 @@ func NewRBAC(service types.ServiceConfig, appName string) *RBAC
NewRBAC creates a new RBAC from a compose service. The appName is the name of the application taken from the project name.
<a name="RepositoryValue"></a>
## type [RepositoryValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L13-L16>)
## type [RepositoryValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L10-L13>)
RepositoryValue is a docker repository image and tag that will be saved in values.yaml.
@@ -712,7 +720,7 @@ NewSecret creates a new Secret from a compose service
### func \(\*Secret\) [AddData](<https://github.com/metal3d/katenary/blob/develop/generator/secret.go#L87>)
```go
func (s *Secret) AddData(key string, value string)
func (s *Secret) AddData(key, value string)
```
AddData adds a key value pair to the secret.
@@ -823,7 +831,7 @@ func (r *ServiceAccount) Yaml() ([]byte, error)
<a name="Value"></a>
## type [Value](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L36-L47>)
## type [Value](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L33-L44>)
Value will be saved in values.yaml. It contains configuraiton for all deployment and services.
@@ -832,18 +840,18 @@ type Value struct {
Repository *RepositoryValue `yaml:"repository,omitempty"`
Persistence map[string]*PersistenceValue `yaml:"persistence,omitempty"`
Ingress *IngressValue `yaml:"ingress,omitempty"`
ImagePullPolicy string `yaml:"imagePullPolicy,omitempty"`
Environment map[string]any `yaml:"environment,omitempty"`
Replicas *uint32 `yaml:"replicas,omitempty"`
CronJob *CronJobValue `yaml:"cronjob,omitempty"`
NodeSelector map[string]string `yaml:"nodeSelector"`
ServiceAccount string `yaml:"serviceAccount"`
Resources map[string]any `yaml:"resources"`
ImagePullPolicy string `yaml:"imagePullPolicy,omitempty"`
ServiceAccount string `yaml:"serviceAccount"`
}
```
<a name="NewValue"></a>
### func [NewValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L62>)
### func [NewValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L59>)
```go
func NewValue(service types.ServiceConfig, main ...bool) *Value
@@ -854,7 +862,7 @@ NewValue creates a new Value from a compose service. The value contains the nece
If \`main\` is true, the tag will be empty because it will be set in the helm chart appVersion.
<a name="Value.AddIngress"></a>
### func \(\*Value\) [AddIngress](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L101>)
### func \(\*Value\) [AddIngress](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L98>)
```go
func (v *Value) AddIngress(host, path string)
@@ -863,7 +871,7 @@ func (v *Value) AddIngress(host, path string)
<a name="Value.AddPersistence"></a>
### func \(\*Value\) [AddPersistence](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L89>)
### func \(\*Value\) [AddPersistence](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L86>)
```go
func (v *Value) AddPersistence(volumeName string)
@@ -872,7 +880,7 @@ func (v *Value) AddPersistence(volumeName string)
AddPersistence adds persistence configuration to the Value.
<a name="VolumeClaim"></a>
## type [VolumeClaim](<https://github.com/metal3d/katenary/blob/develop/generator/volume.go#L18-L23>)
## type [VolumeClaim](<https://github.com/metal3d/katenary/blob/develop/generator/volume.go#L20-L25>)
VolumeClaim is a kubernetes VolumeClaim. This is a PersistentVolumeClaim.
@@ -884,7 +892,7 @@ type VolumeClaim struct {
```
<a name="NewVolumeClaim"></a>
### func [NewVolumeClaim](<https://github.com/metal3d/katenary/blob/develop/generator/volume.go#L26>)
### func [NewVolumeClaim](<https://github.com/metal3d/katenary/blob/develop/generator/volume.go#L28>)
```go
func NewVolumeClaim(service types.ServiceConfig, volumeName, appName string) *VolumeClaim
@@ -893,7 +901,7 @@ func NewVolumeClaim(service types.ServiceConfig, volumeName, appName string) *Vo
NewVolumeClaim creates a new VolumeClaim from a compose service.
<a name="VolumeClaim.Filename"></a>
### func \(\*VolumeClaim\) [Filename](<https://github.com/metal3d/katenary/blob/develop/generator/volume.go#L117>)
### func \(\*VolumeClaim\) [Filename](<https://github.com/metal3d/katenary/blob/develop/generator/volume.go#L127>)
```go
func (v *VolumeClaim) Filename() string
@@ -902,7 +910,7 @@ func (v *VolumeClaim) Filename() string
Filename returns the suggested filename for a VolumeClaim.
<a name="VolumeClaim.Yaml"></a>
### func \(\*VolumeClaim\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/volume.go#L56>)
### func \(\*VolumeClaim\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/volume.go#L63>)
```go
func (v *VolumeClaim) Yaml() ([]byte, error)

View File

@@ -17,7 +17,7 @@ func NotesFile(services []string) string
NotesFile returns the content of the note.txt file.
<a name="ReadMeFile"></a>
## func [ReadMeFile](<https://github.com/metal3d/katenary/blob/develop/generator/extrafiles/readme.go#L25>)
## func [ReadMeFile](<https://github.com/metal3d/katenary/blob/develop/generator/extrafiles/readme.go#L24>)
```go
func ReadMeFile(charname, description string, values map[string]any) string

View File

@@ -158,7 +158,7 @@ func PortsFrom(data string) (Ports, error)
PortsFrom returns a Ports from the given string.
<a name="Probe"></a>
## type [Probe](<https://github.com/metal3d/katenary/blob/develop/generator/labelStructs/probes.go#L8-L11>)
## type [Probe](<https://github.com/metal3d/katenary/blob/develop/generator/labelStructs/probes.go#L11-L14>)
@@ -170,7 +170,7 @@ type Probe struct {
```
<a name="ProbeFrom"></a>
### func [ProbeFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labelStructs/probes.go#L13>)
### func [ProbeFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labelStructs/probes.go#L16>)
```go
func ProbeFrom(data string) (*Probe, error)

View File

@@ -8,7 +8,16 @@ import "katenary/utils"
Utils package provides some utility functions used in katenary. It defines some constants and functions used in the whole project.
## func [CountStartingSpaces](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L31>)
## func [Confirm](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L167>)
```go
func Confirm(question string, icon ...Icon) bool
```
Confirm asks a question and returns true if the answer is y.
<a name="CountStartingSpaces"></a>
## func [CountStartingSpaces](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L33>)
```go
func CountStartingSpaces(line string) int
@@ -16,8 +25,17 @@ func CountStartingSpaces(line string) int
CountStartingSpaces counts the number of spaces at the beginning of a string.
<a name="EncodeBasicYaml"></a>
## func [EncodeBasicYaml](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L179>)
```go
func EncodeBasicYaml(data any) ([]byte, error)
```
EncodeBasicYaml encodes a basic yaml from an interface.
<a name="GetContainerByName"></a>
## func [GetContainerByName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L82>)
## func [GetContainerByName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L84>)
```go
func GetContainerByName(name string, containers []corev1.Container) (*corev1.Container, int)
@@ -26,7 +44,7 @@ func GetContainerByName(name string, containers []corev1.Container) (*corev1.Con
GetContainerByName returns a container by name and its index in the array. It returns nil, \-1 if not found.
<a name="GetKind"></a>
## func [GetKind](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L44>)
## func [GetKind](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L46>)
```go
func GetKind(path string) (kind string)
@@ -35,7 +53,7 @@ func GetKind(path string) (kind string)
GetKind returns the kind of the resource from the file path.
<a name="GetServiceNameByPort"></a>
## func [GetServiceNameByPort](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L72>)
## func [GetServiceNameByPort](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L74>)
```go
func GetServiceNameByPort(port int) string
@@ -44,7 +62,7 @@ func GetServiceNameByPort(port int) string
GetServiceNameByPort returns the service name for a port. It the service name is not found, it returns an empty string.
<a name="GetValuesFromLabel"></a>
## func [GetValuesFromLabel](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L122>)
## func [GetValuesFromLabel](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L124>)
```go
func GetValuesFromLabel(service types.ServiceConfig, LabelValues string) map[string]*EnvConfig
@@ -62,7 +80,7 @@ func HashComposefiles(files []string) (string, error)
HashComposefiles returns a hash of the compose files.
<a name="Int32Ptr"></a>
## func [Int32Ptr](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L25>)
## func [Int32Ptr](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L27>)
```go
func Int32Ptr(i int32) *int32
@@ -71,7 +89,7 @@ func Int32Ptr(i int32) *int32
Int32Ptr returns a pointer to an int32.
<a name="MapKeys"></a>
## func [MapKeys](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L156>)
## func [MapKeys](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L158>)
```go
func MapKeys(m map[string]interface{}) []string
@@ -80,7 +98,7 @@ func MapKeys(m map[string]interface{}) []string
<a name="PathToName"></a>
## func [PathToName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L101>)
## func [PathToName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L103>)
```go
func PathToName(path string) string
@@ -89,7 +107,7 @@ func PathToName(path string) string
PathToName converts a path to a kubernetes complient name.
<a name="StrPtr"></a>
## func [StrPtr](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L28>)
## func [StrPtr](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L30>)
```go
func StrPtr(s string) *string
@@ -98,7 +116,7 @@ func StrPtr(s string) *string
StrPtr returns a pointer to a string.
<a name="TplName"></a>
## func [TplName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L17>)
## func [TplName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L19>)
```go
func TplName(serviceName, appname string, suffix ...string) string
@@ -107,7 +125,7 @@ func TplName(serviceName, appname string, suffix ...string) string
TplName returns the name of the kubernetes resource as a template string. It is used in the templates and defined in \_helper.tpl file.
<a name="TplValue"></a>
## func [TplValue](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L92>)
## func [TplValue](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L94>)
```go
func TplValue(serviceName, variable string, pipes ...string) string
@@ -125,7 +143,7 @@ func Warn(msg ...interface{})
Warn prints a warning message
<a name="WordWrap"></a>
## func [WordWrap](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L152>)
## func [WordWrap](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L154>)
```go
func WordWrap(text string, lineWidth int) string
@@ -134,7 +152,7 @@ func WordWrap(text string, lineWidth int) string
WordWrap wraps a string to a given line width. Warning: it may break the string. You need to check the result.
<a name="Wrap"></a>
## func [Wrap](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L61>)
## func [Wrap](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L63>)
```go
func Wrap(src, above, below string) string
@@ -143,7 +161,7 @@ func Wrap(src, above, below string) string
Wrap wraps a string with a string above and below. It will respect the indentation of the src string.
<a name="WrapBytes"></a>
## func [WrapBytes](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L67>)
## func [WrapBytes](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L69>)
```go
func WrapBytes(src, above, below []byte) []byte
@@ -152,14 +170,14 @@ func WrapBytes(src, above, below []byte) []byte
WrapBytes wraps a byte array with a byte array above and below. It will respect the indentation of the src string.
<a name="EnvConfig"></a>
## type [EnvConfig](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L116-L119>)
## type [EnvConfig](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L118-L121>)
EnvConfig is a struct to hold the description of an environment variable.
```go
type EnvConfig struct {
Description string
Service types.ServiceConfig
Description string
}
```

View File

@@ -77,11 +77,6 @@ h3[id*="katenaryio"] {
}
/*Zoomable images*/
/*[data-md-color-scheme="slate"] #logo {
background-image: url("logo-bright.svg");
}*/
.zoomable svg {
background-color: var(--md-default-bg-color);
padding: 1rem;

View File

@@ -1,30 +1,46 @@
package generator
import "katenary/generator/labelStructs"
import (
"fmt"
"os"
"path/filepath"
"strings"
"katenary/generator/labelStructs"
"katenary/utils"
)
// ConvertOptions are the options to convert a compose project to a helm chart.
type ConvertOptions struct {
AppVersion *string
OutputDir string
ChartVersion string
Profiles []string
Force bool
HelmUpdate bool
}
// ChartTemplate is a template of a chart. It contains the content of the template and the name of the service.
// This is used internally to generate the templates.
//
// TODO: maybe we can set it private.
type ChartTemplate struct {
Content []byte
Servicename string
Content []byte
}
// HelmChart is a Helm Chart representation. It contains all the
// tempaltes, values, versions, helpers...
type HelmChart struct {
Templates map[string]*ChartTemplate `yaml:"-"`
Values map[string]any `yaml:"-"`
VolumeMounts map[string]any `yaml:"-"`
composeHash *string `yaml:"-"`
Name string `yaml:"name"`
ApiVersion string `yaml:"apiVersion"`
Version string `yaml:"version"`
AppVersion string `yaml:"appVersion"`
Description string `yaml:"description"`
Helper string `yaml:"-"`
Dependencies []labelStructs.Dependency `yaml:"dependencies,omitempty"`
Templates map[string]*ChartTemplate `yaml:"-"` // do not export to yaml
Helper string `yaml:"-"` // do not export to yaml
Values map[string]any `yaml:"-"` // do not export to yaml
VolumeMounts map[string]any `yaml:"-"` // do not export to yaml
composeHash *string `yaml:"-"` // do not export to yaml
}
// NewChart creates a new empty chart with the given name.
@@ -42,12 +58,59 @@ func NewChart(name string) *HelmChart {
}
}
// ConvertOptions are the options to convert a compose project to a helm chart.
type ConvertOptions struct {
Force bool // Force the chart directory deletion if it already exists.
OutputDir string // The output directory of the chart.
Profiles []string // Profile to use for the conversion.
HelmUpdate bool // If true, the "helm dep update" command will be run after the chart generation.
AppVersion *string // Set the chart "appVersion" field. If nil, the version will be set to 0.1.0.
ChartVersion string // Set the chart "version" field.
// SaveTemplates the templates of the chart to the given directory.
func (chart *HelmChart) SaveTemplates(templateDir string) {
for name, template := range chart.Templates {
t := template.Content
t = removeNewlinesInsideBrackets(t)
t = removeUnwantedLines(t)
t = addModeline(t)
kind := utils.GetKind(name)
var icon utils.Icon
switch kind {
case "deployment":
icon = utils.IconPackage
case "service":
icon = utils.IconPlug
case "ingress":
icon = utils.IconWorld
case "volumeclaim":
icon = utils.IconCabinet
case "configmap":
icon = utils.IconConfig
case "secret":
icon = utils.IconSecret
default:
icon = utils.IconInfo
}
servicename := template.Servicename
if err := os.MkdirAll(filepath.Join(templateDir, servicename), 0o755); err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
fmt.Println(icon, "Creating", kind, servicename)
// if the name is a path, create the directory
if strings.Contains(name, string(filepath.Separator)) {
name = filepath.Join(templateDir, name)
err := os.MkdirAll(filepath.Dir(name), 0o755)
if err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
} else {
// remove the serivce name from the template name
name = strings.Replace(name, servicename+".", "", 1)
name = filepath.Join(templateDir, servicename, name)
}
f, err := os.Create(name)
if err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
f.Write(t)
f.Close()
}
}

View File

@@ -7,13 +7,13 @@ import (
"regexp"
"strings"
"katenary/generator/labelStructs"
"katenary/utils"
"github.com/compose-spec/compose-go/types"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
"katenary/generator/labelStructs"
"katenary/utils"
)
// only used to check interface implementation
@@ -23,7 +23,7 @@ var (
)
// NewFileMap creates a new DataMap from a compose service. The appName is the name of the application taken from the project name.
func NewFileMap(service types.ServiceConfig, appName string, kind string) DataMap {
func NewFileMap(service types.ServiceConfig, appName, kind string) DataMap {
switch kind {
case "configmap":
return NewConfigMap(service, appName)
@@ -47,8 +47,8 @@ const (
type ConfigMap struct {
*corev1.ConfigMap
service *types.ServiceConfig
usage FileMapUsage
path string
usage FileMapUsage
}
// NewConfigMap creates a new ConfigMap from a compose service. The appName is the name of the application taken from the project name.
@@ -75,14 +75,14 @@ func NewConfigMap(service types.ServiceConfig, appName string) *ConfigMap {
}
// get the secrets from the labels
if secrets, err := labelStructs.SecretsFrom(service.Labels[LabelSecrets]); err != nil {
secrets, err := labelStructs.SecretsFrom(service.Labels[LabelSecrets])
if err != nil {
log.Fatal(err)
} else {
}
// drop the secrets from the environment
for _, secret := range secrets {
drop[secret] = true
}
}
// get the label values from the labels
varDescriptons := utils.GetValuesFromLabel(service, LabelValues)
for value := range varDescriptons {
@@ -95,7 +95,6 @@ func NewConfigMap(service types.ServiceConfig, appName string) *ConfigMap {
done[value] = true
continue
}
// val := `{{ tpl .Values.` + service.Name + `.environment.` + value + ` $ }}`
val := utils.TplValue(service.Name, "environment."+value)
service.Environment[value] = &val
}
@@ -112,10 +111,9 @@ func NewConfigMap(service types.ServiceConfig, appName string) *ConfigMap {
}
}
for key, env := range service.Environment {
if _, ok := done[key]; ok {
continue
}
if _, ok := drop[key]; ok {
_, isDropped := drop[key]
_, isDone := done[key]
if isDropped || isDone {
continue
}
cm.AddData(key, *env)
@@ -127,7 +125,7 @@ func NewConfigMap(service types.ServiceConfig, appName string) *ConfigMap {
// NewConfigMapFromDirectory creates a new ConfigMap from a compose service. This path is the path to the
// file or directory. If the path is a directory, all files in the directory are added to the ConfigMap.
// Each subdirectory are ignored. Note that the Generate() function will create the subdirectories ConfigMaps.
func NewConfigMapFromDirectory(service types.ServiceConfig, appName string, path string) *ConfigMap {
func NewConfigMapFromDirectory(service types.ServiceConfig, appName, path string) *ConfigMap {
normalized := path
normalized = strings.TrimLeft(normalized, ".")
normalized = strings.TrimLeft(normalized, "/")
@@ -163,7 +161,7 @@ func (c *ConfigMap) SetData(data map[string]string) {
}
// AddData adds a key value pair to the configmap. Append or overwrite the value if the key already exists.
func (c *ConfigMap) AddData(key string, value string) {
func (c *ConfigMap) AddData(key, value string) {
c.Data[key] = value
}

View File

@@ -12,13 +12,12 @@ import (
"strings"
"time"
"github.com/compose-spec/compose-go/types"
"katenary/generator/extrafiles"
"katenary/generator/labelStructs"
"katenary/parser"
"katenary/utils"
"github.com/compose-spec/compose-go/types"
goyaml "gopkg.in/yaml.v3"
)
const headerHelp = `# This file is autogenerated by katenary
@@ -76,10 +75,11 @@ func Convert(config ConvertOptions, dockerComposeFile ...string) {
// check if the chart directory exists
// if yes, prevent the user from overwriting it and ask for confirmation
if _, err := os.Stat(config.OutputDir); err == nil {
fmt.Print(utils.IconWarning, " The chart directory "+config.OutputDir+" already exists, do you want to overwrite it? [y/N] ")
var answer string
fmt.Scanln(&answer)
if strings.ToLower(answer) != "y" {
overwrite := utils.Confirm(
"The chart directory "+config.OutputDir+" already exists, do you want to overwrite it?",
utils.IconWarning,
)
if !overwrite {
fmt.Println("Aborting")
os.Exit(126) // 126 is the exit code for "Command invoked cannot execute"
}
@@ -109,168 +109,27 @@ func Convert(config ConvertOptions, dockerComposeFile ...string) {
os.Exit(1)
}
for name, template := range chart.Templates {
t := template.Content
t = removeNewlinesInsideBrackets(t)
t = removeUnwantedLines(t)
t = addModeline(t)
// write the templates to the disk
chart.SaveTemplates(templateDir)
kind := utils.GetKind(name)
var icon utils.Icon
switch kind {
case "deployment":
icon = utils.IconPackage
case "service":
icon = utils.IconPlug
case "ingress":
icon = utils.IconWorld
case "volumeclaim":
icon = utils.IconCabinet
case "configmap":
icon = utils.IconConfig
case "secret":
icon = utils.IconSecret
default:
icon = utils.IconInfo
}
// write the Chart.yaml file
buildCharYamlFile(chart, project, chartPath)
servicename := template.Servicename
if err := os.MkdirAll(filepath.Join(templateDir, servicename), 0o755); err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
fmt.Println(icon, "Creating", kind, servicename)
// if the name is a path, create the directory
if strings.Contains(name, string(filepath.Separator)) {
name = filepath.Join(templateDir, name)
err := os.MkdirAll(filepath.Dir(name), 0o755)
if err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
} else {
// remove the serivce name from the template name
name = strings.Replace(name, servicename+".", "", 1)
name = filepath.Join(templateDir, servicename, name)
}
f, err := os.Create(name)
if err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
// build and write the values.yaml file
buildValues(chart, project, valuesPath)
f.Write(t)
f.Close()
}
// calculate the sha1 hash of the services
buf := bytes.NewBuffer(nil)
encoder := goyaml.NewEncoder(buf)
encoder.SetIndent(2)
if err := encoder.Encode(chart); err != nil {
fmt.Println(err)
os.Exit(1)
}
yamlChart := buf.Bytes()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// concat chart adding a comment with hash of services on top
yamlChart = append([]byte(fmt.Sprintf("# compose hash (sha1): %s\n", *chart.composeHash)), yamlChart...)
// add the list of compose files
files := []string{}
for _, file := range project.ComposeFiles {
base := filepath.Base(file)
files = append(files, base)
}
yamlChart = append([]byte(fmt.Sprintf("# compose files: %s\n", strings.Join(files, ", "))), yamlChart...)
// add generated date
yamlChart = append([]byte(fmt.Sprintf("# generated at: %s\n", time.Now().Format(time.RFC3339))), yamlChart...)
// document Chart.yaml file
yamlChart = addChartDoc(yamlChart, project)
f, err := os.Create(chartPath)
if err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
f.Write(yamlChart)
f.Close()
buf.Reset()
encoder = goyaml.NewEncoder(buf)
encoder.SetIndent(2)
if err = encoder.Encode(&chart.Values); err != nil {
fmt.Println(err)
os.Exit(1)
}
values := buf.Bytes()
values = addDescriptions(values, *project)
values = addDependencyDescription(values, chart.Dependencies)
values = addCommentsToValues(values)
values = addStorageClassHelp(values)
values = addImagePullSecretsHelp(values)
values = addImagePullPolicyHelp(values)
values = addVariablesDoc(values, project)
values = addMainTagAppDoc(values, project)
values = addResourceHelp(values)
values = addYAMLSelectorPath(values)
values = append([]byte(headerHelp), values...)
f, err = os.Create(valuesPath)
if err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
f.Write(values)
f.Close()
f, err = os.Create(helpersPath)
if err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
f.Write([]byte(chart.Helper))
f.Close()
// write the _helpers.tpl to the disk
writeContent(helpersPath, []byte(chart.Helper))
// write the readme to the disk
readme := extrafiles.ReadMeFile(chart.Name, chart.Description, chart.Values)
f, err = os.Create(readmePath)
if err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
f.Write([]byte(readme))
f.Close()
writeContent(readmePath, []byte(readme))
services := make([]string, 0)
for _, service := range project.Services {
services = append(services, service.Name)
}
notes := extrafiles.NotesFile(services)
f, err = os.Create(notesPath)
if err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
f.Write([]byte(notes))
f.Close()
// get the list of services to write in the notes
buildNotesFile(project, notesPath)
executeAndHandleError := func(fn func(ConvertOptions) error, config ConvertOptions, message string) {
if err := fn(config); err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
fmt.Println(utils.IconSuccess, message)
}
if config.HelmUpdate {
executeAndHandleError(helmUpdate, config, "Helm dependencies updated")
executeAndHandleError(helmLint, config, "Helm chart linted")
fmt.Println(utils.IconSuccess, "Helm chart created successfully")
}
// call helm update if needed
callHelmUpdate(config)
}
const ingressClassHelp = `# Default value for ingress.class annotation
@@ -501,13 +360,21 @@ func addResourceHelp(values []byte) []byte {
func addVariablesDoc(values []byte, project *types.Project) []byte {
lines := strings.Split(string(values), "\n")
currentService := ""
for _, service := range project.Services {
lines = addDocToVariable(service, lines)
}
return []byte(strings.Join(lines, "\n"))
}
func addDocToVariable(service types.ServiceConfig, lines []string) []string {
currentService := ""
variables := utils.GetValuesFromLabel(service, LabelValues)
for i, line := range lines {
// if the line is a service, it is a name followed by a colon
if regexp.MustCompile(`(?m)^` + service.Name + `:`).MatchString(line) {
currentService = service.Name
}
// for each variable in the service, add the description
for varname, variable := range variables {
if variable == nil {
continue
@@ -524,8 +391,7 @@ func addVariablesDoc(values []byte, project *types.Project) []byte {
}
}
}
}
return []byte(strings.Join(lines, "\n"))
return lines
}
const mainTagAppDoc = `This is the version of the main application.
@@ -535,8 +401,6 @@ func addMainTagAppDoc(values []byte, project *types.Project) []byte {
lines := strings.Split(string(values), "\n")
for _, service := range project.Services {
inService := false
inRegistry := false
// read the label LabelMainApp
if v, ok := service.Labels[LabelMainApp]; !ok {
continue
@@ -546,6 +410,15 @@ func addMainTagAppDoc(values []byte, project *types.Project) []byte {
fmt.Printf("%s Adding main tag app doc %s\n", utils.IconConfig, service.Name)
}
lines = addMainAppDoc(lines, service)
}
return []byte(strings.Join(lines, "\n"))
}
func addMainAppDoc(lines []string, service types.ServiceConfig) []string {
inService := false
inRegistry := false
for i, line := range lines {
if regexp.MustCompile(`^` + service.Name + `:`).MatchString(line) {
inService = true
@@ -564,9 +437,7 @@ func addMainTagAppDoc(values []byte, project *types.Project) []byte {
}
}
}
}
return []byte(strings.Join(lines, "\n"))
return lines
}
func removeNewlinesInsideBrackets(values []byte) []byte {
@@ -715,3 +586,89 @@ func addYAMLSelectorPath(values []byte) []byte {
}
return []byte(strings.Join(toReturn, "\n"))
}
func writeContent(path string, content []byte) {
f, err := os.Create(path)
if err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
defer f.Close()
f.Write(content)
}
func buildValues(chart *HelmChart, project *types.Project, valuesPath string) {
values, err := utils.EncodeBasicYaml(&chart.Values)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
values = addDescriptions(values, *project)
values = addDependencyDescription(values, chart.Dependencies)
values = addCommentsToValues(values)
values = addStorageClassHelp(values)
values = addImagePullSecretsHelp(values)
values = addImagePullPolicyHelp(values)
values = addVariablesDoc(values, project)
values = addMainTagAppDoc(values, project)
values = addResourceHelp(values)
values = addYAMLSelectorPath(values)
values = append([]byte(headerHelp), values...)
// add vim modeline
values = append(values, []byte("\n# vim: ft=yaml\n")...)
// write the values to the disk
writeContent(valuesPath, values)
}
func buildNotesFile(project *types.Project, notesPath string) {
// get the list of services to write in the notes
services := make([]string, 0)
for _, service := range project.Services {
services = append(services, service.Name)
}
// write the notes to the disk
notes := extrafiles.NotesFile(services)
writeContent(notesPath, []byte(notes))
}
func buildCharYamlFile(chart *HelmChart, project *types.Project, chartPath string) {
// calculate the sha1 hash of the services
yamlChart, err := utils.EncodeBasicYaml(chart)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// concat chart adding a comment with hash of services on top
yamlChart = append([]byte(fmt.Sprintf("# compose hash (sha1): %s\n", *chart.composeHash)), yamlChart...)
// add the list of compose files
files := []string{}
for _, file := range project.ComposeFiles {
base := filepath.Base(file)
files = append(files, base)
}
yamlChart = append([]byte(fmt.Sprintf("# compose files: %s\n", strings.Join(files, ", "))), yamlChart...)
// add generated date
yamlChart = append([]byte(fmt.Sprintf("# generated at: %s\n", time.Now().Format(time.RFC3339))), yamlChart...)
// document Chart.yaml file
yamlChart = addChartDoc(yamlChart, project)
writeContent(chartPath, yamlChart)
}
func callHelmUpdate(config ConvertOptions) {
executeAndHandleError := func(fn func(ConvertOptions) error, config ConvertOptions, message string) {
if err := fn(config); err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
}
fmt.Println(utils.IconSuccess, message)
}
if config.HelmUpdate {
executeAndHandleError(helmUpdate, config, "Helm dependencies updated")
executeAndHandleError(helmLint, config, "Helm chart linted")
fmt.Println(utils.IconSuccess, "Helm chart created successfully")
}
}

View File

@@ -4,14 +4,14 @@ import (
"log"
"strings"
"katenary/generator/labelStructs"
"katenary/utils"
"github.com/compose-spec/compose-go/types"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
"katenary/generator/labelStructs"
"katenary/utils"
)
// only used to check interface implementation

View File

@@ -12,7 +12,7 @@ import (
)
func TestBasicCronJob(t *testing.T) {
compose_file := `
composeFile := `
services:
cron:
image: fedora
@@ -23,7 +23,7 @@ services:
schedule: "*/1 * * * *"
rbac: false
`
tmpDir := setup(compose_file)
tmpDir := setup(composeFile)
defer teardown(tmpDir)
currentDir, _ := os.Getwd()
@@ -64,7 +64,7 @@ services:
}
func TestCronJobbWithRBAC(t *testing.T) {
compose_file := `
composeFile := `
services:
cron:
image: fedora
@@ -76,7 +76,7 @@ services:
rbac: true
`
tmpDir := setup(compose_file)
tmpDir := setup(composeFile)
defer teardown(tmpDir)
currentDir, _ := os.Getwd()

View File

@@ -9,14 +9,14 @@ import (
"strings"
"time"
"katenary/generator/labelStructs"
"katenary/utils"
"github.com/compose-spec/compose-go/types"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
"katenary/generator/labelStructs"
"katenary/utils"
)
var _ Yaml = (*Deployment)(nil)
@@ -204,13 +204,16 @@ func (d *Deployment) AddVolumes(service types.ServiceConfig, appName string) {
isSamePod = v != ""
}
for _, volume := range service.Volumes {
d.bindVolumes(volume, isSamePod, tobind, service, appName)
}
}
func (d *Deployment) bindVolumes(volume types.ServiceVolumeConfig, isSamePod bool, tobind map[string]bool, service types.ServiceConfig, appName string) {
container, index := utils.GetContainerByName(service.Name, d.Spec.Template.Spec.Containers)
defer func(d *Deployment, container *corev1.Container, index int) {
d.Spec.Template.Spec.Containers[index] = *container
}(d, container, index)
for _, volume := range service.Volumes {
// not declared as a bind volume, skip
if _, ok := tobind[volume.Source]; !isSamePod && volume.Type == "bind" && !ok {
utils.Warn(
"Bind volumes are not supported yet, " +
@@ -219,18 +222,18 @@ func (d *Deployment) AddVolumes(service types.ServiceConfig, appName string) {
", skipping volume " + volume.Source +
" from service " + service.Name,
)
continue
return
}
if container == nil {
utils.Warn("Container not found for volume", volume.Source)
continue
return
}
// ensure that the volume is not already present in the container
for _, vm := range container.VolumeMounts {
if vm.Name == volume.Source {
continue
return
}
}
@@ -263,6 +266,14 @@ func (d *Deployment) AddVolumes(service types.ServiceConfig, appName string) {
}
if stat.IsDir() {
d.appendDirectoryToConfigMap(service, appName, volume)
} else {
d.appendFileToConfigMap(service, appName, volume)
}
}
}
func (d *Deployment) appendDirectoryToConfigMap(service types.ServiceConfig, appName string, volume types.ServiceVolumeConfig) {
pathnme := utils.PathToName(volume.Source)
if _, ok := d.configMaps[pathnme]; !ok {
d.configMaps[pathnme] = &ConfigMapMount{
@@ -282,7 +293,9 @@ func (d *Deployment) AddVolumes(service types.ServiceConfig, appName string) {
mountPath: volume.Target,
}),
}
} else {
}
func (d *Deployment) appendFileToConfigMap(service types.ServiceConfig, appName string, volume types.ServiceVolumeConfig) {
// In case of a file, add it to the configmap and use "subPath" to mount it
// Note that the volumes and volume mounts are not added to the deployment yet, they will be added later
// in generate.go
@@ -312,9 +325,6 @@ func (d *Deployment) AddVolumes(service types.ServiceConfig, appName string) {
}
cm.AppendFile(volume.Source)
}
}
}
}
func (d *Deployment) BindFrom(service types.ServiceConfig, binded *Deployment) {

View File

@@ -10,20 +10,22 @@ import (
"sigs.k8s.io/yaml"
)
const webTemplateOutput = `templates/web/deployment.yaml`
func TestGenerate(t *testing.T) {
compose_file := `
composeFile := `
services:
web:
image: nginx:1.29
`
tmpDir := setup(compose_file)
tmpDir := setup(composeFile)
defer teardown(tmpDir)
currentDir, _ := os.Getwd()
os.Chdir(tmpDir)
defer os.Chdir(currentDir)
output := _compile_test(t, "-s", "templates/web/deployment.yaml")
output := _compile_test(t, "-s", webTemplateOutput)
// dt := DeploymentTest{}
dt := v1.Deployment{}
@@ -42,7 +44,7 @@ services:
}
func TestGenerateOneDeploymentWithSamePod(t *testing.T) {
compose_file := `
composeFile := `
services:
web:
image: nginx:1.29
@@ -57,14 +59,15 @@ services:
katenary.v3/same-pod: web
`
tmpDir := setup(compose_file)
outDir := "./chart"
tmpDir := setup(composeFile)
defer teardown(tmpDir)
currentDir, _ := os.Getwd()
os.Chdir(tmpDir)
defer os.Chdir(currentDir)
output := _compile_test(t, "-s", "templates/web/deployment.yaml")
output := _compile_test(t, "-s", webTemplateOutput)
dt := v1.Deployment{}
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
t.Errorf(unmarshalError, err)
@@ -76,8 +79,8 @@ services:
// endsure that the fpm service is not created
var err error
output, err = helmTemplate(ConvertOptions{
OutputDir: "./chart",
_, err = helmTemplate(ConvertOptions{
OutputDir: outDir,
}, "-s", "templates/fpm/deployment.yaml")
if err == nil {
t.Errorf("Expected error, got nil")
@@ -85,7 +88,7 @@ services:
// ensure that the web service is created and has got 2 ports
output, err = helmTemplate(ConvertOptions{
OutputDir: "./chart",
OutputDir: outDir,
}, "-s", "templates/web/service.yaml")
if err != nil {
t.Errorf("Error: %s", err)
@@ -101,7 +104,7 @@ services:
}
func TestDependsOn(t *testing.T) {
compose_file := `
composeFile := `
services:
web:
image: nginx:1.29
@@ -115,14 +118,14 @@ services:
ports:
- 3306:3306
`
tmpDir := setup(compose_file)
tmpDir := setup(composeFile)
defer teardown(tmpDir)
currentDir, _ := os.Getwd()
os.Chdir(tmpDir)
defer os.Chdir(currentDir)
output := _compile_test(t, "-s", "templates/web/deployment.yaml")
output := _compile_test(t, "-s", webTemplateOutput)
dt := v1.Deployment{}
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
t.Errorf(unmarshalError, err)
@@ -138,7 +141,7 @@ services:
}
func TestHelmDependencies(t *testing.T) {
compose_file := `
composeFile := `
services:
web:
image: nginx:1.29
@@ -156,15 +159,15 @@ services:
version: 18.x.X
`
compose_file = fmt.Sprintf(compose_file, Prefix())
tmpDir := setup(compose_file)
composeFile = fmt.Sprintf(composeFile, Prefix())
tmpDir := setup(composeFile)
defer teardown(tmpDir)
currentDir, _ := os.Getwd()
os.Chdir(tmpDir)
defer os.Chdir(currentDir)
output := _compile_test(t, "-s", "templates/web/deployment.yaml")
output := _compile_test(t, "-s", webTemplateOutput)
dt := v1.Deployment{}
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
t.Errorf(unmarshalError, err)
@@ -198,7 +201,7 @@ services:
}
func TestLivenessProbesFromHealthCheck(t *testing.T) {
compose_file := `
composeFile := `
services:
web:
image: nginx:1.29
@@ -210,14 +213,14 @@ services:
timeout: 3s
retries: 3
`
tmpDir := setup(compose_file)
tmpDir := setup(composeFile)
defer teardown(tmpDir)
currentDir, _ := os.Getwd()
os.Chdir(tmpDir)
defer os.Chdir(currentDir)
output := _compile_test(t, "-s", "templates/web/deployment.yaml")
output := _compile_test(t, "-s", webTemplateOutput)
dt := v1.Deployment{}
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
t.Errorf(unmarshalError, err)
@@ -229,7 +232,7 @@ services:
}
func TestProbesFromLabels(t *testing.T) {
compose_file := `
composeFile := `
services:
web:
image: nginx:1.29
@@ -246,15 +249,15 @@ services:
path: /ready
port: 80
`
compose_file = fmt.Sprintf(compose_file, Prefix())
tmpDir := setup(compose_file)
composeFile = fmt.Sprintf(composeFile, Prefix())
tmpDir := setup(composeFile)
defer teardown(tmpDir)
currentDir, _ := os.Getwd()
os.Chdir(tmpDir)
defer os.Chdir(currentDir)
output := _compile_test(t, "-s", "templates/web/deployment.yaml")
output := _compile_test(t, "-s", webTemplateOutput)
dt := v1.Deployment{}
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
t.Errorf(unmarshalError, err)
@@ -280,7 +283,7 @@ services:
}
func TestSetValues(t *testing.T) {
compose_file := `
composeFile := `
services:
web:
image: nginx:1.29
@@ -292,15 +295,15 @@ services:
- FOO
`
compose_file = fmt.Sprintf(compose_file, Prefix())
tmpDir := setup(compose_file)
composeFile = fmt.Sprintf(composeFile, Prefix())
tmpDir := setup(composeFile)
defer teardown(tmpDir)
currentDir, _ := os.Getwd()
os.Chdir(tmpDir)
defer os.Chdir(currentDir)
output := _compile_test(t, "-s", "templates/web/deployment.yaml")
output := _compile_test(t, "-s", webTemplateOutput)
dt := v1.Deployment{}
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
t.Errorf(unmarshalError, err)

View File

@@ -2,13 +2,12 @@ package extrafiles
import (
"bytes"
_ "embed"
"fmt"
"sort"
"strings"
"text/template"
_ "embed"
"gopkg.in/yaml.v3"
)

View File

@@ -1,7 +1,5 @@
package generator
// TODO: configmap from files 20%
import (
"bytes"
"fmt"
@@ -10,11 +8,11 @@ import (
"strconv"
"strings"
"katenary/generator/labelStructs"
"katenary/utils"
"github.com/compose-spec/compose-go/types"
corev1 "k8s.io/api/core/v1"
"katenary/generator/labelStructs"
"katenary/utils"
)
// Generate a chart from a compose project.
@@ -388,7 +386,7 @@ func buildVolumes(service types.ServiceConfig, chart *HelmChart, deployments map
y, _ := pvc.Yaml()
chart.Templates[pvc.Filename()] = &ChartTemplate{
Content: y,
Servicename: service.Name, // TODO, use name
Servicename: service.Name,
}
}
}

View File

@@ -4,13 +4,13 @@ import (
"log"
"strings"
"katenary/generator/labelStructs"
"katenary/utils"
"github.com/compose-spec/compose-go/types"
networkv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
"katenary/generator/labelStructs"
"katenary/utils"
)
var _ Yaml = (*Ingress)(nil)

View File

@@ -10,9 +10,9 @@ import (
"text/tabwriter"
"text/template"
"katenary/utils"
"sigs.k8s.io/yaml"
"katenary/utils"
)
var (

View File

@@ -8,6 +8,8 @@ import (
var testingKatenaryPrefix = Prefix()
const mainAppLabel = "main-app"
func TestPrefix(t *testing.T) {
tests := []struct {
name string
@@ -27,7 +29,7 @@ func TestPrefix(t *testing.T) {
}
}
func Test_labelName(t *testing.T) {
func TestLabelName(t *testing.T) {
type args struct {
name string
}
@@ -39,9 +41,9 @@ func Test_labelName(t *testing.T) {
{
name: "Test_labelName",
args: args{
name: "main-app",
name: mainAppLabel,
},
want: testingKatenaryPrefix + "/main-app",
want: testingKatenaryPrefix + "/" + mainAppLabel,
},
}
for _, tt := range tests {
@@ -65,7 +67,7 @@ func TestGetLabelHelp(t *testing.T) {
}
func TestGetLabelHelpFor(t *testing.T) {
help := GetLabelHelpFor("main-app", false)
help := GetLabelHelpFor(mainAppLabel, false)
if help == "" {
t.Errorf("GetLabelHelpFor() = %v, want %v", help, "Help")
}

View File

@@ -1,13 +1,13 @@
package generator
import (
"katenary/utils"
"github.com/compose-spec/compose-go/types"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
"katenary/utils"
)
var (

View File

@@ -5,12 +5,12 @@ import (
"fmt"
"strings"
"katenary/utils"
"github.com/compose-spec/compose-go/types"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
"katenary/utils"
)
var (
@@ -84,7 +84,7 @@ func (s *Secret) SetData(data map[string]string) {
}
// AddData adds a key value pair to the secret.
func (s *Secret) AddData(key string, value string) {
func (s *Secret) AddData(key, value string) {
if value == "" {
return
}

View File

@@ -4,13 +4,13 @@ import (
"regexp"
"strings"
"katenary/utils"
"github.com/compose-spec/compose-go/types"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/yaml"
"katenary/utils"
)
var _ Yaml = (*Service)(nil)

View File

@@ -6,9 +6,6 @@ import (
"github.com/compose-spec/compose-go/types"
)
// Values is a map of all values for all services. Written to values.yaml.
// var Values = map[string]any{}
// RepositoryValue is a docker repository image and tag that will be saved in values.yaml.
type RepositoryValue struct {
Image string `yaml:"image"`
@@ -17,19 +14,19 @@ type RepositoryValue struct {
// PersistenceValue is a persistence configuration that will be saved in values.yaml.
type PersistenceValue struct {
Enabled bool `yaml:"enabled"`
StorageClass string `yaml:"storageClass"`
Size string `yaml:"size"`
AccessMode []string `yaml:"accessMode"`
Enabled bool `yaml:"enabled"`
}
// IngressValue is a ingress configuration that will be saved in values.yaml.
type IngressValue struct {
Enabled bool `yaml:"enabled"`
Annotations map[string]string `yaml:"annotations"`
Host string `yaml:"host"`
Path string `yaml:"path"`
Class string `yaml:"class"`
Annotations map[string]string `yaml:"annotations"`
Enabled bool `yaml:"enabled"`
}
// Value will be saved in values.yaml. It contains configuraiton for all deployment and services.
@@ -37,13 +34,13 @@ type Value struct {
Repository *RepositoryValue `yaml:"repository,omitempty"`
Persistence map[string]*PersistenceValue `yaml:"persistence,omitempty"`
Ingress *IngressValue `yaml:"ingress,omitempty"`
ImagePullPolicy string `yaml:"imagePullPolicy,omitempty"`
Environment map[string]any `yaml:"environment,omitempty"`
Replicas *uint32 `yaml:"replicas,omitempty"`
CronJob *CronJobValue `yaml:"cronjob,omitempty"`
NodeSelector map[string]string `yaml:"nodeSelector"`
ServiceAccount string `yaml:"serviceAccount"`
Resources map[string]any `yaml:"resources"`
ImagePullPolicy string `yaml:"imagePullPolicy,omitempty"`
ServiceAccount string `yaml:"serviceAccount"`
}
// CronJobValue is a cronjob configuration that will be saved in values.yaml.

View File

@@ -3,17 +3,19 @@ package generator
import (
"strings"
"katenary/utils"
"github.com/compose-spec/compose-go/types"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
"katenary/utils"
)
var _ Yaml = (*VolumeClaim)(nil)
const persistenceKey = "persistence"
// VolumeClaim is a kubernetes VolumeClaim. This is a PersistentVolumeClaim.
type VolumeClaim struct {
*v1.PersistentVolumeClaim
@@ -41,7 +43,12 @@ func NewVolumeClaim(service types.ServiceConfig, volumeName, appName string) *Vo
AccessModes: []v1.PersistentVolumeAccessMode{
v1.ReadWriteOnce,
},
StorageClassName: utils.StrPtr(`{{ .Values.` + service.Name + `.persistence.` + volumeName + `.storageClass }}`),
StorageClassName: utils.StrPtr(
`{{ .Values.` +
service.Name +
"." + persistenceKey +
"." + volumeName + `.storageClass }}`,
),
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: resource.MustParse("1Gi"),
@@ -69,7 +76,7 @@ func (v *VolumeClaim) Yaml() ([]byte, error) {
strings.Replace(
string(out),
"1Gi",
utils.TplValue(serviceName, "persistence."+volumeName+".size"),
utils.TplValue(serviceName, persistenceKey+"."+volumeName+".size"),
1,
),
)
@@ -80,8 +87,8 @@ func (v *VolumeClaim) Yaml() ([]byte, error) {
"- ReadWriteOnce",
"{{- .Values."+
serviceName+
".persistence."+
volumeName+
"."+persistenceKey+
"."+volumeName+
".accessMode | toYaml | nindent __indent__ }}",
1,
),
@@ -92,7 +99,10 @@ func (v *VolumeClaim) Yaml() ([]byte, error) {
if strings.Contains(line, "storageClass") {
lines[i] = utils.Wrap(
line,
"{{- if ne .Values."+serviceName+".persistence."+volumeName+".storageClass \"-\" }}",
"{{- if ne .Values."+
serviceName+
"."+persistenceKey+
"."+volumeName+".storageClass \"-\" }}",
"{{- end }}",
)
}
@@ -103,8 +113,8 @@ func (v *VolumeClaim) Yaml() ([]byte, error) {
out = []byte(
"{{- if .Values." +
serviceName +
".persistence." +
volumeName +
"." + persistenceKey +
"." + volumeName +
".enabled }}\n" +
string(out) +
"\n{{- end }}",

View File

@@ -7,7 +7,6 @@ import (
)
func TestDownloadLatestRelease(t *testing.T) {
// Reset the version to test the latest release
Version = "0.0.0"
@@ -17,15 +16,14 @@ func TestDownloadLatestRelease(t *testing.T) {
// Now call the CheckLatestVersion function
version, assets, err := CheckLatestVersion()
if err != nil {
t.Errorf("Error: %s", err)
t.Errorf("Error getting latest version: %s", err)
}
fmt.Println("Version found", version)
// Touch exe binary
f, _ := os.OpenFile(exe, os.O_RDONLY|os.O_CREATE, 0755)
f, _ := os.OpenFile(exe, os.O_RDONLY|os.O_CREATE, 0o755)
f.Write(nil)
f.Close()
@@ -48,5 +46,4 @@ func TestAlreadyUpToDate(t *testing.T) {
}
t.Log("Version is already the most recent", version)
}

View File

@@ -1,6 +1,8 @@
package utils
import (
"bytes"
"fmt"
"log"
"path/filepath"
"strings"
@@ -114,8 +116,8 @@ func PathToName(path string) string {
// EnvConfig is a struct to hold the description of an environment variable.
type EnvConfig struct {
Description string
Service types.ServiceConfig
Description string
}
// GetValuesFromLabel returns a map of values from a label.
@@ -160,3 +162,27 @@ func MapKeys(m map[string]interface{}) []string {
}
return keys
}
// Confirm asks a question and returns true if the answer is y.
func Confirm(question string, icon ...Icon) bool {
if len(icon) > 0 {
fmt.Printf("%s %s [y/N] ", icon[0], question)
} else {
fmt.Print(question + " [y/N] ")
}
var response string
fmt.Scanln(&response)
return strings.ToLower(response) == "y"
}
// EncodeBasicYaml encodes a basic yaml from an interface.
func EncodeBasicYaml(data any) ([]byte, error) {
buf := bytes.NewBuffer(nil)
enc := yaml.NewEncoder(buf)
enc.SetIndent(2)
err := enc.Encode(data)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}