1 Commits

Author SHA1 Message Date
1fa07c29e5 Work in progress on compose-go v2
As explained in #102, the compose-go package changes types and needs
massive changes.
This branches is OK for now, but needs some tests.
It MUST be quickly fixed to be integrated in main branch.
2024-12-28 19:37:51 +01:00
78 changed files with 503 additions and 1397 deletions

View File

@@ -2,15 +2,12 @@ name: Go-Tests
on: on:
pull_request: pull_request:
types: [opened, synchronize, reopened]
branches: branches:
- develop - develop
push: push:
branches: branches:
- master - master
- main
- develop - develop
- 'releases/**'
jobs: jobs:
tests: tests:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -19,7 +16,12 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: 1.24 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
- name: Launch Test - name: Launch Test
run: | run: |
go mod tidy go mod tidy
@@ -31,9 +33,6 @@ jobs:
coverprofile.out coverprofile.out
gotest.json gotest.json
sonar: sonar:
permissions:
contents: read
pull-requests: read
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: tests needs: tests
steps: steps:
@@ -41,8 +40,8 @@ jobs:
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
with: with:
name: tests-results name: tests-results
- name: SonarQube Scan - name: SonarCloud Scan
uses: SonarSource/sonarqube-scan-action@v5.2.0 uses: SonarSource/sonarcloud-github-action@master
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}

View File

@@ -1,26 +0,0 @@
version: "2"
run:
issues-exit-code: 1
linters:
enabled:
- unused
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
- "(.+)_test.go"
formatters:
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
- "(.+)_test.go"

View File

@@ -4,17 +4,14 @@ VERSION=$(shell git describe --exact-match --tags $(CUR_SHA) 2>/dev/null || echo
CTN:=$(shell which podman 2>&1 1>/dev/null && echo "podman" || echo "docker") CTN:=$(shell which podman 2>&1 1>/dev/null && echo "podman" || echo "docker")
PREFIX=~/.local PREFIX=~/.local
GOVERSION=1.24 GOVERSION=1.23
GO=container GO=container
OUT=katenary OUT=katenary
RELEASE="" RELEASE=""
BLD_CMD=go build -ldflags="-X 'katenary/generator.Version=$(RELEASE)$(VERSION)'" -o $(OUT) ./cmd/katenary BLD_CMD=go build -ldflags="-X 'katenary/generator.Version=$(RELEASE)$(VERSION)'" -o $(OUT) ./cmd/katenary
GOOS=linux GOOS=linux
GOARCH=amd64 GOARCH=amd64
CGO_ENABLED=0
SIGNER=metal3d@gmail.com SIGNER=metal3d@gmail.com
UPX_OPTS =
UPX ?= upx $(UPX_OPTS)
BUILD_IMAGE=docker.io/golang:$(GOVERSION)-alpine BUILD_IMAGE=docker.io/golang:$(GOVERSION)-alpine
# SHELL=/bin/bash # SHELL=/bin/bash
@@ -36,7 +33,7 @@ SHELL := bash
.DELETE_ON_ERROR: .DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules MAKEFLAGS += --no-builtin-rules
.PHONY: help clean build install tests test doc .PHONY: help clean build install tests test
all: build all: build
@@ -90,21 +87,25 @@ endif
katenary: $(SOURCES) Makefile go.mod go.sum katenary: $(SOURCES) Makefile go.mod go.sum
ifeq ($(GO),local) ifeq ($(GO),local)
@echo "=> Build on host using go" @echo "=> Build on host using go"
$(BLD_CMD)
else ifeq ($(CTN),podman)
@echo "=> Build in container using" $(CTN)
echo $(GOOS) $(GOARCH)
podman run -e CGO_ENABLED=$(CGO_ENABLED) -e GOOS=$(GOOS) -e GOARCH=$(GOARCH) -e CC=$(CC) \
--rm -v $(PWD):/go/src/katenary:z -w /go/src/katenary --userns keep-id $(BUILD_IMAGE) $(BLD_CMD)
else else
@echo "=> Build in container using" $(CTN) @echo "=> Build in container using" $(CTN)
@docker run -e CGO_ENABLED=$(CGO_ENABLED) -e GOOS=$(GOOS) -e GOARCH=$(GOARCH) \
--rm -v $(PWD):/go/src/katenary:z -w /go/src/katenary --user $(shell id -u):$(shell id -g) -e HOME=/tmp $(BUILD_IMAGE) $(BLD_CMD)
endif endif
echo $(BLD_CMD)
ifeq ($(GO),local)
$(BLD_CMD)
else ifeq ($(CTN),podman)
@podman run -e CGO_ENABLED=0 -e GOOS=$(GOOS) -e GOARCH=$(GOARCH) \
--rm -v $(PWD):/go/src/katenary:z -w /go/src/katenary --userns keep-id -it $(BUILD_IMAGE) $(BLD_CMD)
else
@docker run -e CGO_ENABLED=0 -e GOOS=$(GOOS) -e GOARCH=$(GOARCH) \
--rm -v $(PWD):/go/src/katenary:z -w /go/src/katenary --user $(shell id -u):$(shell id -g) -e HOME=/tmp -it $(BUILD_IMAGE) $(BLD_CMD)
endif
echo "=> Stripping if possible"
strip $(OUT) 2>/dev/null || echo "=> No strip available"
## Release build ## Release build
dist: prepare $(BINARIES) upx $(ASC_BINARIES) dist: prepare $(BINARIES) $(ASC_BINARIES)
prepare: pull prepare: pull
mkdir -p dist mkdir -p dist
@@ -113,7 +114,6 @@ dist/katenary-linux-amd64:
@echo @echo
@echo -e "\033[1;32mBuilding katenary $(VERSION) for linux-amd64...\033[0m" @echo -e "\033[1;32mBuilding katenary $(VERSION) for linux-amd64...\033[0m"
$(MAKE) katenary GOOS=linux GOARCH=amd64 OUT=$@ $(MAKE) katenary GOOS=linux GOARCH=amd64 OUT=$@
strip $@
dist/katenary-linux-arm64: dist/katenary-linux-arm64:
@echo @echo
@@ -134,7 +134,6 @@ dist/katenary-freebsd-amd64:
@echo @echo
@echo -e "\033[1;32mBuilding katenary $(VERSION) for freebsd...\033[0m" @echo -e "\033[1;32mBuilding katenary $(VERSION) for freebsd...\033[0m"
$(MAKE) katenary GOOS=freebsd GOARCH=amd64 OUT=$@ $(MAKE) katenary GOOS=freebsd GOARCH=amd64 OUT=$@
strip $@
dist/katenary-freebsd-arm64: dist/katenary-freebsd-arm64:
@echo @echo
@@ -148,13 +147,6 @@ gpg-sign:
dist/%.asc: dist/% dist/%.asc: dist/%
gpg --armor --detach-sign --default-key $(SIGNER) $< &>/dev/null || exit 1 gpg --armor --detach-sign --default-key $(SIGNER) $< &>/dev/null || exit 1
upx:
$(UPX) dist/katenary-linux-amd64
$(UPX) dist/katenary-linux-arm64
#$(UPX) dist/katenary.exe
$(UPX) dist/katenary-darwin-amd64 --force-macos
install: build install: build
install -Dm755 katenary $(PREFIX)/bin/katenary install -Dm755 katenary $(PREFIX)/bin/katenary
@@ -212,11 +204,6 @@ push-release: build-all
@rm -f release.id @rm -f release.id
doc:
@echo "=> Generating documentation..."
# generate the labels doc and code doc
$(MAKE) __label_doc
__label_doc: __label_doc:
@command -v gomarkdoc || (echo "==> We need to install gomarkdoc..." && \ @command -v gomarkdoc || (echo "==> We need to install gomarkdoc..." && \
go install github.com/princjef/gomarkdoc/cmd/gomarkdoc@latest) go install github.com/princjef/gomarkdoc/cmd/gomarkdoc@latest)
@@ -241,16 +228,3 @@ __label_doc:
gomarkdoc --repository.default-branch $(shell git branch --show-current) -o doc/docs/packages/$$pack.md $$pack 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 sed -i '/^## Index/,/^##/ { /## Index/d; /^##/! d }' doc/docs/packages/$$pack.md
done 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 \
.

View File

@@ -179,7 +179,7 @@ services:
# this will use the database secrets and environment, # this will use the database secrets and environment,
# see the "database" service to see the values # see the "database" service to see the values
katenary.v3/values-from: |- katenary.v3/values-from: |-
DB_USER: database.MARIADB_USER DB_USER: databse.MARIADB_USER
DB_PASSWORD: database.MARIADB_PASSWORD DB_PASSWORD: database.MARIADB_PASSWORD
database: database:

View File

@@ -10,11 +10,10 @@ import (
"katenary/generator/katenaryfile" "katenary/generator/katenaryfile"
"katenary/generator/labels" "katenary/generator/labels"
"katenary/utils" "katenary/utils"
"log"
"os" "os"
"strings" "strings"
"github.com/compose-spec/compose-go/cli" "github.com/compose-spec/compose-go/v2/cli"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -25,10 +24,7 @@ Each [command] and subcommand has got an "help" and "--help" flag to show more i
func main() { func main() {
rootCmd := buildRootCmd() rootCmd := buildRootCmd()
rootCmd.Execute()
if err := rootCmd.Execute(); err != nil {
log.Fatal(err)
}
} }
func buildRootCmd() *cobra.Command { func buildRootCmd() *cobra.Command {
@@ -101,26 +97,26 @@ func generateCompletionCommand(name string) *cobra.Command {
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
Short: "Generates completion scripts", Short: "Generates completion scripts",
Long: fmt.Sprintf(completionHelp, name), Long: fmt.Sprintf(completionHelp, name),
RunE: func(cmd *cobra.Command, args []string) error { Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 { if len(args) == 0 {
return cmd.Help() cmd.Help()
return
} }
switch args[0] { switch args[0] {
case "bash": case "bash":
// get the bash version // get the bash version
if cmd.Flags().Changed("bash-v1") { if cmd.Flags().Changed("bash-v1") {
return cmd.Root().GenBashCompletion(os.Stdout) cmd.Root().GenBashCompletion(os.Stdout)
return
} }
return cmd.Root().GenBashCompletionV2(os.Stdout, true) cmd.Root().GenBashCompletionV2(os.Stdout, true)
case "zsh": case "zsh":
return cmd.Root().GenZshCompletion(os.Stdout) cmd.Root().GenZshCompletion(os.Stdout)
case "fish": case "fish":
return cmd.Root().GenFishCompletion(os.Stdout, true) cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell": case "powershell":
return cmd.Root().GenPowerShellCompletion(os.Stdout) cmd.Root().GenPowerShellCompletion(os.Stdout)
} }
return fmt.Errorf("unknown completion type: %s", args[0])
}, },
} }
@@ -146,7 +142,7 @@ func generateConvertCommand() *cobra.Command {
Use: "convert", Use: "convert",
Short: "Converts a docker-compose file to a Helm Chart", Short: "Converts a docker-compose file to a Helm Chart",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if len(strings.TrimSpace(givenAppVersion)) > 0 { if givenAppVersion != "" {
appVersion = &givenAppVersion appVersion = &givenAppVersion
} }
return generator.Convert(generator.ConvertOptions{ return generator.Convert(generator.ConvertOptions{

View File

@@ -1,5 +0,0 @@
MD012: false
MD013: false
MD022: false
MD033: false
MD046: false

View File

@@ -460,9 +460,9 @@ database:
MARIADB_USER: myuser MARIADB_USER: myuser
MARIADB_PASSWORD: mypassword MARIADB_PASSWORD: mypassword
labels: labels:
# we can declare secrets # it can be a secret
katenary.v3/secrets: |- katenary.v3/secrets: |-
- MARIADB_PASSWORD - DB_PASSWORD
php: php:
image: php:7.4-fpm image: php:7.4-fpm
environment: environment:

View File

@@ -6,7 +6,7 @@
import "katenary/generator" import "katenary/generator"
``` ```
Package generator generates kubernetes objects from a "compose" file and transforms them into a helm chart. The generator package generates kubernetes objects from a "compose" file and transforms them into a helm chart.
The generator package is the core of katenary. It is responsible for generating kubernetes objects from a compose file and transforming them into a helm chart. Conversion manipulates Yaml representation of kubernetes object to add conditions, labels, annotations, etc. to the objects. It also create the values to be set to the values.yaml file. The generator package is the core of katenary. It is responsible for generating kubernetes objects from a compose file and transforming them into a helm chart. Conversion manipulates Yaml representation of kubernetes object to add conditions, labels, annotations, etc. to the objects. It also create the values to be set to the values.yaml file.
@@ -35,7 +35,7 @@ var Version = "master" // changed at compile time
``` ```
<a name="Convert"></a> <a name="Convert"></a>
## func [Convert](<https://github.com/metal3d/katenary/blob/develop/generator/converter.go#L99>) ## func [Convert](<https://github.com/metal3d/katenary/blob/develop/generator/converter.go#L93>)
```go ```go
func Convert(config ConvertOptions, dockerComposeFile ...string) error func Convert(config ConvertOptions, dockerComposeFile ...string) error
@@ -92,7 +92,7 @@ NewCronJob creates a new CronJob from a compose service. The appName is the name
## func [ToK8SYaml](<https://github.com/metal3d/katenary/blob/develop/generator/utils.go#L90>) ## func [ToK8SYaml](<https://github.com/metal3d/katenary/blob/develop/generator/utils.go#L90>)
```go ```go
func ToK8SYaml(obj any) ([]byte, error) func ToK8SYaml(obj interface{}) ([]byte, error)
``` ```
@@ -107,7 +107,7 @@ func UnWrapTPL(in []byte) []byte
UnWrapTPL removes the line wrapping from a template. UnWrapTPL removes the line wrapping from a template.
<a name="ChartTemplate"></a> <a name="ChartTemplate"></a>
## type [ChartTemplate](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L21-L24>) ## type [ChartTemplate](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L19-L22>)
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. 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.
@@ -149,7 +149,7 @@ func NewConfigMapFromDirectory(service types.ServiceConfig, appName, path string
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. 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.AddBinaryData"></a> <a name="ConfigMap.AddBinaryData"></a>
### func \(\*ConfigMap\) [AddBinaryData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L157>) ### func \(\*ConfigMap\) [AddBinaryData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L155>)
```go ```go
func (c *ConfigMap) AddBinaryData(key string, value []byte) func (c *ConfigMap) AddBinaryData(key string, value []byte)
@@ -158,7 +158,7 @@ func (c *ConfigMap) AddBinaryData(key string, value []byte)
AddBinaryData adds binary data to the configmap. Append or overwrite the value if the key already exists. AddBinaryData adds binary data to the configmap. Append or overwrite the value if the key already exists.
<a name="ConfigMap.AddData"></a> <a name="ConfigMap.AddData"></a>
### func \(\*ConfigMap\) [AddData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L152>) ### func \(\*ConfigMap\) [AddData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L150>)
```go ```go
func (c *ConfigMap) AddData(key, value string) func (c *ConfigMap) AddData(key, value string)
@@ -167,16 +167,16 @@ 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. AddData adds a key value pair to the configmap. Append or overwrite the value if the key already exists.
<a name="ConfigMap.AppendDir"></a> <a name="ConfigMap.AppendDir"></a>
### func \(\*ConfigMap\) [AppendDir](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L166>) ### func \(\*ConfigMap\) [AppendDir](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L164>)
```go ```go
func (c *ConfigMap) AppendDir(path string) error func (c *ConfigMap) AppendDir(path string) error
``` ```
AppendDir 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. 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> <a name="ConfigMap.AppendFile"></a>
### func \(\*ConfigMap\) [AppendFile](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L213>) ### func \(\*ConfigMap\) [AppendFile](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L211>)
```go ```go
func (c *ConfigMap) AppendFile(path string) error func (c *ConfigMap) AppendFile(path string) error
@@ -185,7 +185,7 @@ func (c *ConfigMap) AppendFile(path string) error
<a name="ConfigMap.Filename"></a> <a name="ConfigMap.Filename"></a>
### func \(\*ConfigMap\) [Filename](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L237>) ### func \(\*ConfigMap\) [Filename](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L235>)
```go ```go
func (c *ConfigMap) Filename() string func (c *ConfigMap) Filename() string
@@ -194,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. Filename returns the filename of the configmap. If the configmap is used for files, the filename contains the path.
<a name="ConfigMap.SetData"></a> <a name="ConfigMap.SetData"></a>
### func \(\*ConfigMap\) [SetData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L247>) ### func \(\*ConfigMap\) [SetData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L245>)
```go ```go
func (c *ConfigMap) SetData(data map[string]string) func (c *ConfigMap) SetData(data map[string]string)
@@ -203,7 +203,7 @@ func (c *ConfigMap) SetData(data map[string]string)
SetData sets the data of the configmap. It replaces the entire data. SetData sets the data of the configmap. It replaces the entire data.
<a name="ConfigMap.Yaml"></a> <a name="ConfigMap.Yaml"></a>
### func \(\*ConfigMap\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L252>) ### func \(\*ConfigMap\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L250>)
```go ```go
func (c *ConfigMap) Yaml() ([]byte, error) func (c *ConfigMap) Yaml() ([]byte, error)
@@ -223,7 +223,7 @@ type ConfigMapMount struct {
``` ```
<a name="ConvertOptions"></a> <a name="ConvertOptions"></a>
## type [ConvertOptions](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L27-L36>) ## type [ConvertOptions](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L25-L34>)
ConvertOptions are the options to convert a compose project to a helm chart. ConvertOptions are the options to convert a compose project to a helm chart.
@@ -275,7 +275,7 @@ Yaml returns the yaml representation of the cronjob.
Implements the Yaml interface. Implements the Yaml interface.
<a name="CronJobValue"></a> <a name="CronJobValue"></a>
## type [CronJobValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L118-L123>) ## type [CronJobValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L113-L118>)
CronJobValue is a cronjob configuration that will be saved in values.yaml. CronJobValue is a cronjob configuration that will be saved in values.yaml.
@@ -376,7 +376,7 @@ func (d *Deployment) BindFrom(service types.ServiceConfig, binded *Deployment)
<a name="Deployment.BindMapFilesToContainer"></a> <a name="Deployment.BindMapFilesToContainer"></a>
### func \(\*Deployment\) [BindMapFilesToContainer](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L374>) ### func \(\*Deployment\) [BindMapFilesToContainer](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L377>)
```go ```go
func (d *Deployment) BindMapFilesToContainer(service types.ServiceConfig, secrets []string, appName string) (*corev1.Container, int) func (d *Deployment) BindMapFilesToContainer(service types.ServiceConfig, secrets []string, appName string) (*corev1.Container, int)
@@ -403,7 +403,7 @@ func (d *Deployment) Filename() string
Filename returns the filename of the deployment. Filename returns the filename of the deployment.
<a name="Deployment.MountExchangeVolumes"></a> <a name="Deployment.MountExchangeVolumes"></a>
### func \(\*Deployment\) [MountExchangeVolumes](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L425>) ### func \(\*Deployment\) [MountExchangeVolumes](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L428>)
```go ```go
func (d *Deployment) MountExchangeVolumes() func (d *Deployment) MountExchangeVolumes()
@@ -421,7 +421,7 @@ func (d *Deployment) SetEnvFrom(service types.ServiceConfig, appName string, sam
SetEnvFrom sets the environment variables to a configmap. The configmap is created. SetEnvFrom sets the environment variables to a configmap. The configmap is created.
<a name="Deployment.Yaml"></a> <a name="Deployment.Yaml"></a>
### func \(\*Deployment\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L449>) ### func \(\*Deployment\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/deployment.go#L452>)
```go ```go
func (d *Deployment) Yaml() ([]byte, error) func (d *Deployment) Yaml() ([]byte, error)
@@ -448,7 +448,7 @@ const (
``` ```
<a name="HelmChart"></a> <a name="HelmChart"></a>
## type [HelmChart](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L40-L53>) ## type [HelmChart](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L38-L51>)
HelmChart is a Helm Chart representation. It contains all the templates, values, versions, helpers... HelmChart is a Helm Chart representation. It contains all the templates, values, versions, helpers...
@@ -460,18 +460,18 @@ type HelmChart struct {
Name string `yaml:"name"` Name string `yaml:"name"`
Icon string `yaml:"icon,omitempty"` Icon string `yaml:"icon,omitempty"`
APIVersion string `yaml:"apiVersion"` ApiVersion string `yaml:"apiVersion"`
Version string `yaml:"version"` Version string `yaml:"version"`
AppVersion string `yaml:"appVersion"` AppVersion string `yaml:"appVersion"`
Description string `yaml:"description"` Description string `yaml:"description"`
Helper string `yaml:"-"` Helper string `yaml:"-"`
Dependencies []labelstructs.Dependency `yaml:"dependencies,omitempty"` Dependencies []labelStructs.Dependency `yaml:"dependencies,omitempty"`
// contains filtered or unexported fields // contains filtered or unexported fields
} }
``` ```
<a name="Generate"></a> <a name="Generate"></a>
### func [Generate](<https://github.com/metal3d/katenary/blob/develop/generator/generator.go#L31>) ### func [Generate](<https://github.com/metal3d/katenary/blob/develop/generator/generator.go#L29>)
```go ```go
func Generate(project *types.Project) (*HelmChart, error) func Generate(project *types.Project) (*HelmChart, error)
@@ -491,7 +491,7 @@ The Generate function will create the HelmChart object this way:
- Merge the same\-pod services. - Merge the same\-pod services.
<a name="NewChart"></a> <a name="NewChart"></a>
### func [NewChart](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L56>) ### func [NewChart](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L54>)
```go ```go
func NewChart(name string) *HelmChart func NewChart(name string) *HelmChart
@@ -500,7 +500,7 @@ func NewChart(name string) *HelmChart
NewChart creates a new empty chart with the given name. NewChart creates a new empty chart with the given name.
<a name="HelmChart.SaveTemplates"></a> <a name="HelmChart.SaveTemplates"></a>
### func \(\*HelmChart\) [SaveTemplates](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L71>) ### func \(\*HelmChart\) [SaveTemplates](<https://github.com/metal3d/katenary/blob/develop/generator/chart.go#L69>)
```go ```go
func (chart *HelmChart) SaveTemplates(templateDir string) func (chart *HelmChart) SaveTemplates(templateDir string)
@@ -509,7 +509,7 @@ func (chart *HelmChart) SaveTemplates(templateDir string)
SaveTemplates the templates of the chart to the given directory. SaveTemplates the templates of the chart to the given directory.
<a name="Ingress"></a> <a name="Ingress"></a>
## type [Ingress](<https://github.com/metal3d/katenary/blob/develop/generator/ingress.go#L17-L21>) ## type [Ingress](<https://github.com/metal3d/katenary/blob/develop/generator/ingress.go#L17-L20>)
@@ -521,7 +521,7 @@ type Ingress struct {
``` ```
<a name="NewIngress"></a> <a name="NewIngress"></a>
### func [NewIngress](<https://github.com/metal3d/katenary/blob/develop/generator/ingress.go#L24>) ### func [NewIngress](<https://github.com/metal3d/katenary/blob/develop/generator/ingress.go#L23>)
```go ```go
func NewIngress(service types.ServiceConfig, Chart *HelmChart) *Ingress func NewIngress(service types.ServiceConfig, Chart *HelmChart) *Ingress
@@ -530,7 +530,7 @@ func NewIngress(service types.ServiceConfig, Chart *HelmChart) *Ingress
NewIngress creates a new Ingress from a compose service. NewIngress creates a new Ingress from a compose service.
<a name="Ingress.Filename"></a> <a name="Ingress.Filename"></a>
### func \(\*Ingress\) [Filename](<https://github.com/metal3d/katenary/blob/develop/generator/ingress.go#L128>) ### func \(\*Ingress\) [Filename](<https://github.com/metal3d/katenary/blob/develop/generator/ingress.go#L122>)
```go ```go
func (ingress *Ingress) Filename() string func (ingress *Ingress) Filename() string
@@ -539,7 +539,7 @@ func (ingress *Ingress) Filename() string
<a name="Ingress.Yaml"></a> <a name="Ingress.Yaml"></a>
### func \(\*Ingress\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/ingress.go#L132>) ### func \(\*Ingress\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/ingress.go#L126>)
```go ```go
func (ingress *Ingress) Yaml() ([]byte, error) func (ingress *Ingress) Yaml() ([]byte, error)
@@ -548,7 +548,7 @@ func (ingress *Ingress) Yaml() ([]byte, error)
<a name="IngressValue"></a> <a name="IngressValue"></a>
## type [IngressValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L29-L36>) ## type [IngressValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L28-L35>)
IngressValue is a ingress configuration that will be saved in values.yaml. IngressValue is a ingress configuration that will be saved in values.yaml.
@@ -809,19 +809,18 @@ func (r *ServiceAccount) Yaml() ([]byte, error)
<a name="TLS"></a> <a name="TLS"></a>
## type [TLS](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L23-L26>) ## type [TLS](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L23-L25>)
```go ```go
type TLS struct { type TLS struct {
Enabled bool `yaml:"enabled"` Enabled bool `yaml:"enabled"`
SecretName string `yaml:"secretName"`
} }
``` ```
<a name="Value"></a> <a name="Value"></a>
## type [Value](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L39-L50>) ## type [Value](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L38-L49>)
Value will be saved in values.yaml. It contains configuration for all deployment and services. Value will be saved in values.yaml. It contains configuration for all deployment and services.
@@ -841,7 +840,7 @@ type Value struct {
``` ```
<a name="NewValue"></a> <a name="NewValue"></a>
### func [NewValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L57>) ### func [NewValue](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L56>)
```go ```go
func NewValue(service types.ServiceConfig, main ...bool) *Value func NewValue(service types.ServiceConfig, main ...bool) *Value
@@ -852,7 +851,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. If \`main\` is true, the tag will be empty because it will be set in the helm chart appVersion.
<a name="Value.AddIngress"></a> <a name="Value.AddIngress"></a>
### func \(\*Value\) [AddIngress](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L90>) ### func \(\*Value\) [AddIngress](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L89>)
```go ```go
func (v *Value) AddIngress(host, path string) func (v *Value) AddIngress(host, path string)
@@ -861,7 +860,7 @@ func (v *Value) AddIngress(host, path string)
<a name="Value.AddPersistence"></a> <a name="Value.AddPersistence"></a>
### func \(\*Value\) [AddPersistence](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L104>) ### func \(\*Value\) [AddPersistence](<https://github.com/metal3d/katenary/blob/develop/generator/values.go#L99>)
```go ```go
func (v *Value) AddPersistence(volumeName string) func (v *Value) AddPersistence(volumeName string)

View File

@@ -6,7 +6,7 @@
import "katenary/generator/extrafiles" import "katenary/generator/extrafiles"
``` ```
Package extrafiles provides function to generate the Chart files that are not objects. Like README.md and notes.txt... extrafiles package provides function to generate the Chart files that are not objects. Like README.md and notes.txt...
## func [NotesFile](<https://github.com/metal3d/katenary/blob/develop/generator/extrafiles/notes.go#L13>) ## func [NotesFile](<https://github.com/metal3d/katenary/blob/develop/generator/extrafiles/notes.go#L13>)
@@ -17,7 +17,7 @@ func NotesFile(services []string) string
NotesFile returns the content of the note.txt file. NotesFile returns the content of the note.txt file.
<a name="ReadMeFile"></a> <a name="ReadMeFile"></a>
## func [ReadMeFile](<https://github.com/metal3d/katenary/blob/develop/generator/extrafiles/readme.go#L46>) ## func [ReadMeFile](<https://github.com/metal3d/katenary/blob/develop/generator/extrafiles/readme.go#L45>)
```go ```go
func ReadMeFile(charname, description string, values map[string]any) string func ReadMeFile(charname, description string, values map[string]any) string

View File

@@ -12,7 +12,7 @@ A katenary file, named "katenary.yml" or "katenary.yaml", is a file where you ca
Formely, the file describe the same structure as in labels, and so that can be validated and completed by LSP. It also ease the use of katenary. Formely, the file describe the same structure as in labels, and so that can be validated and completed by LSP. It also ease the use of katenary.
## func [GenerateSchema](<https://github.com/metal3d/katenary/blob/develop/generator/katenaryfile/main.go#L137>) ## func [GenerateSchema](<https://github.com/metal3d/katenary/blob/develop/generator/katenaryfile/main.go#L131>)
```go ```go
func GenerateSchema() string func GenerateSchema() string
@@ -38,20 +38,20 @@ Service is a struct that contains the service configuration for katenary
type Service struct { type Service struct {
MainApp *bool `json:"main-app,omitempty" jsonschema:"title=Is this service the main application"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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)"` ValuesFrom *labelStructs.ValueFrom `json:"values-from,omitempty" jsonschema:"title=Values From,description=Inject values from another service (secret or configmap environment variables)"`
} }
``` ```

View File

@@ -6,8 +6,6 @@
import "katenary/generator/labels" import "katenary/generator/labels"
``` ```
Package labels provides functionality to parse and manipulate labels.
## Constants ## Constants
<a name="KatenaryLabelPrefix"></a> <a name="KatenaryLabelPrefix"></a>
@@ -17,16 +15,16 @@ const KatenaryLabelPrefix = "katenary.v3"
``` ```
<a name="GetLabelHelp"></a> <a name="GetLabelHelp"></a>
## func [GetLabelHelp](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L88>) ## func [GetLabelHelp](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L87>)
```go ```go
func GetLabelHelp(asMarkdown bool) string func GetLabelHelp(asMarkdown bool) string
``` ```
GetLabelHelp return the help for the labels. Generate the help for the labels.
<a name="GetLabelHelpFor"></a> <a name="GetLabelHelpFor"></a>
## func [GetLabelHelpFor](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L97>) ## func [GetLabelHelpFor](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L96>)
```go ```go
func GetLabelHelpFor(labelname string, asMarkdown bool) string func GetLabelHelpFor(labelname string, asMarkdown bool) string
@@ -35,7 +33,7 @@ func GetLabelHelpFor(labelname string, asMarkdown bool) string
GetLabelHelpFor returns the help for a specific label. GetLabelHelpFor returns the help for a specific label.
<a name="GetLabelNames"></a> <a name="GetLabelNames"></a>
## func [GetLabelNames](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L72>) ## func [GetLabelNames](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L71>)
```go ```go
func GetLabelNames() []string func GetLabelNames() []string
@@ -44,7 +42,7 @@ func GetLabelNames() []string
GetLabelNames returns a sorted list of all katenary label names. GetLabelNames returns a sorted list of all katenary label names.
<a name="Prefix"></a> <a name="Prefix"></a>
## func [Prefix](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L235>) ## func [Prefix](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L224>)
```go ```go
func Prefix() string func Prefix() string
@@ -53,7 +51,7 @@ func Prefix() string
<a name="Help"></a> <a name="Help"></a>
## type [Help](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L64-L69>) ## type [Help](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L63-L68>)
Help is the documentation of a label. Help is the documentation of a label.
@@ -67,7 +65,7 @@ type Help struct {
``` ```
<a name="Label"></a> <a name="Label"></a>
## type [Label](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L57>) ## type [Label](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L56>)
Label is a katenary label to find in compose files. Label is a katenary label to find in compose files.
@@ -99,7 +97,7 @@ const (
``` ```
<a name="LabelName"></a> <a name="LabelName"></a>
### func [LabelName](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L59>) ### func [LabelName](<https://github.com/metal3d/katenary/blob/develop/generator/labels/katenaryLabels.go#L58>)
```go ```go
func LabelName(name string) Label func LabelName(name string) Label

View File

@@ -1,246 +0,0 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->
# 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](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/configMap.go#L5>)
```go
type ConfigMapFile []string
```
<a name="ConfigMapFileFrom"></a>
### func [ConfigMapFileFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/configMap.go#L7>)
```go
func ConfigMapFileFrom(data string) (ConfigMapFile, error)
```
<a name="CronJob"></a>
## type [CronJob](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/cronJob.go#L5-L10>)
```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"`
}
```
<a name="CronJobFrom"></a>
### func [CronJobFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/cronJob.go#L12>)
```go
func CronJobFrom(data string) (*CronJob, error)
```
<a name="Dependency"></a>
## type [Dependency](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/dependencies.go#L6-L12>)
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"`
}
```
<a name="DependenciesFrom"></a>
### func [DependenciesFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/dependencies.go#L15>)
```go
func DependenciesFrom(data string) ([]Dependency, error)
```
DependenciesFrom returns a slice of dependencies from the given string.
<a name="EnvFrom"></a>
## type [EnvFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/envFrom.go#L5>)
```go
type EnvFrom []string
```
<a name="EnvFromFrom"></a>
### func [EnvFromFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/envFrom.go#L8>)
```go
func EnvFromFrom(data string) (EnvFrom, error)
```
EnvFromFrom returns a EnvFrom from the given string.
<a name="ExchangeVolume"></a>
## type [ExchangeVolume](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/exchangeVolume.go#L5-L10>)
```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"`
}
```
<a name="NewExchangeVolumes"></a>
### func [NewExchangeVolumes](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/exchangeVolume.go#L12>)
```go
func NewExchangeVolumes(data string) ([]*ExchangeVolume, error)
```
<a name="HealthCheck"></a>
## type [HealthCheck](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/probes.go#L11-L14>)
```go
type HealthCheck struct {
LivenessProbe *corev1.Probe `yaml:"livenessProbe,omitempty" json:"livenessProbe,omitempty"`
ReadinessProbe *corev1.Probe `yaml:"readinessProbe,omitempty" json:"readinessProbe,omitempty"`
}
```
<a name="ProbeFrom"></a>
### func [ProbeFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/probes.go#L16>)
```go
func ProbeFrom(data string) (*HealthCheck, error)
```
<a name="Ingress"></a>
## type [Ingress](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/ingress.go#L14-L22>)
```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"`
}
```
<a name="IngressFrom"></a>
### func [IngressFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/ingress.go#L25>)
```go
func IngressFrom(data string) (*Ingress, error)
```
IngressFrom creates a new Ingress from a compose service.
<a name="MapEnv"></a>
## type [MapEnv](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/mapenv.go#L5>)
```go
type MapEnv map[string]string
```
<a name="MapEnvFrom"></a>
### func [MapEnvFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/mapenv.go#L8>)
```go
func MapEnvFrom(data string) (MapEnv, error)
```
MapEnvFrom returns a MapEnv from the given string.
<a name="Ports"></a>
## type [Ports](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/ports.go#L5>)
```go
type Ports []uint32
```
<a name="PortsFrom"></a>
### func [PortsFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/ports.go#L8>)
```go
func PortsFrom(data string) (Ports, error)
```
PortsFrom returns a Ports from the given string.
<a name="Secrets"></a>
## type [Secrets](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/secrets.go#L5>)
```go
type Secrets []string
```
<a name="SecretsFrom"></a>
### func [SecretsFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/secrets.go#L7>)
```go
func SecretsFrom(data string) (Secrets, error)
```
<a name="TLS"></a>
## type [TLS](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/ingress.go#L10-L12>)
```go
type TLS struct {
Enabled bool `yaml:"enabled" json:"enabled,omitempty"`
}
```
<a name="ValueFrom"></a>
## type [ValueFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/valueFrom.go#L5>)
```go
type ValueFrom map[string]string
```
<a name="GetValueFrom"></a>
### func [GetValueFrom](<https://github.com/metal3d/katenary/blob/develop/generator/labels/labelstructs/valueFrom.go#L7>)
```go
func GetValueFrom(data string) (*ValueFrom, error)
```
Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)

View File

@@ -6,7 +6,7 @@
import "katenary/parser" import "katenary/parser"
``` ```
Package parser is a wrapper around compose\-go to parse compose files. Parser package is a wrapper around compose\-go to parse compose files.
## func [Parse](<https://github.com/metal3d/katenary/blob/develop/parser/main.go#L29>) ## func [Parse](<https://github.com/metal3d/katenary/blob/develop/parser/main.go#L29>)

View File

@@ -0,0 +1,60 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->
# update
```go
import "katenary/update"
```
Update package is used to check if a new version of katenary is available.
## Variables
<a name="Version"></a>
```go
var (
Version = "master" // reset by cmd/main.go
)
```
<a name="DownloadFile"></a>
## func [DownloadFile](<https://github.com/metal3d/katenary/blob/develop/update/main.go#L134>)
```go
func DownloadFile(url, exe string) error
```
DownloadFile will download a url to a local file. It also ensure that the file is executable.
<a name="DownloadLatestVersion"></a>
## func [DownloadLatestVersion](<https://github.com/metal3d/katenary/blob/develop/update/main.go#L80>)
```go
func DownloadLatestVersion(assets []Asset) error
```
DownloadLatestVersion will download the latest version of katenary.
<a name="Asset"></a>
## type [Asset](<https://github.com/metal3d/katenary/blob/develop/update/main.go#L23-L26>)
Asset is a github asset from release url.
```go
type Asset struct {
Name string `json:"name"`
URL string `json:"browser_download_url"`
}
```
<a name="CheckLatestVersion"></a>
### func [CheckLatestVersion](<https://github.com/metal3d/katenary/blob/develop/update/main.go#L29>)
```go
func CheckLatestVersion() (string, []Asset, error)
```
CheckLatestVersion check katenary latest version from release and propose to download it
Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)

View File

@@ -6,18 +6,9 @@
import "katenary/utils" import "katenary/utils"
``` ```
Package utils provides some utility functions used in katenary. It defines some constants and functions used in the whole project. Utils package provides some utility functions used in katenary. It defines some constants and functions used in the whole project.
## Constants ## func [AsResourceName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L191>)
<a name="DirectoryPermission"></a>DirectoryPermission is the default values for permissions apply to created directories.
```go
const DirectoryPermission = 0o755
```
<a name="AsResourceName"></a>
## func [AsResourceName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L196>)
```go ```go
func AsResourceName(name string) string func AsResourceName(name string) string
@@ -26,7 +17,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. AsResourceName returns a resource name with underscores to respect the kubernetes naming convention. It's the opposite of FixedResourceName.
<a name="Confirm"></a> <a name="Confirm"></a>
## func [Confirm](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L164>) ## func [Confirm](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L161>)
```go ```go
func Confirm(question string, icon ...Icon) bool func Confirm(question string, icon ...Icon) bool
@@ -35,7 +26,7 @@ func Confirm(question string, icon ...Icon) bool
Confirm asks a question and returns true if the answer is y. Confirm asks a question and returns true if the answer is y.
<a name="CountStartingSpaces"></a> <a name="CountStartingSpaces"></a>
## func [CountStartingSpaces](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L41>) ## func [CountStartingSpaces](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L38>)
```go ```go
func CountStartingSpaces(line string) int func CountStartingSpaces(line string) int
@@ -44,7 +35,7 @@ func CountStartingSpaces(line string) int
CountStartingSpaces counts the number of spaces at the beginning of a string. CountStartingSpaces counts the number of spaces at the beginning of a string.
<a name="EncodeBasicYaml"></a> <a name="EncodeBasicYaml"></a>
## func [EncodeBasicYaml](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L178>) ## func [EncodeBasicYaml](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L173>)
```go ```go
func EncodeBasicYaml(data any) ([]byte, error) func EncodeBasicYaml(data any) ([]byte, error)
@@ -53,7 +44,7 @@ func EncodeBasicYaml(data any) ([]byte, error)
EncodeBasicYaml encodes a basic yaml from an interface. EncodeBasicYaml encodes a basic yaml from an interface.
<a name="FixedResourceName"></a> <a name="FixedResourceName"></a>
## func [FixedResourceName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L190>) ## func [FixedResourceName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L185>)
```go ```go
func FixedResourceName(name string) string func FixedResourceName(name string) string
@@ -62,7 +53,7 @@ func FixedResourceName(name string) string
FixedResourceName returns a resource name without underscores to respect the kubernetes naming convention. FixedResourceName returns a resource name without underscores to respect the kubernetes naming convention.
<a name="GetContainerByName"></a> <a name="GetContainerByName"></a>
## func [GetContainerByName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L87>) ## func [GetContainerByName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L84>)
```go ```go
func GetContainerByName(name string, containers []corev1.Container) (*corev1.Container, int) func GetContainerByName(name string, containers []corev1.Container) (*corev1.Container, int)
@@ -71,7 +62,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. GetContainerByName returns a container by name and its index in the array. It returns nil, \-1 if not found.
<a name="GetKind"></a> <a name="GetKind"></a>
## func [GetKind](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L54>) ## func [GetKind](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L51>)
```go ```go
func GetKind(path string) (kind string) func GetKind(path string) (kind string)
@@ -80,7 +71,7 @@ func GetKind(path string) (kind string)
GetKind returns the kind of the resource from the file path. GetKind returns the kind of the resource from the file path.
<a name="GetServiceNameByPort"></a> <a name="GetServiceNameByPort"></a>
## func [GetServiceNameByPort](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L77>) ## func [GetServiceNameByPort](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L74>)
```go ```go
func GetServiceNameByPort(port int) string func GetServiceNameByPort(port int) string
@@ -89,7 +80,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. GetServiceNameByPort returns the service name for a port. It the service name is not found, it returns an empty string.
<a name="GetValuesFromLabel"></a> <a name="GetValuesFromLabel"></a>
## func [GetValuesFromLabel](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L129>) ## func [GetValuesFromLabel](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L126>)
```go ```go
func GetValuesFromLabel(service types.ServiceConfig, LabelValues string) map[string]*EnvConfig func GetValuesFromLabel(service types.ServiceConfig, LabelValues string) map[string]*EnvConfig
@@ -107,7 +98,7 @@ func HashComposefiles(files []string) (string, error)
HashComposefiles returns a hash of the compose files. HashComposefiles returns a hash of the compose files.
<a name="Int32Ptr"></a> <a name="Int32Ptr"></a>
## func [Int32Ptr](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L35>) ## func [Int32Ptr](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L32>)
```go ```go
func Int32Ptr(i int32) *int32 func Int32Ptr(i int32) *int32
@@ -116,7 +107,7 @@ func Int32Ptr(i int32) *int32
Int32Ptr returns a pointer to an int32. Int32Ptr returns a pointer to an int32.
<a name="PathToName"></a> <a name="PathToName"></a>
## func [PathToName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L106>) ## func [PathToName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L103>)
```go ```go
func PathToName(path string) string func PathToName(path string) string
@@ -125,7 +116,7 @@ func PathToName(path string) string
PathToName converts a path to a kubernetes complient name. PathToName converts a path to a kubernetes complient name.
<a name="StrPtr"></a> <a name="StrPtr"></a>
## func [StrPtr](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L38>) ## func [StrPtr](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L35>)
```go ```go
func StrPtr(s string) *string func StrPtr(s string) *string
@@ -134,7 +125,7 @@ func StrPtr(s string) *string
StrPtr returns a pointer to a string. StrPtr returns a pointer to a string.
<a name="TplName"></a> <a name="TplName"></a>
## func [TplName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L22>) ## func [TplName](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L19>)
```go ```go
func TplName(serviceName, appname string, suffix ...string) string func TplName(serviceName, appname string, suffix ...string) string
@@ -143,25 +134,25 @@ 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. 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> <a name="TplValue"></a>
## func [TplValue](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L97>) ## func [TplValue](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L94>)
```go ```go
func TplValue(serviceName, variable string, pipes ...string) string func TplValue(serviceName, variable string, pipes ...string) string
``` ```
TplValue returns a container by name and its index in the array. GetContainerByName returns a container by name and its index in the array.
<a name="Warn"></a> <a name="Warn"></a>
## func [Warn](<https://github.com/metal3d/katenary/blob/develop/utils/icons.go#L25>) ## func [Warn](<https://github.com/metal3d/katenary/blob/develop/utils/icons.go#L25>)
```go ```go
func Warn(msg ...any) func Warn(msg ...interface{})
``` ```
Warn prints a warning message Warn prints a warning message
<a name="WordWrap"></a> <a name="WordWrap"></a>
## func [WordWrap](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L159>) ## func [WordWrap](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L156>)
```go ```go
func WordWrap(text string, lineWidth int) string func WordWrap(text string, lineWidth int) string
@@ -170,7 +161,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. 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> <a name="Wrap"></a>
## func [Wrap](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L71>) ## func [Wrap](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L68>)
```go ```go
func Wrap(src, above, below string) string func Wrap(src, above, below string) string
@@ -179,7 +170,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. Wrap wraps a string with a string above and below. It will respect the indentation of the src string.
<a name="EnvConfig"></a> <a name="EnvConfig"></a>
## type [EnvConfig](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L123-L126>) ## type [EnvConfig](<https://github.com/metal3d/katenary/blob/develop/utils/utils.go#L120-L123>)
EnvConfig is a struct to hold the description of an environment variable. EnvConfig is a struct to hold the description of an environment variable.

View File

@@ -54,6 +54,7 @@ nav:
- Go Packages: - Go Packages:
- packages/cmd/katenary.md - packages/cmd/katenary.md
- packages/parser.md - packages/parser.md
- packages/update.md
- packages/utils.md - packages/utils.md
- Generator: - Generator:
- Index: packages/generator.md - Index: packages/generator.md

View File

@@ -3,16 +3,14 @@ package generator
import ( import (
"fmt" "fmt"
"katenary/generator/labels" "katenary/generator/labels"
"katenary/generator/labels/labelstructs" "katenary/generator/labels/labelStructs"
"katenary/utils" "katenary/utils"
"log" "log"
"maps"
"os" "os"
"path/filepath" "path/filepath"
"slices"
"strings" "strings"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
) )
@@ -44,12 +42,12 @@ type HelmChart struct {
composeHash *string `yaml:"-"` composeHash *string `yaml:"-"`
Name string `yaml:"name"` Name string `yaml:"name"`
Icon string `yaml:"icon,omitempty"` Icon string `yaml:"icon,omitempty"`
APIVersion string `yaml:"apiVersion"` ApiVersion string `yaml:"apiVersion"`
Version string `yaml:"version"` Version string `yaml:"version"`
AppVersion string `yaml:"appVersion"` AppVersion string `yaml:"appVersion"`
Description string `yaml:"description"` Description string `yaml:"description"`
Helper string `yaml:"-"` 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. // NewChart creates a new empty chart with the given name.
@@ -58,7 +56,7 @@ func NewChart(name string) *HelmChart {
Name: name, Name: name,
Templates: make(map[string]*ChartTemplate, 0), Templates: make(map[string]*ChartTemplate, 0),
Description: "A Helm chart for " + name, Description: "A Helm chart for " + name,
APIVersion: "v2", ApiVersion: "v2",
Version: "", Version: "",
AppVersion: "", // set to 0.1.0 by default if no "main-app" label is found AppVersion: "", // set to 0.1.0 by default if no "main-app" label is found
Values: map[string]any{ Values: map[string]any{
@@ -95,7 +93,7 @@ func (chart *HelmChart) SaveTemplates(templateDir string) {
} }
servicename := template.Servicename servicename := template.Servicename
if err := os.MkdirAll(filepath.Join(templateDir, servicename), utils.DirectoryPermission); err != nil { if err := os.MkdirAll(filepath.Join(templateDir, servicename), 0o755); err != nil {
fmt.Println(utils.IconFailure, err) fmt.Println(utils.IconFailure, err)
os.Exit(1) os.Exit(1)
} }
@@ -103,7 +101,7 @@ func (chart *HelmChart) SaveTemplates(templateDir string) {
// if the name is a path, create the directory // if the name is a path, create the directory
if strings.Contains(name, string(filepath.Separator)) { if strings.Contains(name, string(filepath.Separator)) {
name = filepath.Join(templateDir, name) name = filepath.Join(templateDir, name)
err := os.MkdirAll(filepath.Dir(name), utils.DirectoryPermission) err := os.MkdirAll(filepath.Dir(name), 0o755)
if err != nil { if err != nil {
fmt.Println(utils.IconFailure, err) fmt.Println(utils.IconFailure, err)
os.Exit(1) os.Exit(1)
@@ -118,11 +116,9 @@ func (chart *HelmChart) SaveTemplates(templateDir string) {
fmt.Println(utils.IconFailure, err) fmt.Println(utils.IconFailure, err)
os.Exit(1) os.Exit(1)
} }
defer f.Close()
if _, err := f.Write(t); err != nil {
log.Fatal("error writing template file:", err)
}
f.Write(t)
f.Close()
} }
} }
@@ -130,7 +126,7 @@ func (chart *HelmChart) SaveTemplates(templateDir string) {
func (chart *HelmChart) generateConfigMapsAndSecrets(project *types.Project) error { func (chart *HelmChart) generateConfigMapsAndSecrets(project *types.Project) error {
appName := chart.Name appName := chart.Name
for _, s := range project.Services { for _, s := range project.Services {
if len(s.Environment) == 0 { if s.Environment == nil || len(s.Environment) == 0 {
continue continue
} }
@@ -138,10 +134,12 @@ func (chart *HelmChart) generateConfigMapsAndSecrets(project *types.Project) err
secretsVar := types.MappingWithEquals{} secretsVar := types.MappingWithEquals{}
// copy env to originalEnv // copy env to originalEnv
maps.Copy(originalEnv, s.Environment) for k, v := range s.Environment {
originalEnv[k] = v
}
if v, ok := s.Labels[labels.LabelSecrets]; ok { if v, ok := s.Labels[labels.LabelSecrets]; ok {
list, err := labelstructs.SecretsFrom(v) list, err := labelStructs.SecretsFrom(v)
if err != nil { if err != nil {
log.Fatal("error unmarshaling secrets label:", err) log.Fatal("error unmarshaling secrets label:", err)
} }
@@ -214,7 +212,7 @@ func (chart *HelmChart) generateDeployment(service types.ServiceConfig, deployme
if exchange, ok := service.Labels[labels.LabelExchangeVolume]; ok { if exchange, ok := service.Labels[labels.LabelExchangeVolume]; ok {
// we need to add a volume and a mount point // we need to add a volume and a mount point
ex, err := labelstructs.NewExchangeVolumes(exchange) ex, err := labelStructs.NewExchangeVolumes(exchange)
if err != nil { if err != nil {
return err return err
} }
@@ -298,7 +296,7 @@ func (chart *HelmChart) setCronJob(service types.ServiceConfig, appName string)
func (chart *HelmChart) setDependencies(service types.ServiceConfig) (bool, error) { func (chart *HelmChart) setDependencies(service types.ServiceConfig) (bool, error) {
// helm dependency // helm dependency
if v, ok := service.Labels[labels.LabelDependencies]; ok { if v, ok := service.Labels[labels.LabelDependencies]; ok {
d, err := labelstructs.DependenciesFrom(v) d, err := labelStructs.DependenciesFrom(v)
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -326,7 +324,7 @@ func (chart *HelmChart) setSharedConf(service types.ServiceConfig, deployments m
if _, ok := service.Labels[labels.LabelEnvFrom]; !ok { if _, ok := service.Labels[labels.LabelEnvFrom]; !ok {
return return
} }
fromservices, err := labelstructs.EnvFromFrom(service.Labels[labels.LabelEnvFrom]) fromservices, err := labelStructs.EnvFromFrom(service.Labels[labels.LabelEnvFrom])
if err != nil { if err != nil {
log.Fatal("error unmarshaling env-from label:", err) log.Fatal("error unmarshaling env-from label:", err)
} }
@@ -351,7 +349,7 @@ func (chart *HelmChart) setEnvironmentValuesFrom(service types.ServiceConfig, de
if _, ok := service.Labels[labels.LabelValueFrom]; !ok { if _, ok := service.Labels[labels.LabelValueFrom]; !ok {
return return
} }
mapping, err := labelstructs.GetValueFrom(service.Labels[labels.LabelValueFrom]) mapping, err := labelStructs.GetValueFrom(service.Labels[labels.LabelValueFrom])
if err != nil { if err != nil {
log.Fatal("error unmarshaling values-from label:", err) log.Fatal("error unmarshaling values-from label:", err)
} }
@@ -374,7 +372,7 @@ func (chart *HelmChart) setEnvironmentValuesFrom(service types.ServiceConfig, de
if dep == nil || target == nil { if dep == nil || target == nil {
log.Fatalf("deployment %s or %s not found", depName[0], service.Name) log.Fatalf("deployment %s or %s not found", depName[0], service.Name)
} }
container, index := utils.GetContainerByName(target.service.ContainerName, target.Spec.Template.Spec.Containers) container, index := utils.GetContainerByName(target.service.Name, target.Spec.Template.Spec.Containers)
if container == nil { if container == nil {
log.Fatalf("Container %s not found", target.GetName()) log.Fatalf("Container %s not found", target.GetName())
} }
@@ -383,10 +381,13 @@ func (chart *HelmChart) setEnvironmentValuesFrom(service types.ServiceConfig, de
// is it a secret? // is it a secret?
isSecret := false isSecret := false
secrets, err := labelstructs.SecretsFrom(dep.service.Labels[labels.LabelSecrets]) secrets, err := labelStructs.SecretsFrom(dep.service.Labels[labels.LabelSecrets])
if err == nil { if err == nil {
if slices.Contains(secrets, depName[1]) { for _, secret := range secrets {
if secret == depName[1] {
isSecret = true isSecret = true
break
}
} }
} }

View File

@@ -3,7 +3,7 @@ package generator
import ( import (
"fmt" "fmt"
"katenary/generator/labels" "katenary/generator/labels"
"katenary/generator/labels/labelstructs" "katenary/generator/labels/labelStructs"
"katenary/utils" "katenary/utils"
"log" "log"
"os" "os"
@@ -12,7 +12,7 @@ import (
"strings" "strings"
"unicode/utf8" "unicode/utf8"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
@@ -65,7 +65,7 @@ func NewConfigMap(service types.ServiceConfig, appName string, forFile bool) *Co
} }
// get the secrets from the labels // 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 { if err != nil {
log.Fatal(err) 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 // do not bind env variables to the configmap
// remove the variables that are already defined in the environment // remove the variables that are already defined in the environment
if l, ok := service.Labels[labels.LabelMapEnv]; ok { if l, ok := service.Labels[labels.LabelMapEnv]; ok {
envmap, err := labelstructs.MapEnvFrom(l) envmap, err := labelStructs.MapEnvFrom(l)
if err != nil { if err != nil {
log.Fatal("Error parsing map-env", err) log.Fatal("Error parsing map-env", err)
} }
@@ -142,9 +142,7 @@ func NewConfigMapFromDirectory(service types.ServiceConfig, appName, path string
// cumulate the path to the WorkingDir // cumulate the path to the WorkingDir
path = filepath.Join(service.WorkingDir, path) path = filepath.Join(service.WorkingDir, path)
path = filepath.Clean(path) path = filepath.Clean(path)
if err := cm.AppendDir(path); err != nil { cm.AppendDir(path)
log.Fatal("Error adding files to configmap:", err)
}
return cm return cm
} }
@@ -161,13 +159,13 @@ func (c *ConfigMap) AddBinaryData(key string, value []byte) {
c.BinaryData[key] = value c.BinaryData[key] = value
} }
// AppendDir adds files from given path to the configmap. It is not recursive, to add all files in a directory, // 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. // you need to call this function for each subdirectory.
func (c *ConfigMap) AppendDir(path string) error { func (c *ConfigMap) AppendDir(path string) error {
// read all files in the path and add them to the configmap // read all files in the path and add them to the configmap
stat, err := os.Stat(path) stat, err := os.Stat(path)
if err != nil { if err != nil {
return fmt.Errorf("path %s does not exist, %w", path, err) return fmt.Errorf("Path %s does not exist, %w\n", path, err)
} }
// recursively read all files in the path and add them to the configmap // recursively read all files in the path and add them to the configmap
if stat.IsDir() { if stat.IsDir() {
@@ -214,7 +212,7 @@ func (c *ConfigMap) AppendFile(path string) error {
// read all files in the path and add them to the configmap // read all files in the path and add them to the configmap
stat, err := os.Stat(path) stat, err := os.Stat(path)
if err != nil { if err != nil {
return fmt.Errorf("path %s doesn not exists, %w", path, err) return fmt.Errorf("Path %s doesn not exists, %w", path, err)
} }
// recursively read all files in the path and add them to the configmap // recursively read all files in the path and add them to the configmap
if !stat.IsDir() { if !stat.IsDir() {

View File

@@ -6,7 +6,7 @@ import (
"os" "os"
"testing" "testing"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )

View File

@@ -7,7 +7,7 @@ import (
"katenary/generator/extrafiles" "katenary/generator/extrafiles"
"katenary/generator/katenaryfile" "katenary/generator/katenaryfile"
"katenary/generator/labels" "katenary/generator/labels"
"katenary/generator/labels/labelstructs" "katenary/generator/labels/labelStructs"
"katenary/parser" "katenary/parser"
"katenary/utils" "katenary/utils"
"log" "log"
@@ -18,7 +18,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
) )
const ingressClassHelp = `# Default value for ingress.class annotation const ingressClassHelp = `# Default value for ingress.class annotation
@@ -113,11 +113,7 @@ func Convert(config ConvertOptions, dockerComposeFile ...string) error {
fmt.Println(utils.IconFailure, err) fmt.Println(utils.IconFailure, err)
return err return err
} }
defer func() { defer os.Chdir(currentDir) // after the generation, go back to the original directory
if err := os.Chdir(currentDir); err != nil { // after the generation, go back to the original directory
log.Fatal(err)
}
}()
// repove the directory part of the docker-compose files // repove the directory part of the docker-compose files
for i, f := range dockerComposeFile { for i, f := range dockerComposeFile {
@@ -173,8 +169,9 @@ func Convert(config ConvertOptions, dockerComposeFile ...string) error {
os.RemoveAll(config.OutputDir) os.RemoveAll(config.OutputDir)
// create the chart directory // create the chart directory
if err := os.MkdirAll(templateDir, utils.DirectoryPermission); err != nil { if err := os.MkdirAll(templateDir, 0o755); err != nil {
return err fmt.Println(utils.IconFailure, err)
os.Exit(1)
} }
// add icon from the command line // add icon from the command line
@@ -258,7 +255,7 @@ func addCommentsToValues(values []byte) []byte {
return []byte(strings.Join(lines, "\n")) 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 { for _, d := range dependencies {
name := d.Name name := d.Name
if d.Alias != "" { if d.Alias != "" {
@@ -521,16 +518,16 @@ func buildCharYamlFile(chart *HelmChart, project *types.Project, chartPath strin
os.Exit(1) os.Exit(1)
} }
// concat chart adding a comment with hash of services on top // concat chart adding a comment with hash of services on top
yamlChart = append(fmt.Appendf(nil, "# compose hash (sha1): %s\n", *chart.composeHash), yamlChart...) yamlChart = append([]byte(fmt.Sprintf("# compose hash (sha1): %s\n", *chart.composeHash)), yamlChart...)
// add the list of compose files // add the list of compose files
files := []string{} files := []string{}
for _, file := range project.ComposeFiles { for _, file := range project.ComposeFiles {
base := filepath.Base(file) base := filepath.Base(file)
files = append(files, base) files = append(files, base)
} }
yamlChart = append(fmt.Appendf(nil, "# compose files: %s\n", strings.Join(files, ", ")), yamlChart...) yamlChart = append([]byte(fmt.Sprintf("# compose files: %s\n", strings.Join(files, ", "))), yamlChart...)
// add generated date // add generated date
yamlChart = append(fmt.Appendf(nil, "# generated at: %s\n", time.Now().Format(time.RFC3339)), yamlChart...) yamlChart = append([]byte(fmt.Sprintf("# generated at: %s\n", time.Now().Format(time.RFC3339))), yamlChart...)
// document Chart.yaml file // document Chart.yaml file
yamlChart = addChartDoc(yamlChart, project) yamlChart = addChartDoc(yamlChart, project)
@@ -630,11 +627,7 @@ func writeContent(path string, content []byte) {
os.Exit(1) os.Exit(1)
} }
defer f.Close() defer f.Close()
defer func() { f.Write(content)
if _, err := f.Write(content); err != nil {
log.Fatal(err)
}
}()
} }
// helmLint runs "helm lint" on the output directory. // helmLint runs "helm lint" on the output directory.

View File

@@ -2,12 +2,12 @@ package generator
import ( import (
"katenary/generator/labels" "katenary/generator/labels"
"katenary/generator/labels/labelstructs" "katenary/generator/labels/labelStructs"
"katenary/utils" "katenary/utils"
"log" "log"
"strings" "strings"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
batchv1 "k8s.io/api/batch/v1" batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -30,7 +30,7 @@ func NewCronJob(service types.ServiceConfig, chart *HelmChart, appName string) (
if !ok { if !ok {
return nil, nil return nil, nil
} }
mapping, err := labelstructs.CronJobFrom(labels) mapping, err := labelStructs.CronJobFrom(labels)
if err != nil { if err != nil {
log.Fatalf("Error parsing cronjob labels: %s", err) log.Fatalf("Error parsing cronjob labels: %s", err)
return nil, nil return nil, nil

View File

@@ -3,7 +3,7 @@ package generator
import ( import (
"fmt" "fmt"
"katenary/generator/labels" "katenary/generator/labels"
"katenary/generator/labels/labelstructs" "katenary/generator/labels/labelStructs"
"katenary/utils" "katenary/utils"
"log" "log"
"os" "os"
@@ -12,7 +12,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -39,7 +39,7 @@ type Deployment struct {
service *types.ServiceConfig `yaml:"-"` service *types.ServiceConfig `yaml:"-"`
defaultTag string `yaml:"-"` defaultTag string `yaml:"-"`
isMainApp bool `yaml:"-"` isMainApp bool `yaml:"-"`
exchangesVolumes map[string]*labelstructs.ExchangeVolume `yaml:"-"` exchangesVolumes map[string]*labelStructs.ExchangeVolume `yaml:"-"`
boundEnvVar []string `yaml:"-"` // environement to remove boundEnvVar []string `yaml:"-"` // environement to remove
} }
@@ -94,7 +94,7 @@ func NewDeployment(service types.ServiceConfig, chart *HelmChart) *Deployment {
}, },
configMaps: make(map[string]*ConfigMapMount), configMaps: make(map[string]*ConfigMapMount),
volumeMap: make(map[string]string), volumeMap: make(map[string]string),
exchangesVolumes: map[string]*labelstructs.ExchangeVolume{}, exchangesVolumes: map[string]*labelStructs.ExchangeVolume{},
boundEnvVar: []string{}, boundEnvVar: []string{},
} }
@@ -131,7 +131,7 @@ func (d *Deployment) AddContainer(service types.ServiceConfig) {
Image: utils.TplValue(service.Name, "repository.image") + ":" + Image: utils.TplValue(service.Name, "repository.image") + ":" +
utils.TplValue(service.Name, "repository.tag", d.defaultTag), utils.TplValue(service.Name, "repository.tag", d.defaultTag),
Ports: ports, Ports: ports,
Name: service.ContainerName, Name: service.Name,
ImagePullPolicy: corev1.PullIfNotPresent, ImagePullPolicy: corev1.PullIfNotPresent,
Resources: corev1.ResourceRequirements{ Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{}, Requests: corev1.ResourceList{},
@@ -160,7 +160,7 @@ func (d *Deployment) AddContainer(service types.ServiceConfig) {
func (d *Deployment) AddHealthCheck(service types.ServiceConfig, container *corev1.Container) { func (d *Deployment) AddHealthCheck(service types.ServiceConfig, container *corev1.Container) {
// get the label for healthcheck // get the label for healthcheck
if v, ok := service.Labels[labels.LabelHealthCheck]; ok { if v, ok := service.Labels[labels.LabelHealthCheck]; ok {
probes, err := labelstructs.ProbeFrom(v) probes, err := labelStructs.ProbeFrom(v)
if err != nil { if err != nil {
log.Fatal(err) 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) { func (d *Deployment) AddVolumes(service types.ServiceConfig, appName string) {
tobind := map[string]bool{} tobind := map[string]bool{}
if v, ok := service.Labels[labels.LabelConfigMapFiles]; ok { if v, ok := service.Labels[labels.LabelConfigMapFiles]; ok {
binds, err := labelstructs.ConfigMapFileFrom(v) binds, err := labelStructs.ConfigMapFileFrom(v)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@@ -253,8 +253,8 @@ func (d *Deployment) BindFrom(service types.ServiceConfig, binded *Deployment) {
// get the container // get the container
} }
// add volume mount to the container // add volume mount to the container
targetContainer, ti := utils.GetContainerByName(service.ContainerName, d.Spec.Template.Spec.Containers) targetContainer, ti := utils.GetContainerByName(service.Name, d.Spec.Template.Spec.Containers)
sourceContainer, _ := utils.GetContainerByName(service.ContainerName, binded.Spec.Template.Spec.Containers) sourceContainer, _ := utils.GetContainerByName(service.Name, binded.Spec.Template.Spec.Containers)
for _, bindedMount := range sourceContainer.VolumeMounts { for _, bindedMount := range sourceContainer.VolumeMounts {
if bindedMount.Name == bindedVolume.Name { if bindedMount.Name == bindedVolume.Name {
targetContainer.VolumeMounts = append(targetContainer.VolumeMounts, bindedMount) targetContainer.VolumeMounts = append(targetContainer.VolumeMounts, bindedMount)
@@ -305,7 +305,10 @@ func (d *Deployment) SetEnvFrom(service types.ServiceConfig, appName string, sam
if len(service.Environment) == 0 { if len(service.Environment) == 0 {
return return
} }
inSamePod := len(samePod) > 0 && samePod[0] inSamePod := false
if len(samePod) > 0 && samePod[0] {
inSamePod = true
}
drop := []string{} drop := []string{}
secrets := []string{} secrets := []string{}
@@ -320,7 +323,7 @@ func (d *Deployment) SetEnvFrom(service types.ServiceConfig, appName string, sam
}() }()
// secrets from label // secrets from label
labelSecrets, err := labelstructs.SecretsFrom(service.Labels[labels.LabelSecrets]) labelSecrets, err := labelStructs.SecretsFrom(service.Labels[labels.LabelSecrets])
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@@ -408,7 +411,7 @@ func (d *Deployment) BindMapFilesToContainer(service types.ServiceConfig, secret
}) })
} }
container, index := utils.GetContainerByName(service.ContainerName, d.Spec.Template.Spec.Containers) container, index := utils.GetContainerByName(service.Name, d.Spec.Template.Spec.Containers)
if container == nil { if container == nil {
utils.Warn("Container not found for service " + service.Name) utils.Warn("Container not found for service " + service.Name)
return nil, -1 return nil, -1
@@ -657,13 +660,11 @@ func (d *Deployment) appendFileToConfigMap(service types.ServiceConfig, appName
d.configMaps[pathname].mountPath = mp d.configMaps[pathname].mountPath = mp
} }
if err := cm.AppendFile(volume.Source); err != nil { cm.AppendFile(volume.Source)
log.Fatal("Error adding file to configmap:", err)
}
} }
func (d *Deployment) bindVolumes(volume types.ServiceVolumeConfig, isSamePod bool, tobind map[string]bool, service types.ServiceConfig, appName string) { func (d *Deployment) bindVolumes(volume types.ServiceVolumeConfig, isSamePod bool, tobind map[string]bool, service types.ServiceConfig, appName string) {
container, index := utils.GetContainerByName(service.ContainerName, d.Spec.Template.Spec.Containers) container, index := utils.GetContainerByName(service.Name, d.Spec.Template.Spec.Containers)
defer func(d *Deployment, container *corev1.Container, index int) { defer func(d *Deployment, container *corev1.Container, index int) {
d.Spec.Template.Spec.Containers[index] = *container d.Spec.Template.Spec.Containers[index] = *container

View File

@@ -4,10 +4,8 @@ import (
"fmt" "fmt"
"katenary/generator/labels" "katenary/generator/labels"
"os" "os"
"strings"
"testing" "testing"
yaml3 "gopkg.in/yaml.v3"
v1 "k8s.io/api/apps/v1" v1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
@@ -326,172 +324,14 @@ services:
Environment map[string]string `yaml:"environment"` Environment map[string]string `yaml:"environment"`
} `yaml:"web"` } `yaml:"web"`
}{} }{}
if err := yaml3.Unmarshal(valuesContent, &mapping); err != nil { if err := yaml.Unmarshal(valuesContent, &mapping); err != nil {
t.Errorf(unmarshalError, err) t.Errorf(unmarshalError, err)
} }
if v, ok := mapping.Web.Environment["FOO"]; !ok { if _, ok := mapping.Web.Environment["FOO"]; !ok {
t.Errorf("Expected FOO in web environment") t.Errorf("Expected FOO in web environment")
if v != "bar" {
t.Errorf("Expected FOO to be bar, got %s", v)
} }
} if _, ok := mapping.Web.Environment["BAZ"]; ok {
if v, ok := mapping.Web.Environment["BAZ"]; ok {
t.Errorf("Expected BAZ not in web environment") t.Errorf("Expected BAZ not in web environment")
if v != "qux" {
t.Errorf("Expected BAZ to be qux, got %s", v)
}
}
}
func TestWithUnderscoreInContainerName(t *testing.T) {
composeFile := `
services:
web-app:
image: nginx:1.29
container_name: web_app_container
environment:
FOO: BAR
labels:
%s/values: |
- FOO
`
composeFile = fmt.Sprintf(composeFile, labels.Prefix())
tmpDir := setup(composeFile)
defer teardown(tmpDir)
currentDir, _ := os.Getwd()
os.Chdir(tmpDir)
defer os.Chdir(currentDir)
output := internalCompileTest(t, "-s", "templates/web_app/deployment.yaml")
dt := v1.Deployment{}
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
t.Errorf(unmarshalError, err)
}
// find container.name
containerName := dt.Spec.Template.Spec.Containers[0].Name
if strings.Contains(containerName, "_") {
t.Errorf("Expected container name to not contain underscores, got %s", containerName)
}
}
func TestWithDashes(t *testing.T) {
composeFile := `
services:
web-app:
image: nginx:1.29
environment:
FOO: BAR
labels:
%s/values: |
- FOO
`
composeFile = fmt.Sprintf(composeFile, labels.Prefix())
tmpDir := setup(composeFile)
defer teardown(tmpDir)
currentDir, _ := os.Getwd()
os.Chdir(tmpDir)
defer os.Chdir(currentDir)
output := internalCompileTest(t, "-s", "templates/web_app/deployment.yaml")
dt := v1.Deployment{}
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
t.Errorf(unmarshalError, err)
}
valuesFile := "./chart/values.yaml"
if _, err := os.Stat(valuesFile); os.IsNotExist(err) {
t.Errorf("values.yaml does not exist")
}
valuesContent, err := os.ReadFile(valuesFile)
if err != nil {
t.Errorf("Error reading values.yaml: %s", err)
}
mapping := struct {
Web struct {
Environment map[string]string `yaml:"environment"`
} `yaml:"web_app"`
}{}
if err := yaml3.Unmarshal(valuesContent, &mapping); err != nil {
t.Errorf(unmarshalError, err)
}
// we must have FOO in web_app environment (not web-app)
// this validates that the service name is converted to a valid k8s name
if v, ok := mapping.Web.Environment["FOO"]; !ok {
t.Errorf("Expected FOO in web_app environment")
if v != "BAR" {
t.Errorf("Expected FOO to be BAR, got %s", v)
}
}
}
func TestDashesWithValueFrom(t *testing.T) {
composeFile := `
services:
web-app:
image: nginx:1.29
environment:
FOO: BAR
labels:
%[1]s/values: |
- FOO
web2:
image: nginx:1.29
labels:
%[1]s/values-from: |
BAR: web-app.FOO
`
composeFile = fmt.Sprintf(composeFile, labels.Prefix())
tmpDir := setup(composeFile)
defer teardown(tmpDir)
currentDir, _ := os.Getwd()
os.Chdir(tmpDir)
defer os.Chdir(currentDir)
output := internalCompileTest(t, "-s", "templates/web2/deployment.yaml")
dt := v1.Deployment{}
if err := yaml.Unmarshal([]byte(output), &dt); err != nil {
t.Errorf(unmarshalError, err)
}
valuesFile := "./chart/values.yaml"
if _, err := os.Stat(valuesFile); os.IsNotExist(err) {
t.Errorf("values.yaml does not exist")
}
valuesContent, err := os.ReadFile(valuesFile)
if err != nil {
t.Errorf("Error reading values.yaml: %s", err)
}
mapping := struct {
Web struct {
Environment map[string]string `yaml:"environment"`
} `yaml:"web_app"`
}{}
if err := yaml3.Unmarshal(valuesContent, &mapping); err != nil {
t.Errorf(unmarshalError, err)
}
// we must have FOO in web_app environment (not web-app)
// this validates that the service name is converted to a valid k8s name
if v, ok := mapping.Web.Environment["FOO"]; !ok {
t.Errorf("Expected FOO in web_app environment")
if v != "BAR" {
t.Errorf("Expected FOO to be BAR, got %s", v)
}
}
// ensure that the deployment has the value from the other service
barenv := dt.Spec.Template.Spec.Containers[0].Env[0]
if barenv.Value != "" {
t.Errorf("Expected value to be empty")
}
if barenv.ValueFrom == nil {
t.Errorf("Expected valueFrom to be set")
} }
} }

View File

@@ -1,17 +1,14 @@
/* /*
Package generator generates kubernetes objects from a "compose" file and transforms them into a helm chart. The generator package generates kubernetes objects from a "compose" file and transforms them into a helm chart.
The generator package is the core of katenary. It is responsible for generating kubernetes objects from a compose file The generator package is the core of katenary. It is responsible for generating kubernetes objects from a compose file and transforming them into a helm chart.
and transforming them into a helm chart. Conversion manipulates Yaml representation of kubernetes object to add conditions, labels, annotations, etc. to the objects. It also create the values to be set to
Conversion manipulates Yaml representation of kubernetes object to add conditions, labels, annotations, etc. to the the values.yaml file.
objects. It also create the values to be set to the values.yaml file.
The generate.Convert() create an HelmChart object and call "Generate()" method to convert from a compose file to a helm The generate.Convert() create an HelmChart object and call "Generate()" method to convert from a compose file to a helm chart.
chart. It saves the helm chart in the given directory. It saves the helm chart in the given directory.
If you want to change or override the write behavior, you can use the HelmChart.Generate() function and implement your If you want to change or override the write behavior, you can use the HelmChart.Generate() function and implement your own write function. This function returns
own write function. This function returns the helm chart object containing all kubernetes objects and helm chart ingormation. It does not write the helm chart to the disk.
the helm chart object containing all kubernetes objects and helm chart ingormation. It does not write the helm chart to
the disk.
*/ */
package generator package generator

View File

@@ -1,2 +1,2 @@
/* Package extrafiles provides function to generate the Chart files that are not objects. Like README.md and notes.txt... */ /* extrafiles package provides function to generate the Chart files that are not objects. Like README.md and notes.txt... */
package extrafiles package extrafiles

View File

@@ -1,40 +0,0 @@
package extrafiles
import (
"strings"
"testing"
)
// override the embedded template for testing
var testTemplate = `
Some header
{{ ingress_list }}
Some footer
`
func init() {
notesTemplate = testTemplate
}
func TestNotesFile_NoServices(t *testing.T) {
result := NotesFile([]string{})
if !strings.Contains(result, "Some header") || !strings.Contains(result, "Some footer") {
t.Errorf("Expected template header/footer in output, got: %s", result)
}
}
func TestNotesFile_WithServices(t *testing.T) {
services := []string{"svc1", "svc2"}
result := NotesFile(services)
for _, svc := range services {
cond := "{{- if and .Values." + svc + ".ingress .Values." + svc + ".ingress.enabled }}"
line := "{{- $count = add1 $count -}}{{- $listOfURL = printf \"%s\\n- http://%s\" $listOfURL (tpl .Values." + svc + ".ingress.host .) -}}"
if !strings.Contains(result, cond) {
t.Errorf("Expected condition for service %s in output", svc)
}
if !strings.Contains(result, line) {
t.Errorf("Expected line for service %s in output", svc)
}
}
}

View File

@@ -4,7 +4,6 @@ import (
"bytes" "bytes"
_ "embed" _ "embed"
"fmt" "fmt"
"log"
"sort" "sort"
"strings" "strings"
"text/template" "text/template"
@@ -21,7 +20,7 @@ type chart struct {
Values []string Values []string
} }
func parseValues(prefix string, values map[string]any, result map[string]string) { func parseValues(prefix string, values map[string]interface{}, result map[string]string) {
for key, value := range values { for key, value := range values {
path := key path := key
if prefix != "" { if prefix != "" {
@@ -29,11 +28,11 @@ func parseValues(prefix string, values map[string]any, result map[string]string)
} }
switch v := value.(type) { switch v := value.(type) {
case []any: case []interface{}:
for i, u := range v { for i, u := range v {
parseValues(fmt.Sprintf("%s[%d]", path, i), map[string]any{"value": u}, result) parseValues(fmt.Sprintf("%s[%d]", path, i), map[string]interface{}{"value": u}, result)
} }
case map[string]any: case map[string]interface{}:
parseValues(path, v, result) parseValues(path, v, result)
default: default:
strValue := fmt.Sprintf("`%v`", value) strValue := fmt.Sprintf("`%v`", value)
@@ -49,9 +48,7 @@ func ReadMeFile(charname, description string, values map[string]any) string {
vv := map[string]any{} vv := map[string]any{}
out, _ := yaml.Marshal(values) out, _ := yaml.Marshal(values)
if err := yaml.Unmarshal(out, &vv); err != nil { yaml.Unmarshal(out, &vv)
log.Printf("Error parsing values: %s", err)
}
result := make(map[string]string) result := make(map[string]string)
parseValues("", vv, result) parseValues("", vv, result)

View File

@@ -1,33 +0,0 @@
package extrafiles
import (
"regexp"
"testing"
)
func TestReadMeFile_Basic(t *testing.T) {
values := map[string]any{
"replicas": 2,
"image": map[string]any{
"repository": "nginx",
"tag": "latest",
},
}
result := ReadMeFile("testchart", "A test chart", values)
t.Logf("Generated README content:\n%s", result)
paramerRegExp := regexp.MustCompile(`\|\s+` + "`" + `(.*?)` + "`" + `\s+\|\s+` + "`" + `(.*?)` + "`" + `\s+\|`)
matches := paramerRegExp.FindAllStringSubmatch(result, -1)
if len(matches) != 3 {
t.Errorf("Expected 5 lines in the table for headers and parameters, got %d", len(matches))
}
if matches[0][1] != "image.repository" || matches[0][2] != "nginx" {
t.Errorf("Expected third line to be image.repository, got %s", matches[1])
}
if matches[1][1] != "image.tag" || matches[1][2] != "latest" {
t.Errorf("Expected fourth line to be image.tag, got %s", matches[2])
}
if matches[2][1] != "replicas" || matches[2][2] != "2" {
t.Errorf("Expected second line to be replicas, got %s", matches[0])
}
}

View File

@@ -4,15 +4,13 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"katenary/generator/labels" "katenary/generator/labels"
"katenary/generator/labels/labelstructs"
"katenary/utils" "katenary/utils"
"log" "log"
"regexp" "regexp"
"strings" "strings"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml"
) )
// Generate a chart from a compose project. // Generate a chart from a compose project.
@@ -45,16 +43,6 @@ func Generate(project *types.Project) (*HelmChart, error) {
Annotations[labels.LabelName("compose-hash")] = hash Annotations[labels.LabelName("compose-hash")] = hash
chart.composeHash = &hash chart.composeHash = &hash
// drop all services with the "ignore" label
dropIngoredServices(project)
fixContainerNames(project)
// rename all services name to remove dashes
if err := fixResourceNames(project); err != nil {
return nil, err
}
// find the "main-app" label, and set chart.AppVersion to the tag if exists // find the "main-app" label, and set chart.AppVersion to the tag if exists
mainCount := 0 mainCount := 0
for _, service := range project.Services { for _, service := range project.Services {
@@ -119,10 +107,7 @@ func Generate(project *types.Project) (*HelmChart, error) {
for _, s := range project.Services { for _, s := range project.Services {
for _, d := range s.GetDependencies() { for _, d := range s.GetDependencies() {
if dep, ok := deployments[d]; ok { if dep, ok := deployments[d]; ok {
err := deployments[s.Name].DependsOn(dep, d) deployments[s.Name].DependsOn(dep, d)
if err != nil {
log.Printf("error creating init container for service %[1]s: %[2]s", s.Name, err)
}
} else { } else {
log.Printf("service %[1]s depends on %[2]s, but %[2]s is not defined", s.Name, d) log.Printf("service %[1]s depends on %[2]s, but %[2]s is not defined", s.Name, d)
} }
@@ -134,9 +119,7 @@ func Generate(project *types.Project) (*HelmChart, error) {
} }
// generate configmaps with environment variables // generate configmaps with environment variables
if err := chart.generateConfigMapsAndSecrets(project); err != nil { chart.generateConfigMapsAndSecrets(project)
log.Fatalf("error generating configmaps and secrets: %s", err)
}
// if the env-from label is set, we need to add the env vars from the configmap // if the env-from label is set, we need to add the env vars from the configmap
// to the environment of the service // to the environment of the service
@@ -204,52 +187,6 @@ func Generate(project *types.Project) (*HelmChart, error) {
return chart, nil return chart, nil
} }
// dropIngoredServices removes all services with the "ignore" label set to true (or yes).
func dropIngoredServices(project *types.Project) {
for i, service := range project.Services {
if isIgnored(service) {
project.Services = append(project.Services[:i], project.Services[i+1:]...)
}
}
}
// fixResourceNames renames all services and related resources to remove dashes.
func fixResourceNames(project *types.Project) error {
// rename all services name to remove dashes
for i, service := range project.Services {
if service.Name != utils.AsResourceName(service.Name) {
fixed := utils.AsResourceName(service.Name)
for j, s := range project.Services {
// for the same-pod services, we need to keep the original name
if samepod, ok := s.Labels[labels.LabelSamePod]; ok && samepod == service.Name {
s.Labels[labels.LabelSamePod] = fixed
project.Services[j] = s
}
// also, the value-from label should be updated
if valuefrom, ok := s.Labels[labels.LabelValueFrom]; ok {
vf, err := labelstructs.GetValueFrom(valuefrom)
if err != nil {
return err
}
for varname, bind := range *vf {
log.Printf("service %s, varname %s, bind %s", service.Name, varname, bind)
bind := strings.ReplaceAll(bind, service.Name, fixed)
(*vf)[varname] = bind
}
output, err := yaml.Marshal(vf)
if err != nil {
return err
}
s.Labels[labels.LabelValueFrom] = string(output)
}
}
service.Name = fixed
project.Services[i] = service
}
}
return nil
}
// serviceIsMain returns true if the service is the main app. // serviceIsMain returns true if the service is the main app.
func serviceIsMain(service types.ServiceConfig) bool { func serviceIsMain(service types.ServiceConfig) bool {
if main, ok := service.Labels[labels.LabelMainApp]; ok { if main, ok := service.Labels[labels.LabelMainApp]; ok {
@@ -267,7 +204,7 @@ func addStaticVolumes(deployments map[string]*Deployment, service types.ServiceC
return return
} }
container, index := utils.GetContainerByName(service.ContainerName, d.Spec.Template.Spec.Containers) container, index := utils.GetContainerByName(service.Name, d.Spec.Template.Spec.Containers)
if container == nil { // may append for the same-pod services if container == nil { // may append for the same-pod services
return return
} }
@@ -319,7 +256,7 @@ func computeNIndent(b []byte) []byte {
startSpaces = spaces[0] startSpaces = spaces[0]
} }
line = []byte(startSpaces + strings.TrimLeft(string(line), " ")) line = []byte(startSpaces + strings.TrimLeft(string(line), " "))
line = bytes.ReplaceAll(line, []byte("__indent__"), fmt.Appendf(nil, "%d", len(startSpaces))) line = bytes.ReplaceAll(line, []byte("__indent__"), []byte(fmt.Sprintf("%d", len(startSpaces))))
lines[i] = line lines[i] = line
} }
return bytes.Join(lines, []byte("\n")) return bytes.Join(lines, []byte("\n"))
@@ -392,7 +329,7 @@ func samePodVolume(service types.ServiceConfig, v types.ServiceVolumeConfig, dep
return false return false
} }
if len(service.Volumes) == 0 { if service.Volumes == nil || len(service.Volumes) == 0 {
return false return false
} }
@@ -418,15 +355,3 @@ func samePodVolume(service types.ServiceConfig, v types.ServiceVolumeConfig, dep
} }
return false return false
} }
func fixContainerNames(project *types.Project) {
// fix container names to be unique
for i, service := range project.Services {
if service.ContainerName == "" {
service.ContainerName = utils.FixedResourceName(service.Name)
} else {
service.ContainerName = utils.FixedResourceName(service.ContainerName)
}
project.Services[i] = service
}
}

View File

@@ -2,12 +2,12 @@ package generator
import ( import (
"katenary/generator/labels" "katenary/generator/labels"
"katenary/generator/labels/labelstructs" "katenary/generator/labels/labelStructs"
"katenary/utils" "katenary/utils"
"log" "log"
"strings" "strings"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
networkv1 "k8s.io/api/networking/v1" networkv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
@@ -33,7 +33,7 @@ func NewIngress(service types.ServiceConfig, Chart *HelmChart) *Ingress {
return nil return nil
} }
mapping, err := labelstructs.IngressFrom(label) mapping, err := labelStructs.IngressFrom(label)
if err != nil { if err != nil {
log.Fatalf("Failed to parse ingress label: %s\n", err) log.Fatalf("Failed to parse ingress label: %s\n", err)
} }

View File

@@ -5,14 +5,14 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"katenary/generator/labels" "katenary/generator/labels"
"katenary/generator/labels/labelstructs" "katenary/generator/labels/labelStructs"
"katenary/utils" "katenary/utils"
"log" "log"
"os" "os"
"reflect" "reflect"
"strings" "strings"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
"github.com/invopop/jsonschema" "github.com/invopop/jsonschema"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -27,20 +27,20 @@ type StringOrMap any
type Service struct { type Service struct {
MainApp *bool `json:"main-app,omitempty" jsonschema:"title=Is this service the main application"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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"` 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)"` 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 // OverrideWithConfig overrides the project with the katenary.yaml file. It
@@ -72,33 +72,29 @@ func OverrideWithConfig(project *types.Project) {
} }
for i, p := range project.Services { for i, p := range project.Services {
name := p.Name name := p.Name
if project.Services[i].Labels == nil { if p.Labels == nil {
project.Services[i].Labels = make(map[string]string) p.Labels = make(map[string]string)
} project.Services[i] = p
mustGetLabelContent := func(o any, s *types.ServiceConfig, labelName string) {
err := getLabelContent(o, s, labelName)
if err != nil {
log.Fatal(err)
}
} }
if s, ok := services[name]; ok { if s, ok := services[name]; ok {
mustGetLabelContent(s.MainApp, &project.Services[i], labels.LabelMainApp) service := project.Services[i]
mustGetLabelContent(s.Values, &project.Services[i], labels.LabelValues) getLabelContent(s.MainApp, &service, labels.LabelMainApp)
mustGetLabelContent(s.Secrets, &project.Services[i], labels.LabelSecrets) getLabelContent(s.Values, &service, labels.LabelValues)
mustGetLabelContent(s.Ports, &project.Services[i], labels.LabelPorts) getLabelContent(s.Secrets, &service, labels.LabelSecrets)
mustGetLabelContent(s.Ingress, &project.Services[i], labels.LabelIngress) getLabelContent(s.Ports, &service, labels.LabelPorts)
mustGetLabelContent(s.HealthCheck, &project.Services[i], labels.LabelHealthCheck) getLabelContent(s.Ingress, &service, labels.LabelIngress)
mustGetLabelContent(s.SamePod, &project.Services[i], labels.LabelSamePod) getLabelContent(s.HealthCheck, &service, labels.LabelHealthCheck)
mustGetLabelContent(s.Description, &project.Services[i], labels.LabelDescription) getLabelContent(s.SamePod, &service, labels.LabelSamePod)
mustGetLabelContent(s.Ignore, &project.Services[i], labels.LabelIgnore) getLabelContent(s.Description, &service, labels.LabelDescription)
mustGetLabelContent(s.Dependencies, &project.Services[i], labels.LabelDependencies) getLabelContent(s.Ignore, &service, labels.LabelIgnore)
mustGetLabelContent(s.ConfigMapFile, &project.Services[i], labels.LabelConfigMapFiles) getLabelContent(s.Dependencies, &service, labels.LabelDependencies)
mustGetLabelContent(s.MapEnv, &project.Services[i], labels.LabelMapEnv) getLabelContent(s.ConfigMapFile, &service, labels.LabelConfigMapFiles)
mustGetLabelContent(s.CronJob, &project.Services[i], labels.LabelCronJob) getLabelContent(s.MapEnv, &service, labels.LabelMapEnv)
mustGetLabelContent(s.EnvFrom, &project.Services[i], labels.LabelEnvFrom) getLabelContent(s.CronJob, &service, labels.LabelCronJob)
mustGetLabelContent(s.ExchangeVolumes, &project.Services[i], labels.LabelExchangeVolume) getLabelContent(s.EnvFrom, &service, labels.LabelEnvFrom)
mustGetLabelContent(s.ValuesFrom, &project.Services[i], labels.LabelValueFrom) getLabelContent(s.ExchangeVolumes, &service, labels.LabelExchangeVolume)
getLabelContent(s.ValuesFrom, &service, labels.LabelValueFrom)
} }
} }
fmt.Println(utils.IconInfo, "Katenary file loaded successfully, the services are now configured.") fmt.Println(utils.IconInfo, "Katenary file loaded successfully, the services are now configured.")
@@ -117,7 +113,7 @@ func getLabelContent(o any, service *types.ServiceConfig, labelName string) erro
val := strings.TrimSpace(string(c)) val := strings.TrimSpace(string(c))
if labelName == labels.LabelIngress { if labelName == labels.LabelIngress {
// special case, values must be set from some defaults // special case, values must be set from some defaults
ing, err := labelstructs.IngressFrom(val) ing, err := labelStructs.IngressFrom(val)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
return err return err
@@ -129,6 +125,9 @@ func getLabelContent(o any, service *types.ServiceConfig, labelName string) erro
val = strings.TrimSpace(string(c)) val = strings.TrimSpace(string(c))
} }
if service.Labels == nil {
service.Labels = make(map[string]string)
}
service.Labels[labelName] = val service.Labels[labelName] = val
return nil return nil
} }
@@ -161,5 +160,5 @@ func GenerateSchema() string {
return err.Error() return err.Error()
} }
return out.String() return string(out.Bytes())
} }

View File

@@ -1,13 +1,14 @@
package katenaryfile package katenaryfile
import ( import (
"context"
"katenary/generator/labels" "katenary/generator/labels"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/compose-spec/compose-go/cli" "github.com/compose-spec/compose-go/v2/cli"
) )
func TestBuildSchema(t *testing.T) { func TestBuildSchema(t *testing.T) {
@@ -33,7 +34,7 @@ webapp:
// create /tmp/katenary-test-override directory, save the compose.yaml file // create /tmp/katenary-test-override directory, save the compose.yaml file
tmpDir, err := os.MkdirTemp("", "katenary-test-override") tmpDir, err := os.MkdirTemp("", "katenary-test-override")
if err != nil { if err != nil {
t.Fatalf("Failed to create temp directory: %s", err.Error()) t.Fatal(err.Error())
} }
composeFile := filepath.Join(tmpDir, "compose.yaml") composeFile := filepath.Join(tmpDir, "compose.yaml")
katenaryFile := filepath.Join(tmpDir, "katenary.yaml") katenaryFile := filepath.Join(tmpDir, "katenary.yaml")
@@ -56,13 +57,13 @@ webapp:
cli.WithWorkingDirectory(tmpDir), cli.WithWorkingDirectory(tmpDir),
cli.WithDefaultConfigPath, cli.WithDefaultConfigPath,
) )
project, err := cli.ProjectFromOptions(options) project, err := cli.ProjectFromOptions(context.TODO(), options)
if err != nil { if err != nil {
t.Fatalf("Failed to create project from options: %s", err.Error()) t.Fatal(err)
} }
OverrideWithConfig(project) OverrideWithConfig(project)
w := project.Services[0].Labels w := project.Services["webapp"].Labels
if v, ok := w[labels.LabelPorts]; !ok { if v, ok := w[labels.LabelPorts]; !ok {
t.Fatal("Expected ports to be defined", v) t.Fatal("Expected ports to be defined", v)
} }
@@ -86,7 +87,7 @@ webapp:
// create /tmp/katenary-test-override directory, save the compose.yaml file // create /tmp/katenary-test-override directory, save the compose.yaml file
tmpDir, err := os.MkdirTemp("", "katenary-test-override") tmpDir, err := os.MkdirTemp("", "katenary-test-override")
if err != nil { if err != nil {
t.Fatalf("Failed to create temp directory: %s", err.Error()) t.Fatal(err.Error())
} }
composeFile := filepath.Join(tmpDir, "compose.yaml") composeFile := filepath.Join(tmpDir, "compose.yaml")
katenaryFile := filepath.Join(tmpDir, "katenary.yaml") katenaryFile := filepath.Join(tmpDir, "katenary.yaml")
@@ -109,13 +110,14 @@ webapp:
cli.WithWorkingDirectory(tmpDir), cli.WithWorkingDirectory(tmpDir),
cli.WithDefaultConfigPath, cli.WithDefaultConfigPath,
) )
project, err := cli.ProjectFromOptions(options) project, err := cli.ProjectFromOptions(context.TODO(), options)
if err != nil { if err != nil {
t.Fatalf("Failed to create project from options: %s", err.Error()) t.Fatal(err)
} }
OverrideWithConfig(project) OverrideWithConfig(project)
w := project.Services[0].Labels log.Println(project.Services["webapp"].Labels)
w := project.Services["webapp"].Labels
if v, ok := w[labels.LabelPorts]; !ok { if v, ok := w[labels.LabelPorts]; !ok {
t.Fatal("Expected ports to be defined", v) t.Fatal("Expected ports to be defined", v)
} }

View File

@@ -1,2 +0,0 @@
// Package labels provides functionality to parse and manipulate labels.
package labels

View File

@@ -5,7 +5,6 @@ import (
_ "embed" _ "embed"
"fmt" "fmt"
"katenary/utils" "katenary/utils"
"log"
"regexp" "regexp"
"sort" "sort"
"strings" "strings"
@@ -84,7 +83,7 @@ func init() {
} }
} }
// GetLabelHelp return the help for the labels. // Generate the help for the labels.
func GetLabelHelp(asMarkdown bool) string { func GetLabelHelp(asMarkdown bool) string {
names := GetLabelNames() // sorted names := GetLabelNames() // sorted
if !asMarkdown { if !asMarkdown {
@@ -126,30 +125,23 @@ func GetLabelHelpFor(labelname string, asMarkdown bool) string {
} }
var buf bytes.Buffer var buf bytes.Buffer
var err error template.Must(template.New("shorthelp").Parse(help.Long)).Execute(&buf, struct {
err = template.Must(template.New("shorthelp").Parse(help.Long)).Execute(&buf, struct {
KatenaryPrefix string KatenaryPrefix string
}{ }{
KatenaryPrefix: KatenaryLabelPrefix, KatenaryPrefix: KatenaryLabelPrefix,
}) })
if err != nil {
log.Fatalf("Error executing template: %v", err)
}
help.Long = buf.String() help.Long = buf.String()
buf.Reset() buf.Reset()
err = template.Must(template.New("example").Parse(help.Example)).Execute(&buf, struct { template.Must(template.New("example").Parse(help.Example)).Execute(&buf, struct {
KatenaryPrefix string KatenaryPrefix string
}{ }{
KatenaryPrefix: KatenaryLabelPrefix, KatenaryPrefix: KatenaryLabelPrefix,
}) })
if err != nil {
log.Fatalf("Error executing template: %v", err)
}
help.Example = buf.String() help.Example = buf.String()
buf.Reset() buf.Reset()
err = template.Must(template.New("complete").Parse(helpTemplate)).Execute(&buf, struct { template.Must(template.New("complete").Parse(helpTemplate)).Execute(&buf, struct {
Name string Name string
Help Help Help Help
KatenaryPrefix string KatenaryPrefix string
@@ -158,9 +150,6 @@ func GetLabelHelpFor(labelname string, asMarkdown bool) string {
Help: help, Help: help,
KatenaryPrefix: KatenaryLabelPrefix, KatenaryPrefix: KatenaryLabelPrefix,
}) })
if err != nil {
log.Fatalf("Error executing template: %v", err)
}
return buf.String() return buf.String()
} }

View File

@@ -1,4 +1,4 @@
package labelstructs package labelStructs
import "gopkg.in/yaml.v3" import "gopkg.in/yaml.v3"

View File

@@ -1,4 +1,4 @@
package labelstructs package labelStructs
import "gopkg.in/yaml.v3" import "gopkg.in/yaml.v3"

View File

@@ -1,4 +1,4 @@
package labelstructs package labelStructs
import "gopkg.in/yaml.v3" import "gopkg.in/yaml.v3"

View File

@@ -0,0 +1,2 @@
// labelStructs is a package that contains the structs used to represent the labels in the yaml files.
package labelStructs

View File

@@ -1,4 +1,4 @@
package labelstructs package labelStructs
import "gopkg.in/yaml.v3" import "gopkg.in/yaml.v3"

View File

@@ -1,4 +1,4 @@
package labelstructs package labelStructs
import "gopkg.in/yaml.v3" import "gopkg.in/yaml.v3"

View File

@@ -1,4 +1,4 @@
package labelstructs package labelStructs
import ( import (
"fmt" "fmt"
@@ -14,10 +14,10 @@ type TLS struct {
type Ingress struct { type Ingress struct {
Port *int32 `yaml:"port,omitempty" json:"port,omitempty"` Port *int32 `yaml:"port,omitempty" json:"port,omitempty"`
Annotations map[string]string `yaml:"annotations,omitempty" jsonschema:"nullable" json:"annotations,omitempty"` Annotations map[string]string `yaml:"annotations,omitempty" jsonschema:"nullable" json:"annotations,omitempty"`
Hostname string `yaml:"hostname,omitempty" json:"hostname,omitempty"` Hostname string `yaml:"hostname" json:"hostname,omitempty"`
Path *string `yaml:"path,omitempty" json:"path,omitempty"` Path *string `yaml:"path,omitempty" json:"path,omitempty"`
Class *string `yaml:"class,omitempty" json:"class,omitempty" jsonschema:"default:-"` Class *string `yaml:"class,omitempty" json:"class,omitempty" jsonschema:"default:-"`
Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"` Enabled bool `yaml:"enabled" json:"enabled,omitempty"`
TLS *TLS `yaml:"tls,omitempty" json:"tls,omitempty"` TLS *TLS `yaml:"tls,omitempty" json:"tls,omitempty"`
} }

View File

@@ -1,4 +1,4 @@
package labelstructs package labelStructs
import "gopkg.in/yaml.v3" import "gopkg.in/yaml.v3"

View File

@@ -1,4 +1,4 @@
package labelstructs package labelStructs
import "gopkg.in/yaml.v3" import "gopkg.in/yaml.v3"

View File

@@ -1,4 +1,4 @@
package labelstructs package labelStructs
import ( import (
"encoding/json" "encoding/json"

View File

@@ -1,4 +1,4 @@
package labelstructs package labelStructs
import "gopkg.in/yaml.v3" import "gopkg.in/yaml.v3"

View File

@@ -1,4 +1,4 @@
package labelstructs package labelStructs
import "gopkg.in/yaml.v3" import "gopkg.in/yaml.v3"

View File

@@ -1,17 +0,0 @@
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])
}
}

View File

@@ -1,25 +0,0 @@
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)
}
}

View File

@@ -1,14 +0,0 @@
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)
}
}

View File

@@ -1,2 +0,0 @@
// Package labelstructs is a package that contains the structs used to represent the labels in the yaml files.
package labelstructs

View File

@@ -1,17 +0,0 @@
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])
}
}

View File

@@ -1,17 +0,0 @@
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)
}
}

View File

@@ -1,31 +0,0 @@
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")
}
}

View File

@@ -1,11 +0,0 @@
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))
}
}

View File

@@ -1,23 +0,0 @@
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)
}
}
}

View File

@@ -1,16 +0,0 @@
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)
}
}

View File

@@ -1,17 +0,0 @@
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)
}
}
}

View File

@@ -1,25 +0,0 @@
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"])
}
}

View File

@@ -3,7 +3,7 @@ package generator
import ( import (
"katenary/utils" "katenary/utils"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

View File

@@ -6,7 +6,7 @@ import (
"katenary/utils" "katenary/utils"
"strings" "strings"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )

View File

@@ -82,9 +82,7 @@ services:
AppVersion: appVersion, AppVersion: appVersion,
ChartVersion: chartVersion, ChartVersion: chartVersion,
} }
if err := Convert(convertOptions, "compose.yml"); err != nil { Convert(convertOptions, "compose.yml")
t.Fatalf("Failed to convert compose file: %s", err)
}
c, err := os.ReadFile("chart/values.yaml") c, err := os.ReadFile("chart/values.yaml")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View File

@@ -5,7 +5,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"

View File

@@ -49,7 +49,6 @@ func internalCompileTest(t *testing.T, options ...string) string {
ChartVersion: chartVersion, ChartVersion: chartVersion,
} }
if err := Convert(convertOptions, "compose.yml"); err != nil { if err := Convert(convertOptions, "compose.yml"); err != nil {
log.Printf("Failed to convert: %s", err)
return err.Error() return err.Error()
} }

View File

@@ -2,13 +2,13 @@ package generator
import ( import (
"katenary/generator/labels" "katenary/generator/labels"
"katenary/generator/labels/labelstructs" "katenary/generator/labels/labelStructs"
"katenary/utils" "katenary/utils"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -50,11 +50,11 @@ func fixPorts(service *types.ServiceConfig) error {
if portsLabel, ok = service.Labels[labels.LabelPorts]; !ok { if portsLabel, ok = service.Labels[labels.LabelPorts]; !ok {
return nil return nil
} }
ports, err := labelstructs.PortsFrom(portsLabel) ports, err := labelStructs.PortsFrom(portsLabel)
if err != nil { if err != nil {
// maybe it's a string, comma separated // maybe it's a string, comma separated
parts := strings.SplitSeq(portsLabel, ",") parts := strings.Split(portsLabel, ",")
for part := range parts { for _, part := range parts {
part = strings.TrimSpace(part) part = strings.TrimSpace(part)
if part == "" { if part == "" {
continue continue
@@ -87,7 +87,7 @@ func UnWrapTPL(in []byte) []byte {
return regexpLineWrap.ReplaceAll(in, []byte(" }}")) return regexpLineWrap.ReplaceAll(in, []byte(" }}"))
} }
func ToK8SYaml(obj any) ([]byte, error) { func ToK8SYaml(obj interface{}) ([]byte, error) {
if o, err := yaml.Marshal(obj); err != nil { if o, err := yaml.Marshal(obj); err != nil {
return nil, nil return nil, nil
} else { } else {

View File

@@ -1,14 +1,14 @@
package generator package generator
import ( import (
"context"
"fmt" "fmt"
"katenary/generator/labels" "katenary/generator/labels"
"katenary/utils"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/compose-spec/compose-go/cli" "github.com/compose-spec/compose-go/v2/cli"
) )
func TestSplitPorts(t *testing.T) { func TestSplitPorts(t *testing.T) {
@@ -26,7 +26,7 @@ services:
} }
composeFile := filepath.Join(tmpDir, "compose.yaml") composeFile := filepath.Join(tmpDir, "compose.yaml")
os.MkdirAll(tmpDir, utils.DirectoryPermission) os.MkdirAll(tmpDir, 0755)
if err := os.WriteFile(composeFile, []byte(composeFileContent), 0644); err != nil { if err := os.WriteFile(composeFile, []byte(composeFileContent), 0644); err != nil {
t.Log(err) t.Log(err)
} }
@@ -38,15 +38,18 @@ services:
cli.WithWorkingDirectory(tmpDir), cli.WithWorkingDirectory(tmpDir),
cli.WithDefaultConfigPath, cli.WithDefaultConfigPath,
) )
project, err := cli.ProjectFromOptions(options) project, err := cli.ProjectFromOptions(context.TODO(), options)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := fixPorts(&project.Services[0]); err != nil { service := project.Services["foo"]
if err := fixPorts(&service); err != nil {
t.Errorf("Expected no error, got %s", err) t.Errorf("Expected no error, got %s", err)
} }
project.Services["foo"] = service
found := 0 found := 0
for _, p := range project.Services[0].Ports { t.Log(service.Ports, project.Services["foo"].Ports)
for _, p := range project.Services["foo"].Ports {
switch p.Target { switch p.Target {
case 80, 443: case 80, 443:
found++ found++
@@ -74,7 +77,7 @@ services:
} }
composeFile := filepath.Join(tmpDir, "compose.yaml") composeFile := filepath.Join(tmpDir, "compose.yaml")
os.MkdirAll(tmpDir, utils.DirectoryPermission) os.MkdirAll(tmpDir, 0755)
if err := os.WriteFile(composeFile, []byte(composeFileContent), 0644); err != nil { if err := os.WriteFile(composeFile, []byte(composeFileContent), 0644); err != nil {
t.Log(err) t.Log(err)
} }
@@ -86,15 +89,16 @@ services:
cli.WithWorkingDirectory(tmpDir), cli.WithWorkingDirectory(tmpDir),
cli.WithDefaultConfigPath, cli.WithDefaultConfigPath,
) )
project, err := cli.ProjectFromOptions(options) project, err := cli.ProjectFromOptions(context.TODO(), options)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := fixPorts(&project.Services[0]); err != nil { service := project.Services["foo"]
if err := fixPorts(&service); err != nil {
t.Errorf("Expected no error, got %s", err) t.Errorf("Expected no error, got %s", err)
} }
found := 0 found := 0
for _, p := range project.Services[0].Ports { for _, p := range service.Ports {
switch p.Target { switch p.Target {
case 80, 443, 8080: case 80, 443, 8080:
found++ found++

View File

@@ -3,7 +3,7 @@ package generator
import ( import (
"strings" "strings"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
) )
// RepositoryValue is a docker repository image and tag that will be saved in values.yaml. // RepositoryValue is a docker repository image and tag that will be saved in values.yaml.

View File

@@ -8,7 +8,7 @@ import (
func TestVersion(t *testing.T) { func TestVersion(t *testing.T) {
// we build on "devel" branch // we build on "devel" branch
v := GetVersion() v := GetVersion()
if !strings.Contains(v, "(devel)") { if strings.Contains(v, "(devel)") {
t.Errorf("Expected version to be set, got %s", v) t.Errorf("Expected version to be set, got %s", v)
} }

View File

@@ -4,7 +4,7 @@ import (
"katenary/utils" "katenary/utils"
"strings" "strings"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

View File

@@ -6,7 +6,6 @@ import (
"image/color" "image/color"
"image/png" "image/png"
"katenary/generator/labels" "katenary/generator/labels"
"katenary/utils"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@@ -20,7 +19,7 @@ import (
const ( const (
htmlContent = "<html><body><h1>Hello, World!</h1></body></html>" htmlContent = "<html><body><h1>Hello, World!</h1></body></html>"
developementFile = "templates/web/deployment.yaml" developementFile = "templates/web/deployment.yaml"
indexHMLFile = "index.html" indexHtmlFile = "index.html"
) )
func TestGenerateWithBoundVolume(t *testing.T) { func TestGenerateWithBoundVolume(t *testing.T) {
@@ -69,7 +68,7 @@ services:
// create a static directory with an index.html file // create a static directory with an index.html file
staticDir := tmpDir + "/static" staticDir := tmpDir + "/static"
os.Mkdir(staticDir, utils.DirectoryPermission) os.Mkdir(staticDir, 0o755)
indexFile, err := os.Create(staticDir + "/index.html") indexFile, err := os.Create(staticDir + "/index.html")
if err != nil { if err != nil {
t.Errorf("Failed to create index.html: %s", err) t.Errorf("Failed to create index.html: %s", err)
@@ -107,8 +106,8 @@ services:
if len(data) != 1 { if len(data) != 1 {
t.Errorf("Expected 1 data, got %d", len(data)) t.Errorf("Expected 1 data, got %d", len(data))
} }
if data[indexHMLFile] != htmlContent { if data[indexHtmlFile] != htmlContent {
t.Errorf("Expected index.html to be "+htmlContent+", got %s", data[indexHMLFile]) t.Errorf("Expected index.html to be "+htmlContent+", got %s", data[indexHtmlFile])
} }
} }
@@ -129,7 +128,7 @@ services:
// create a static directory with an index.html file // create a static directory with an index.html file
staticDir := tmpDir + "/static" staticDir := tmpDir + "/static"
os.Mkdir(staticDir, utils.DirectoryPermission) os.Mkdir(staticDir, 0o755)
indexFile, err := os.Create(staticDir + "/index.html") indexFile, err := os.Create(staticDir + "/index.html")
if err != nil { if err != nil {
t.Errorf("Failed to create index.html: %s", err) t.Errorf("Failed to create index.html: %s", err)
@@ -154,7 +153,7 @@ services:
} }
// but this time, we need a subpath // but this time, we need a subpath
subPath := dt.Spec.Template.Spec.Containers[0].VolumeMounts[0].SubPath subPath := dt.Spec.Template.Spec.Containers[0].VolumeMounts[0].SubPath
if subPath != indexHMLFile { if subPath != indexHtmlFile {
t.Errorf("Expected subpath to be index.html, got %s", subPath) t.Errorf("Expected subpath to be index.html, got %s", subPath)
} }
} }
@@ -175,15 +174,15 @@ services:
log.Println(tmpDir) log.Println(tmpDir)
defer teardown(tmpDir) defer teardown(tmpDir)
os.Mkdir(filepath.Join(tmpDir, "images"), utils.DirectoryPermission) os.Mkdir(filepath.Join(tmpDir, "images"), 0o755)
// create a png image // create a png image
pngFile := tmpDir + "/images/foo.png" pngFile := tmpDir + "/images/foo.png"
w, h := 100, 100 w, h := 100, 100
img := image.NewRGBA(image.Rect(0, 0, w, h)) img := image.NewRGBA(image.Rect(0, 0, w, h))
red := color.RGBA{255, 0, 0, 255} red := color.RGBA{255, 0, 0, 255}
for y := range h { for y := 0; y < h; y++ {
for x := range w { for x := 0; x < w; x++ {
img.Set(x, y, red) img.Set(x, y, red)
} }
} }
@@ -245,15 +244,15 @@ services:
log.Println(tmpDir) log.Println(tmpDir)
defer teardown(tmpDir) defer teardown(tmpDir)
os.Mkdir(filepath.Join(tmpDir, "images"), utils.DirectoryPermission) os.Mkdir(filepath.Join(tmpDir, "images"), 0o755)
// create a png image // create a png image
pngFile := tmpDir + "/images/foo.png" pngFile := tmpDir + "/images/foo.png"
w, h := 100, 100 w, h := 100, 100
img := image.NewRGBA(image.Rect(0, 0, w, h)) img := image.NewRGBA(image.Rect(0, 0, w, h))
red := color.RGBA{255, 0, 0, 255} red := color.RGBA{255, 0, 0, 255}
for y := range h { for y := 0; y < h; y++ {
for x := range w { for x := 0; x < w; x++ {
img.Set(x, y, red) img.Set(x, y, red)
} }
} }

44
go.mod
View File

@@ -1,19 +1,19 @@
module katenary // github.com/metal3d/katenary module katenary // github.com/metal3d/katenary
go 1.24.0 go 1.23.0
toolchain go1.24.3 toolchain go1.23.4
require ( require (
github.com/compose-spec/compose-go v1.20.2 github.com/compose-spec/compose-go/v2 v2.4.6
github.com/invopop/jsonschema v0.13.0 github.com/invopop/jsonschema v0.12.0
github.com/mitchellh/go-wordwrap v1.0.1 github.com/mitchellh/go-wordwrap v1.0.1
github.com/spf13/cobra v1.9.1 github.com/spf13/cobra v1.8.1
github.com/thediveo/netdb v1.1.2 github.com/thediveo/netdb v1.1.2
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.33.2 k8s.io/api v0.32.0
k8s.io/apimachinery v0.33.2 k8s.io/apimachinery v0.32.0
sigs.k8s.io/yaml v1.5.0 sigs.k8s.io/yaml v1.4.0
) )
require ( require (
@@ -22,38 +22,34 @@ require (
github.com/distribution/reference v0.6.0 // indirect github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/imdario/mergo v0.3.16 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e // indirect
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect golang.org/x/net v0.32.0 // indirect
golang.org/x/net v0.41.0 // indirect golang.org/x/sync v0.10.0 // indirect
golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.28.0 // indirect
golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.21.0 // indirect
golang.org/x/text v0.26.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
) )

109
go.sum
View File

@@ -2,39 +2,43 @@ github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPn
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/compose-spec/compose-go v1.20.2 h1:u/yfZHn4EaHGdidrZycWpxXgFffjYULlTbRfJ51ykjQ= github.com/compose-spec/compose-go/v2 v2.4.6 h1:QiqXQ2L/f0OCbAl41bPpeiGAWVRIQ+GEDrYxO+dRPhQ=
github.com/compose-spec/compose-go v1.20.2/go.mod h1:+MdqXV4RA7wdFsahh/Kb8U0pAJqkg7mr4PM9tFKU8RM= github.com/compose-spec/compose-go/v2 v2.4.6/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -49,8 +53,6 @@ github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebG
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -62,24 +64,23 @@ github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/thediveo/netdb v1.1.2 h1:XdLx/YJPutxrSkPYtmCAIY5sgAvxtkS1Tz+Z0UX2I+U= github.com/thediveo/netdb v1.1.2 h1:XdLx/YJPutxrSkPYtmCAIY5sgAvxtkS1Tz+Z0UX2I+U=
github.com/thediveo/netdb v1.1.2/go.mod h1:KJczM//7VIIiovQO1qDooHvM8+0pt6RdRt3rVDZxEGM= github.com/thediveo/netdb v1.1.2/go.mod h1:KJczM//7VIIiovQO1qDooHvM8+0pt6RdRt3rVDZxEGM=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
@@ -95,44 +96,40 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e h1:4qufH0hlUYs6AO6XmZC3GqfDPGSXHVXUFR6OND+iJX4=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -147,21 +144,17 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
k8s.io/api v0.33.2 h1:YgwIS5jKfA+BZg//OQhkJNIfie/kmRsO0BmNaVSimvY= k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE=
k8s.io/api v0.33.2/go.mod h1:fhrbphQJSM2cXzCWgqU29xLDuks4mu7ti9vveEnpSXs= k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0=
k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY= k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg=
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI=
sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=

View File

@@ -35,8 +35,8 @@ if [ -z "$INSTALL_PATH" ]; then
fi fi
# ensure that $INSTALL_PATH is in the PATH # ensure that $INSTALL_PATH is in the PATH
if ! echo "$PATH" | grep -q "$INSTALL_PATH"; then if ! echo $PATH | grep -q $INSTALL_PATH; then
echo "Sorry, ${INSTALL_PATH} is not in the PATH" echo "Sorry, $INSTALL_PATH is not in the PATH"
echo "Please, add it to your PATH in your shell configuration file" echo "Please, add it to your PATH in your shell configuration file"
echo "then restart your shell and run this script again" echo "then restart your shell and run this script again"
exit 1 exit 1
@@ -58,8 +58,8 @@ echo "Downloading $BIN_URL"
T=$(mktemp -u) T=$(mktemp -u)
curl -SL -# $BIN_URL -o $T || (echo "Failed to download katenary" && rm -f $T && exit 1) curl -SL -# $BIN_URL -o $T || (echo "Failed to download katenary" && rm -f $T && exit 1)
mv "$T" "${INSTALL_PATH}/katenary" mv $T $INSTALL_PATH/katenary
chmod +x "${INSTALL_PATH}/katenary" chmod +x $INSTALL_PATH/katenary
echo echo
echo "Installed to $INSTALL_PATH/katenary" echo "Installed to $INSTALL_PATH/katenary"
echo "Installation complete! Run 'katenary help' to get started." echo "Installation complete! Run 'katenary help' to get started."

View File

@@ -1,12 +1,13 @@
// Package parser is a wrapper around compose-go to parse compose files. // Parser package is a wrapper around compose-go to parse compose files.
package parser package parser
import ( import (
"context"
"log" "log"
"path/filepath" "path/filepath"
"github.com/compose-spec/compose-go/cli" "github.com/compose-spec/compose-go/v2/cli"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
) )
func init() { func init() {
@@ -53,5 +54,5 @@ func Parse(profiles []string, envFiles []string, dockerComposeFile ...string) (*
if err != nil { if err != nil {
return nil, err return nil, err
} }
return cli.ProjectFromOptions(options) return cli.ProjectFromOptions(context.TODO(), options)
} }

View File

@@ -1,60 +0,0 @@
package parser
import (
"log"
"os"
"path/filepath"
"testing"
)
const composeFile = `
services:
app:
image: nginx:latest
`
func setupTest() (string, error) {
// write the composeFile to a temporary file
tmpDir, err := os.MkdirTemp("", "katenary-test-parse")
if err != nil {
return "", err
}
writeFile := filepath.Join(tmpDir, "compose.yaml")
writeErr := os.WriteFile(writeFile, []byte(composeFile), 0644)
return writeFile, writeErr
}
func tearDownTest(tmpDir string) {
if tmpDir != "" {
if err := os.RemoveAll(tmpDir); err != nil {
log.Fatalf("Failed to remove temporary directory %s: %s", tmpDir, err.Error())
}
}
}
func TestParse(t *testing.T) {
file, err := setupTest()
dirname := filepath.Dir(file)
currentDir, _ := os.Getwd()
if err := os.Chdir(dirname); err != nil {
t.Fatalf("Failed to change directory to %s: %s", dirname, err.Error())
}
defer func() {
tearDownTest(dirname)
if err := os.Chdir(currentDir); err != nil {
t.Fatalf("Failed to change back to original directory %s: %s", currentDir, err.Error())
}
}()
if err != nil {
t.Fatalf("Failed to setup test: %s", err.Error())
}
Project, err := Parse(nil, nil)
if err != nil {
t.Fatalf("Failed to parse compose file: %s", err.Error())
}
if Project == nil {
t.Fatal("Expected project to be not nil")
}
}

View File

@@ -1,3 +1,2 @@
// Package utils provides some utility functions used in katenary. // Utils package provides some utility functions used in katenary. It defines some constants and functions used in the whole project.
// It defines some constants and functions used in the whole project.
package utils package utils

View File

@@ -1,13 +0,0 @@
package utils
import "testing"
func TestHash(t *testing.T) {
h, err := HashComposefiles([]string{"./hash.go"})
if err != nil {
t.Fatalf("failed to hash compose files: %v", err)
}
if len(h) == 0 {
t.Fatal("hash should not be empty")
}
}

View File

@@ -22,7 +22,7 @@ const (
) )
// Warn prints a warning message // Warn prints a warning message
func Warn(msg ...any) { func Warn(msg ...interface{}) {
orange := "\033[38;5;214m" orange := "\033[38;5;214m"
reset := "\033[0m" reset := "\033[0m"
fmt.Print(IconWarning, orange, " ") fmt.Print(IconWarning, orange, " ")

View File

@@ -7,16 +7,13 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/compose-spec/compose-go/types" "github.com/compose-spec/compose-go/v2/types"
"github.com/mitchellh/go-wordwrap" "github.com/mitchellh/go-wordwrap"
"github.com/thediveo/netdb" "github.com/thediveo/netdb"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
corev1 "k8s.io/api/core/v1" 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. // TplName returns the name of the kubernetes resource as a template string.
// It is used in the templates and defined in _helper.tpl file. // It is used in the templates and defined in _helper.tpl file.
func TplName(serviceName, appname string, suffix ...string) string { func TplName(serviceName, appname string, suffix ...string) string {
@@ -93,7 +90,7 @@ func GetContainerByName(name string, containers []corev1.Container) (*corev1.Con
return nil, -1 return nil, -1
} }
// TplValue returns a container by name and its index in the array. // GetContainerByName returns a container by name and its index in the array.
func TplValue(serviceName, variable string, pipes ...string) string { func TplValue(serviceName, variable string, pipes ...string) string {
if len(pipes) == 0 { if len(pipes) == 0 {
return `{{ tpl .Values.` + serviceName + `.` + variable + ` $ }}` return `{{ tpl .Values.` + serviceName + `.` + variable + ` $ }}`
@@ -139,12 +136,12 @@ func GetValuesFromLabel(service types.ServiceConfig, LabelValues string) map[str
switch val := value.(type) { switch val := value.(type) {
case string: case string:
descriptions[val] = nil descriptions[val] = nil
case map[string]any: case map[string]interface{}:
for k, v := range value.(map[string]any) { for k, v := range value.(map[string]interface{}) {
descriptions[k] = &EnvConfig{Service: service, Description: v.(string)} descriptions[k] = &EnvConfig{Service: service, Description: v.(string)}
} }
case map[any]any: case map[interface{}]interface{}:
for k, v := range value.(map[any]any) { for k, v := range value.(map[interface{}]interface{}) {
descriptions[k.(string)] = &EnvConfig{Service: service, Description: v.(string)} descriptions[k.(string)] = &EnvConfig{Service: service, Description: v.(string)}
} }
default: default:
@@ -168,9 +165,7 @@ func Confirm(question string, icon ...Icon) bool {
fmt.Print(question + " [y/N] ") fmt.Print(question + " [y/N] ")
} }
var response string var response string
if _, err := fmt.Scanln(&response); err != nil { fmt.Scanln(&response)
log.Fatalf("Error parsing response: %s", err.Error())
}
return strings.ToLower(response) == "y" return strings.ToLower(response) == "y"
} }