diff --git a/.github/workflows/go-test.yaml b/.github/workflows/go-test.yaml index 4b43eed..dc99fbf 100644 --- a/.github/workflows/go-test.yaml +++ b/.github/workflows/go-test.yaml @@ -2,12 +2,15 @@ name: Go-Tests on: pull_request: + types: [opened, synchronize, reopened] branches: - develop push: branches: - master + - main - develop + - 'releases/**' jobs: tests: runs-on: ubuntu-latest @@ -16,12 +19,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: 1.23 - - name: Install Helm - run: | - curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 - chmod 700 get_helm.sh - ./get_helm.sh + go-version: 1.24 - name: Launch Test run: | go mod tidy @@ -33,15 +31,18 @@ jobs: coverprofile.out gotest.json sonar: + permissions: + contents: read + pull-requests: read runs-on: ubuntu-latest needs: tests steps: - - uses: actions/checkout@v4 - - uses: actions/download-artifact@v4 - with: - name: tests-results - - name: SonarCloud Scan - uses: SonarSource/sonarcloud-github-action@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + name: tests-results + - name: SonarQube Scan + uses: SonarSource/sonarqube-scan-action@v5.2.0 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} diff --git a/Makefile b/Makefile index 104a800..2fe526b 100644 --- a/Makefile +++ b/Makefile @@ -242,3 +242,16 @@ __label_doc: gomarkdoc --repository.default-branch $(shell git branch --show-current) -o doc/docs/packages/$$pack.md $$pack sed -i '/^## Index/,/^##/ { /## Index/d; /^##/! d }' doc/docs/packages/$$pack.md done + + +# Scan the source code. +# - we don't need detection of text/template as it's not a web application, and +# - we don't need sha1 detection as it is not used for cryptographic purposes. +# Note: metrics are actually not sent to anyone - it's a thing that is removed from the code in the future. +sast: + opengrep \ + --config auto \ + --exclude-rule go.lang.security.audit.xss.import-text-template.import-text-template \ + --exclude-rule go.lang.security.audit.crypto.use_of_weak_crypto.use-of-sha1 \ + --metrics=on \ + . diff --git a/cmd/katenary/main.go b/cmd/katenary/main.go index 3276d31..f2810aa 100644 --- a/cmd/katenary/main.go +++ b/cmd/katenary/main.go @@ -146,7 +146,7 @@ func generateConvertCommand() *cobra.Command { Use: "convert", Short: "Converts a docker-compose file to a Helm Chart", RunE: func(cmd *cobra.Command, args []string) error { - if givenAppVersion != "" { + if len(strings.TrimSpace(givenAppVersion)) > 0 { appVersion = &givenAppVersion } return generator.Convert(generator.ConvertOptions{ diff --git a/doc/docs/packages/generator.md b/doc/docs/packages/generator.md index a761861..9b029da 100644 --- a/doc/docs/packages/generator.md +++ b/doc/docs/packages/generator.md @@ -465,7 +465,7 @@ type HelmChart struct { AppVersion string `yaml:"appVersion"` Description string `yaml:"description"` Helper string `yaml:"-"` - Dependencies []labelStructs.Dependency `yaml:"dependencies,omitempty"` + Dependencies []labelstructs.Dependency `yaml:"dependencies,omitempty"` // contains filtered or unexported fields } ``` diff --git a/doc/docs/packages/generator/katenaryfile.md b/doc/docs/packages/generator/katenaryfile.md index 3e1d02e..a2a1824 100644 --- a/doc/docs/packages/generator/katenaryfile.md +++ b/doc/docs/packages/generator/katenaryfile.md @@ -38,20 +38,20 @@ Service is a struct that contains the service configuration for katenary type Service struct { MainApp *bool `json:"main-app,omitempty" jsonschema:"title=Is this service the main application"` Values []StringOrMap `json:"values,omitempty" jsonschema:"description=Environment variables to be set in values.yaml with or without a description"` - Secrets *labelStructs.Secrets `json:"secrets,omitempty" jsonschema:"title=Secrets,description=Environment variables to be set as secrets"` - Ports *labelStructs.Ports `json:"ports,omitempty" jsonschema:"title=Ports,description=Ports to be exposed in services"` - Ingress *labelStructs.Ingress `json:"ingress,omitempty" jsonschema:"title=Ingress,description=Ingress configuration"` - HealthCheck *labelStructs.HealthCheck `json:"health-check,omitempty" jsonschema:"title=Health Check,description=Health check configuration that respects the kubernetes api"` + Secrets *labelstructs.Secrets `json:"secrets,omitempty" jsonschema:"title=Secrets,description=Environment variables to be set as secrets"` + Ports *labelstructs.Ports `json:"ports,omitempty" jsonschema:"title=Ports,description=Ports to be exposed in services"` + Ingress *labelstructs.Ingress `json:"ingress,omitempty" jsonschema:"title=Ingress,description=Ingress configuration"` + HealthCheck *labelstructs.HealthCheck `json:"health-check,omitempty" jsonschema:"title=Health Check,description=Health check configuration that respects the kubernetes api"` SamePod *string `json:"same-pod,omitempty" jsonschema:"title=Same Pod,description=Service that should be in the same pod"` Description *string `json:"description,omitempty" jsonschema:"title=Description,description=Description of the service that will be injected in the values.yaml file"` Ignore *bool `json:"ignore,omitempty" jsonschema:"title=Ignore,description=Ignore the service in the conversion"` - Dependencies []labelStructs.Dependency `json:"dependencies,omitempty" jsonschema:"title=Dependencies,description=Services that should be injected in the Chart.yaml file"` - ConfigMapFile *labelStructs.ConfigMapFile `json:"configmap-files,omitempty" jsonschema:"title=ConfigMap Files,description=Files that should be injected as ConfigMap"` - MapEnv *labelStructs.MapEnv `json:"map-env,omitempty" jsonschema:"title=Map Env,description=Map environment variables to another value"` - CronJob *labelStructs.CronJob `json:"cron-job,omitempty" jsonschema:"title=Cron Job,description=Cron Job configuration"` - EnvFrom *labelStructs.EnvFrom `json:"env-from,omitempty" jsonschema:"title=Env From,description=Inject environment variables from another service"` - ExchangeVolumes []*labelStructs.ExchangeVolume `json:"exchange-volumes,omitempty" jsonschema:"title=Exchange Volumes,description=Exchange volumes between services"` - ValuesFrom *labelStructs.ValueFrom `json:"values-from,omitempty" jsonschema:"title=Values From,description=Inject values from another service (secret or configmap environment variables)"` + Dependencies []labelstructs.Dependency `json:"dependencies,omitempty" jsonschema:"title=Dependencies,description=Services that should be injected in the Chart.yaml file"` + ConfigMapFile *labelstructs.ConfigMapFile `json:"configmap-files,omitempty" jsonschema:"title=ConfigMap Files,description=Files that should be injected as ConfigMap"` + MapEnv *labelstructs.MapEnv `json:"map-env,omitempty" jsonschema:"title=Map Env,description=Map environment variables to another value"` + CronJob *labelstructs.CronJob `json:"cron-job,omitempty" jsonschema:"title=Cron Job,description=Cron Job configuration"` + EnvFrom *labelstructs.EnvFrom `json:"env-from,omitempty" jsonschema:"title=Env From,description=Inject environment variables from another service"` + ExchangeVolumes []*labelstructs.ExchangeVolume `json:"exchange-volumes,omitempty" jsonschema:"title=Exchange Volumes,description=Exchange volumes between services"` + ValuesFrom *labelstructs.ValueFrom `json:"values-from,omitempty" jsonschema:"title=Values From,description=Inject values from another service (secret or configmap environment variables)"` } ``` diff --git a/doc/docs/packages/generator/labels.md b/doc/docs/packages/generator/labels.md index c990ce8..9728333 100644 --- a/doc/docs/packages/generator/labels.md +++ b/doc/docs/packages/generator/labels.md @@ -6,6 +6,8 @@ import "katenary/generator/labels" ``` +Package labels provides functionality to parse and manipulate labels. + ## Constants @@ -21,7 +23,7 @@ const KatenaryLabelPrefix = "katenary.v3" func GetLabelHelp(asMarkdown bool) string ``` -Generate the help for the labels. +GetLabelHelp return the help for the labels. ## func [GetLabelHelpFor]() diff --git a/doc/docs/packages/generator/labels/labelstructs.md b/doc/docs/packages/generator/labels/labelstructs.md new file mode 100644 index 0000000..9170aa6 --- /dev/null +++ b/doc/docs/packages/generator/labels/labelstructs.md @@ -0,0 +1,246 @@ + + +# labelstructs + +```go +import "katenary/generator/labels/labelstructs" +``` + +Package labelstructs is a package that contains the structs used to represent the labels in the yaml files. + +## type [ConfigMapFile]() + + + +```go +type ConfigMapFile []string +``` + + +### func [ConfigMapFileFrom]() + +```go +func ConfigMapFileFrom(data string) (ConfigMapFile, error) +``` + + + + +## type [CronJob]() + + + +```go +type CronJob struct { + Image string `yaml:"image,omitempty" json:"image,omitempty"` + Command string `yaml:"command" json:"command,omitempty"` + Schedule string `yaml:"schedule" json:"schedule,omitempty"` + Rbac bool `yaml:"rbac" json:"rbac,omitempty"` +} +``` + + +### func [CronJobFrom]() + +```go +func CronJobFrom(data string) (*CronJob, error) +``` + + + + +## type [Dependency]() + +Dependency is a dependency of a chart to other charts. + +```go +type Dependency struct { + Values map[string]any `yaml:"-" json:"values,omitempty"` + Name string `yaml:"name" json:"name"` + Version string `yaml:"version" json:"version"` + Repository string `yaml:"repository" json:"repository"` + Alias string `yaml:"alias,omitempty" json:"alias,omitempty"` +} +``` + + +### func [DependenciesFrom]() + +```go +func DependenciesFrom(data string) ([]Dependency, error) +``` + +DependenciesFrom returns a slice of dependencies from the given string. + + +## type [EnvFrom]() + + + +```go +type EnvFrom []string +``` + + +### func [EnvFromFrom]() + +```go +func EnvFromFrom(data string) (EnvFrom, error) +``` + +EnvFromFrom returns a EnvFrom from the given string. + + +## type [ExchangeVolume]() + + + +```go +type ExchangeVolume struct { + Name string `yaml:"name" json:"name"` + MountPath string `yaml:"mountPath" json:"mountPath"` + Type string `yaml:"type,omitempty" json:"type,omitempty"` + Init string `yaml:"init,omitempty" json:"init,omitempty"` +} +``` + + +### func [NewExchangeVolumes]() + +```go +func NewExchangeVolumes(data string) ([]*ExchangeVolume, error) +``` + + + + +## type [HealthCheck]() + + + +```go +type HealthCheck struct { + LivenessProbe *corev1.Probe `yaml:"livenessProbe,omitempty" json:"livenessProbe,omitempty"` + ReadinessProbe *corev1.Probe `yaml:"readinessProbe,omitempty" json:"readinessProbe,omitempty"` +} +``` + + +### func [ProbeFrom]() + +```go +func ProbeFrom(data string) (*HealthCheck, error) +``` + + + + +## type [Ingress]() + + + +```go +type Ingress struct { + Port *int32 `yaml:"port,omitempty" json:"port,omitempty"` + Annotations map[string]string `yaml:"annotations,omitempty" jsonschema:"nullable" json:"annotations,omitempty"` + Hostname string `yaml:"hostname,omitempty" json:"hostname,omitempty"` + Path *string `yaml:"path,omitempty" json:"path,omitempty"` + Class *string `yaml:"class,omitempty" json:"class,omitempty" jsonschema:"default:-"` + Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"` + TLS *TLS `yaml:"tls,omitempty" json:"tls,omitempty"` +} +``` + + +### func [IngressFrom]() + +```go +func IngressFrom(data string) (*Ingress, error) +``` + +IngressFrom creates a new Ingress from a compose service. + + +## type [MapEnv]() + + + +```go +type MapEnv map[string]string +``` + + +### func [MapEnvFrom]() + +```go +func MapEnvFrom(data string) (MapEnv, error) +``` + +MapEnvFrom returns a MapEnv from the given string. + + +## type [Ports]() + + + +```go +type Ports []uint32 +``` + + +### func [PortsFrom]() + +```go +func PortsFrom(data string) (Ports, error) +``` + +PortsFrom returns a Ports from the given string. + + +## type [Secrets]() + + + +```go +type Secrets []string +``` + + +### func [SecretsFrom]() + +```go +func SecretsFrom(data string) (Secrets, error) +``` + + + + +## type [TLS]() + + + +```go +type TLS struct { + Enabled bool `yaml:"enabled" json:"enabled,omitempty"` +} +``` + + +## type [ValueFrom]() + + + +```go +type ValueFrom map[string]string +``` + + +### func [GetValueFrom]() + +```go +func GetValueFrom(data string) (*ValueFrom, error) +``` + + + +Generated by [gomarkdoc]() diff --git a/doc/docs/packages/utils.md b/doc/docs/packages/utils.md index b512f96..78a6551 100644 --- a/doc/docs/packages/utils.md +++ b/doc/docs/packages/utils.md @@ -8,7 +8,16 @@ import "katenary/utils" Package utils provides some utility functions used in katenary. It defines some constants and functions used in the whole project. -## func [AsResourceName]() +## Constants + +DirectoryPermission is the default values for permissions apply to created directories. + +```go +const DirectoryPermission = 0o755 +``` + + +## func [AsResourceName]() ```go func AsResourceName(name string) string @@ -17,7 +26,7 @@ func AsResourceName(name string) string AsResourceName returns a resource name with underscores to respect the kubernetes naming convention. It's the opposite of FixedResourceName. -## func [Confirm]() +## func [Confirm]() ```go func Confirm(question string, icon ...Icon) bool @@ -26,7 +35,7 @@ func Confirm(question string, icon ...Icon) bool Confirm asks a question and returns true if the answer is y. -## func [CountStartingSpaces]() +## func [CountStartingSpaces]() ```go func CountStartingSpaces(line string) int @@ -35,7 +44,7 @@ func CountStartingSpaces(line string) int CountStartingSpaces counts the number of spaces at the beginning of a string. -## func [EncodeBasicYaml]() +## func [EncodeBasicYaml]() ```go func EncodeBasicYaml(data any) ([]byte, error) @@ -44,7 +53,7 @@ func EncodeBasicYaml(data any) ([]byte, error) EncodeBasicYaml encodes a basic yaml from an interface. -## func [FixedResourceName]() +## func [FixedResourceName]() ```go func FixedResourceName(name string) string @@ -53,7 +62,7 @@ func FixedResourceName(name string) string FixedResourceName returns a resource name without underscores to respect the kubernetes naming convention. -## func [GetContainerByName]() +## func [GetContainerByName]() ```go func GetContainerByName(name string, containers []corev1.Container) (*corev1.Container, int) @@ -62,7 +71,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. -## func [GetKind]() +## func [GetKind]() ```go func GetKind(path string) (kind string) @@ -71,7 +80,7 @@ func GetKind(path string) (kind string) GetKind returns the kind of the resource from the file path. -## func [GetServiceNameByPort]() +## func [GetServiceNameByPort]() ```go func GetServiceNameByPort(port int) string @@ -80,7 +89,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. -## func [GetValuesFromLabel]() +## func [GetValuesFromLabel]() ```go func GetValuesFromLabel(service types.ServiceConfig, LabelValues string) map[string]*EnvConfig @@ -98,7 +107,7 @@ func HashComposefiles(files []string) (string, error) HashComposefiles returns a hash of the compose files. -## func [Int32Ptr]() +## func [Int32Ptr]() ```go func Int32Ptr(i int32) *int32 @@ -107,7 +116,7 @@ func Int32Ptr(i int32) *int32 Int32Ptr returns a pointer to an int32. -## func [PathToName]() +## func [PathToName]() ```go func PathToName(path string) string @@ -116,7 +125,7 @@ func PathToName(path string) string PathToName converts a path to a kubernetes complient name. -## func [StrPtr]() +## func [StrPtr]() ```go func StrPtr(s string) *string @@ -125,7 +134,7 @@ func StrPtr(s string) *string StrPtr returns a pointer to a string. -## func [TplName]() +## func [TplName]() ```go func TplName(serviceName, appname string, suffix ...string) string @@ -134,7 +143,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. -## func [TplValue]() +## func [TplValue]() ```go func TplValue(serviceName, variable string, pipes ...string) string @@ -152,7 +161,7 @@ func Warn(msg ...any) Warn prints a warning message -## func [WordWrap]() +## func [WordWrap]() ```go func WordWrap(text string, lineWidth int) string @@ -161,7 +170,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. -## func [Wrap]() +## func [Wrap]() ```go func Wrap(src, above, below string) string @@ -170,7 +179,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. -## type [EnvConfig]() +## type [EnvConfig]() EnvConfig is a struct to hold the description of an environment variable. diff --git a/generator/chart.go b/generator/chart.go index 45f3791..25c0f89 100644 --- a/generator/chart.go +++ b/generator/chart.go @@ -3,7 +3,7 @@ package generator import ( "fmt" "katenary/generator/labels" - "katenary/generator/labels/labelStructs" + "katenary/generator/labels/labelstructs" "katenary/utils" "log" "maps" @@ -49,7 +49,7 @@ type HelmChart struct { AppVersion string `yaml:"appVersion"` Description string `yaml:"description"` Helper string `yaml:"-"` - Dependencies []labelStructs.Dependency `yaml:"dependencies,omitempty"` + Dependencies []labelstructs.Dependency `yaml:"dependencies,omitempty"` } // NewChart creates a new empty chart with the given name. @@ -95,7 +95,7 @@ func (chart *HelmChart) SaveTemplates(templateDir string) { } servicename := template.Servicename - if err := os.MkdirAll(filepath.Join(templateDir, servicename), 0o755); err != nil { + if err := os.MkdirAll(filepath.Join(templateDir, servicename), utils.DirectoryPermission); err != nil { fmt.Println(utils.IconFailure, err) os.Exit(1) } @@ -103,7 +103,7 @@ func (chart *HelmChart) SaveTemplates(templateDir string) { // 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) + err := os.MkdirAll(filepath.Dir(name), utils.DirectoryPermission) if err != nil { fmt.Println(utils.IconFailure, err) os.Exit(1) @@ -141,7 +141,7 @@ func (chart *HelmChart) generateConfigMapsAndSecrets(project *types.Project) err maps.Copy(originalEnv, s.Environment) if v, ok := s.Labels[labels.LabelSecrets]; ok { - list, err := labelStructs.SecretsFrom(v) + list, err := labelstructs.SecretsFrom(v) if err != nil { log.Fatal("error unmarshaling secrets label:", err) } @@ -214,7 +214,7 @@ func (chart *HelmChart) generateDeployment(service types.ServiceConfig, deployme if exchange, ok := service.Labels[labels.LabelExchangeVolume]; ok { // we need to add a volume and a mount point - ex, err := labelStructs.NewExchangeVolumes(exchange) + ex, err := labelstructs.NewExchangeVolumes(exchange) if err != nil { return err } @@ -298,7 +298,7 @@ func (chart *HelmChart) setCronJob(service types.ServiceConfig, appName string) func (chart *HelmChart) setDependencies(service types.ServiceConfig) (bool, error) { // helm dependency if v, ok := service.Labels[labels.LabelDependencies]; ok { - d, err := labelStructs.DependenciesFrom(v) + d, err := labelstructs.DependenciesFrom(v) if err != nil { return false, err } @@ -326,7 +326,7 @@ func (chart *HelmChart) setSharedConf(service types.ServiceConfig, deployments m if _, ok := service.Labels[labels.LabelEnvFrom]; !ok { return } - fromservices, err := labelStructs.EnvFromFrom(service.Labels[labels.LabelEnvFrom]) + fromservices, err := labelstructs.EnvFromFrom(service.Labels[labels.LabelEnvFrom]) if err != nil { log.Fatal("error unmarshaling env-from label:", err) } @@ -351,7 +351,7 @@ func (chart *HelmChart) setEnvironmentValuesFrom(service types.ServiceConfig, de if _, ok := service.Labels[labels.LabelValueFrom]; !ok { return } - mapping, err := labelStructs.GetValueFrom(service.Labels[labels.LabelValueFrom]) + mapping, err := labelstructs.GetValueFrom(service.Labels[labels.LabelValueFrom]) if err != nil { log.Fatal("error unmarshaling values-from label:", err) } @@ -383,7 +383,7 @@ func (chart *HelmChart) setEnvironmentValuesFrom(service types.ServiceConfig, de // is it a secret? isSecret := false - secrets, err := labelStructs.SecretsFrom(dep.service.Labels[labels.LabelSecrets]) + secrets, err := labelstructs.SecretsFrom(dep.service.Labels[labels.LabelSecrets]) if err == nil { if slices.Contains(secrets, depName[1]) { isSecret = true diff --git a/generator/configMap.go b/generator/configMap.go index 585dee3..d7fedf5 100644 --- a/generator/configMap.go +++ b/generator/configMap.go @@ -3,7 +3,7 @@ package generator import ( "fmt" "katenary/generator/labels" - "katenary/generator/labels/labelStructs" + "katenary/generator/labels/labelstructs" "katenary/utils" "log" "os" @@ -65,7 +65,7 @@ func NewConfigMap(service types.ServiceConfig, appName string, forFile bool) *Co } // get the secrets from the labels - secrets, err := labelStructs.SecretsFrom(service.Labels[labels.LabelSecrets]) + secrets, err := labelstructs.SecretsFrom(service.Labels[labels.LabelSecrets]) if err != nil { log.Fatal(err) } @@ -91,7 +91,7 @@ func NewConfigMap(service types.ServiceConfig, appName string, forFile bool) *Co // do not bind env variables to the configmap // remove the variables that are already defined in the environment if l, ok := service.Labels[labels.LabelMapEnv]; ok { - envmap, err := labelStructs.MapEnvFrom(l) + envmap, err := labelstructs.MapEnvFrom(l) if err != nil { log.Fatal("Error parsing map-env", err) } diff --git a/generator/converter.go b/generator/converter.go index 77ceb73..52f9451 100644 --- a/generator/converter.go +++ b/generator/converter.go @@ -7,7 +7,7 @@ import ( "katenary/generator/extrafiles" "katenary/generator/katenaryfile" "katenary/generator/labels" - "katenary/generator/labels/labelStructs" + "katenary/generator/labels/labelstructs" "katenary/parser" "katenary/utils" "log" @@ -173,9 +173,8 @@ func Convert(config ConvertOptions, dockerComposeFile ...string) error { os.RemoveAll(config.OutputDir) // create the chart directory - if err := os.MkdirAll(templateDir, 0o755); err != nil { - fmt.Println(utils.IconFailure, err) - os.Exit(1) + if err := os.MkdirAll(templateDir, utils.DirectoryPermission); err != nil { + return err } // add icon from the command line @@ -259,7 +258,7 @@ func addCommentsToValues(values []byte) []byte { return []byte(strings.Join(lines, "\n")) } -func addDependencyDescription(values []byte, dependencies []labelStructs.Dependency) []byte { +func addDependencyDescription(values []byte, dependencies []labelstructs.Dependency) []byte { for _, d := range dependencies { name := d.Name if d.Alias != "" { @@ -522,16 +521,16 @@ func buildCharYamlFile(chart *HelmChart, project *types.Project, chartPath strin 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...) + yamlChart = append(fmt.Appendf(nil, "# 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...) + yamlChart = append(fmt.Appendf(nil, "# 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...) + yamlChart = append(fmt.Appendf(nil, "# generated at: %s\n", time.Now().Format(time.RFC3339)), yamlChart...) // document Chart.yaml file yamlChart = addChartDoc(yamlChart, project) diff --git a/generator/cronJob.go b/generator/cronJob.go index 9cdf498..0c33c9f 100644 --- a/generator/cronJob.go +++ b/generator/cronJob.go @@ -2,7 +2,7 @@ package generator import ( "katenary/generator/labels" - "katenary/generator/labels/labelStructs" + "katenary/generator/labels/labelstructs" "katenary/utils" "log" "strings" @@ -30,7 +30,7 @@ func NewCronJob(service types.ServiceConfig, chart *HelmChart, appName string) ( if !ok { return nil, nil } - mapping, err := labelStructs.CronJobFrom(labels) + mapping, err := labelstructs.CronJobFrom(labels) if err != nil { log.Fatalf("Error parsing cronjob labels: %s", err) return nil, nil diff --git a/generator/deployment.go b/generator/deployment.go index 16c6ebd..2345882 100644 --- a/generator/deployment.go +++ b/generator/deployment.go @@ -3,7 +3,7 @@ package generator import ( "fmt" "katenary/generator/labels" - "katenary/generator/labels/labelStructs" + "katenary/generator/labels/labelstructs" "katenary/utils" "log" "os" @@ -39,7 +39,7 @@ type Deployment struct { service *types.ServiceConfig `yaml:"-"` defaultTag string `yaml:"-"` isMainApp bool `yaml:"-"` - exchangesVolumes map[string]*labelStructs.ExchangeVolume `yaml:"-"` + exchangesVolumes map[string]*labelstructs.ExchangeVolume `yaml:"-"` boundEnvVar []string `yaml:"-"` // environement to remove } @@ -94,7 +94,7 @@ func NewDeployment(service types.ServiceConfig, chart *HelmChart) *Deployment { }, configMaps: make(map[string]*ConfigMapMount), volumeMap: make(map[string]string), - exchangesVolumes: map[string]*labelStructs.ExchangeVolume{}, + exchangesVolumes: map[string]*labelstructs.ExchangeVolume{}, boundEnvVar: []string{}, } @@ -160,7 +160,7 @@ func (d *Deployment) AddContainer(service types.ServiceConfig) { func (d *Deployment) AddHealthCheck(service types.ServiceConfig, container *corev1.Container) { // get the label for healthcheck if v, ok := service.Labels[labels.LabelHealthCheck]; ok { - probes, err := labelStructs.ProbeFrom(v) + probes, err := labelstructs.ProbeFrom(v) if err != nil { log.Fatal(err) } @@ -195,7 +195,7 @@ func (d *Deployment) AddIngress(service types.ServiceConfig, appName string) *In func (d *Deployment) AddVolumes(service types.ServiceConfig, appName string) { tobind := map[string]bool{} if v, ok := service.Labels[labels.LabelConfigMapFiles]; ok { - binds, err := labelStructs.ConfigMapFileFrom(v) + binds, err := labelstructs.ConfigMapFileFrom(v) if err != nil { log.Fatal(err) } @@ -320,7 +320,7 @@ func (d *Deployment) SetEnvFrom(service types.ServiceConfig, appName string, sam }() // secrets from label - labelSecrets, err := labelStructs.SecretsFrom(service.Labels[labels.LabelSecrets]) + labelSecrets, err := labelstructs.SecretsFrom(service.Labels[labels.LabelSecrets]) if err != nil { log.Fatal(err) } diff --git a/generator/generator.go b/generator/generator.go index 21f8559..cbaa288 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -4,7 +4,7 @@ import ( "bytes" "fmt" "katenary/generator/labels" - "katenary/generator/labels/labelStructs" + "katenary/generator/labels/labelstructs" "katenary/utils" "log" "regexp" @@ -227,7 +227,7 @@ func fixResourceNames(project *types.Project) error { } // also, the value-from label should be updated if valuefrom, ok := s.Labels[labels.LabelValueFrom]; ok { - vf, err := labelStructs.GetValueFrom(valuefrom) + vf, err := labelstructs.GetValueFrom(valuefrom) if err != nil { return err } diff --git a/generator/ingress.go b/generator/ingress.go index 7e2d753..31d9ed9 100644 --- a/generator/ingress.go +++ b/generator/ingress.go @@ -2,7 +2,7 @@ package generator import ( "katenary/generator/labels" - "katenary/generator/labels/labelStructs" + "katenary/generator/labels/labelstructs" "katenary/utils" "log" "strings" @@ -33,7 +33,7 @@ func NewIngress(service types.ServiceConfig, Chart *HelmChart) *Ingress { return nil } - mapping, err := labelStructs.IngressFrom(label) + mapping, err := labelstructs.IngressFrom(label) if err != nil { log.Fatalf("Failed to parse ingress label: %s\n", err) } diff --git a/generator/katenaryfile/main.go b/generator/katenaryfile/main.go index b9b938d..47f4624 100644 --- a/generator/katenaryfile/main.go +++ b/generator/katenaryfile/main.go @@ -5,7 +5,7 @@ import ( "encoding/json" "fmt" "katenary/generator/labels" - "katenary/generator/labels/labelStructs" + "katenary/generator/labels/labelstructs" "katenary/utils" "log" "os" @@ -27,20 +27,20 @@ type StringOrMap any type Service struct { MainApp *bool `json:"main-app,omitempty" jsonschema:"title=Is this service the main application"` Values []StringOrMap `json:"values,omitempty" jsonschema:"description=Environment variables to be set in values.yaml with or without a description"` - Secrets *labelStructs.Secrets `json:"secrets,omitempty" jsonschema:"title=Secrets,description=Environment variables to be set as secrets"` - Ports *labelStructs.Ports `json:"ports,omitempty" jsonschema:"title=Ports,description=Ports to be exposed in services"` - Ingress *labelStructs.Ingress `json:"ingress,omitempty" jsonschema:"title=Ingress,description=Ingress configuration"` - HealthCheck *labelStructs.HealthCheck `json:"health-check,omitempty" jsonschema:"title=Health Check,description=Health check configuration that respects the kubernetes api"` + Secrets *labelstructs.Secrets `json:"secrets,omitempty" jsonschema:"title=Secrets,description=Environment variables to be set as secrets"` + Ports *labelstructs.Ports `json:"ports,omitempty" jsonschema:"title=Ports,description=Ports to be exposed in services"` + Ingress *labelstructs.Ingress `json:"ingress,omitempty" jsonschema:"title=Ingress,description=Ingress configuration"` + HealthCheck *labelstructs.HealthCheck `json:"health-check,omitempty" jsonschema:"title=Health Check,description=Health check configuration that respects the kubernetes api"` SamePod *string `json:"same-pod,omitempty" jsonschema:"title=Same Pod,description=Service that should be in the same pod"` Description *string `json:"description,omitempty" jsonschema:"title=Description,description=Description of the service that will be injected in the values.yaml file"` Ignore *bool `json:"ignore,omitempty" jsonschema:"title=Ignore,description=Ignore the service in the conversion"` - Dependencies []labelStructs.Dependency `json:"dependencies,omitempty" jsonschema:"title=Dependencies,description=Services that should be injected in the Chart.yaml file"` - ConfigMapFile *labelStructs.ConfigMapFile `json:"configmap-files,omitempty" jsonschema:"title=ConfigMap Files,description=Files that should be injected as ConfigMap"` - MapEnv *labelStructs.MapEnv `json:"map-env,omitempty" jsonschema:"title=Map Env,description=Map environment variables to another value"` - CronJob *labelStructs.CronJob `json:"cron-job,omitempty" jsonschema:"title=Cron Job,description=Cron Job configuration"` - EnvFrom *labelStructs.EnvFrom `json:"env-from,omitempty" jsonschema:"title=Env From,description=Inject environment variables from another service"` - ExchangeVolumes []*labelStructs.ExchangeVolume `json:"exchange-volumes,omitempty" jsonschema:"title=Exchange Volumes,description=Exchange volumes between services"` - ValuesFrom *labelStructs.ValueFrom `json:"values-from,omitempty" jsonschema:"title=Values From,description=Inject values from another service (secret or configmap environment variables)"` + Dependencies []labelstructs.Dependency `json:"dependencies,omitempty" jsonschema:"title=Dependencies,description=Services that should be injected in the Chart.yaml file"` + ConfigMapFile *labelstructs.ConfigMapFile `json:"configmap-files,omitempty" jsonschema:"title=ConfigMap Files,description=Files that should be injected as ConfigMap"` + MapEnv *labelstructs.MapEnv `json:"map-env,omitempty" jsonschema:"title=Map Env,description=Map environment variables to another value"` + CronJob *labelstructs.CronJob `json:"cron-job,omitempty" jsonschema:"title=Cron Job,description=Cron Job configuration"` + EnvFrom *labelstructs.EnvFrom `json:"env-from,omitempty" jsonschema:"title=Env From,description=Inject environment variables from another service"` + ExchangeVolumes []*labelstructs.ExchangeVolume `json:"exchange-volumes,omitempty" jsonschema:"title=Exchange Volumes,description=Exchange volumes between services"` + ValuesFrom *labelstructs.ValueFrom `json:"values-from,omitempty" jsonschema:"title=Values From,description=Inject values from another service (secret or configmap environment variables)"` } // OverrideWithConfig overrides the project with the katenary.yaml file. It @@ -117,7 +117,7 @@ func getLabelContent(o any, service *types.ServiceConfig, labelName string) erro val := strings.TrimSpace(string(c)) if labelName == labels.LabelIngress { // special case, values must be set from some defaults - ing, err := labelStructs.IngressFrom(val) + ing, err := labelstructs.IngressFrom(val) if err != nil { log.Fatal(err) return err diff --git a/generator/katenaryfile/main_test.go b/generator/katenaryfile/main_test.go index 559f322..263c612 100644 --- a/generator/katenaryfile/main_test.go +++ b/generator/katenaryfile/main_test.go @@ -57,6 +57,9 @@ webapp: cli.WithDefaultConfigPath, ) project, err := cli.ProjectFromOptions(options) + if err != nil { + t.Fatalf("Failed to create project from options: %s", err.Error()) + } OverrideWithConfig(project) w := project.Services[0].Labels @@ -107,6 +110,9 @@ webapp: cli.WithDefaultConfigPath, ) project, err := cli.ProjectFromOptions(options) + if err != nil { + t.Fatalf("Failed to create project from options: %s", err.Error()) + } OverrideWithConfig(project) w := project.Services[0].Labels diff --git a/generator/labels/doc.go b/generator/labels/doc.go new file mode 100644 index 0000000..f75a717 --- /dev/null +++ b/generator/labels/doc.go @@ -0,0 +1,2 @@ +// Package labels provides functionality to parse and manipulate labels. +package labels diff --git a/generator/labels/katenaryLabels.go b/generator/labels/katenaryLabels.go index 5c2ea33..550520f 100644 --- a/generator/labels/katenaryLabels.go +++ b/generator/labels/katenaryLabels.go @@ -84,7 +84,7 @@ func init() { } } -// Generate the help for the labels. +// GetLabelHelp return the help for the labels. func GetLabelHelp(asMarkdown bool) string { names := GetLabelNames() // sorted if !asMarkdown { diff --git a/generator/labels/labelStructs/doc.go b/generator/labels/labelStructs/doc.go deleted file mode 100644 index 5373fcf..0000000 --- a/generator/labels/labelStructs/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// labelStructs is a package that contains the structs used to represent the labels in the yaml files. -package labelStructs diff --git a/generator/labels/labelStructs/configMap.go b/generator/labels/labelstructs/configMap.go similarity index 92% rename from generator/labels/labelStructs/configMap.go rename to generator/labels/labelstructs/configMap.go index 2b5112f..a771d7b 100644 --- a/generator/labels/labelStructs/configMap.go +++ b/generator/labels/labelstructs/configMap.go @@ -1,4 +1,4 @@ -package labelStructs +package labelstructs import "gopkg.in/yaml.v3" diff --git a/generator/labels/labelstructs/configMap_test.go b/generator/labels/labelstructs/configMap_test.go new file mode 100644 index 0000000..55f11f2 --- /dev/null +++ b/generator/labels/labelstructs/configMap_test.go @@ -0,0 +1,17 @@ +package labelstructs_test + +import ( + "katenary/generator/labels/labelstructs" + "testing" +) + +func TestConfigMapFileFrom(t *testing.T) { + ts := "- foo/bar" + tc2, _ := labelstructs.ConfigMapFileFrom(ts) + if len(tc2) != 1 { + t.Errorf("Expected ConfigMapFile to have 1 item, got %d", len(tc2)) + } + if tc2[0] != "foo/bar" { + t.Errorf("Expected ConfigMapFile to contain 'foo/bar', got %s", tc2[0]) + } +} diff --git a/generator/labels/labelStructs/cronJob.go b/generator/labels/labelstructs/cronJob.go similarity index 95% rename from generator/labels/labelStructs/cronJob.go rename to generator/labels/labelstructs/cronJob.go index 972e456..9c20ecb 100644 --- a/generator/labels/labelStructs/cronJob.go +++ b/generator/labels/labelstructs/cronJob.go @@ -1,4 +1,4 @@ -package labelStructs +package labelstructs import "gopkg.in/yaml.v3" diff --git a/generator/labels/labelstructs/cronJob_test.go b/generator/labels/labelstructs/cronJob_test.go new file mode 100644 index 0000000..85dfe75 --- /dev/null +++ b/generator/labels/labelstructs/cronJob_test.go @@ -0,0 +1,25 @@ +package labelstructs + +import "testing" + +func TestCronJobFrom(t *testing.T) { + ts := ` +image: fooimage +command: thecommand +schedule: "0/3 0 * * *" +Rbac: false +` + tc, _ := CronJobFrom(ts) + if tc.Image != "fooimage" { + t.Errorf("Expected CronJob image to be 'fooimage', got %s", tc.Image) + } + if tc.Command != "thecommand" { + t.Errorf("Expected CronJob command to be 'thecommand', got %s", tc.Command) + } + if tc.Schedule != "0/3 0 * * *" { + t.Errorf("Expected CronJob schedule to be '0/3 0 * * *', got %s", tc.Schedule) + } + if tc.Rbac != false { + t.Errorf("Expected CronJob rbac to be false, got %t", tc.Rbac) + } +} diff --git a/generator/labels/labelStructs/dependencies.go b/generator/labels/labelstructs/dependencies.go similarity index 97% rename from generator/labels/labelStructs/dependencies.go rename to generator/labels/labelstructs/dependencies.go index 90511fe..e82a585 100644 --- a/generator/labels/labelStructs/dependencies.go +++ b/generator/labels/labelstructs/dependencies.go @@ -1,4 +1,4 @@ -package labelStructs +package labelstructs import "gopkg.in/yaml.v3" diff --git a/generator/labels/labelstructs/dependencies_test.go b/generator/labels/labelstructs/dependencies_test.go new file mode 100644 index 0000000..b6fdd6c --- /dev/null +++ b/generator/labels/labelstructs/dependencies_test.go @@ -0,0 +1,14 @@ +package labelstructs + +import "testing" + +func TestDependenciesLabel(t *testing.T) { + ts := "- name: mongodb" + tc, _ := DependenciesFrom(ts) + if len(tc) != 1 { + t.Errorf("Expected DependenciesLabel to have 1 item, got %d", len(tc)) + } + if tc[0].Name != "mongodb" { + t.Errorf("Expected DependenciesLabel to contain 'mongodb', got %s", tc[0].Name) + } +} diff --git a/generator/labels/labelstructs/doc.go b/generator/labels/labelstructs/doc.go new file mode 100644 index 0000000..2b8d5fb --- /dev/null +++ b/generator/labels/labelstructs/doc.go @@ -0,0 +1,2 @@ +// Package labelstructs is a package that contains the structs used to represent the labels in the yaml files. +package labelstructs diff --git a/generator/labels/labelStructs/envFrom.go b/generator/labels/labelstructs/envFrom.go similarity index 93% rename from generator/labels/labelStructs/envFrom.go rename to generator/labels/labelstructs/envFrom.go index f2c8f2f..cbc4151 100644 --- a/generator/labels/labelStructs/envFrom.go +++ b/generator/labels/labelstructs/envFrom.go @@ -1,4 +1,4 @@ -package labelStructs +package labelstructs import "gopkg.in/yaml.v3" diff --git a/generator/labels/labelstructs/envFrom_test.go b/generator/labels/labelstructs/envFrom_test.go new file mode 100644 index 0000000..0f35d46 --- /dev/null +++ b/generator/labels/labelstructs/envFrom_test.go @@ -0,0 +1,17 @@ +package labelstructs + +import "testing" + +func TestEnvFromLabel(t *testing.T) { + ts := "- foo\n- bar" + tc, _ := EnvFromFrom(ts) + if len(tc) != 2 { + t.Errorf("Expected EnvFrom to have 2 items, got %d", len(tc)) + } + if tc[0] != "foo" { + t.Errorf("Expected EnvFrom to contain 'foo', got %s", tc[0]) + } + if tc[1] != "bar" { + t.Errorf("Expected EnvFrom to contain 'bar', got %s", tc[1]) + } +} diff --git a/generator/labels/labelStructs/exchangeVolume.go b/generator/labels/labelstructs/exchangeVolume.go similarity index 95% rename from generator/labels/labelStructs/exchangeVolume.go rename to generator/labels/labelstructs/exchangeVolume.go index 1faa997..2f32894 100644 --- a/generator/labels/labelStructs/exchangeVolume.go +++ b/generator/labels/labelstructs/exchangeVolume.go @@ -1,4 +1,4 @@ -package labelStructs +package labelstructs import "gopkg.in/yaml.v3" diff --git a/generator/labels/labelstructs/exchangeVolume_test.go b/generator/labels/labelstructs/exchangeVolume_test.go new file mode 100644 index 0000000..a3f380c --- /dev/null +++ b/generator/labels/labelstructs/exchangeVolume_test.go @@ -0,0 +1,17 @@ +package labelstructs + +import "testing" + +func TestExchangeVolumeLabel(t *testing.T) { + ts := "- name: exchange-volume\n mountPath: /exchange\n readOnly: true" + tc, _ := NewExchangeVolumes(ts) + if len(tc) != 1 { + t.Errorf("Expected ExchangeVolumeLabel to have 1 item, got %d", len(tc)) + } + if tc[0].Name != "exchange-volume" { + t.Errorf("Expected ExchangeVolumeLabel to contain 'exchange-volume', got %s", tc[0].Name) + } + if tc[0].MountPath != "/exchange" { + t.Errorf("Expected MountPath to be '/exchange', got %s", tc[0].MountPath) + } +} diff --git a/generator/labels/labelStructs/ingress.go b/generator/labels/labelstructs/ingress.go similarity index 85% rename from generator/labels/labelStructs/ingress.go rename to generator/labels/labelstructs/ingress.go index df40da3..b7285cb 100644 --- a/generator/labels/labelStructs/ingress.go +++ b/generator/labels/labelstructs/ingress.go @@ -1,4 +1,4 @@ -package labelStructs +package labelstructs import ( "fmt" @@ -14,10 +14,10 @@ type TLS struct { type Ingress struct { Port *int32 `yaml:"port,omitempty" json:"port,omitempty"` Annotations map[string]string `yaml:"annotations,omitempty" jsonschema:"nullable" json:"annotations,omitempty"` - Hostname string `yaml:"hostname" json:"hostname,omitempty"` + Hostname string `yaml:"hostname,omitempty" json:"hostname,omitempty"` Path *string `yaml:"path,omitempty" json:"path,omitempty"` Class *string `yaml:"class,omitempty" json:"class,omitempty" jsonschema:"default:-"` - Enabled bool `yaml:"enabled" json:"enabled,omitempty"` + Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"` TLS *TLS `yaml:"tls,omitempty" json:"tls,omitempty"` } diff --git a/generator/labels/labelstructs/ingress_test.go b/generator/labels/labelstructs/ingress_test.go new file mode 100644 index 0000000..d4fe8c1 --- /dev/null +++ b/generator/labels/labelstructs/ingress_test.go @@ -0,0 +1,31 @@ +package labelstructs + +import "testing" + +func TestIngressLabel(t *testing.T) { + ts := "\nhostname: example.com\npath: /\nenabled: true\nport: 8888" + tc, err := IngressFrom(ts) + if err != nil { + t.Errorf("Error parsing IngressLabel: %v", err) + } + if tc.Hostname != "example.com" { + t.Errorf("Expected IngressLabel to contain 'example.com', got %s", tc.Hostname) + } + if tc.Path == nil || *tc.Path != "/" { + t.Errorf("Expected IngressLabel to contain '/', got %v", tc.Path) + } + if tc.Enabled != true { + t.Errorf("Expected IngressLabel to be enabled, got %v", tc.Enabled) + } + if tc.Port == nil || *tc.Port != 8888 { + t.Errorf("Expected IngressLabel to have port 8888, got %d", tc.Port) + } +} + +func TestIngressLabelNoPort(t *testing.T) { + ts := "\nhostname: example.com\npath: /\nenabled: true" + _, err := IngressFrom(ts) + if err == nil { + t.Errorf("Expected error when parsing IngressLabel without port, got nil") + } +} diff --git a/generator/labels/labelStructs/mapenv.go b/generator/labels/labelstructs/mapenv.go similarity index 93% rename from generator/labels/labelStructs/mapenv.go rename to generator/labels/labelstructs/mapenv.go index 6b4cdfa..dfccf4f 100644 --- a/generator/labels/labelStructs/mapenv.go +++ b/generator/labels/labelstructs/mapenv.go @@ -1,4 +1,4 @@ -package labelStructs +package labelstructs import "gopkg.in/yaml.v3" diff --git a/generator/labels/labelstructs/mapenv_test.go b/generator/labels/labelstructs/mapenv_test.go new file mode 100644 index 0000000..67171c1 --- /dev/null +++ b/generator/labels/labelstructs/mapenv_test.go @@ -0,0 +1,11 @@ +package labelstructs + +import "testing" + +func TestConfigMapLabel(t *testing.T) { + ts := "foo: bar" + tc, _ := MapEnvFrom(ts) + if len(tc) != 1 { + t.Errorf("Expected ConfigMapFile to have 1 item, got %d", len(tc)) + } +} diff --git a/generator/labels/labelStructs/ports.go b/generator/labels/labelstructs/ports.go similarity index 92% rename from generator/labels/labelStructs/ports.go rename to generator/labels/labelstructs/ports.go index 253a075..43253c8 100644 --- a/generator/labels/labelStructs/ports.go +++ b/generator/labels/labelstructs/ports.go @@ -1,4 +1,4 @@ -package labelStructs +package labelstructs import "gopkg.in/yaml.v3" diff --git a/generator/labels/labelstructs/ports_test.go b/generator/labels/labelstructs/ports_test.go new file mode 100644 index 0000000..032568b --- /dev/null +++ b/generator/labels/labelstructs/ports_test.go @@ -0,0 +1,23 @@ +package labelstructs + +import "testing" + +func TestPortsFromLabel(t *testing.T) { + data := "- 8080\n- 9090\n" + expected := Ports{8080, 9090} + + ports, err := PortsFrom(data) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if len(ports) != len(expected) { + t.Fatalf("expected length %d, got %d", len(expected), len(ports)) + } + + for i, port := range ports { + if port != expected[i] { + t.Errorf("expected port %d at index %d, got %d", expected[i], i, port) + } + } +} diff --git a/generator/labels/labelStructs/probes.go b/generator/labels/labelstructs/probes.go similarity index 98% rename from generator/labels/labelStructs/probes.go rename to generator/labels/labelstructs/probes.go index bdd3813..1feeeb4 100644 --- a/generator/labels/labelStructs/probes.go +++ b/generator/labels/labelstructs/probes.go @@ -1,4 +1,4 @@ -package labelStructs +package labelstructs import ( "encoding/json" diff --git a/generator/labels/labelstructs/probes_test.go b/generator/labels/labelstructs/probes_test.go new file mode 100644 index 0000000..2ceaf4c --- /dev/null +++ b/generator/labels/labelstructs/probes_test.go @@ -0,0 +1,16 @@ +package labelstructs + +import "testing" + +func TestProbesLabel(t *testing.T) { + readiness := "readinessProbe:\n httpGet:\n path: /healthz\n port: 8080\n initialDelaySeconds: 5\n periodSeconds: 10" + tc, err := ProbeFrom(readiness) + if err != nil { + t.Errorf("Error parsing ProbesLabel: %v %v", err, tc) + } + liveness := "livenessProbe:\n httpGet:\n path: /healthz\n port: 8080\n initialDelaySeconds: 5\n periodSeconds: 10" + tc2, err := ProbeFrom(liveness) + if err != nil { + t.Errorf("Error parsing ProbesLabel: %v %v", err, tc2) + } +} diff --git a/generator/labels/labelStructs/secrets.go b/generator/labels/labelstructs/secrets.go similarity index 91% rename from generator/labels/labelStructs/secrets.go rename to generator/labels/labelstructs/secrets.go index e5cfb36..2ac8d59 100644 --- a/generator/labels/labelStructs/secrets.go +++ b/generator/labels/labelstructs/secrets.go @@ -1,4 +1,4 @@ -package labelStructs +package labelstructs import "gopkg.in/yaml.v3" diff --git a/generator/labels/labelstructs/secrets_test.go b/generator/labels/labelstructs/secrets_test.go new file mode 100644 index 0000000..5e34b08 --- /dev/null +++ b/generator/labels/labelstructs/secrets_test.go @@ -0,0 +1,17 @@ +package labelstructs + +import "testing" + +func TestSecretLabel(t *testing.T) { + data := "- foo\n- bar" + tc, err := SecretsFrom(data) + if err != nil { + t.Errorf("Error parsing SecretLabel: %v %v", err, tc) + } + items := []string{"foo", "bar"} + for i, item := range tc { + if item != items[i] { + t.Errorf("Expected SecretLabel to contain '%s', got '%s'", items[i], item) + } + } +} diff --git a/generator/labels/labelStructs/valueFrom.go b/generator/labels/labelstructs/valueFrom.go similarity index 91% rename from generator/labels/labelStructs/valueFrom.go rename to generator/labels/labelstructs/valueFrom.go index 9ad5712..5d86176 100644 --- a/generator/labels/labelStructs/valueFrom.go +++ b/generator/labels/labelstructs/valueFrom.go @@ -1,4 +1,4 @@ -package labelStructs +package labelstructs import "gopkg.in/yaml.v3" diff --git a/generator/labels/labelstructs/valueFrom_test.go b/generator/labels/labelstructs/valueFrom_test.go new file mode 100644 index 0000000..5eb1f15 --- /dev/null +++ b/generator/labels/labelstructs/valueFrom_test.go @@ -0,0 +1,25 @@ +package labelstructs + +import ( + "testing" +) + +func TestValueFromLabel(t *testing.T) { + data := "data: foo\ndata2: bar" + tc, err := GetValueFrom(data) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + if tc == nil { + t.Fatalf("expected non-nil map, got nil") + } + if len(*tc) != 2 { + t.Errorf("expected 2 items, got %d", len(*tc)) + } + if (*tc)["data"] != "foo" { + t.Errorf("expected 'data' to be 'foo', got %s", (*tc)["data"]) + } + if (*tc)["data2"] != "bar" { + t.Errorf("expected 'data2' to be 'bar', got %s", (*tc)["data2"]) + } +} diff --git a/generator/secret_test.go b/generator/secret_test.go index fdefdaf..62dae1f 100644 --- a/generator/secret_test.go +++ b/generator/secret_test.go @@ -82,7 +82,9 @@ services: AppVersion: appVersion, ChartVersion: chartVersion, } - Convert(convertOptions, "compose.yml") + if err := Convert(convertOptions, "compose.yml"); err != nil { + t.Fatalf("Failed to convert compose file: %s", err) + } c, err := os.ReadFile("chart/values.yaml") if err != nil { t.Fatal(err) diff --git a/generator/utils.go b/generator/utils.go index f931e73..51feb08 100644 --- a/generator/utils.go +++ b/generator/utils.go @@ -2,7 +2,7 @@ package generator import ( "katenary/generator/labels" - "katenary/generator/labels/labelStructs" + "katenary/generator/labels/labelstructs" "katenary/utils" "regexp" "strconv" @@ -50,7 +50,7 @@ func fixPorts(service *types.ServiceConfig) error { if portsLabel, ok = service.Labels[labels.LabelPorts]; !ok { return nil } - ports, err := labelStructs.PortsFrom(portsLabel) + ports, err := labelstructs.PortsFrom(portsLabel) if err != nil { // maybe it's a string, comma separated parts := strings.SplitSeq(portsLabel, ",") diff --git a/generator/utils_test.go b/generator/utils_test.go index b08b1fd..f6a92f6 100644 --- a/generator/utils_test.go +++ b/generator/utils_test.go @@ -3,6 +3,7 @@ package generator import ( "fmt" "katenary/generator/labels" + "katenary/utils" "os" "path/filepath" "testing" @@ -25,7 +26,7 @@ services: } composeFile := filepath.Join(tmpDir, "compose.yaml") - os.MkdirAll(tmpDir, 0755) + os.MkdirAll(tmpDir, utils.DirectoryPermission) if err := os.WriteFile(composeFile, []byte(composeFileContent), 0644); err != nil { t.Log(err) } @@ -73,7 +74,7 @@ services: } composeFile := filepath.Join(tmpDir, "compose.yaml") - os.MkdirAll(tmpDir, 0755) + os.MkdirAll(tmpDir, utils.DirectoryPermission) if err := os.WriteFile(composeFile, []byte(composeFileContent), 0644); err != nil { t.Log(err) } diff --git a/generator/volume_test.go b/generator/volume_test.go index d838f56..8f0b78a 100644 --- a/generator/volume_test.go +++ b/generator/volume_test.go @@ -6,6 +6,7 @@ import ( "image/color" "image/png" "katenary/generator/labels" + "katenary/utils" "log" "os" "path/filepath" @@ -19,7 +20,7 @@ import ( const ( htmlContent = "

Hello, World!

" developementFile = "templates/web/deployment.yaml" - indexHtmlFile = "index.html" + indexHMLFile = "index.html" ) func TestGenerateWithBoundVolume(t *testing.T) { @@ -68,7 +69,7 @@ services: // create a static directory with an index.html file staticDir := tmpDir + "/static" - os.Mkdir(staticDir, 0o755) + os.Mkdir(staticDir, utils.DirectoryPermission) indexFile, err := os.Create(staticDir + "/index.html") if err != nil { t.Errorf("Failed to create index.html: %s", err) @@ -106,8 +107,8 @@ services: if len(data) != 1 { t.Errorf("Expected 1 data, got %d", len(data)) } - if data[indexHtmlFile] != htmlContent { - t.Errorf("Expected index.html to be "+htmlContent+", got %s", data[indexHtmlFile]) + if data[indexHMLFile] != htmlContent { + t.Errorf("Expected index.html to be "+htmlContent+", got %s", data[indexHMLFile]) } } @@ -128,7 +129,7 @@ services: // create a static directory with an index.html file staticDir := tmpDir + "/static" - os.Mkdir(staticDir, 0o755) + os.Mkdir(staticDir, utils.DirectoryPermission) indexFile, err := os.Create(staticDir + "/index.html") if err != nil { t.Errorf("Failed to create index.html: %s", err) @@ -153,7 +154,7 @@ services: } // but this time, we need a subpath subPath := dt.Spec.Template.Spec.Containers[0].VolumeMounts[0].SubPath - if subPath != indexHtmlFile { + if subPath != indexHMLFile { t.Errorf("Expected subpath to be index.html, got %s", subPath) } } @@ -174,15 +175,15 @@ services: log.Println(tmpDir) defer teardown(tmpDir) - os.Mkdir(filepath.Join(tmpDir, "images"), 0o755) + os.Mkdir(filepath.Join(tmpDir, "images"), utils.DirectoryPermission) // create a png image pngFile := tmpDir + "/images/foo.png" w, h := 100, 100 img := image.NewRGBA(image.Rect(0, 0, w, h)) red := color.RGBA{255, 0, 0, 255} - for y := 0; y < h; y++ { - for x := 0; x < w; x++ { + for y := range h { + for x := range w { img.Set(x, y, red) } } @@ -244,15 +245,15 @@ services: log.Println(tmpDir) defer teardown(tmpDir) - os.Mkdir(filepath.Join(tmpDir, "images"), 0o755) + os.Mkdir(filepath.Join(tmpDir, "images"), utils.DirectoryPermission) // create a png image pngFile := tmpDir + "/images/foo.png" w, h := 100, 100 img := image.NewRGBA(image.Rect(0, 0, w, h)) red := color.RGBA{255, 0, 0, 255} - for y := 0; y < h; y++ { - for x := 0; x < w; x++ { + for y := range h { + for x := range w { img.Set(x, y, red) } } diff --git a/install.sh b/install.sh index 15da7fa..03e5f86 100644 --- a/install.sh +++ b/install.sh @@ -18,28 +18,28 @@ COMON_INSTALL_PATHS="$HOME/.local/bin $HOME/.bin $HOME/bin" INSTALL_PATH="" for p in $COMON_INSTALL_PATHS; do - if [ -d $p ]; then - INSTALL_PATH=$p - break - fi + if [ -d $p ]; then + INSTALL_PATH=$p + break + fi done # check if the user has write access to the INSTALL_PATH if [ -z "$INSTALL_PATH" ]; then - INSTALL_PATH="/usr/local/bin" - if [ ! -w $INSTALL_PATH ]; then - echo "You don't have write access to $INSTALL_PATH" - echo "Please, run with sudo or install locally" - exit 1 - fi + INSTALL_PATH="/usr/local/bin" + if [ ! -w $INSTALL_PATH ]; then + echo "You don't have write access to $INSTALL_PATH" + echo "Please, run with sudo or install locally" + exit 1 + fi fi # ensure that $INSTALL_PATH is in the PATH -if ! echo $PATH | grep -q $INSTALL_PATH; then - echo "Sorry, $INSTALL_PATH is not in the PATH" - echo "Please, add it to your PATH in your shell configuration file" - echo "then restart your shell and run this script again" - exit 1 +if ! echo "$PATH" | grep -q "$INSTALL_PATH"; then + echo "Sorry, ${INSTALL_PATH} is not in the PATH" + echo "Please, add it to your PATH in your shell configuration file" + echo "then restart your shell and run this script again" + exit 1 fi # Where to download the binary @@ -47,7 +47,7 @@ BASE="https://github.com/metal3d/katenary/releases/latest/download/" # for compatibility with older ARM versions if [ $ARCH = "x86_64" ]; then - ARCH="amd64" + ARCH="amd64" fi BIN_URL="$BASE/katenary-$OS-$ARCH" @@ -58,8 +58,8 @@ echo "Downloading $BIN_URL" T=$(mktemp -u) curl -SL -# $BIN_URL -o $T || (echo "Failed to download katenary" && rm -f $T && exit 1) -mv $T $INSTALL_PATH/katenary -chmod +x $INSTALL_PATH/katenary +mv "$T" "${INSTALL_PATH}/katenary" +chmod +x "${INSTALL_PATH}/katenary" echo echo "Installed to $INSTALL_PATH/katenary" echo "Installation complete! Run 'katenary help' to get started." diff --git a/utils/utils.go b/utils/utils.go index 6ebd5ab..78cca18 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -14,6 +14,9 @@ import ( corev1 "k8s.io/api/core/v1" ) +// DirectoryPermission is the default values for permissions apply to created directories. +const DirectoryPermission = 0o755 + // TplName returns the name of the kubernetes resource as a template string. // It is used in the templates and defined in _helper.tpl file. func TplName(serviceName, appname string, suffix ...string) string {