368 Commits

Author SHA1 Message Date
c85e61f5d0 Merge branch 'master' of repo.katenary.io:Katenary/katenary
All checks were successful
Go-Tests / tests (push) Successful in 1m49s
Go-Tests / sonar (push) Successful in 49s
2025-08-19 23:20:12 +02:00
f1c31b0eeb feat(doc): move some things in README
As we moved project to repo.katenary.io, badges were not OK
2025-08-19 22:31:50 +02:00
677b0d7b3c Merge pull request 'feat(actions): Change action to Gitea' (#161) from test-workflow into master
All checks were successful
Go-Tests / tests (push) Successful in 1m59s
Go-Tests / sonar (push) Successful in 1m7s
Reviewed-on: #161
2025-08-19 19:59:17 +00:00
f539be8f52 Remove proxy
All checks were successful
Go-Tests / tests (pull_request) Successful in 2m29s
Go-Tests / sonar (pull_request) Successful in 51s
2025-08-19 21:54:01 +02:00
4336490a72 feat(actions): Change action to Gitea
- Build and packages on Gitea action
- Use go 1.25
- Update dependencies
2025-08-19 21:52:33 +02:00
9f75f874d1 Features/buildah build (#159)
* feat(oci): Build using pure Buildah
2025-08-13 10:20:48 +02:00
90eda75219 feat(oci): Use Buildah
And set version the right way...
2025-08-04 16:18:35 +02:00
b13e922966 feat(package) Version were not correctly set
As the code moved in "internal" package, we need to set the version to
the new location
2025-08-04 16:13:35 +02:00
0d36a48f93 fix(securty): Fix security alert
Code scanning alert: we need to specify permissions on this action.
2025-08-03 23:29:39 +02:00
60016b8c3e Merge pull request #157 from Katenary/features/oci-image
Features/oci image
2025-08-03 23:06:06 +02:00
07263520b6 feat(oci): remove testing on push branches 2025-08-03 23:01:54 +02:00
0f3812b666 feat(oci): Add OCI image generation
Not fully tested but it should produce an image in ghcr.io
2025-08-03 22:54:19 +02:00
50a9d0ddde feat(md): fixes typo and rules 2025-08-03 22:53:53 +02:00
f5d3005c3b Merge pull request #156 from Katenary/feat-move-to-internal
feat(refacto): move everything in internal package
2025-08-03 16:03:17 +02:00
76b3b83d07 feat(workflow): change when to make CI
We will abandon develop branch for a while. It's better to work with
feature branches.
2025-08-03 15:59:00 +02:00
14ca5bf0ea feat(refacto): move everything in internal package
This allows to install katenary with `go install` and to clean up the
project folder.
2025-08-03 15:54:58 +02:00
d1768e5742 Merge pull request #155 from Katenary/develop
Develop
2025-08-03 14:20:17 +02:00
0fcf0f60e8 chore(modules): Update versions 2025-08-03 14:15:55 +02:00
e9ad85a0ac chore(make): Split and enhancements
- split Makefile in several sub files
- use a separated container for tests
-
2025-08-03 14:15:35 +02:00
b4b122fe7f Do not ignore cmd/katenary directory 2025-08-03 14:14:29 +02:00
6aaeda7a3c chore(modernize): use iterator 2025-08-03 14:13:57 +02:00
0b3f7b2b5c chore(code): remove dead code 2025-08-03 14:13:42 +02:00
e9e7c5f7b5 chore(test): fix concurrency problem 2025-08-03 14:12:49 +02:00
0df53ed5ae Merge pull request #154 from Katenary/develop
fix(configmap): File from the root of project ends by a dash
2025-08-03 13:16:21 +02:00
136478aff7 fix(configmap): File from the root of project ends by a dash
fixes #150

If we mount a file from the root of project, the names ends by a dash.
That makes helm failing to install the package.
2025-08-03 10:20:11 +02:00
5b812b30f7 Merge pull request #146 from Katenary/develop
fix(katenaryfile): Schema is broken without json annotation
2025-07-15 21:25:39 +02:00
f2b5c16e71 fix(katenaryfile): Schema is broken without json annotation
We need to add json annotation. The yaml annoation is not enought when
generating the schema.
2025-07-15 21:24:17 +02:00
df1f29c0ff Merge pull request #145 from Katenary/develop
fix(katenaryfile): Fix parsing katenary file
2025-07-15 21:18:45 +02:00
afbd6fc1ac fix(katenaryfile): Fix parsing katenary file
It seems that "json" annotation fails to get dashed names (like
configmap-files). That means that all labels in katernay.yaml file
couldn't be parsed.

Using `yaml` annotaiton fixes the problem. Fixes #144.
2025-07-15 21:13:22 +02:00
327e0aa799 Merge pull request #142 from Katenary/develop
chore(make): manpage requirements are now in requirements
2025-07-15 13:58:48 +02:00
b7e948954b chore(make): manpage requirements are now in requirements 2025-07-15 13:57:35 +02:00
93c0658c19 Merge pull request #141 from Katenary/develop
Develop
2025-07-15 13:53:20 +02:00
bdf74f2deb chore(cleanup): removed unecessary file 2025-07-15 13:51:21 +02:00
2435bb9f8a chore(doc): Rebuild label list and description 2025-07-15 13:50:07 +02:00
5ee38dcf7f Merge pull request #140 from Katenary/develop
Develop
2025-07-15 13:45:38 +02:00
a754dc9275 fix(make): no need to auto retrieve key now 2025-07-15 13:44:25 +02:00
732e080da3 Merge branch 'master' into develop 2025-07-15 13:42:15 +02:00
22e1ebdce1 chore(make): huge refactor, add more tests
- more test on gpg signature
- change build target in the help message
- refactor upx compression
-
2025-07-15 13:41:49 +02:00
e4280f2c4e chore(build): Fixes cache, add better cleanup, add PHONY 2025-07-15 10:27:24 +02:00
a99609d21d Merge pull request #139 from Katenary/develop
fix(doc): Fix documentation linkg
2025-07-13 15:41:01 +02:00
e55658d382 fix(doc): Fix documentation linkg
No translation, so remove "en" from the url.
2025-07-13 15:40:30 +02:00
9dbe9e1114 Merge pull request #138 from Katenary/develop
Develop
2025-07-13 15:37:31 +02:00
eea198786a fix(doc): Readthedocs needs manpage plugin
A pitty, I wanted to leave it separated
2025-07-13 15:29:16 +02:00
da9c0a7b58 chore(doc): regenerate and fixes a typo 2025-07-13 15:16:39 +02:00
d582b884bf chore(rules): Avoid mardownlint to complain 2025-07-13 15:16:20 +02:00
aa3e4c64ee chore(ignore): ignoring binary in the current directory 2025-07-13 15:08:32 +02:00
ce479629f6 fix(service): Fixes naming service ports
- ports should be named "port-XXX" with XXX to be the port number if the
port name cannot be discovered
- it follows what we do in Deployment objects
- this should fix #132
2025-07-13 15:02:26 +02:00
89fd516b20 Merge pull request #137 from Katenary/develop
Develop
2025-07-13 00:53:45 +02:00
8bbb991afb chore(doc): Move to new repository 2025-07-13 00:43:21 +02:00
4c5c9ba553 chore(doc): Move to new repository 2025-07-13 00:39:25 +02:00
ef1511d4f7 Merge pull request #134 from Katenary/develop
Develop
2025-07-13 00:21:15 +02:00
a290219c81 chore(licence): I prefer to mention the project 2025-07-13 00:19:02 +02:00
c4e406f855 chore(moving): Moved repository to organization 2025-07-13 00:17:48 +02:00
2beac5f4dd Merge pull request #135 from Katenary/issue-133
fix(deployment): Missed the command from compose file
2025-07-13 00:10:22 +02:00
5f5cd0268a fix(deployment): Missed the command from compose file
Fixes #133
2025-07-13 00:07:07 +02:00
796cbb6d30 chore(package): Rewrite package targets
- rewrite to make packages building more consistent
- add rpm signature with GPG
- auto test packages in several distributions to ensure the packages are
OK

Even if loops to build stuffs are sometimes easier to automate, it's
simpler to fixup if some rules (targets) are explicitly defined.
2025-07-12 09:12:12 +02:00
39ebf7eb78 chore(package): change description, add a final dot 2025-07-12 09:08:41 +02:00
a1c5161ff7 chore(package): Be able to sign RPM 2025-07-12 09:08:28 +02:00
2307ad667e chore(dev): changed how to test version
- force release version check to use "v" (or not) as first character
- one line condtion
2025-07-11 09:17:33 +02:00
a26e832fed fix(doc): recompiled documentation 2025-07-11 09:16:43 +02:00
8582010d90 fix(versionning): Fix version
Version should be x.y.z for releases. It makes things homogenous for
debian version (that needs a number as first char) and it is
semantically better.

At this time, the Makefile is OK. Maybe we don't need `PKG_VERSION`
later.
2025-07-10 15:15:25 +02:00
b7186eb8b4 chore(makefile): Refacto
Refactor the makefile:

- use the "package" container image to build windows installer
- cleanup and reorder targets
- enhance the way we named packages (add version and architecture to the
name)
2025-07-10 08:13:49 +02:00
0f3528818f chore(package): Add NSIS to the package image
This container image can make Windows installer too.
2025-07-10 08:11:39 +02:00
9fadc77979 chore(package): Add application in registry
Allow the application to appear in "Install/Uninstall" Windows
parameters.
2025-07-10 08:10:11 +02:00
13d231a62c feat(package): Add RPM, deb, pacman and tar packages and manpage
Build package using fpm. As I don't want ruby/gem in my computer, the
build system creates an OCI image with Podman (at this time), and
generate dist pacakges.

To enhance the packages, a manpage is now generated from the
documentation.
2025-07-09 14:11:46 +02:00
6cbaa06bec chore(license): Change date and copyright
I keep MIT license, just changed date and pseudo
2025-07-07 23:50:03 +02:00
1552e9c877 Merge pull request #131 from metal3d/develop
Develop
2025-07-07 23:02:13 +02:00
3b2cd37ea4 chore(test): add tests 2025-07-07 22:48:13 +02:00
4c566cbc1a fix(doc): Fix function doc 2025-07-07 22:47:59 +02:00
69c528e5e0 chore(doc): Enhance documentation, fix typo 2025-07-07 22:10:27 +02:00
42bf35593f chore(makefile): rewrite correctly some rules
- cleanup
- sign and check gpg signature
- fixup NSIS installer method
- also sign windows installer
2025-07-07 21:58:15 +02:00
4e739c5f08 fix(doc): Fixing duplicated package 2025-07-07 21:57:14 +02:00
f75623b4ff chore(nsis): sort language
Sort all languages, excepting the first one wich is the default language
2025-07-07 21:31:58 +02:00
d82b5e4a98 feat(nsis): Add more languages, files and license page
- Include maximum of language for the Windows installer
- Add licence page to inform the user that Katenary is free (as freedom)
2025-07-07 18:18:20 +02:00
42c738211c fix(build): Fixing build, do not use alpine
Alpine image seems to break the Windows binary (striped by error) and
makes katenary unusable.
2025-07-07 17:59:16 +02:00
c54bc35c9f feat(nsis): Create installer for Windows
Use nsis to create an installer for Windows user. It installs the binary
for the user (for now) and set the Path to access katenary binary from
cmd or Powershell.
2025-07-07 17:58:21 +02:00
1f406dc558 Merge pull request #130 from metal3d/develop
fix(build): Windows is a shame
2025-07-07 13:50:31 +02:00
0c0b50b0df fix(build): Windows is a shame
- Windows defender says that UPX compressed files are virus
(https://github.com/upx/upx/issues/437) so I stop using this for
Windows. Sorry, you will download a huge binary
- Windows, one more time, stop making the binary working when it's
stripped. Then, one more time, I cannot reduce the binary size

I'm thinking very seriously about no longer offering support for Windows
and letting people compile Katenary on their own.
2025-07-07 13:47:27 +02:00
6efee2fbae Merge pull request #129 from metal3d/develop
Many fixes on code quality and CI
2025-07-06 15:37:08 +02:00
47e149e20b chore(doc): Regenerated doc 2025-07-06 15:36:43 +02:00
d163700147 feat(tests): Add tests for label structures 2025-07-06 15:31:21 +02:00
fa3befaa88 fix(tests): Fixing unbound error 2025-07-06 15:31:05 +02:00
e5b7c0d550 fix(doc): Respect Go recommandation in docstring 2025-07-06 15:30:31 +02:00
9e4663cc6e chore(convention): Respect Go convention, package name should be lowercase 2025-07-06 14:34:16 +02:00
1a5c95d240 fix(ci): typo in permission block 2025-07-06 14:26:20 +02:00
21845c5de1 fix(ci): typo in permission block 2025-07-06 14:24:12 +02:00
4a91d458fd fix(ci): try to add permission block 2025-07-06 14:21:56 +02:00
a52b20efab Merge pull request #128 from metal3d/feat-change-actions
Feat change actions
2025-07-06 14:16:38 +02:00
02c4c5168b feat(ci): Changes CI
- use SonarQube action
- remove the helm installation as it is already present
- changes types and branches activation
2025-07-06 14:13:23 +02:00
7b875454cb feat(chore): use range over int
More readable range
2025-07-06 11:53:56 +02:00
2da5d9df08 feat(chore): modernize code 2025-07-06 11:53:24 +02:00
a0eb3c0bb6 fix(doc): Fix doc string 2025-07-06 11:52:48 +02:00
7e76543c48 fix(doc): Fix the docstring following Go recommandation 2025-07-06 11:41:19 +02:00
09c2c86d59 fix(permission): globalize and fixes
Permission alert frop OpenGrep is wrong, as the directory must use 0755.
To make things working and to ease futur changes, I set the default
permission in a constant.
2025-07-06 11:39:19 +02:00
e58948bb44 feat(code): use better check on empty string 2025-07-06 10:53:18 +02:00
a00b03b2aa feat(chore): Add SAST with opengrep 2025-07-06 10:52:56 +02:00
b47b956798 fix(install): Enhance checks and code
- Opengrep complains about non quoted strings
- Bad indentation fixed
2025-07-06 10:52:35 +02:00
a409a1347e feat(chore): rename variable 2025-07-06 10:51:25 +02:00
8999aabc21 fix(security): Change access rights on generated directories
Ppengrep complains, it is right.
2025-07-06 10:51:09 +02:00
14e8907437 feat(doc): Add package doc for labels 2025-07-06 10:49:54 +02:00
740c400b9a Merge pull request #127 from metal3d/develop
feat(doc): regenerate the package docs
2025-07-04 15:05:34 +02:00
7cd38bbd23 feat(doc): regenerate the package docs 2025-07-04 14:59:49 +02:00
8c509b5bff Merge pull request #126 from metal3d/develop
Merge branch 'master' into develop
2025-07-04 14:53:00 +02:00
28b22a0b30 Merge branch 'master' into develop 2025-07-04 14:49:35 +02:00
748d0bf1ea Merge pull request #120 from metal3d/develop 2025-06-27 00:27:49 +02:00
130e6d4e24 chore(dependencies): Update dependencies 2025-06-26 23:57:44 +02:00
a66fec07e1 chore(optim): Optimizing some piece of code
Simply use modern methods
2025-06-26 23:57:19 +02:00
a3d1e9342f fix(doc): Follow Go recommendations 2025-06-26 23:56:06 +02:00
72bc88661a fix(convension): Fix APIVersion
Sonarlint complains about "ApiVersion"
2025-06-26 23:37:20 +02:00
e2b897eb9d fix(generation): Fix container name
Container names were built using the service name. We didn't checked the
name and leave underscores inside.

This commit does:
- fix all service and rename containers (`container_name`)
- use `ContainerName` everywhere we need to get the container by name

See #106
2025-06-26 23:37:20 +02:00
9fcce059e5 fix(doc): Fixing method name in comment 2025-06-26 23:18:11 +02:00
36f6413917 feat(build): UPX compression
UPX can compress binaries from 22Mo to 7Mo on Linux / Windows and 14Mo
on MacOS. Let's use it to reduce binary size.

I don't know why it doesn't work on FreeBSD.
2025-06-15 16:02:45 +02:00
8c729f3c57 fix(build): remove "-it" options
Building in parallel make a warn message as TTY is not OK
2025-06-15 16:01:16 +02:00
063cc9d439 Merge pull request #119 from metal3d/develop
Develop
2025-06-15 16:14:10 +03:00
ac5317e600 feat(doc): regenerate docs 2025-06-15 14:48:35 +02:00
bc0b65006d feat(doc): Add doc target
This only regenerates docs for packages.
2025-06-15 14:48:24 +02:00
933f04bf5e feat(version): change Go version 2025-06-15 14:43:58 +02:00
7d46435ba2 Merge pull request #118 from metal3d/develop
Develop
2025-06-04 15:22:09 +02:00
d94bb8ac32 feat(doc): regenerate documentation 2025-06-04 15:18:11 +02:00
b143f743ef feat(chore): Add tests and use "any" instead of "inteface" 2025-06-04 15:17:26 +02:00
a8341a9b44 feat(tests): .Close() can be "unchecked" 2025-06-04 14:41:53 +02:00
def5d097a4 feat(tests): Fixing linter problems
Using golangci-lint
2025-06-04 14:29:13 +02:00
ba0ae1bc60 Merge pull request #117 from metal3d/develop
Develop
2025-06-04 09:51:48 +02:00
d77029b597 feat(version): update dependencies
New versions for:
- Cobra
- K8S API
- indirect subpackages
2025-06-04 09:48:52 +02:00
b5f62d43af fix(test): Fix non constant string format
Go 1.24 made several changes about formatting messages:
https://tip.golang.org/doc/go1.24#vet

These changes make tests (and vet) craching.

The fix is to use a string format and give error message as argument.
2025-06-04 09:46:13 +02:00
9220dc3278 fix(test): Fix bad test
We were failing if the test is OK...
2025-06-04 09:24:33 +02:00
36c72fb665 Merge pull request #114 from GSergeevich/readme_fix
Fix example of docker-compose.yml
2025-04-02 15:01:06 +02:00
Герман Плотников
c90504f4f1 Fix example of docker-compose.yml 2025-03-26 18:57:47 +03:00
9ef961ae7c issue(107): Really drop ignored services
It's, at this time, not needed to keep the ignored services inside the
project. Maybe later someone will ask to keep env variables, or
something like this... But at this time, it's a source of bug like #107.
2025-01-19 23:38:17 +01:00
fe6663f9f4 issue(106): Fix service names with dashes
See #106, I need to add a test on "same-pod" label.
2025-01-19 23:24:09 +01:00
1190b316bb Merge pull request #108 from metal3d/develop
Bump versions
2025-01-17 22:19:42 +01:00
7068dc229c Merge pull request #104 from metal3d/dependabot/go_modules/develop/k8s.io/api-0.32.1
chore(deps): bump k8s.io/api from 0.32.0 to 0.32.1
2025-01-17 22:12:36 +01:00
dependabot[bot]
61ce6fb25b chore(deps): bump k8s.io/api from 0.32.0 to 0.32.1
Bumps [k8s.io/api](https://github.com/kubernetes/api) from 0.32.0 to 0.32.1.
- [Commits](https://github.com/kubernetes/api/compare/v0.32.0...v0.32.1)

---
updated-dependencies:
- dependency-name: k8s.io/api
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-17 21:10:36 +00:00
116ef658d8 Merge pull request #103 from metal3d/dependabot/go_modules/develop/github.com/invopop/jsonschema-0.13.0
chore(deps): bump github.com/invopop/jsonschema from 0.12.0 to 0.13.0
2025-01-17 22:09:53 +01:00
d7b354de8c Merge pull request #105 from metal3d/dependabot/go_modules/develop/k8s.io/apimachinery-0.32.1
chore(deps): bump k8s.io/apimachinery from 0.32.0 to 0.32.1
2025-01-17 22:09:28 +01:00
dependabot[bot]
d06c0574fb chore(deps): bump k8s.io/apimachinery from 0.32.0 to 0.32.1
Bumps [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) from 0.32.0 to 0.32.1.
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.32.0...v0.32.1)

---
updated-dependencies:
- dependency-name: k8s.io/apimachinery
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-16 22:31:32 +00:00
dependabot[bot]
bcd7894e3b chore(deps): bump github.com/invopop/jsonschema from 0.12.0 to 0.13.0
Bumps [github.com/invopop/jsonschema](https://github.com/invopop/jsonschema) from 0.12.0 to 0.13.0.
- [Commits](https://github.com/invopop/jsonschema/compare/v0.12.0...v0.13.0)

---
updated-dependencies:
- dependency-name: github.com/invopop/jsonschema
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-31 22:51:12 +00:00
80aba05f66 Merge pull request #101 from metal3d/develop
Enhance tests
2024-12-17 18:25:34 +01:00
41a4292939 chore(test): enhance error handling and avoid repetitions 2024-12-17 18:22:45 +01:00
131ea5d569 mod(version): update k8s.io api modules 2024-12-17 18:22:20 +01:00
e36bbf41f4 Update go-test.yaml
Use mod tidy as dependabot may change the go.mod file
2024-12-17 18:09:40 +01:00
c97b398914 Merge pull request #96 from metal3d/develop
Fixing docs
2024-12-05 09:47:29 +01:00
d27ed76cf4 doc(readme): better comments 2024-12-05 07:24:23 +01:00
17b6ea51af fix(doc): Bad variable name in secrets 2024-12-05 07:23:58 +01:00
0986f73f06 Merge pull request #95 from metal3d/develop
Add SecretName in TLS + adapt github actions
2024-12-05 07:16:33 +01:00
dcd282779f test(gh): Ordering steps...
I'm stupid... I need to upload artifacts after the creation.
2024-12-05 07:08:16 +01:00
b93a3df98c test(gh): Checkout project 2024-12-05 07:04:47 +01:00
4768330c1a test(gh): Fix job name 2024-12-05 06:55:50 +01:00
23d0afd85f test(gh): Use 2 steps
I prefer to split tests and sonar upload
2024-12-05 06:54:38 +01:00
5c939383be chore(tls): Add secretName to the values
The seret name for TLS wasn't editable, it may be useful to change it
when we generate TLS certificates for specific installation.
2024-12-05 06:43:43 +01:00
72ddb8aa74 Merge pull request #94 from metal3d/develop
chore(presentation): Update README
2024-12-03 15:14:54 +01:00
4d0e5b2e6a chore(presentation): Update README 2024-12-03 15:13:28 +01:00
58d1e8e450 Merge pull request #92 from metal3d/develop
doc(refresh): Refresh doc after changing functions
2024-12-03 14:47:52 +01:00
d0790f37a8 doc(refresh): Refresh doc after changing functions 2024-12-03 14:45:27 +01:00
62b0576c2d Merge pull request #91 from metal3d/develop
Fix binary data, Add tests, Error management
2024-12-03 14:44:19 +01:00
e574a2e2a8 chore(errors): Better error management
We must remove all "Fatal" calls and use errors instead, to be returned
and managed globally.
This is the first step, but it is, at this time, a real problem. Tests
are complicated without this.
2024-12-03 14:37:13 +01:00
eb760d4299 test(subdir): Add globally mount binary files 2024-12-03 14:03:36 +01:00
3833037862 doc(refresh): after changing names and adding functions 2024-12-03 13:52:22 +01:00
6dc92df4b5 chore(icons): Better icons for warning and info 2024-12-03 13:51:39 +01:00
d458cdbd73 chore(configmap): Manage binary data in configMap
We should be now able to detect and manage binary files to be injected
in configMaps
2024-12-03 13:50:58 +01:00
628b35d471 doc(readme): fix the schema location 2024-12-01 08:48:33 +01:00
84363be0e8 Fix Readme after merging master into develop 2024-11-27 09:27:28 +01:00
5284cdf5cc Merge pull request #90 from metal3d/develop
Remove update functions and fix dependabot target branch
2024-11-27 09:23:44 +01:00
632ffc2b66 chore(module): update dependencies
Updated k8s.io/api, x/exp, x/net, and structured-merge-diff
2024-11-27 09:21:33 +01:00
2ff705164e chore(module): Cleanup 2024-11-27 09:17:45 +01:00
73ab867509 chore(update): remove the update methods
The update functions were not linked and absolutely not stable anyway. I
will find a better way to propose a check-update method later.
2024-11-27 09:16:49 +01:00
45e44dee14 wip(update): the update function is not clean 2024-11-27 00:46:15 +01:00
689c2a4803 Use develop branch please 2024-11-27 00:26:25 +01:00
e023544c8a Merge pull request #88 from metal3d/develop
Enhance doc, description, examples
2024-11-27 00:00:58 +01:00
10b7a49bbf doc(enhancement): Katenary is complete, and we can add values-from example
- Katenary is no longer a "bootstraper", it **should** generate a
complete Helm chart
- Add a `values-from` example, because this label is very useful
2024-11-26 23:43:57 +01:00
bb1354e228 doc(refacto): Rewrite label types, and format table
- I prefer using Go types, more explicit in my humble opinion
- The markdown table wasn't well-formed
2024-11-26 23:42:39 +01:00
957cc4bcf6 Merge pull request #87 from metal3d/develop
Add schema to the root, fix test coverage
2024-11-26 17:55:46 +01:00
9f1f6c7e78 test(version): cover the version function 2024-11-26 17:53:43 +01:00
a80ddcc054 test(main): More tests in main command package 2024-11-26 17:45:57 +01:00
921eaff367 chore(output): Version should be printed in stdout
It seems that "println" failed to write on stdout
2024-11-26 17:45:42 +01:00
b63d8e4210 doc(readme): Explain the use of the yaml/json schema
And set heading level to 2
2024-11-26 17:10:54 +01:00
9766dac763 yaml(schema): Propose a schema to use with LSP
This schema works, at least, with yaml-lsp (yamlls) in NeoVim.
2024-11-26 17:02:25 +01:00
441be30720 chore(version): Get the version following how katenary is installed 2024-11-26 16:47:37 +01:00
e13653fba1 doc(readme): fix the command line help output 2024-11-26 16:47:05 +01:00
6b301f3171 Merge pull request #86 from metal3d/develop
Add "values-from" and more tests
2024-11-26 16:29:14 +01:00
9181c389d4 doc(refresh): Refresh documentation 2024-11-26 16:11:46 +01:00
3b4dade699 chore(label): new label "values-from"
This labels allow to use some environment variables from another service
and use the configMap / secret instead of the original value. This is
useful to avoid duplication of values for several variables.
2024-11-26 16:11:12 +01:00
4f0298c0a9 test(utils): Add some tests 2024-11-26 16:09:12 +01:00
5f20585fb2 Merge pull request #85 from metal3d/develop
test(codecov): remove codecov
2024-11-26 09:10:48 +01:00
0714eac615 test(codecov): remove codecov
Codecov is very nice but we already use SonarQube to evaluate the code
coverage.
2024-11-26 09:08:49 +01:00
456e7f41f2 Merge pull request #82 from metal3d/develop
Some fixes on "same-pod" and volumes + add some tests
2024-11-25 23:17:56 +01:00
fc335247f8 chore(tests): exlude tests for sonarqube 2024-11-25 23:11:14 +01:00
ad16005091 test(schema): Add test on ingress 2024-11-25 12:10:41 +01:00
a676372bbe chore(clean): remove unused functions 2024-11-25 12:02:19 +01:00
7fadc45e9e chore(typo): bad markdow, bad label name 2024-11-25 12:02:04 +01:00
41a0dc58a5 chore(typo): fix some typos 2024-11-25 12:00:04 +01:00
dc34d32c5c test(secrets): add tests 2024-11-25 11:54:38 +01:00
36984e3825 chore(clean): remove unused functions 2024-11-25 11:54:18 +01:00
046410a5ec chore(refacto): use utils package 2024-11-25 11:54:00 +01:00
827b5bc830 test(values): add map-env and exchange volumes tests 2024-11-22 16:23:00 +01:00
8aee6d9983 fix(nil): The service can not exist 2024-11-22 16:11:55 +01:00
7b890df1c5 fix(schema): Use ingress default values
Ingress has some default values, like path and classname. We need to
ensure that values are taken or nil, and to apply them if they are not
set explicitally. Port is a sepcial case.
2024-11-22 15:55:59 +01:00
e925f58e82 doc(add): Add more documentation about katenary file 2024-11-22 15:12:44 +01:00
91fc0fd9f0 fix(doc): missed a new line 2024-11-22 14:58:43 +01:00
1a1d2b5ee8 fix(configmap): do not write env var in file CM
File CM are configmap to store "static" data (file content), do not set
environment variables inside.
2024-11-22 14:55:42 +01:00
95f3abfa74 feat(volume): add "exchange volumes"
This volumes are "emptyDir" and can have init command. For example, in a
"same-pod", it allow the user to copy data from image to a directory
that is mounted on others pods.
2024-11-22 14:54:36 +01:00
3b51f41716 chore(names): Fix resource name
Use an utility function to fix some names
2024-11-21 11:12:38 +01:00
3f63375b60 refacto(labels): use external files
Files are more readable as external. Use "go:embed" to inject them.
2024-11-21 11:08:55 +01:00
8c97937b44 test(schema): Add tests 2024-11-21 11:08:09 +01:00
96f843630a chore(misc): make the code more readable here 2024-11-21 11:04:19 +01:00
48f6045cd3 fix(same-pod): environnment and volume mapping
We must ensure that the volume is owned by the container.
The environmment configMap wasn't bound.
2024-11-21 11:03:10 +01:00
49045a2ccd Merge pull request #81 from metal3d/develop
Cleanup, re-factorization and allow the use of a katenary.yaml file.
Fixing a wrapping string problem.
Move the label management in a package.
2024-11-19 13:44:04 +01:00
af8dabba85 fix(secrets): Wrapping values is unecessary
It seems that go-compose now escape the string
2024-11-18 17:42:21 +01:00
cc1019b5a8 chore(refacto): fix secret and use katenary schema
- add possibility to use a katenary.yaml file to setup values
- fix secret generation
2024-11-18 17:12:12 +01:00
14877fbfa3 Remove example for now 2024-11-10 00:49:38 +01:00
7b5e45131c doc(fix): fixes the main-app documentation
There were a problem, sentence was truncated
2024-11-09 14:19:56 +01:00
b09316b416 refactor(yaml): globalize fixups
The ToK8SYaml() function makes the job, it's now simpler to manage fixes
2024-11-09 14:18:27 +01:00
315f5da970 Merge pull request #79 from metal3d/develop
Activable tls
2024-11-09 13:57:35 +01:00
9b392a1f64 Fix problem on getting tls mapping
The types were not compatible to get TLS activation
2024-11-08 16:55:18 +01:00
9358076a36 chore(ingress): Allow tls activation 2024-11-08 15:51:36 +01:00
427f2909d5 Merge pull request #78 from metal3d/develop
Fixes template injection and variable names on volumes
2024-11-08 15:03:20 +01:00
8ab1763902 version(actions) Update actions and Go version
- checkout is now v4
- setup-go is now v5
- go version to use 1.23
2024-11-08 13:35:52 +01:00
e409be235e version(go) go 1.23 is now mandatory 2024-11-08 13:30:47 +01:00
1ca71f264c version(mod) Update others dependencies 2024-11-08 13:29:06 +01:00
5f464253c4 version(k8s/api) Update kubernetes api package 2024-11-08 13:26:14 +01:00
75869975f6 version(netdb) Update version 2024-11-08 13:24:04 +01:00
ac43256511 version(cobra) Update cobra version 2024-11-08 13:20:38 +01:00
5b51ba6d98 doc(fix): Regenerate the doc 2024-11-08 13:13:27 +01:00
dd63bb6343 chore(fixes): Unwrap yaml before converting and fix volume name variable
2 fixes:

- the first problem to resolve is that some volume names can have "-" in
the name. We now replace them by "_"
- the second problem is that k8s.io library truncates the lines and so
we cannot split the files by lines. We now "unwrap" the result.

TODO: globalize the `yaml.Marshal()` code to our own specific function
2024-11-08 13:11:14 +01:00
817ebe0e53 Remove logs
Made for debug, we must remove them.

Todo, add a logger system for debug.
2024-11-08 13:08:20 +01:00
6023ca4508 Merge pull request #77 from metal3d/develop
Merge Develop to prepare V3
2024-10-29 17:45:05 +01:00
e0c829c2ce Merge branch 'master' into develop 2024-10-29 17:42:17 +01:00
2d33367422 chore(optim): manage space in struct
Warning given by sonarlint, saves a few bytes.
2024-10-24 17:24:36 +02:00
d72f371c59 fix(generation): fix the volume var/path name
Underscores are forbidden by Kubernetes (should be a valid URL string),
we replace "_" by "-" in names, and we leave the values file using the
original name. So a volume named "foo_bar" in compose file, is
registered as "foo_bar" in the values file, but "foo-bar" is used as
volume name in deployments, volume claims, ...
2024-10-24 17:23:26 +02:00
db168c91c9 fix(generation): use tpl in note for hostnames
The value in hostname in values file can be a templated string. So, we
should execute the content.
2024-10-24 17:21:04 +02:00
d31993953b fix(doc): livenessProbe and readinessProbe must be set
In the health-check label, we now need to specify the kind of check to
do - in the expected form from Kubernetes specification.
2024-10-23 16:32:50 +02:00
63c6d5d0ef chore(format): moved import
Formatter changed the import order
2024-10-23 16:20:29 +02:00
164a617869 fix(values): Remove tplString in environment directive
We badly set a "tpl string" in values.yaml file for "values" labels set
in compose.yaml file.
2024-10-23 16:17:01 +02:00
865473b41b Enhance documentation
- upgraded mkdocs and dependencise (+ add mermaid)
- linted markdown
- add more details
2024-10-18 09:36:56 +02:00
533e1422d0 Sonar complience
Use valid names and factorize some constants checks
2024-10-18 09:36:55 +02:00
d2c8d08b7f Validate markdown
Use markdownlint / marksman in your editor please
2024-10-18 09:36:55 +02:00
4703aa7df5 MkDocs needs 4 spaces for lists 2024-10-18 09:36:55 +02:00
918f1b845b Fix problems and adding functionnalities
Many fixes and enhancements:

- Add icon option
- Add env file managment
- Ordering compose parsing options
- Fix path with underscores
- Fix image and tag discovery
- Better documentation for labels
2024-10-18 09:36:54 +02:00
78dfb15cf5 Add the command package 2024-05-07 13:19:04 +02:00
adc44a5e8b Refactorization and ordering 2024-05-07 13:18:00 +02:00
4367a01769 Big refactorization
- reduce complexity
- use better tools to format the code
- add more tests
- and too many things to list here

We are rewriting for V3, so these commits are sometimes big and not
fully detailed. Of course, further work will be more documented.
2024-05-06 21:11:36 +02:00
d98268f45b Add more tests on probes and dependencies 2024-04-25 00:20:04 +02:00
ccfebd1a70 We need helm linting at this time
Because the linting makes the dependency update. We will need to split
linting and dep update later.
2024-04-25 00:18:57 +02:00
e4f67dbd31 Fix the parsing of probes 2024-04-25 00:18:04 +02:00
d01a35e2d4 Use real types to parse labels
We were using `yaml.Unmarshal` on basic types or inline structs. This
was not efficient and not clear to defined what we expect in labels.
We now use types to unmarshal the labels.

Only the `values` label is, at this time, parsed by GetValuesFromLabel
because this `utils` function is clearly a special case.
2024-04-24 23:06:45 +02:00
0aa7023947 Avoid repetition 2024-04-24 21:53:24 +02:00
451a1341bd Do not check coverage on test file dude 2024-04-24 21:52:59 +02:00
da7d92bbfa Add more tests, refactor to fix problems
Signed-off-by: Patrice Ferlet <metal3d@gmail.com>
2024-04-24 20:55:27 +02:00
15a2f25e51 Exclude doc from the analysis 2024-04-24 14:23:31 +02:00
f73d598bb4 Standardization
- changed variables that was uppercased, that's not OK for linters
- cleanup some documentation
- remove the "/" in label prefix, a function is now used to get the
  complete label (`labelName()`)
- some cleanup in tpl files, and so on...
2024-04-24 14:03:41 +02:00
98c7c6ddc1 Use latest Go compiler 2024-04-24 13:57:06 +02:00
39d63c11b1 Remove coverage files 2024-04-24 13:51:46 +02:00
8f4d69d6e2 Add sonar profile 2024-04-24 13:51:25 +02:00
531756d8ea Use sonarcloud 2024-04-24 13:48:01 +02:00
e0c18ec2ad Avoid repetitions 2024-04-23 15:45:31 +02:00
a3e7435544 Add test for static volumes
And moved a test from deployment_test that was not the right place to be
created.
2024-04-23 15:38:50 +02:00
c01cdf50c8 Only on PR and on push to master 2024-04-23 14:59:20 +02:00
2e3dd5032f Add codecov 2024-04-23 14:54:42 +02:00
8b01807568 Add workflows 2024-04-23 14:43:57 +02:00
46c878b56e Add coverage files 2024-04-23 14:42:55 +02:00
10b9342607 Update README.md
Alert on v3 version
2024-04-23 14:37:19 +02:00
77de53c999 Create more tests 2024-04-23 14:26:23 +02:00
6a7fedee7e We shouldn't quote encoded values
Quoting before encoding in base64 adds the quotes in the encoded data.
That's a bad behavior.
2024-04-23 14:24:06 +02:00
c31299197f Remove the bats tests
That was interesting but finally not so useful. We will make better
tests in Go. The hard part will be to make them working in CI/CD.
2024-04-23 10:13:38 +02:00
49c1fa5fb0 Explain what happens 2024-04-23 10:10:58 +02:00
d1186ee1e1 Refresh doc 2024-04-23 08:07:20 +02:00
50975ae94a Fix static volume binding
It is possible there are many things like this to fix. I made too much
complexity on searching services in deployment while the map key is
enough to get the righ deployment for a compose service.

Need to check the "same-pod" possibilities later.
2024-04-23 08:05:00 +02:00
7e8cb57979 Fixes 2024-04-22 15:43:02 +02:00
12814f4732 Cleanup 2024-04-22 15:36:49 +02:00
dc41826691 Back to normal 2024-04-22 15:32:39 +02:00
6770f8176a Make pure svg calls 2024-04-22 15:29:32 +02:00
cd946b2df6 Try another thing 2024-04-22 15:20:29 +02:00
734b0ed39d Try to embed logos 2024-04-22 15:17:28 +02:00
78d37c4405 Ease installation 2024-04-22 13:55:53 +02:00
fd3ba6d577 Fix doc after changes in source files 2024-04-22 13:31:30 +02:00
8ae9350d31 Add YAML keys in the comments
I eases developpers and admins to know the key to override when they
create an override file or to use `--set` argument for Helm.
2024-04-22 13:28:22 +02:00
9621493343 Add resources in containers and values 2024-04-22 13:27:44 +02:00
f291d17aa3 Fixup documentation after changing packages 2024-04-21 16:37:20 +02:00
9826a54187 Fix notes.txt problems
We were using a bad method to read the ingress values. It's not ensured
by using the service names + checking the "ingress" key.
2024-04-21 16:35:32 +02:00
d48fd2f911 make a better override + add more values (serviceAccount, nodeSelector...) 2024-04-21 16:34:21 +02:00
ec62a79d82 Better override list and documentation 2024-04-19 22:26:45 +02:00
58d19cce52 Fix the chart app version 2024-04-19 22:12:09 +02:00
3bb635a627 Add FAQ page 2024-04-19 12:11:43 +02:00
35f464a1cb Add footnotes and search 2024-04-19 12:11:18 +02:00
85f1b2d43c Fixes the ingress doc 2024-04-19 11:28:27 +02:00
77e8be4e63 Container can be null
In case of deployment with "same-pod" label, the container can be not
found in the deployment.
2024-04-19 11:27:48 +02:00
57b274e345 Fixing documentation 2024-04-19 11:17:54 +02:00
3c743fb135 Fixup hard problems on bound volumes
Recreated the method to bind local content to configMaps with subPath.
That simplify a few how we can bound files and not only directory
content.
2024-04-19 11:13:24 +02:00
ab15614076 Use "helm" filtype first for vim modeline + tests
"helm" can be managed by vim/neovim plugins, so it's a good idea to add
it as default, then use "gotmpl.yaml".

Add basic tests...
2024-04-11 09:37:10 +02:00
c41fa22c59 Update k8s.io/api + fix changed function 2024-04-11 09:34:58 +02:00
81ea0fbb6f fix fonts in svg, one more time... 2024-04-10 22:55:58 +02:00
821c038206 Fix fonts 2024-04-10 22:53:32 +02:00
19a37ace18 Better styles, logo, effects...
- Make a SVG with classes to invert the color of strokes
- Set a better logo + one vertical
2024-04-10 22:25:07 +02:00
d8bd66e66f Change license date, enhance and fix documentation 2024-04-10 14:19:07 +02:00
c780e6c2a2 Change doc, icon and logo 2024-04-10 13:53:58 +02:00
3317459b0b Code cleaning 2024-04-10 04:54:38 +02:00
564b939464 Remove useless composition call 2024-04-10 04:54:16 +02:00
c7c18f01cd Fixup documentation
- better gomarkdown generation that now fixed the escaped strings, no
  need to use pandoc anymore
- added workflow image
- upgraded versions of mkdocs requirements
2024-04-10 04:51:45 +02:00
2f53638f82 Add documentation 2024-04-10 04:49:36 +02:00
5070101706 Add workflow image and zoom on click 2024-04-10 04:48:51 +02:00
cc3c42b8fd Add venv to ignore list 2024-04-10 04:47:52 +02:00
45de7ab543 Fix samepod generation
The container was not merged to the target deployment. It necessary to
make one more loop to apply the container + remove the source
deployment.
2024-04-08 23:15:05 +02:00
984b50356a Do not pass by composed struct
It useless to use the composed struct field as the current "object" is
composed by this one. Get Name field directly. (Thanks staticcheck)
2024-04-05 07:58:37 +02:00
441b30a570 Code cleaning
Using gofumpt. Add documentation.
Some fixes on type checking and const icon type declaration.
2024-04-05 07:56:27 +02:00
2aad5d4b9d Upgrading mod package 2024-04-04 09:53:33 +02:00
50169c8fbc Changing logo, fix doc, catchy text... 2024-04-04 09:50:17 +02:00
fc67cb668d Fix line width to 120 columns 2024-04-03 23:30:24 +02:00
8e4b3be108 Add nvimrc file
Editorconfig seems to not allow the line wrapping. I'm using neovim with
`set exrc`, so this local file is used to fix some behaviors.

If a contributor can give solutions for a global configuration that
works everywhere...
2024-04-03 23:28:04 +02:00
3ae5ec99ff Typo, format of markdown
I prefer to limit 120 columns. A .nvimrc will be proposed to avoid
having to wide markdown lines.
2024-04-03 23:26:54 +02:00
ef7fcb6133 Some users want to use "host" instead of "hostname"
We accept both, nothing more.
2024-04-03 22:44:41 +02:00
50c2f9d1dc Use new labels in documentation, and fix content 2024-04-03 22:38:23 +02:00
eeb044bab0 Add markdown editor configuration 2024-04-03 22:37:56 +02:00
6ce52cc037 Reindentation and change labels 2024-04-03 22:37:22 +02:00
9a3fc6a2b4 Fix "depends_on" check
If "depends_on" is set, we need to ensure that the target service has
got declared ports. It's necessary, at this time, to ensure the target
is started (with an initContainer)

As soon as Kubernetes proposes a better check, we will be able to fix
this requirement.
2024-04-03 22:22:48 +02:00
4ded4d4e09 Set the compose file list using overrides
This is a hack to force the compose package to read all files provided
with the -c flag.
2024-04-03 22:08:01 +02:00
dc47e73b4b Temorary fixing overrides
WIP: this breaks the -c flag, but it's a start

We can now use compose.katenary.yaml file as a default override.
2024-04-03 21:35:04 +02:00
76aa332dc2 Fix dependencies parsing
The code was fetching a simple object while it should have been fetching an array of objects.
2024-04-03 21:34:15 +02:00
5d4f72e984 Fix unued variable, useless functions... 2024-04-03 21:33:26 +02:00
5a358f0a6a Add labels docs
This file was ignored by error in .gitignore
2024-04-03 16:16:28 +02:00
ed9b4681ad Unignore some yaml files
katenaryLabelsDoc.yaml is a file that is used to generate the documentation, it is not a file that should be ignored.
2024-04-03 16:15:24 +02:00
d135f5bc0a Remove the Smile sponsorship
One more time, thanks a lot for the adventure
2024-04-03 14:24:43 +02:00
f9cf3972d5 Removed Smile sponsorship
Thanks for the given time.
2024-04-03 14:18:16 +02:00
6e33fa474a Get the newest version of go-compose
- better integration of compose file
- needed to impose to not resolve path in "parser" package to get them
  from the working directory. This should be improved later
2024-04-03 14:15:50 +02:00
3a0cf1a7db Set test(s) to PHONY 2024-04-03 14:15:28 +02:00
475a025d9e Go to Katenary V3
This is the next-gen of Katenary
2023-12-06 15:24:02 +01:00
c37bde487b Add note for the next release 2023-06-13 09:30:06 +02:00
samzong.lu
982917c25b Update README.md
fix error command  of  completion with zsh
2023-02-07 09:40:58 +01:00
adrian-salas
7feb7427f2 ISSUE-30 - Fix syntax on tcp liveness probe
Closes #30
2023-02-07 09:40:22 +01:00
a2bf9c005b Fix bracket
fixes #50 - the brackets are valid in JSON/YAML
2023-02-07 09:39:12 +01:00
119b5d699c Use compose-go 1.2.8 2022-07-08 11:40:16 +02:00
dependabot[bot]
eca5154d75 Bump gopkg.in/yaml.v3 from 3.0.0 to 3.0.1
Bumps [gopkg.in/yaml.v3](https://github.com/go-yaml/yaml) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/go-yaml/yaml/releases)
- [Commits](https://github.com/go-yaml/yaml/compare/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: gopkg.in/yaml.v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-08 11:38:35 +02:00
16196aae34 Set smile logo to base_url 2022-07-08 10:37:17 +02:00
9726cc4a18 Fix and comment empty-dirs + volume-from 2022-07-08 10:26:01 +02:00
7eb75015a0 Fix first dot in name 2022-07-08 10:00:54 +02:00
66dec780ae Fix secret/conf environment from file 2022-07-08 09:56:55 +02:00
6cd361705d Try to fix the problem with Smile logo 2022-07-08 08:58:37 +02:00
9c449eefab Fix secret that disapeared from Values
- must fix #24
- optimisation on memory locks
- add `AddEnvironment` function to help management
2022-06-22 10:55:11 +02:00
dependabot[bot]
16c8e3dd20 Bump github.com/spf13/cobra from 1.4.0 to 1.5.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.4.0...v1.5.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-22 09:55:35 +02:00
b21c821086 Split source file 2022-06-22 09:54:41 +02:00
890d7b5017 Fixup broken merge 2022-06-22 09:54:41 +02:00
dependabot[bot]
1266545919 Bump github.com/compose-spec/compose-go from 1.2.5 to 1.2.7 (#19)
Bumps [github.com/compose-spec/compose-go](https://github.com/compose-spec/compose-go) from 1.2.5 to 1.2.7.
- [Release notes](https://github.com/compose-spec/compose-go/releases)
- [Commits](https://github.com/compose-spec/compose-go/compare/v1.2.5...v1.2.7)

---
updated-dependencies:
- dependency-name: github.com/compose-spec/compose-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-22 09:53:13 +02:00
874e12f35d Fix links 2022-06-14 10:26:24 +02:00
b25b2fda2c Fix colors 2022-06-14 10:21:55 +02:00
e2a4d296f6 Fix Smile logo image 2022-06-14 10:14:08 +02:00
7d5b4d9306 Fixup styles 2022-06-14 10:08:22 +02:00
a1d963c15b Fix dependencies 2022-06-14 09:28:14 +02:00
bb83384d62 Better doc for ReadTheDocs 2022-06-14 09:25:43 +02:00
bc3c5dc932 Change nav top color 2022-06-13 14:00:10 +02:00
203449aa9e Fix logo path 2022-06-13 13:55:43 +02:00
a3dbdfd8c6 Add site_dirs 2022-06-13 13:50:57 +02:00
1b1397b45f Fix paths 2022-06-13 13:47:58 +02:00
1122c02e07 Fix MarkupSafe 2022-06-13 13:40:45 +02:00
01e045e4b7 Try to fix mkdocs again... 2022-06-13 13:37:09 +02:00
d28cbab0fe Fix mkdocs 2022-06-13 13:29:57 +02:00
b1dc94e0e9 Add doc (WIP) 2022-06-13 13:18:31 +02:00
f9fd6332d6 Feat cronjob (#23)
Make possible to declare cronTabs inside docker-compose file.

⇒ Also, add multiple compose file injection with `-c` arguments 

⇒ Also, fixes “ignore depends on” for same pod 

⇒ Also fixes
 
* fix [Be able to specify compose.yml files and its override #21](https://github.com/metal3d/katenary/issues/21)
* fix [Be able to ignore ports to expose in a katenary.io/ports list #16](https://github.com/metal3d/katenary/issues/16)

And more fixes… (later, we will use branches in a better way, that was a hard, long fix process)
2022-06-10 16:15:18 +02:00
Thomas BERNARD
7203928d95 Fix a typo (#22) 2022-06-09 18:35:04 +02:00
dc9b198208 Quote path label 2022-06-01 16:22:45 +02:00
0f9a46f52d Fix env with points to underscore 2022-06-01 16:13:10 +02:00
8cf3ff9f73 Add local test directory 2022-05-24 11:17:46 +02:00
114fab4870 Fix the problem with environment as secret
We needed to filter the environment coming from a env file, but declared
as secet in `secret-vars` label

fix #17
2022-05-23 12:11:23 +02:00
5fc8c06d8b Layout again 2022-05-23 11:24:06 +02:00
51ca4e81d1 Layout... 2022-05-23 11:23:27 +02:00
a6f6b91ab9 Layout 2022-05-23 11:20:52 +02:00
63afc3066b Fix problems with local volumes to configmap
- make it possible to mount only one file as configmap
- remove "dots" from path to convert it to volume name
- see #11 that can be resolved
2022-05-23 11:13:42 +02:00
adrian-salas
1e8fd44857 ISSUE-12 - Trim space on port label (#14)
Co-authored-by: Adrian SALAS <adrian.salas@smile.fr>

fix #12
2022-05-22 22:53:05 +02:00
adrian-salas
1403f9b198 ISSUE-13 - Ignore comment prefix in envfiles (#15)
Co-authored-by: Adrian SALAS <adrian.salas@smile.fr>

fix #13
2022-05-22 22:52:07 +02:00
dependabot[bot]
22ef9211ec Bump github.com/compose-spec/compose-go from 1.2.4 to 1.2.5 (#10)
Bumps [github.com/compose-spec/compose-go](https://github.com/compose-spec/compose-go) from 1.2.4 to 1.2.5.
- [Release notes](https://github.com/compose-spec/compose-go/releases)
- [Commits](https://github.com/compose-spec/compose-go/compare/v1.2.4...v1.2.5)

---
updated-dependencies:
- dependency-name: github.com/compose-spec/compose-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-18 14:20:03 +02:00
200 changed files with 14413 additions and 4283 deletions

View File

@@ -4,3 +4,8 @@ root = true
indent_style=tab
indent_size=4
[*.md]
trim_trailing_whitespace = false
indent_style = space
indent_size = 4

View File

@@ -0,0 +1,47 @@
name: Build and push OCI image
on:
push:
tags:
- "**"
env:
REGISTRY: repo.katenary.io
IMAGE_NAME: ${{ gitea.repository }}
VERSION: ${{ gitea.ref_name }}
STORAGE_DRIVER: vfs
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Install Buildah
run: |-
env
if ! command -v buildah 2>/dev/null; then
echo "Install Buildah"
sudo apt-get update
sudo apt-get install -y buildah
else
echo "Buildah already installed"
fi
- name: Login to repository
run: |
buildah login $REGISTRY -u ${{ gitea.actor }} -p ${{ secrets.ACCESS_TOKEN }}
- name: Checkout repository
uses: actions/checkout@v4
- name: Build and tag
run: |-
buildah build --isolation=chroot --build-arg VERSION=$VERSION -t katenary -f ./oci/katenary/Containerfile .
buildah tag katenary $REGISTRY/${IMAGE_NAME,,}:$VERSION
buildah tag katenary $REGISTRY/${IMAGE_NAME,,}:latest
- name: Push image
run: |-
buildah push $REGISTRY/${IMAGE_NAME,,}:$VERSION
buildah push $REGISTRY/${IMAGE_NAME,,}:latest

View File

@@ -0,0 +1,68 @@
name: Go-Tests
on:
pull_request:
types:
- opened
- edited
push:
branches:
- "master"
- "main"
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
jobs:
tests:
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.25
- name: Install helm
run: |
command -v helm || curl -sSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
- name: Tidy
run: |
go mod tidy
- name: Launch Test
run: |
go vet ./...
go test -coverprofile=coverprofile.out -json -v ./... > gotest.json
# - uses: actions/upload-artifact@v4
- name: Upload artifact
uses: christopherhx/gitea-upload-artifact@v4
with:
name: tests-results
path: |
coverprofile.out
gotest.json
sonar:
permissions:
contents: read
pull-requests: read
runs-on: ubuntu-latest
needs: tests
steps:
- name: Checkout
uses: actions/checkout@v4
#- uses: actions/download-artifact@v4
- name: Download artifact
uses: christopherhx/gitea-download-artifact@v4
with:
name: tests-results
- name: Sonar
run: |
export SONAR_SCANNER_VERSION=7.2.0.5079
export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux-x64
curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux-x64.zip
unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/
export PATH=$SONAR_SCANNER_HOME/bin:$PATH
sonar-scanner \
-Dsonar.organization=katenary \
-Dsonar.projectKey=katenary_katenary

View File

@@ -1,11 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/" # Location of package manifests
schedule:
interval: "daily"

35
.gitignore vendored
View File

@@ -1,10 +1,33 @@
dist/*
.cache/*
chart/*
docker-compose.yaml
./katenary
*.env
docker-compose*
!examples/**/docker-compose*
.credentials
release.id
cover*
.sq
.aider*
.python_history
.bash_history
.cache/
.aider/
.config/
*/venv
# will be treated later
/examples/*
# nsis
nsis/*.dll
nsis/*.exe
doc/share
__pycache__
.rpmmacros
*.gpg
# local binaries
katenary
!cmd/katenary
!oci/katenary

0
.gitmodules vendored Normal file
View File

26
.golangci.yml Normal file
View File

@@ -0,0 +1,26 @@
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"

21
.markdownlint.yaml Normal file
View File

@@ -0,0 +1,21 @@
# markdownlint configuration file
default: true
MD013: # Line length
line_length: 120
MD010: # Hard tabs
code_blocks: false
# no inline HTML
MD033: false
# heading as first line element...
MD041: false
# list indentation
MD007:
indent: 4
# no problem using several code blocks styles
MD046: false

20
.readthedocs.yaml Normal file
View File

@@ -0,0 +1,20 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-20.04
tools:
python: "3.9"
mkdocs:
configuration: doc/mkdocs.yml
# Optionally declare the Python requirements required to build your docs
python:
install:
- requirements: doc/requirements.txt

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 Patrice Ferlet
Copyright (c) 2022-2025 The Katenary authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

183
Makefile
View File

@@ -1,30 +1,79 @@
# Strict mode
SHELL := bash
.SHELLFLAGS := -eu -o pipefail -c
.ONESHELL:
.DELETE_ON_ERROR:
.PHONY: all binaries build check-dist-all check-dist-archlinux check-dist-debian check-dist-fedora check-dist-rocky check-dist-ubuntu check-sign clean-all clean-dist clean-go-cache clean-package-signer cover deb dist dist-full doc freebsd gpg-sign help install install-gomarkdoc katenary manpage packager-oci-image packages pacman prepare pull rpm rpm-sign sast serve-doc show-cover tar test uninstall upx warn-docker
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
# Get a version string from git
CUR_SHA=$(shell git log -n1 --pretty='%h')
CUR_BRANCH=$(shell git branch --show-current)
VERSION=$(shell git describe --exact-match --tags $(CUR_SHA) 2>/dev/null || echo $(CUR_BRANCH)-$(CUR_SHA))
CTN:=$(shell which podman 2>&1 1>/dev/null && echo "podman" || echo "docker")
PREFIX=~/.local
VERSION=$(shell git describe --exact-match --tags $(CUR_SHA) 2>/dev/null || echo $(CUR_BRANCH)-$(CUR_SHA))# use by golang flags
GO=container
OUT=katenary
BLD_CMD=go build -ldflags="-X 'main.Version=$(VERSION)'" -o $(OUT) ./cmd/katenary/*.go
# Go build command and environment variables for target OS and architecture
GOVERSION=1.25
GO=container# container, local
OUTPUT=katenary
GOOS=linux
GOARCH=amd64
CGO_ENABLED=0
PREFIX=~/.local
BUILD_IMAGE=docker.io/golang:1.18-alpine
# UPX compression
UPX_OPTS =
UPX ?= upx $(UPX_OPTS)
.PHONY: help clean build
# List of source files
SOURCES=$(shell find -name "*.go" -or -name "*.tpl" -type f | grep -v -P "^./example|^./vendor")
# List of binaries to build and sign
BINARIES=\
dist/katenary-linux-amd64\
dist/katenary-linux-arm64\
dist/katenary-darwin-amd64\
dist/katenary-freebsd-amd64\
dist/katenary-freebsd-arm64\
dist/katenary.exe\
dist/katenary-windows-setup.exe
## GPG
# List of signatures to build
ASC_BINARIES=$(patsubst %,%.asc,$(BINARIES))
# GPG signer
SIGNER=metal3d@gmail.com
# Browser command to see coverage report after tests
BROWSER=$(shell command -v epiphany || echo xdg-open)
include makefiles/build.mk
include makefiles/containers.mk
include makefiles/doc.mk
include makefiles/gpg.mk
include makefiles/packager.mk
include makefiles/test.mk
all: build
# if docker is used instead of podman, we warn the user
warn-docker:
@echo -e "\033[1;31mWarning: Docker is not recommended, use Podman instead.\033[0m"
sleep 5
.ONESHELL:
help:
@cat <<EOF
@cat <<EOF | fold -s -w 80
=== HELP ===
To avoid you to install Go, the build is made by podman or docker.
You can use:
Installinf (you can use local Go by setting GO=local)):
# use podman or docker to build
$$ make install
# or use local Go
$$ make install GO=local
This will build and install katenary inside the PREFIX(/bin) value (default is $(PREFIX))
To change the PREFIX to somewhere where only root or sudo users can save the binary, it is recommended to build before install:
To change the PREFIX to somewhere where only root or sudo users can save the binary, it is recommended to build before install, one more time you can use local Go by setting GO=local:
$$ make build
$$ sudo make install PREFIX=/usr/local
@@ -44,113 +93,29 @@ help:
This will build the binary for darwin amd64.
Or you can build all versions:
$$ make build-all
$$ make binaries
EOF
build: pull katenary
build-all:
rm -f dist/*
$(MAKE) _build-all
_build-all: pull dist dist/katenary-linux-amd64 dist/katenary-linux-arm64 dist/katenary.exe dist/katenary-darwin-amd64 dist/katenary-freebsd-amd64 dist/katenary-freebsd-arm64
pull:
ifneq ($(GO),local)
@echo -e "\033[1;32mPulling $(BUILD_IMAGE) docker image\033[0m"
@$(CTN) pull $(BUILD_IMAGE)
endif
dist:
mkdir -p dist
dist/katenary-linux-amd64:
@echo
@echo -e "\033[1;32mBuilding katenary $(VERSION) for linux-amd64...\033[0m"
$(MAKE) katenary GOOS=linux GOARCH=amd64 OUT=$@
dist/katenary-linux-arm64:
@echo
@echo -e "\033[1;32mBuilding katenary $(VERSION) for linux-arm...\033[0m"
$(MAKE) katenary GOOS=linux GOARCH=arm64 OUT=$@
dist/katenary.exe:
@echo
@echo -e "\033[1;32mBuilding katenary $(VERSION) for windows...\033[0m"
$(MAKE) katenary GOOS=windows GOARCH=amd64 OUT=$@
dist/katenary-darwin-amd64:
@echo
@echo -e "\033[1;32mBuilding katenary $(VERSION) for darwin...\033[0m"
$(MAKE) katenary GOOS=darwin GOARCH=amd64 OUT=$@
dist/katenary-freebsd-amd64:
@echo
@echo -e "\033[1;32mBuilding katenary $(VERSION) for freebsd...\033[0m"
$(MAKE) katenary GOOS=freebsd GOARCH=amd64 OUT=$@
dist/katenary-freebsd-arm64:
@echo
@echo -e "\033[1;32mBuilding katenary $(VERSION) for freebsd-arm64...\033[0m"
$(MAKE) katenary GOOS=freebsd GOARCH=arm64 OUT=$@
katenary: $(wildcard */*.go Makefile go.mod go.sum)
ifeq ($(GO),local)
@echo "=> Build in host using go"
else
@echo "=> Build in container using" $(CTN)
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"
## installation and uninstallation
install: build
cp katenary $(PREFIX)/bin/katenary
install -Dm755 katenary $(PREFIX)/bin/katenary
uninstall:
rm -f $(PREFIX)/bin/katenary
clean:
rm -rf katenary dist/* release.id
## Miscellaneous
clean-all: clean-dist clean-package-signer clean-go-cache
tests: test
test:
@echo -e "\033[1;33mTesting katenary $(VERSION)...\033[0m"
go test -v ./...
clean-dist:
rm -rf dist
rm -f katenary
clean-package-signer:
rm -f .secret.gpg .rpmmacros
clean-go-cache:
$(CTN) volume rm -f go-cache
.ONESHELL:
push-release: build-all
@rm -f release.id
# read personal access token from .git-credentials
TOKEN=$(shell cat .credentials)
# create a new release based on current tag and get the release id
@curl -sSL -X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token $$TOKEN" \
-d "{\"tag_name\": \"$(VERSION)\", \"target_commitish\": \"\", \"name\": \"$(VERSION)\", \"draft\": true, \"prerelease\": true}" \
https://api.github.com/repos/metal3d/katenary/releases | jq -r '.id' > release.id
@echo "Release id: $$(cat release.id) created"
@echo "Uploading assets..."
# push all dist binary as assets to the release
@for i in $$(find dist -type f -name "katenary*"); do
curl -sSL -H "Authorization: token $$TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-H "Content-Type: application/octet-stream" \
--data-binary @$$i \
https://uploads.github.com/repos/metal3d/katenary/releases/$$(cat release.id)/assets?name=$$(basename $$i)
done
@rm -f release.id

272
README.md
View File

@@ -1,30 +1,55 @@
<div style="text-align:center">
<img src="./misc/logo.png" alt="Katenary Logo" />
<div style="text-align:center; margin: auto 0 4em 0" align="center">
<img src="./doc/docs/statics/logo-vertical.svg" alt="Katenary Logo" style="max-width: 90%" align="center"/>
</div>
Katenary is a tool to help transforming `docker-compose` files to a working Helm Chart for Kubernetes.
<div style="text-align:center; margin: auto 0 4em 0" align="center">
> **Important Note:** Katenary is a tool to help building Helm Chart from a docker-compose file, but docker-compose doesn't propose as many features as what can do Kubernetes. So, we strongly recommend to use Katenary as a "bootstrap" tool and then to manually enhance the generated helm chart.
[![Documentation Status](https://readthedocs.org/projects/katenary/badge/?version=latest)](https://katenary.readthedocs.io/latest/?badge=latest)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=katenary_katenary&metric=coverage)](https://sonarcloud.io/summary/new_code?id=katenary_katenary)
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=katenary_katenary&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=katenary_katenary)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=katenary_katenary&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=katenary_katenary)
This project is partially made at [Smile](https://www.smile.eu)
<div style="text-align:center">
<a href="https://www.smile.eu"><img src="./misc/Logo_Smile.png" alt="Smile Logo" width="250" /></a>
</div>
# Install
<div style="text-align:center; margin: auto 0 4em 0" align="center">
<h3>🚀 Unleash Productivity with Katenary! 🚀</h3>
</div>
You can download the binaries from the [Release](https://github.com/metal3d/katenary/releases) section. Copy the binary and rename it to `katenary`. Place the binary inside your `PATH`. You should now be able to call the `katenary` command.
Tired of manual conversions? Katenary harnesses the labels from your "`compose`" file to craft complete Helm Charts
effortlessly, saving you time and energy.
You can of course get the binary with `go install -u github.com/metal3d/katenary/cmd/katenary/...` but the `main` branch is continuously updated. It's preferable to use releases.
🛠️ Simple automated CLI: Katenary handles the grunt work, generating everything needed for seamless service binding
and Helm Chart creation.
💡 Effortless Efficiency: You only need to add labels when it's necessary to precise things.
Then call `katenary convert` and let the magic happen.
## What ?
Katenary is a tool to help to transform `compose` (`docker compose`, `podman compose`, `nerdctl compose`, ...) files
to a working Helm Chart for Kubernetes.
Today, it's partially developed in collaboration with [Klee Group](https://www.kleegroup.com). Note that Katenary is
and **will stay an open source and free (as freedom) project**. We are convinced that the best way to make it better is to
share it with the community.
The main developer is [Patrice FERLET](https://github.com/metal3d).
## Install
You can download the binaries from the [Release](https://github.com/Katenary/katenary/releases) section. Copy the binary
and rename it to `katenary`. Place the binary inside your `PATH`. You should now be able to call the `katenary` command.
You can of course get the binary with `go install -u github.com/Katenary/katenary/cmd/katenary/...` but the `main` branch
is continuously updated. It's preferable to use releases.
You can use this commands on Linux:
```bash
sh <(curl -sSL https://raw.githubusercontent.com/metal3d/katenary/master/install.sh)
sh <(curl -sSL https://raw.githubusercontent.com/Katenary/katenary/master/install.sh)
```
# Else... Build yourself
## Or, build yourself
If you've got `podman` or `docker`, you can build `katenary` by using:
@@ -33,11 +58,13 @@ make build
```
You can then install it with:
```bash
make install
```
It will use the default PREFIX (`~/.local/`) to install the binary in the `bin` subdirectory. You can force the PREFIX value at install time, but maybe you need to use "sudo":
It will use the default PREFIX (`~/.local/`) to install the binary in the `bin` subdirectory. You can force the PREFIX
value at install time, but maybe you need to use "sudo":
```bash
sudo make install PREFIX=/usr/local
@@ -54,12 +81,12 @@ make build GO=local GOOS=linux GOARCH=arm64
Then place the `katenary` binary file inside your PATH.
## Tips
# Tips
We strongly recommend adding the completion call to you SHELL using the common `bashrc`, or whatever the profile file
you use.
We strongly recommand to add the "completion" call to you SHELL using the common bashrc, or whatever the profile file you use.
E.g. :
E.g.,
```bash
# bash in ~/.bashrc file
@@ -68,76 +95,69 @@ source <(katenary completion bash)
source <(katenary completion bash --no-description)
# zsh in ~/.zshrc
source <(helm completion zsh)
source <(katenary completion zsh)
# fish in ~/.config/fish/config.fish
katenary completion fish | source
# experimental
# powershell (as we don't provide any support on Windows yet, please avoid this...)
```
# Usage
## Usage
```
Katenary aims to be a tool to convert docker-compose files to Helm Charts.
It will create deployments, services, volumes, secrets, and ingress resources.
But it will also create initContainers based on depend_on, healthcheck, and other features.
It's not magical, sometimes you'll need to fix the generated charts.
The general way to use it is to call one of these commands:
```text
katenary convert
katenary convert -c docker-compose.yml
katenary convert -c docker-compose.yml -o ./charts
Katenary is a tool to convert compose files to Helm Charts.
In case of, check the help of each command using:
katenary <command> --help
or
"katenary help <command>"
Each [command] and subcommand has got an "help" and "--help" flag to show more information.
Usage:
katenary [command]
Examples:
katenary convert -c docker-compose.yml -o ./charts
Available Commands:
completion Generate the autocompletion script for the specified shell
convert Convert docker-compose to helm chart
help Help about any command
show-labels Show labels of a resource
upgrade Upgrade katenary to the latest version if available
version Display version
completion Generates completion scripts
convert Converts a docker-compose file to a Helm Chart
hash-composefiles Print the hash of the composefiles
help Help about any command
help-labels Print the labels help for all or a specific label
schema Print the schema of the katenary file
version Print the version number of Katenary
Flags:
-h, --help help for katenary
-h, --help help for katenary
-v, --version version for katenary
Use "katenary [command] --help" for more information about a command.
```
Katenary will try to find a `docker-compose.yaml` or `docker-compose.yml` file inside the current directory. It will check *the existence of the `chart` directory to create a new Helm Chart inside a named subdirectory. Katenary will ask you if you want to delete it before recreating.
Katenary will try to find a `docker-compose.yaml` or `docker-compose.yml` file inside the current directory. It will
check \*the existence of the `chart` directory to create a new Helm Chart inside a named subdirectory. Katenary will ask
you if you want to delete it before recreating.
It creates a subdirectory inside `chart` that is named with the `appname` option (default is `MyApp`)
> To respect the ability to install the same application in the same namespace, Katenary will create "variable" names like `{{ .Release.Name }}-servicename`. So, you will need to use some labels inside your docker-compose file to help katenary to build a correct helm chart.
> To respect the ability to install the same application in the same namespace, Katenary will create variable names
> like `{{ .Release.Name }}-servicename`. So, you will need to use some labels inside your docker-compose file to help
> Katenary to build a correct helm chart.
What can be interpreted by Katenary:
- Services with "image" section (cannot work with "build" section)
- **Named Volumes** are transformed to persistent volume claims - note that local volume will break the transformation to Helm Chart because there is (for now) no way to make it working (see below for resolution)
- if `ports` and/or `expose` section, katenary will create Services and bind the port to the corresponding container port
- `depends_on` will add init containers to wait for the depending service (using the first port)
- `env_file` list will create a configMap object per environemnt file (⚠ todo: the "to-service" label doesn't work with configMap for now)
- some labels can help to bind values, for example:
- `katenary.io/ingress: 80` will expose the port 80 in a ingress
- `katenary.io/mapenv: |`: allow to map environment to something else than the given value in the compose file
Exemple of a possible `docker-compose.yaml` file:
Example of a possible `docker-compose.yaml` file:
```yaml
version: "3"
services:
webapp:
image: php:7-apache
environment:
# note that "database" is a service name
# note that "database" is a "compose" service name
# so we need to adapt it with the map-env label
DB_HOST: database
# a pitty to repeat this values, isn't it?
# so, let's change them with "values-from" label
DB_USER: foo
DB_PASSWORD: bar
expose:
- 80
depends_on:
@@ -146,15 +166,23 @@ services:
- database
labels:
# expose the port 80 as an ingress
katenary.io/ingress: 80
katenary.v3/ingress: |-
hostname: myapp.example.com
port: 80
# make adaptations, DB_HOST environment is actually the service name
# to hit (note the yaml style, start with "|")
katenary.io/mapenv: |
DB_HOST: {{ .Release.Name }}-database
katenary.v3/map-env: |-
DB_HOST: '{{ .Release.Name }}-database'
# get the values from the "database" service
# this will use the database secrets and environment,
# see the "database" service to see the values
katenary.v3/values-from: |-
DB_USER: database.MARIADB_USER
DB_PASSWORD: database.MARIADB_PASSWORD
database:
image: mariadb:10
env_file:
# this will create a configMap
# this values will be added in a configMap
- my_env.env
environment:
MARIADB_USER: foo
@@ -163,38 +191,112 @@ services:
labels:
# no need to declare this port in docker-compose
# but katenary will need it
katenary.io/ports: 3306
katenary.v3/ports: |-
- 3306
# these variables are secrets
katenary.io/secret-vars: MARIADB_ROOT_PASSWORD, MARIADB_PASSWORD
katenary.v3/secrets: |-
- MARIADB_ROOT_PASSWORD
- MARIADB_PASSWORD
```
# Labels
## Labels
These labels could be found by `katenary show-labels`, and can be placed as "labels" inside your docker-compose file:
These labels could be found by `katenary help-labels`, and can be placed as labels inside your docker-compose file:
```
katenary.io/ignore : ignore the container, it will not yied any object in the helm chart
katenary.io/secret-vars : secret variables to push on a secret file
katenary.io/secret-envfiles : set the given file names as a secret instead of configmap
katenary.io/mapenv : map environment variable to a template string (yaml style)
katenary.io/ports : set the ports to expose as a service (coma separated)
katenary.io/ingress : set the port to expose in an ingress (coma separated)
katenary.io/configmap-volumes : specifies that the volumes points on a configmap (coma separated)
katenary.io/same-pod : specifies that the pod should be deployed in the same pod than the given service name
katenary.io/empty-dirs : specifies that the given volume names should be "emptyDir" instead of persistentVolumeClaim (coma separated)
katenary.io/healthcheck : specifies that the container should be monitored by a healthcheck, **it overrides the docker-compose healthcheck**.
You can use these form of label values:
- "http://[not used address][:port][/path]" to specify an http healthcheck
- "tcp://[not used address]:port" to specify a tcp healthcheck
- other string is condidered as a "command" healthcheck
```text
To get more information about a label, use `katenary help-label <name_without_prefix>
e.g. katenary help-label dependencies
katenary.v3/configmap-files: []string Inject files as Configmap.
katenary.v3/cronjob: object Create a cronjob from the service.
katenary.v3/dependencies: []object Add Helm dependencies to the service.
katenary.v3/description: string Description of the service
katenary.v3/env-from: []string Add environment variables from another service.
katenary.v3/exchange-volumes: []object Add exchange volumes (empty directory on the node) to share data
katenary.v3/health-check: object Health check to be added to the deployment.
katenary.v3/ignore: bool Ignore the service
katenary.v3/ingress: object Ingress rules to be added to the service.
katenary.v3/main-app: bool Mark the service as the main app.
katenary.v3/map-env: map[string]string Map env vars from the service to the deployment.
katenary.v3/ports: []uint32 Ports to be added to the service.
katenary.v3/same-pod: string Move the same-pod deployment to the target deployment.
katenary.v3/secrets: []string Env vars to be set as secrets.
katenary.v3/values: []string or map[string]string Environment variables to be added to the values.yaml
katenary.v3/values-from: map[string]string Add values from another service.
```
# What a name...
## Katenary.yaml file and schema validation
Instead of using labels inside the docker-compose file, you can use a `katenary.yaml` file to define the labels. This
file is simpler to read and maintain, but you need to keep it up-to-date with the docker-compose file.
For example, instead of using this:
```yaml
services:
web:
image: nginx:latest
katenary.v3/ingress: |-
hostname: myapp.example.com
port: 80
```
You can remove the labels, and use a `katenary.yaml` file:
```yaml
web:
ingress:
hostname: myapp.example.com
port: 80
```
To validate the `katenary.yaml` file, you can use the JSON schema using the "master" raw content:
`https://raw.githubusercontent.com/Katenary/katenary/refs/heads/master/katenary.json`
It's easy to configure in [LazyVim](https://www.lazyvim.org/), using `nvim-lspconfig`,
create a Lua file in your `plugins` directory, or apply the settings as the example below:
```lua
-- yaml.lua
return {
{
"neovim/nvim-lspconfig",
opts = {
servers = {
yamlls = {
settings = {
yaml = {
schemas = {
["https://raw.githubusercontent.com/Katenary/katenary/master/katenary.json"] = "katenary.yaml",
},
},
},
},
},
},
},
}
```
Use this address to validate the `katenary.yaml` file in VSCode:
```json
{
"yaml.schemas": {
"https://raw.githubusercontent.com/Katenary/katenary/master/katenary.json": "katenary.yaml"
}
}
```
> You can, of course, replace the `master` with a specific tag or branch.
## What a name…
Katenary is the stylized name of the project that comes from the "catenary" word.
A catenary is a curve formed by a wire, rope, or chain hanging freely from two points that are not in the same vertical line. For example, the anchor chain between a bot and the anchor.
This "curved link" represents what we try to do, the project is a "streched link from docker-compose to helm chart".
A catenary is a curve formed by a wire, rope, or chain hanging freely from two points that are not in the same vertical
line. For example, the anchor chain between a boat and the anchor.
This curved link represents what we try to do, the project is a stretched link from docker-compose to helm chart.

View File

@@ -1,154 +1,320 @@
// Katenary CLI, main package.
//
// This package is not intended to be imported. It contains the
// main function that build the command line with `cobra` package.
package main
import (
"fmt"
"katenary/generator/writers"
"katenary/helm"
"katenary/update"
"strconv"
"log"
"os"
"strings"
"github.com/katenary/katenary/internal/generator"
"github.com/katenary/katenary/internal/generator/katenaryfile"
"github.com/katenary/katenary/internal/generator/labels"
"github.com/katenary/katenary/internal/utils"
"github.com/compose-spec/compose-go/cli"
"github.com/spf13/cobra"
)
var Version = "master" // changed at compile time
const longHelp = `Katenary is a tool to convert compose files to Helm Charts.
var longHelp = `Katenary aims to be a tool to convert docker-compose files to Helm Charts.
It will create deployments, services, volumes, secrets, and ingress resources.
But it will also create initContainers based on depend_on, healthcheck, and other features.
It's not magical, sometimes you'll need to fix the generated charts.
The general way to use it is to call one of these commands:
katenary convert
katenary convert -c docker-compose.yml
katenary convert -c docker-compose.yml -o ./charts
In case of, check the help of each command using:
katenary <command> --help
or
"katenary help <command>"
Each [command] and subcommand has got an "help" and "--help" flag to show more information.
`
func init() {
// apply the version to the "update" package
update.Version = Version
func main() {
rootCmd := buildRootCmd()
if err := rootCmd.Execute(); err != nil {
log.Fatal(err)
}
}
func main() {
// The base command
func buildRootCmd() *cobra.Command {
rootCmd := &cobra.Command{
Use: "katenary",
Long: longHelp,
Short: "Katenary is a tool to convert docker-compose files to Helm Charts",
}
rootCmd.Example = ` katenary convert -c docker-compose.yml -o ./charts`
// to display the version
versionCmd := &cobra.Command{
Use: "version",
Short: "Display version",
Run: func(c *cobra.Command, args []string) { c.Println(Version) },
}
// convert command, need some flags
convertCmd := &cobra.Command{
Use: "convert",
Short: "Convert docker-compose to helm chart",
Long: "Convert docker-compose to helm chart. The resulting helm chart will be in the current directory/" +
ChartsDir + "/" + AppName +
".\nThe appversion will be generated that way:\n" +
"- if it's in a git project, it takes git version or tag\n" +
"- if it's not defined, so the version will be get from the --app-version flag \n" +
"- if it's not defined, so the 0.0.1 version is used",
Run: func(c *cobra.Command, args []string) {
force := c.Flag("force").Changed
// TODO: is there a way to get typed values from cobra?
appversion := c.Flag("app-version").Value.String()
composeFile := c.Flag("compose-file").Value.String()
appName := c.Flag("app-name").Value.String()
chartVersion := c.Flag("chart-version").Value.String()
chartDir := c.Flag("output-dir").Value.String()
indentation, err := strconv.Atoi(c.Flag("indent-size").Value.String())
if err != nil {
writers.IndentSize = indentation
}
Convert(composeFile, appversion, appName, chartDir, chartVersion, force)
},
}
convertCmd.Flags().BoolP(
"force", "f", false, "force overwrite of existing output files")
convertCmd.Flags().StringP(
"app-version", "a", AppVersion, "app version")
convertCmd.Flags().StringP(
"chart-version", "v", ChartVersion, "chart version")
convertCmd.Flags().StringP(
"compose-file", "c", ComposeFile, "docker compose file")
convertCmd.Flags().StringP(
"app-name", "n", AppName, "application name")
convertCmd.Flags().StringP(
"output-dir", "o", ChartsDir, "chart directory")
convertCmd.Flags().IntP(
"indent-size", "i", 2, "set the indent size of the YAML files")
// show possible labels to set in docker-compose file
showLabelsCmd := &cobra.Command{
Use: "show-labels",
Short: "Show labels of a resource",
Run: func(c *cobra.Command, args []string) {
c.Println(helm.GetLabelsDocumentation())
},
}
// Update the binary to the latest version
updateCmd := &cobra.Command{
Use: "upgrade",
Short: "Upgrade katenary to the latest version if available",
Run: func(c *cobra.Command, args []string) {
version, assets, err := update.CheckLatestVersion()
if err != nil {
c.Println(err)
return
}
c.Println("Updating to version: " + version)
err = update.DownloadLatestVersion(assets)
if err != nil {
c.Println(err)
return
}
c.Println("Update completed")
},
}
rootCmd.Version = generator.GetVersion()
rootCmd.CompletionOptions.DisableDescriptions = false
rootCmd.CompletionOptions.DisableNoDescFlag = false
rootCmd.AddCommand(
versionCmd,
convertCmd,
showLabelsCmd,
updateCmd,
generateCompletionCommand(rootCmd.Name()),
generateVersionCommand(),
generateConvertCommand(),
generateHashComposefilesCommand(),
generateLabelHelpCommand(),
generateSchemaCommand(),
)
// in parallel, check if the current katenary version is the latest
ch := make(chan string)
go func() {
version, _, err := update.CheckLatestVersion()
if err != nil {
ch <- ""
return
}
if Version != version {
ch <- fmt.Sprintf("\x1b[33mNew version available: " +
version +
" - to auto upgrade katenary, you can execute: katenary upgrade\x1b[0m\n")
}
}()
// Execute the command
finalize := make(chan error)
go func() {
finalize <- rootCmd.Execute()
}()
// Wait for both goroutines to finish
if err := <-finalize; err != nil {
fmt.Println(err)
}
fmt.Print(<-ch)
return rootCmd
}
const completionHelp = `To load completions:
Bash:
# Add this line in your ~/.bashrc or ~/.bash_profile file
$ source <(%[1]s completion bash)
# Or, you can load completions for each users session. Execute once:
# Linux:
$ %[1]s completion bash > /etc/bash_completion.d/%[1]s
# macOS:
$ %[1]s completion bash > $(brew --prefix)/etc/bash_completion.d/%[1]s
Zsh:
# If shell completion is not already enabled in your environment,
# you will need to enable it. You can execute the following once:
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
# To load completions for each session, execute once:
$ %[1]s completion zsh > "${fpath[1]}/_%[1]s"
# You will need to start a new shell for this setup to take effect.
fish:
$ %[1]s completion fish | source
# To load completions for each session, execute once:
$ %[1]s completion fish > ~/.config/fish/completions/%[1]s.fish
PowerShell:
PS> %[1]s completion powershell | Out-String | Invoke-Expression
# To load completions for every new session, run:
PS> %[1]s completion powershell > %[1]s.ps1
# and source this file from your PowerShell profile.
`
func generateCompletionCommand(name string) *cobra.Command {
bashV1 := false
cmd := &cobra.Command{
Use: "completion",
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
Short: "Generates completion scripts",
Long: fmt.Sprintf(completionHelp, name),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return cmd.Help()
}
switch args[0] {
case "bash":
// get the bash version
if cmd.Flags().Changed("bash-v1") {
return cmd.Root().GenBashCompletion(os.Stdout)
}
return cmd.Root().GenBashCompletionV2(os.Stdout, true)
case "zsh":
return cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
return cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
return cmd.Root().GenPowerShellCompletion(os.Stdout)
}
return fmt.Errorf("unknown completion type: %s", args[0])
},
}
// add a flag to force bash completion v1
cmd.Flags().Bool("bash-v1", bashV1, "Force bash completion v1")
return cmd
}
func generateConvertCommand() *cobra.Command {
force := false
outputDir := "./chart"
dockerComposeFile := make([]string, 0)
profiles := make([]string, 0)
helmdepUpdate := false
var appVersion *string
givenAppVersion := ""
chartVersion := "0.1.0"
icon := ""
envFiles := []string{}
convertCmd := &cobra.Command{
Use: "convert",
Short: "Converts a docker-compose file to a Helm Chart",
RunE: func(cmd *cobra.Command, args []string) error {
if len(strings.TrimSpace(givenAppVersion)) > 0 {
appVersion = &givenAppVersion
}
return generator.Convert(generator.ConvertOptions{
Force: force,
OutputDir: outputDir,
Profiles: profiles,
HelmUpdate: helmdepUpdate,
AppVersion: appVersion,
ChartVersion: chartVersion,
Icon: icon,
EnvFiles: envFiles,
}, dockerComposeFile...)
},
}
convertCmd.Flags().BoolVarP(
&force,
"force",
"f",
force,
"Force the overwrite of the chart directory",
)
convertCmd.Flags().BoolVarP(
&helmdepUpdate,
"helm-update",
"u",
helmdepUpdate,
"Update helm dependencies if helm is installed",
)
convertCmd.Flags().StringSliceVarP(
&profiles,
"profile",
"p",
profiles,
"Specify the profiles to use",
)
convertCmd.Flags().StringVarP(
&outputDir,
"output-dir",
"o",
outputDir,
"Specify the output directory",
)
convertCmd.Flags().StringSliceVarP(
&dockerComposeFile,
"compose-file",
"c",
cli.DefaultFileNames,
"Specify an alternate compose files - can be specified multiple times or use coma to separate them.\n"+
"Note that overides files are also used whatever the files you specify here.\nThe overides files are:\n"+
strings.Join(cli.DefaultOverrideFileNames, ", \n")+
"\n",
)
convertCmd.Flags().StringVarP(
&givenAppVersion,
"app-version",
"a",
"",
"Specify the app version (in Chart.yaml)",
)
convertCmd.Flags().StringVarP(
&chartVersion,
"chart-version",
"v",
chartVersion,
"Specify the chart version (in Chart.yaml)",
)
convertCmd.Flags().StringVarP(
&icon,
"icon",
"i",
"",
"Specify the icon (in Chart.yaml), use a valid URL, Helm does not support local files at this time.",
)
convertCmd.Flags().StringSliceVarP(
&envFiles,
"env-file",
"e",
envFiles,
"Specify the env file to use additonnaly to the .env file. Can be specified multiple times.",
)
return convertCmd
}
func generateVersionCommand() *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "Print the version number of Katenary",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(generator.GetVersion())
},
}
}
func generateLabelHelpCommand() *cobra.Command {
markdown := false
all := false
cmd := &cobra.Command{
Use: "help-labels [label]",
Short: "Print the labels help for all or a specific label",
Long: `Print the labels help for all or a specific label
If no label is specified, the help for all labels is printed.
If a label is specified, the help for this label is printed.
The name of the label must be specified without the prefix ` + labels.Prefix() + `.
e.g.
kanetary help-labels
katenary help-labels ingress
katenary help-labels map-env
`,
ValidArgs: labels.GetLabelNames(),
Run: func(cmd *cobra.Command, args []string) {
if len(args) > 0 {
fmt.Println(labels.GetLabelHelpFor(args[0], markdown))
return
}
if all {
// show the help for all labels
l := len(labels.GetLabelNames())
for i, label := range labels.GetLabelNames() {
fmt.Println(labels.GetLabelHelpFor(label, markdown))
if !markdown && i < l-1 {
fmt.Println(strings.Repeat("-", 80))
}
}
return
}
fmt.Println(labels.GetLabelHelp(markdown))
},
}
cmd.Flags().BoolVarP(&markdown, "markdown", "m", markdown, "Use the markdown format")
cmd.Flags().BoolVarP(&all, "all", "a", all, "Print the full help for all labels")
return cmd
}
func generateHashComposefilesCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "hash-composefiles [composefile]",
Short: "Print the hash of the composefiles",
Long: `Print the hash of the composefiles
If no composefile is specified, the hash of all composefiles is printed.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) > 0 {
if hash, err := utils.HashComposefiles(args); err != nil {
fmt.Println(err)
} else {
fmt.Println(hash)
}
return
}
},
}
return cmd
}
func generateSchemaCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "schema",
Short: "Print the schema of the katenary file",
Long: "Generate a schama for katenary.yaml file that can be used to validate the file or to use with yaml LSP to complete and check your configuration.",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(katenaryfile.GenerateSchema())
},
}
return cmd
}

78
cmd/katenary/main_test.go Normal file
View File

@@ -0,0 +1,78 @@
package main
import (
"bytes"
"encoding/json"
"io"
"os"
"strings"
"testing"
)
func TestBuildCommand(t *testing.T) {
rootCmd := buildRootCmd()
if rootCmd == nil {
t.Errorf("Expected rootCmd to be defined")
} else if rootCmd.Use != "katenary" {
t.Errorf("Expected rootCmd.Use to be katenary, got %s", rootCmd.Use)
}
numCommands := 6
if len(rootCmd.Commands()) != numCommands {
t.Errorf("Expected %d command, got %d", numCommands, len(rootCmd.Commands()))
}
}
func TestGetVersion(t *testing.T) {
cmd := buildRootCmd()
if cmd == nil {
t.Errorf("Expected cmd to be defined")
}
version := generateVersionCommand()
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
version.Run(cmd, nil)
w.Close()
os.Stdout = old
var buf bytes.Buffer
io.Copy(&buf, r)
output := buf.String()
if !strings.Contains(output, "(devel)") {
t.Errorf("Expected output to contain '(devel)', got %s", output)
}
}
func TestSchemaCommand(t *testing.T) {
cmd := buildRootCmd()
if cmd == nil {
t.Errorf("Expected cmd to be defined")
}
schema := generateSchemaCommand()
old := os.Stdout
r, w, err := os.Pipe()
if err != nil {
t.Fatalf("Failed to create pipe: %v", err)
}
os.Stdout = w
var buf bytes.Buffer
done := make(chan struct{})
go func() {
schema.Run(cmd, nil)
w.Close()
close(done)
}()
io.Copy(&buf, r)
<-done
os.Stdout = old
// try to parse json
schemaContent := make(map[string]any)
if err := json.Unmarshal(buf.Bytes(), &schemaContent); err != nil {
t.Errorf("Expected valid json")
}
}

View File

@@ -1,144 +0,0 @@
package main
import (
"errors"
"fmt"
"katenary/compose"
"katenary/generator"
"os"
"os/exec"
"path/filepath"
"strings"
)
var (
composeFiles = []string{"compose.yml", "compose.yaml", "docker-compose.yaml", "docker-compose.yml"}
ComposeFile = ""
AppName = "MyApp"
ChartsDir = "chart"
AppVersion = "0.0.1"
ChartVersion = "0.1.0"
)
func init() {
FindComposeFile()
SetAppName()
SetAppVersion()
}
func FindComposeFile() bool {
for _, file := range composeFiles {
if _, err := os.Stat(file); err == nil {
ComposeFile = file
return true
}
}
return false
}
// SetAppName sets the application name from the current directory name.
func SetAppName() {
wd, err := os.Getwd()
if err != nil {
return
}
AppName = filepath.Base(wd)
if AppName == "" {
AppName = "MyApp"
}
}
// SetAppVersion set the AppVersion variable to the git version/tag
func SetAppVersion() {
AppVersion, _ = detectGitVersion()
}
// Try to detect the git version/tag.
func detectGitVersion() (string, error) {
defaulVersion := "0.0.1"
// Check if .git directory exists
if s, err := os.Stat(".git"); err != nil {
// .git should be a directory
return defaulVersion, errors.New("no git repository found")
} else if !s.IsDir() {
// .git should be a directory
return defaulVersion, errors.New(".git is not a directory")
}
// check if "git" executable is callable
if _, err := exec.LookPath("git"); err != nil {
return defaulVersion, errors.New("git executable not found")
}
// get the latest commit hash
if out, err := exec.Command("git", "log", "-n1", "--pretty=format:%h").Output(); err == nil {
latestCommit := strings.TrimSpace(string(out))
// then get the current branch/tag
out, err := exec.Command("git", "branch", "--show-current").Output()
if err != nil {
return defaulVersion, errors.New("git branch --show-current failed")
} else {
currentBranch := strings.TrimSpace(string(out))
// finally, check if the current tag (if exists) correspond to the current commit
// git describe --exact-match --tags <latestCommit>
out, err := exec.Command("git", "describe", "--exact-match", "--tags", latestCommit).Output()
if err == nil {
return strings.TrimSpace(string(out)), nil
} else {
return currentBranch + "-" + latestCommit, nil
}
}
}
return defaulVersion, errors.New("git log failed")
}
func Convert(composeFile, appVersion, appName, chartDir, chartVersion string, force bool) {
if len(composeFile) == 0 {
fmt.Println("No compose file given")
return
}
_, err := os.Stat(composeFile)
if err != nil {
fmt.Println("No compose file found")
os.Exit(1)
}
// Parse the compose file now
p := compose.NewParser(composeFile)
p.Parse(appName)
dirname := filepath.Join(chartDir, appName)
if _, err := os.Stat(dirname); err == nil && !force {
response := ""
for response != "y" && response != "n" {
response = "n"
fmt.Printf(""+
"The %s directory already exists, it will be \x1b[31;1mremoved\x1b[0m!\n"+
"Do you really want to continue? [y/N]: ", dirname)
fmt.Scanf("%s", &response)
response = strings.ToLower(response)
}
if response == "n" {
fmt.Println("Cancelled")
os.Exit(0)
}
}
// cleanup and create the chart directory (until "templates")
if err := os.RemoveAll(dirname); err != nil {
fmt.Printf("Error removing %s: %s\n", dirname, err)
os.Exit(1)
}
// create the templates directory
templatesDir := filepath.Join(dirname, "templates")
if err := os.MkdirAll(templatesDir, 0755); err != nil {
fmt.Printf("Error creating %s: %s\n", templatesDir, err)
os.Exit(1)
}
// start generator
generator.Generate(p, Version, appName, appVersion, chartVersion, ComposeFile, dirname)
}

View File

@@ -1,91 +0,0 @@
package compose
import (
"io/ioutil"
"log"
"os"
"path/filepath"
"github.com/compose-spec/compose-go/cli"
"github.com/compose-spec/compose-go/types"
)
const (
ICON_EXCLAMATION = "❕"
)
// Parser is a docker-compose parser.
type Parser struct {
Data *types.Project
temporary *string
}
var (
Appname = ""
CURRENT_DIR, _ = os.Getwd()
)
// NewParser create a Parser and parse the file given in filename. If filename is empty, we try to parse the content[0] argument that should be a valid YAML content.
func NewParser(filename string, content ...string) *Parser {
p := &Parser{}
if len(content) > 0 { // mainly for the tests...
dir := filepath.Dir(filename)
err := os.MkdirAll(dir, 0755)
if err != nil {
log.Fatal(err)
}
p.temporary = &dir
ioutil.WriteFile(filename, []byte(content[0]), 0644)
cli.DefaultFileNames = []string{filename}
}
// if filename is not in cli Default files, add it
if len(filename) > 0 {
found := false
for _, f := range cli.DefaultFileNames {
if f == filename {
found = true
break
}
}
// add the file at first position
if !found {
cli.DefaultFileNames = append([]string{filename}, cli.DefaultFileNames...)
}
}
return p
}
// Parse using compose-go parser, adapt a bit the Project and set Appname.
func (p *Parser) Parse(appname string) {
// Reminder:
// - set Appname
// - loas services
options, err := cli.NewProjectOptions(nil,
cli.WithDefaultConfigPath,
cli.WithNormalization(true),
cli.WithInterpolation(true),
cli.WithResolvedPaths(true),
)
if err != nil {
log.Fatal(err)
}
proj, err := cli.ProjectFromOptions(options)
if err != nil {
log.Fatal("Failed to create project", err)
}
Appname = proj.Name
p.Data = proj
CURRENT_DIR = p.Data.WorkingDir
}
func GetCurrentDir() string {
return CURRENT_DIR
}

View File

@@ -0,0 +1,9 @@
MD012: false
MD013: false
MD022: false
MD033: false
MD041: false
MD046: false
# list indentation
MD007:
indent: 4

105
doc/docs/coding.md Normal file
View File

@@ -0,0 +1,105 @@
# How Katenary works behind the scene
This section is for developers who want to take part in Katenary. Here we describe how it works and the expected
principles.
## A few important points
Katenary is developed in Go. The version currently supported is 1.20. For reasons of readability, the `any` type is
preferred to `interface{}`.
Since version v3, Katenary uses, in addition to `go-compose`, the `k8s` library to generate objects that are guaranteed
to work before transformation. Katenary adds Helm syntax entries to add loops, transformations, and conditions.
We really try to follow best practices and code principles. But, Katenary needs a lot of workarounds and string
manipulation during the process. There are, also, some drawbacks using standard k8s packages that make a lot of type
checks when generating the objects. We need to finalize the values after object generation.
**This makes the coding a bit harder than simply converting from YAML to YAML.**
> If Katenary only generated YAML objects, the algorithms would be much simpler and would require less generation work.
## General principle
During conversion, the `generator` package is primarily responsible for creating "objects". The principle is to generate
one `Deployment` per `compose` service. If the container coming from "compose" exposes ports (explicitly), then a
service is created.
```mermaid
flowchart TD
D[Deployment]:::outputs@{shape: curv-trap}
C[Container List]@{shape: docs}
ConfigMap:::outputs@{shape: curv-trap}
Secrets:::outputs@{shape: curv-trap}
H[Helm Chart.yaml file]:::outputs@{shape: curv-trap}
Val[Values files]:::outputs@{shape: curv-trap}
PVC:::outputs@{shape: curv-trap}
S[Service]:::outputs@{shape: curv-trap}
A[Compose file]:::inputs --> B[Compose parser]
B --> G[Generator]
G --> P[Ports exposed to services] ---> S
G ------> H
G --> C --> D
G ------> Val
G ....-> M[Merge Continainers if same-pod]
M ..-> C
G --> E[Environment variables] ----> Secrets & ConfigMap
G--> V[Bind volumes] -------> PVC
V -----> CF[ Create ConfigMap\nfor static files as\nconfigmap-files] --> ConfigMap
Secrets & ConfigMap -- create envFrom --> D
V -- bind volumes --> D
```
If the declaration of a container is to be integrated into another pod (via the `same-pod` label), this `Deployment` and
its associated service are still created. They are deleted last, once the merge has been completed.
## Conversion in "`generator`" package
The `generator` package is where object struct are defined, and where you can find the `Generate()` function.
The generation fills `HelmChart` object using a loop:
```golang
for _, service := range project.Services {
dep := NewDeployment(service)
y, _ := dep.Yaml()
chart.Templates[dep.Filename()] = &ChartTemplate{
Content: y,
Servicename: service.Name,
}
}
```
**A lot** of string manipulations are made by each `Yaml()` methods. This is where you find the complex and impacting
operations. The `Yaml` methods **don't return a valid YAML content**. This is a Helm Chart YAML content with template
conditions, values and calls to helper templates.
> The `Yaml()` methods, in each object, need contribution, help, fixes, enhancements... They work, but there is a lot of
> complexity. Please, create issues, pull-requests and conversation in the GitHub repository.
The final step, before sending all templates to chart, is to bind the containers inside the same pod where it's
specified.
For each source container linked to the destination:
- we get the deployment of the source
- we copy the container to the destination deployment
- we get the associated service (if any)
- we then copy the service port to the destination service
- we finally remove the source service and deployment
> The `Configmap`, secrets, variables... are kept.
It finally computes the `helper` file.
## Conversion command
The `generator` works the same as described above. But the "convert" command makes some final steps:
- generate `values.yaml` and `Chart.yaml` files from the `HelmChart` object
- add comments to the `values.yaml` files
- add comments to the `Chart.yaml` files

18
doc/docs/dependencies.md Normal file
View File

@@ -0,0 +1,18 @@
# Why those dependencies?
Katenary uses `compose-go` and several Kubernetes official packages.
- `github.com/compose-spec/compose-go`: to parse compose files. It ensures :
- that the project respects the "compose" specification
- that Katenary uses the "compose" struct exactly the same way `podman compose` or `docker compose` does
- `github.com/spf13/cobra`: to parse command line arguments, sub-commands and flags. It also generates completion for
bash, zsh, fish and PowerShell.
- `github.com/thediveo/netdb`: to get the standard names of a service from its port number
- `gopkg.in/yaml.v3`:
- to generate `Chart.yaml` and `values.yaml` files (only)
- to parse Katenary labels in the compose file
- `k8s.io/api` and `k8s.io/apimachinery` to create Kubernetes objects
- `sigs.k8s.io/yaml`: to generate Katenary YAML files in the format of Kubernetes objects
There are also some other packages used in the project, like `gopkg.in/yaml` to parse labels. I'm sorry to not list the
entire dependencies. You can check the `go.mod` file to see all the dependencies.

112
doc/docs/faq.md Normal file
View File

@@ -0,0 +1,112 @@
# Frequently Asked Questions
## Why Katenary?
The main author[^1] of Katenary is a big fan of Podman, Docker and makes a huge use of Compose. He uses it a lot in his
daily work. When he started to work with Kubernetes, he wanted to have the same experience as with Docker Compose.
He wanted to have a tool that could convert his `docker-compose` files to Kubernetes manifests, but also to Helm charts.
Kompose was a good option. But the lacks of some options and configuration for the output Helm chart made him think
about creating a new tool. He wanted to have a tool that could generate a complete Helm chart, with a lot of options
and flexibility.
[^1]: I'm talking about myself :sunglasses: - Patrice FERLET, aka Metal3d, Tech Lead and DevOps Engineer at Klee Group.
## What's the difference between Katenary and Kompose?
[Kompose](https://kompose.io/) is a very nice tool, made by the Kubernetes community. It's a tool to convert
`docker-compose` files to Kubernetes manifests. It's a very good tool, and it's more mature than Katenary.
Kompose is able to generate Helm charts, but [it could be not the case in future releases](https://github.com/kubernetes/kompose/issues/1716) for several reasons[^2].
[^2]:
The author of Kompose explains that they have no bandwidth to maintain the Helm chart generation. It's a complex
task, and we can confirm. Katenary takes a lot of time to be developed and maintained. This issue mentions Katenary as
an alternative to Helm chart generation :smile:
The project is focused on Kubernetes manifests and proposes to use "Kustomize" to adapt the manifests. Helm seems to be
not the priority.
Anyway, before this decision, the Helm chart generation was not what we expected. We wanted to have a more complete
chart, with more options and more flexibility.
> That's why we decided to create Katenary.
Kompose didn't manage to generate a values file, complex volume binding, and many other things. It was also not able
to manage dependencies between services.
> Be sure that we don't want to compete with Kompose. We just want to propose a different approach to the problem.
Kompose is an excellent tool, and we use it in some projects. It's a good choice if you want to convert
your `docker-compose` files to Kubernetes manifests, but if you want to use Helm, Katenary is the tool you need.
## Why not using "one label" for all the configuration?
That was a discussion I had with my colleagues. The idea was to use a single label to store all the configuration.
But, it's not a good idea.
Sometimes, you will have a long list of things to configure, like ports, ingress, dependencies, etc. It's better to have
a clear and readable configuration. Segmented labels are easier to read and to maintain. It also avoids having too
many indentation levels in the YAML file.
It is also more flexible. You can add or remove labels without changing the others.
## Why not using a configuration file?
The idea was to keep the configuration at a same place, and using the go-compose library to read the labels. It's
easier to have a single file to manage.
By the way, Katenary auto accepts a `compose.katenary.yaml` file in the same directory. It's a way to separate the
configuration from the compose file. It uses
the [overrides' mechanism](https://docs.docker.com/compose/multiple-compose-files/merge/) like "compose" does.
## Why not developing with Rust?
Seriously...
OK, I will answer.
Rust is a good language. But, Podman, Docker, Kubernetes, Helm, and mostly all technologies around Kubernetes are
written in Go. We have a large ecosystem in Go to manipulate, read, and write Kubernetes manifests as parsing
Compose files.
> Go is better for this task.
There is no reason to use Rust for this project.
## Any chance to have a GUI?
Yes, it's a possibility. But, it's not a priority. We have a lot of things to do before. We need to stabilize the
project, to have a good documentation, to have a good test coverage, and to have a good community.
But, in a not so far future, we could have a GUI. The choice of [Fyne.io](https://fyne.io) is already made, and we
tested some concepts.
## I'm rich (or not), I want to help you. How can I do?
You can help us in many ways.
- The first things we really need, more than money, more than anything else, is to have feedback. If you use Katenary,
if you have some issues, if you have some ideas, please open an issue on the [GitHub repository](https://github.com/Katenary/katenary).
- The second thing is to help us to fix issues. If you're a Go developer, or if you want to fix the documentation,
your help is greatly appreciated.
- And then, of course, we need money, or sponsors.
### If you're a company
We will be happy to communicate your help by putting your logo on the website and in the documentation. You can sponsor
us by giving us some money, or by giving us some time of your developers, or leaving us some time to work on the project.
### If you're an individual
All donators will be listed on the website and in the documentation. You can give us some money by using
the [GitHub Sponsors]()
All main contributors[^3] will be listed on the website and in the documentation.
> If you want to be anonymous, please tell us.
[^3]:
Main contributors are the people who have made a significant contribution to the project. It could be code,
documentation, or any other help. There is no defined rules, at this time, to evaluate the contribution.
It's a subjective decision.

162
doc/docs/index.md Normal file
View File

@@ -0,0 +1,162 @@
<div class="md-center">
![Katenary Logo](statics/logo-vertical.svg)
</div>
# Welcome to Katenary documentation
🚀 Unleash Productivity with Katenary! 🚀
Tired of manual conversions? Katenary harnesses the labels from your "compose" file to craft complete Helm Charts
effortlessly, saving you time and energy.
🛠️ Simple automated CLI: Katenary handles the grunt work, generating everything needed for seamless service binding
and Helm Chart creation.
💡 Effortless Efficiency: You only need to add labels when it's necessary to precise things. Then call `katenary convert`
and let the magic happen.
<div style="margin: auto" class="zoomable">
![](statics/workflow.svg)
</div>
# What is it?
Katenary is a tool made to help you to transform "compose" files (`compose.yaml`, `docker-compose.yml`,
`podman-compose.yml`...) to complete and production ready [Helm Chart](https://helm.sh).
You'll be able to deploy your project in [:material-kubernetes: Kubernetes](https://kubernetes.io) in a few seconds
(of course, more if you need to tweak with labels).
It uses your current file and optionally labels to configure the result.
It's an open source project, under MIT license, originally partially developed at [Smile](https://www.smile.eu).
Today, it's partially developed in collaboration with [Klee Group](https://www.kleegroup.com). Note that Katenary is
and **will stay an open source and free (as freedom) project**. We are convinced that the best way to make it better is to
share it with the community.
<div id="klee">
![](./statics/klee.svg)
</div>
The main developer is [Patrice FERLET](https://github.com/metal3d).
The project source
code is hosted on the [:fontawesome-brands-github: Katenary GitHub Repository](https://github.com/Katenary/katenary).
## Install Katenary
Katenary is developed using the :fontawesome-brands-golang:{ .gopher } [Go](https://go.dev) language.
The binary is statically linked, so you can simply download it from the [release
page](https://github.com/Katenary/katenary/releases) of the project in GitHub.
You need to select the right binary for your operating system and architecture, and copy the binary in a directory
that is in your `PATH`.
If you are a Linux user, you can use the "one line installation command" which will download the binary in your
`$HOME/.local/bin` directory if it exists.
```bash
sh <(curl -sSL https://raw.githubusercontent.com/Katenary/katenary/master/install.sh)
```
!!! Info "Upgrading is integrated to the `katenary` command"
Katenary propose a `upgrade` sub-command to update the current binary to the latest stable release.
Of course, you need to install Katenary once :smile:
!!! Note "You prefer to compile it, no need to install Go"
You can also build and install it yourself, the provided Makefile has got a `build` command that uses `podman` or
`docker` to build the binary.
So, you don't need to install Go compiler :+1:.
But, note that the "master" branch is not the "stable" version. It's preferable to switch to a tag, or to use the
releases.
To compile it, you can use the following commands:
```bash
git clone https://github.com/Katenary/katenary.git
cd katenary
make build
make install
```
`make install` copies `./katenary` binary to your user binary path (`~/.local/bin`)
You can install it in other directory by changing the `PREFIX` variable. E.g.:
```bash
make build
sudo make install PREFIX=/usr/local
```
Check if everything is OK using `katenary version` and / or `katenary help`
## Install completion
Katenary uses the very nice project named `cobra` to manage flags, argument and auto-completion.
You can activate it with:
```bash
# replace "bash" by "zsh" if needed
source <(katenary completion bash)
```
Add this line in you `~/.profile`, `~/.bash_aliases` or `~/.bashrc` file to have completion at startup.
## What a name
A catenary is the curve that a hanging chain or cable assumes under its own weight when supported only at its ends.
I, the maintainer, decided to name "Katenary" this project because it's like a chain that links a boat to a dock.
Making the link between the "compose" world and the "Kubernetes" world is the main goal of this project.
Anyway, it's too late to change the name now :smile:
!!! Note "But I like this name!"
I spent time to find it :wink:
## Special thanks to
I really want to thank all the contributors, testers, and of course, the authors of the packages and tools that are used
in this project. There is too many to list here. Katenary can work because of all these people. Open source is a great
thing! :heart:
!!! Edit "Special thanks"
**Katenary is built with:** <br />
<a href="https://go.dev" target="_blank">:fontawesome-brands-golang:{ .go-logo }</a>
Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.
Because Docker, Podman, Kubernetes, and Helm are written in Go, Katenary is also written in Go and borrows packages
from these projects to make it as efficient as possible.
Thanks to Kubernetes to provide [Kind](https://kind.sigs.k8s.io) that is used to test Katenary locally.
**Thanks to everyone who contributes to all these projects.**
Katenary can progress because of all these people. All contributions, as comments, issues, pull requests and
feedbacks are welcome.
**Everything was also possible because of:** <br />
<ul>
<li><a href="https://helm.sh" target="_blank"><img src="https://helm.sh/img/helm.svg" style="height: 1rem"/>
Helm</a> that is the main toppic of Katenary, Kubernetes is easier to use with it.</li>
<li><a href="https://cobra.dev/"><img src="https://cobra.dev/home/logo.png" style="height: 1rem"/> Cobra</a> that
makes command, subcommand and completion possible for Katenary with ease.</li>
<li>Podman, Docker, Kubernetes that are the main tools that Katenary is made for.</li>
</ul>
**Documentation is built with:** <br />
<a href="https://www.mkdocs.org/" target="_blank">MkDocs</a> using <a
href="https://squidfunk.github.io/mkdocs-material/" target="_blank">Material for MkDocs</a> theme template.
## License
Katenary is an open source project under the MIT license. You can use it, modify it, and distribute it as you want.

489
doc/docs/labels.md Normal file
View File

@@ -0,0 +1,489 @@
# Labels documentation
Katenary proposes labels to set in `compose.yaml` files (or override files) to configure the Helm Chart generation.
Because it is sometimes needed to have structured values, it is necessary to use the YAML syntax.
While compose labels are string, we can use _here-doc_ syntax using `|` to use YAML multiline as value.
```yaml
label-name: |-
# this is actually a multiline string here
key1: value1
key2: value2
```
Katenary will try to _Unmarshal_ these labels.
## Label list and types
<!-- START_LABEL_DOC : do not remove this tag !-->
| Label name | Description | Type |
| ------------------------------ | ---------------------------------------------------------------- | -------------------------------- |
| `katenary.v3/configmap-files` | Inject files as Configmap. | `[]string` |
| `katenary.v3/cronjob` | Create a cronjob from the service. | `object` |
| `katenary.v3/dependencies` | Add Helm dependencies to the service. | `[]object` |
| `katenary.v3/description` | Description of the service | `string` |
| `katenary.v3/env-from` | Add environment variables from another service. | `[]string` |
| `katenary.v3/exchange-volumes` | Add exchange volumes (empty directory on the node) to share data | `[]object` |
| `katenary.v3/health-check` | Health check to be added to the deployment. | `object` |
| `katenary.v3/ignore` | Ignore the service | `bool` |
| `katenary.v3/ingress` | Ingress rules to be added to the service. | `object` |
| `katenary.v3/main-app` | Mark the service as the main app. | `bool` |
| `katenary.v3/map-env` | Map env vars from the service to the deployment. | `map[string]string` |
| `katenary.v3/ports` | Ports to be added to the service. | `[]uint32` |
| `katenary.v3/same-pod` | Move the same-pod deployment to the target deployment. | `string` |
| `katenary.v3/secrets` | Env vars to be set as secrets. | `[]string` |
| `katenary.v3/values` | Environment variables to be added to the values.yaml | `[]string or map[string]string` |
| `katenary.v3/values-from` | Add values from another service. | `map[string]string` |
<!-- STOP_LABEL_DOC : do not remove this tag !-->
## Detailed description
<!-- START_DETAILED_DOC : do not remove this tag !-->
### katenary.v3/configmap-files
Inject files as Configmap.
**Type**: `[]string`
It makes a file or directory to be converted to one or more ConfigMaps
and mounted in the pod. The file or directory is relative to the
service directory.
If it is a directory, all files inside it are added to the ConfigMap.
If the directory as subdirectories, so one configmap per subpath are created.
!!! Warning
It is not intended to be used to store an entire project in configmaps.
It is intended to be used to store configuration files that are not managed
by the application, like nginx configuration files. Keep in mind that your
project sources should be stored in an application image or in a storage.
**Example:**
```yaml
volumes
- ./conf.d:/etc/nginx/conf.d
labels:
katenary.v3/configmap-files: |-
- ./conf.d
```
### katenary.v3/cronjob
Create a cronjob from the service.
**Type**: `object`
This adds a cronjob to the chart.
The label value is a YAML object with the following attributes:
- command: the command to be executed
- schedule: the cron schedule (cron format or @every where "every" is a
duration like 1h30m, daily, hourly...)
- rbac: false (optionnal), if true, it will create a role, a rolebinding and
a serviceaccount to make your cronjob able to connect the Kubernetes API
**Example:**
```yaml
labels:
katenary.v3/cronjob: |-
command: echo "hello world"
schedule: "* */1 * * *" # or @hourly for example
```
### katenary.v3/dependencies
Add Helm dependencies to the service.
**Type**: `[]object`
Set the service to be, actually, a Helm dependency. This means that the
service will not be exported as template. The dependencies are added to
the Chart.yaml file and the values are added to the values.yaml file.
It's a list of objects with the following attributes:
- name: the name of the dependency
- repository: the repository of the dependency
- alias: the name of the dependency in values.yaml (optional)
- values: the values to be set in values.yaml (optional)
!!! Info
Katenary doesn't update the helm depenedencies by default.
Use `--helm-update` (or `-u`) flag to update the dependencies.
example: <code>katenary convert -u</code>
By setting an alias, it is possible to change the name of the dependency
in values.yaml.
**Example:**
```yaml
labels:
katenary.v3/dependencies: |-
- name: mariadb
repository: oci://registry-1.docker.io/bitnamicharts
## optional, it changes the name of the section in values.yaml
# alias: mydatabase
## optional, it adds the values to values.yaml
values:
auth:
database: mydatabasename
username: myuser
password: the secret password
```
### katenary.v3/description
Description of the service
**Type**: `string`
This replaces the default comment in values.yaml file to the given description.
It is useful to document the service and configuration.
The value can be set with a documentation in multiline format.
**Example:**
```yaml
labels:
katenary.v3/description: |-
This is a description of the service.
It can be multiline.
```
### katenary.v3/env-from
Add environment variables from another service.
**Type**: `[]string`
It adds environment variables from another service to the current service.
**Example:**
```yaml
service1:
image: nginx:1.19
environment:
FOO: bar
service2:
image: php:7.4-fpm
labels:
# get the congigMap from service1 where FOO is
# defined inside this service too
katenary.v3/env-from: |-
- myservice1
```
### katenary.v3/exchange-volumes
Add exchange volumes (empty directory on the node) to share data
**Type**: `[]object`
This label allows sharing data between containres. The volume is created in
the node and mounted in the pod. It is useful to share data between containers
in a "same pod" logic. For example to let PHP-FPM and Nginx share the same direcotory.
This will create:
- an `emptyDir` volume in the deployment
- a `voumeMount` in the pod for **each container**
- a `initContainer` for each definition
Fields:
- name: the name of the volume (manadatory)
- mountPath: the path where the volume is mounted in the pod (optional, default is `/opt`)
- init: a command to run to initialize the volume with data (optional)
!!! Warning
This is highly experimental. This is mainly useful when using the "same-pod" label.
**Example:**
```yaml
nginx:
# ...
labels;
katenary.v3/exchange-volumes: |-
- name: php-fpm
mountPath: /var/www/html
php:
# ...
labels:
katenary.v3/exchange-volumes: |-
- name: php-fpm
mountPath: /opt
init: cp -ra /var/www/html/* /opt
```
### katenary.v3/health-check
Health check to be added to the deployment.
**Type**: `object`
Health check to be added to the deployment.
**Example:**
```yaml
labels:
katenary.v3/health-check: |-
livenessProbe:
httpGet:
path: /health
port: 8080
```
### katenary.v3/ignore
Ignore the service
**Type**: `bool`
Ingoring a service to not be exported in helm chart.
**Example:**
```yaml
labels:
katenary.v3/ignore: "true"
```
### katenary.v3/ingress
Ingress rules to be added to the service.
**Type**: `object`
Declare an ingress rule for the service. The port should be exposed or
declared with `katenary.v3/ports`.
**Example:**
```yaml
labels:
katenary.v3/ingress: |-
port: 80
hostname: mywebsite.com (optional)
```
### katenary.v3/main-app
Mark the service as the main app.
**Type**: `bool`
This makes the service to be the main application. Its image tag is
considered to be the Chart appVersion and to be the defaultvalue in Pod
container image attribute.
!!! Warning
This label cannot be repeated in others services. If this label is
set in more than one service as true, Katenary will return an error.
**Example:**
```yaml
ghost:
image: ghost:1.25.5
labels:
# The chart is now named ghost, and the appVersion is 1.25.5.
# In Deployment, the image attribute is set to ghost:1.25.5 if
# you don't change the "tag" attribute in values.yaml
katenary.v3/main-app: true
```
### katenary.v3/map-env
Map env vars from the service to the deployment.
**Type**: `map[string]string`
Because you may need to change the variable for Kubernetes, this label
forces the value to another. It is also particullary helpful to use a template
value instead. For example, you could bind the value to a service name
with Helm attributes:
`{{ tpl .Release.Name . }}`.
If you use `__APP__` in the value, it will be replaced by the Chart name.
**Example:**
```yaml
env:
DB_HOST: database
RUNNING: docker
OTHER: value
labels:
katenary.v3/map-env: |-
RUNNING: kubernetes
DB_HOST: '{{ include "__APP__.fullname" . }}-database'
```
### katenary.v3/ports
Ports to be added to the service.
**Type**: `[]uint32`
Only useful for services without exposed port. It is mandatory if the
service is a dependency of another service.
**Example:**
```yaml
labels:
katenary.v3/ports: |-
- 8080
- 8081
```
### katenary.v3/same-pod
Move the same-pod deployment to the target deployment.
**Type**: `string`
This will make the service to be included in another service pod. Some services
must work together in the same pod, like a sidecar or a proxy or nginx + php-fpm.
Note that volume and VolumeMount are copied from the source to the target
deployment.
**Example:**
```yaml
web:
image: nginx:1.19
php:
image: php:7.4-fpm
labels:
katenary.v3/same-pod: web
```
### katenary.v3/secrets
Env vars to be set as secrets.
**Type**: `[]string`
This label allows setting the environment variables as secrets. The variable
is removed from the environment and added to a secret object.
The variable can be set to the `katenary.v3/values` too,
so the secret value can be configured in values.yaml
**Example:**
```yaml
env:
PASSWORD: a very secret password
NOT_A_SECRET: a public value
labels:
katenary.v3/secrets: |-
- PASSWORD
```
### katenary.v3/values
Environment variables to be added to the values.yaml
**Type**: `[]string or map[string]string`
By default, all environment variables in the "env" and environment
files are added to configmaps with the static values set. This label
allows adding environment variables to the values.yaml file.
Note that the value inside the configmap is `{{ tpl vaname . }}`, so
you can set the value to a template that will be rendered with the
values.yaml file.
The value can be set with a documentation. This may help to understand
the purpose of the variable.
**Example:**
```yaml
env:
FOO: bar
DB_NAME: mydb
TO_CONFIGURE: something that can be changed in values.yaml
A_COMPLEX_VALUE: example
labels:
katenary.v3/values: |-
# simple values, set as is in values.yaml
- TO_CONFIGURE
# complex values, set as a template in values.yaml with a documentation
- A_COMPLEX_VALUE: |-
This is the documentation for the variable to
configure in values.yaml.
It can be, of course, a multiline text.
```
### katenary.v3/values-from
Add values from another service.
**Type**: `map[string]string`
This label allows adding values from another service to the current service.
It avoid duplicating values, environment or secrets that should be the same.
The key is the value to be added, and the value is the "key" to fetch in the
form `service_name.environment_name`.
**Example:**
```yaml
database:
image: mariadb:10.5
environment:
MARIADB_USER: myuser
MARIADB_PASSWORD: mypassword
labels:
# we can declare secrets
katenary.v3/secrets: |-
- MARIADB_PASSWORD
php:
image: php:7.4-fpm
environment:
# it's duplicated in docker / podman
DB_USER: myuser
DB_PASSWORD: mypassword
labels:
# removes the duplicated, use the configMap and secrets from "database"
katenary.v3/values-from: |-
DB_USER: database.MARIADB_USER
DB_PASSWORD: database.MARIADB_PASSWORD
```
<!-- STOP_DETAILED_DOC : do not remove this tag !-->

View File

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

View File

@@ -0,0 +1,923 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->
# generator
```go
import "github.com/katenary/katenary/internal/generator"
```
Package generator 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 generate.Convert\(\) create an HelmChart object and call "Generate\(\)" method to convert from a compose file to a helm chart. 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 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.
## Variables
<a name="Annotations"></a>
```go
var (
// Standard annotationss
Annotations = map[string]string{
labels.LabelName("version"): Version,
}
)
```
<a name="Version"></a>Version is the version of katenary. It is set at compile time.
```go
var Version = "master" // changed at compile time
```
<a name="Convert"></a>
## func [Convert](<https://github.com/katenary/katenary/blob/master/internal/generator/converter.go#L100>)
```go
func Convert(config ConvertOptions, dockerComposeFile ...string) error
```
Convert a compose \(docker, podman...\) project to a helm chart. It calls Generate\(\) to generate the chart and then write it to the disk.
<a name="GetLabels"></a>
## func [GetLabels](<https://github.com/katenary/katenary/blob/master/internal/generator/labels.go#L12>)
```go
func GetLabels(serviceName, appName string) map[string]string
```
GetLabels returns the labels for a service. It uses the appName to replace the \_\_replace\_\_ in the labels. This is used to generate the labels in the templates.
<a name="GetMatchLabels"></a>
## func [GetMatchLabels](<https://github.com/katenary/katenary/blob/master/internal/generator/labels.go#L25>)
```go
func GetMatchLabels(serviceName, appName string) map[string]string
```
GetMatchLabels returns the matchLabels for a service. It uses the appName to replace the \_\_replace\_\_ in the labels. This is used to generate the matchLabels in the templates.
<a name="GetVersion"></a>
## func [GetVersion](<https://github.com/katenary/katenary/blob/master/internal/generator/version.go#L14>)
```go
func GetVersion() string
```
GetVersion return the version of katneary. It's important to understand that the version is set at compile time for the github release. But, it the user get katneary using \`go install\`, the version should be different.
<a name="Helper"></a>
## func [Helper](<https://github.com/katenary/katenary/blob/master/internal/generator/helper.go#L15>)
```go
func Helper(name string) string
```
Helper returns the \_helpers.tpl file for a chart.
<a name="NewCronJob"></a>
## func [NewCronJob](<https://github.com/katenary/katenary/blob/master/internal/generator/cronJob.go#L28>)
```go
func NewCronJob(service types.ServiceConfig, chart *HelmChart, appName string) (*CronJob, *RBAC)
```
NewCronJob creates a new CronJob from a compose service. The appName is the name of the application taken from the project name.
<a name="ToK8SYaml"></a>
## func [ToK8SYaml](<https://github.com/katenary/katenary/blob/master/internal/generator/utils.go#L91>)
```go
func ToK8SYaml(obj any) ([]byte, error)
```
<a name="UnWrapTPL"></a>
## func [UnWrapTPL](<https://github.com/katenary/katenary/blob/master/internal/generator/utils.go#L87>)
```go
func UnWrapTPL(in []byte) []byte
```
UnWrapTPL removes the line wrapping from a template.
<a name="ChartTemplate"></a>
## type [ChartTemplate](<https://github.com/katenary/katenary/blob/master/internal/generator/chart.go#L22-L25>)
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.
```go
type ChartTemplate struct {
Servicename string
Content []byte
}
```
<a name="ConfigMap"></a>
## type [ConfigMap](<https://github.com/katenary/katenary/blob/master/internal/generator/configMap.go#L37-L42>)
ConfigMap is a kubernetes ConfigMap. Implements the DataMap interface.
```go
type ConfigMap struct {
*corev1.ConfigMap
// contains filtered or unexported fields
}
```
<a name="NewConfigMap"></a>
### func [NewConfigMap](<https://github.com/katenary/katenary/blob/master/internal/generator/configMap.go#L46>)
```go
func NewConfigMap(service types.ServiceConfig, appName string, forFile bool) *ConfigMap
```
NewConfigMap creates a new ConfigMap from a compose service. The appName is the name of the application taken from the project name. The ConfigMap is filled by environment variables and labels "map\-env".
<a name="NewConfigMapFromDirectory"></a>
### func [NewConfigMapFromDirectory](<https://github.com/katenary/katenary/blob/master/internal/generator/configMap.go#L119>)
```go
func NewConfigMapFromDirectory(service types.ServiceConfig, appName, path string) *ConfigMap
```
NewConfigMapFromDirectory creates a new ConfigMap from a compose service. This path is the path to the file or directory. If the path is a directory, all files in the directory are added to the ConfigMap. Each subdirectory are ignored. Note that the Generate\(\) function will create the subdirectories ConfigMaps.
<a name="ConfigMap.AddBinaryData"></a>
### func \(\*ConfigMap\) [AddBinaryData](<https://github.com/katenary/katenary/blob/master/internal/generator/configMap.go#L157>)
```go
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.
<a name="ConfigMap.AddData"></a>
### func \(\*ConfigMap\) [AddData](<https://github.com/katenary/katenary/blob/master/internal/generator/configMap.go#L152>)
```go
func (c *ConfigMap) AddData(key, value string)
```
AddData adds a key value pair to the configmap. Append or overwrite the value if the key already exists.
<a name="ConfigMap.AppendDir"></a>
### func \(\*ConfigMap\) [AppendDir](<https://github.com/katenary/katenary/blob/master/internal/generator/configMap.go#L166>)
```go
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.
<a name="ConfigMap.AppendFile"></a>
### func \(\*ConfigMap\) [AppendFile](<https://github.com/katenary/katenary/blob/master/internal/generator/configMap.go#L213>)
```go
func (c *ConfigMap) AppendFile(path string) error
```
<a name="ConfigMap.Filename"></a>
### func \(\*ConfigMap\) [Filename](<https://github.com/katenary/katenary/blob/master/internal/generator/configMap.go#L237>)
```go
func (c *ConfigMap) Filename() string
```
Filename returns the filename of the configmap. If the configmap is used for files, the filename contains the path.
<a name="ConfigMap.SetData"></a>
### func \(\*ConfigMap\) [SetData](<https://github.com/katenary/katenary/blob/master/internal/generator/configMap.go#L247>)
```go
func (c *ConfigMap) SetData(data map[string]string)
```
SetData sets the data of the configmap. It replaces the entire data.
<a name="ConfigMap.Yaml"></a>
### func \(\*ConfigMap\) [Yaml](<https://github.com/katenary/katenary/blob/master/internal/generator/configMap.go#L252>)
```go
func (c *ConfigMap) Yaml() ([]byte, error)
```
Yaml returns the yaml representation of the configmap
<a name="ConfigMapMount"></a>
## type [ConfigMapMount](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L28-L31>)
```go
type ConfigMapMount struct {
// contains filtered or unexported fields
}
```
<a name="ConvertOptions"></a>
## type [ConvertOptions](<https://github.com/katenary/katenary/blob/master/internal/generator/chart.go#L28-L37>)
ConvertOptions are the options to convert a compose project to a helm chart.
```go
type ConvertOptions struct {
AppVersion *string
OutputDir string
ChartVersion string
Icon string
Profiles []string
EnvFiles []string
Force bool
HelmUpdate bool
}
```
<a name="CronJob"></a>
## type [CronJob](<https://github.com/katenary/katenary/blob/master/internal/generator/cronJob.go#L22-L25>)
CronJob is a kubernetes CronJob.
```go
type CronJob struct {
*batchv1.CronJob
// contains filtered or unexported fields
}
```
<a name="CronJob.Filename"></a>
### func \(\*CronJob\) [Filename](<https://github.com/katenary/katenary/blob/master/internal/generator/cronJob.go#L114>)
```go
func (c *CronJob) Filename() string
```
Filename returns the filename of the cronjob.
Implements the Yaml interface.
<a name="CronJob.Yaml"></a>
### func \(\*CronJob\) [Yaml](<https://github.com/katenary/katenary/blob/master/internal/generator/cronJob.go#L121>)
```go
func (c *CronJob) Yaml() ([]byte, error)
```
Yaml returns the yaml representation of the cronjob.
Implements the Yaml interface.
<a name="CronJobValue"></a>
## type [CronJobValue](<https://github.com/katenary/katenary/blob/master/internal/generator/values.go#L118-L123>)
CronJobValue is a cronjob configuration that will be saved in values.yaml.
```go
type CronJobValue struct {
Repository *RepositoryValue `yaml:"repository,omitempty"`
Environment map[string]any `yaml:"environment,omitempty"`
ImagePullPolicy string `yaml:"imagePullPolicy,omitempty"`
Schedule string `yaml:"schedule"`
}
```
<a name="DataMap"></a>
## type [DataMap](<https://github.com/katenary/katenary/blob/master/internal/generator/types.go#L4-L7>)
DataMap is a kubernetes ConfigMap or Secret. It can be used to add data to the ConfigMap or Secret.
```go
type DataMap interface {
SetData(map[string]string)
AddData(string, string)
}
```
<a name="Deployment"></a>
## type [Deployment](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L34-L44>)
Deployment is a kubernetes Deployment.
```go
type Deployment struct {
*appsv1.Deployment `yaml:",inline"`
// contains filtered or unexported fields
}
```
<a name="NewDeployment"></a>
### func [NewDeployment](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L48>)
```go
func NewDeployment(service types.ServiceConfig, chart *HelmChart) *Deployment
```
NewDeployment creates a new Deployment from a compose service. The appName is the name of the application taken from the project name. It also creates the Values map that will be used to create the values.yaml file.
<a name="Deployment.AddContainer"></a>
### func \(\*Deployment\) [AddContainer](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L115>)
```go
func (d *Deployment) AddContainer(service types.ServiceConfig)
```
AddContainer adds a container to the deployment.
<a name="Deployment.AddHealthCheck"></a>
### func \(\*Deployment\) [AddHealthCheck](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L161>)
```go
func (d *Deployment) AddHealthCheck(service types.ServiceConfig, container *corev1.Container)
```
<a name="Deployment.AddIngress"></a>
### func \(\*Deployment\) [AddIngress](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L190>)
```go
func (d *Deployment) AddIngress(service types.ServiceConfig, appName string) *Ingress
```
AddIngress adds an ingress to the deployment. It creates the ingress object.
<a name="Deployment.AddLegacyVolume"></a>
### func \(\*Deployment\) [AddLegacyVolume](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L220>)
```go
func (d *Deployment) AddLegacyVolume(name, kind string)
```
<a name="Deployment.AddVolumes"></a>
### func \(\*Deployment\) [AddVolumes](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L196>)
```go
func (d *Deployment) AddVolumes(service types.ServiceConfig, appName string)
```
AddVolumes adds a volume to the deployment. It does not create the PVC, it only adds the volumes to the deployment. If the volume is a bind volume it will warn the user that it is not supported yet.
<a name="Deployment.BindFrom"></a>
### func \(\*Deployment\) [BindFrom](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L241>)
```go
func (d *Deployment) BindFrom(service types.ServiceConfig, binded *Deployment)
```
<a name="Deployment.BindMapFilesToContainer"></a>
### func \(\*Deployment\) [BindMapFilesToContainer](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L375>)
```go
func (d *Deployment) BindMapFilesToContainer(service types.ServiceConfig, secrets []string, appName string) (*corev1.Container, int)
```
<a name="Deployment.DependsOn"></a>
### func \(\*Deployment\) [DependsOn](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L269>)
```go
func (d *Deployment) DependsOn(to *Deployment, servicename string) error
```
DependsOn adds a initContainer to the deployment that will wait for the service to be up.
<a name="Deployment.Filename"></a>
### func \(\*Deployment\) [Filename](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L300>)
```go
func (d *Deployment) Filename() string
```
Filename returns the filename of the deployment.
<a name="Deployment.MountExchangeVolumes"></a>
### func \(\*Deployment\) [MountExchangeVolumes](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L426>)
```go
func (d *Deployment) MountExchangeVolumes()
```
<a name="Deployment.SetEnvFrom"></a>
### func \(\*Deployment\) [SetEnvFrom](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L305>)
```go
func (d *Deployment) SetEnvFrom(service types.ServiceConfig, appName string, samePod ...bool)
```
SetEnvFrom sets the environment variables to a configmap. The configmap is created.
<a name="Deployment.Yaml"></a>
### func \(\*Deployment\) [Yaml](<https://github.com/katenary/katenary/blob/master/internal/generator/deployment.go#L450>)
```go
func (d *Deployment) Yaml() ([]byte, error)
```
Yaml returns the yaml representation of the deployment.
<a name="FileMapUsage"></a>
## type [FileMapUsage](<https://github.com/katenary/katenary/blob/master/internal/generator/configMap.go#L21>)
FileMapUsage is the usage of the filemap.
```go
type FileMapUsage uint8
```
<a name="FileMapUsageConfigMap"></a>FileMapUsage constants.
```go
const (
FileMapUsageConfigMap FileMapUsage = iota // pure configmap for key:values.
FileMapUsageFiles // files in a configmap.
)
```
<a name="HelmChart"></a>
## type [HelmChart](<https://github.com/katenary/katenary/blob/master/internal/generator/chart.go#L41-L54>)
HelmChart is a Helm Chart representation. It contains all the templates, values, versions, helpers...
```go
type HelmChart struct {
Templates map[string]*ChartTemplate `yaml:"-"`
Values map[string]any `yaml:"-"`
VolumeMounts map[string]any `yaml:"-"`
Name string `yaml:"name"`
Icon string `yaml:"icon,omitempty"`
APIVersion string `yaml:"apiVersion"`
Version string `yaml:"version"`
AppVersion string `yaml:"appVersion"`
Description string `yaml:"description"`
Helper string `yaml:"-"`
Dependencies []labelstructs.Dependency `yaml:"dependencies,omitempty"`
// contains filtered or unexported fields
}
```
<a name="Generate"></a>
### func [Generate](<https://github.com/katenary/katenary/blob/master/internal/generator/generator.go#L32>)
```go
func Generate(project *types.Project) (*HelmChart, error)
```
Generate a chart from a compose project. This does not write files to disk, it only creates the HelmChart object.
The Generate function will create the HelmChart object this way:
- Detect the service port name or leave the port number if not found.
- Create a deployment for each service that are not ingnore.
- Create a service and ingresses for each service that has ports and/or declared ingresses.
- Create a PVC or Configmap volumes for each volume.
- Create init containers for each service which has dependencies to other services.
- Create a chart dependencies.
- Create a configmap and secrets from the environment variables.
- Merge the same\-pod services.
<a name="NewChart"></a>
### func [NewChart](<https://github.com/katenary/katenary/blob/master/internal/generator/chart.go#L57>)
```go
func NewChart(name string) *HelmChart
```
NewChart creates a new empty chart with the given name.
<a name="HelmChart.SaveTemplates"></a>
### func \(\*HelmChart\) [SaveTemplates](<https://github.com/katenary/katenary/blob/master/internal/generator/chart.go#L72>)
```go
func (chart *HelmChart) SaveTemplates(templateDir string)
```
SaveTemplates the templates of the chart to the given directory.
<a name="Ingress"></a>
## type [Ingress](<https://github.com/katenary/katenary/blob/master/internal/generator/ingress.go#L18-L22>)
```go
type Ingress struct {
*networkv1.Ingress
// contains filtered or unexported fields
}
```
<a name="NewIngress"></a>
### func [NewIngress](<https://github.com/katenary/katenary/blob/master/internal/generator/ingress.go#L25>)
```go
func NewIngress(service types.ServiceConfig, Chart *HelmChart) *Ingress
```
NewIngress creates a new Ingress from a compose service.
<a name="Ingress.Filename"></a>
### func \(\*Ingress\) [Filename](<https://github.com/katenary/katenary/blob/master/internal/generator/ingress.go#L129>)
```go
func (ingress *Ingress) Filename() string
```
<a name="Ingress.Yaml"></a>
### func \(\*Ingress\) [Yaml](<https://github.com/katenary/katenary/blob/master/internal/generator/ingress.go#L133>)
```go
func (ingress *Ingress) Yaml() ([]byte, error)
```
<a name="IngressValue"></a>
## type [IngressValue](<https://github.com/katenary/katenary/blob/master/internal/generator/values.go#L29-L36>)
IngressValue is a ingress configuration that will be saved in values.yaml.
```go
type IngressValue struct {
Annotations map[string]string `yaml:"annotations"`
Host string `yaml:"host"`
Path string `yaml:"path"`
Class string `yaml:"class"`
Enabled bool `yaml:"enabled"`
TLS TLS `yaml:"tls"`
}
```
<a name="PersistenceValue"></a>
## type [PersistenceValue](<https://github.com/katenary/katenary/blob/master/internal/generator/values.go#L16-L21>)
PersistenceValue is a persistence configuration that will be saved in values.yaml.
```go
type PersistenceValue struct {
StorageClass string `yaml:"storageClass"`
Size string `yaml:"size"`
AccessMode []string `yaml:"accessMode"`
Enabled bool `yaml:"enabled"`
}
```
<a name="RBAC"></a>
## type [RBAC](<https://github.com/katenary/katenary/blob/master/internal/generator/rbac.go#L20-L24>)
RBAC is a kubernetes RBAC containing a role, a rolebinding and an associated serviceaccount.
```go
type RBAC struct {
RoleBinding *RoleBinding
Role *Role
ServiceAccount *ServiceAccount
}
```
<a name="NewRBAC"></a>
### func [NewRBAC](<https://github.com/katenary/katenary/blob/master/internal/generator/rbac.go#L27>)
```go
func NewRBAC(service types.ServiceConfig, appName string) *RBAC
```
NewRBAC creates a new RBAC from a compose service. The appName is the name of the application taken from the project name.
<a name="RepositoryValue"></a>
## type [RepositoryValue](<https://github.com/katenary/katenary/blob/master/internal/generator/values.go#L10-L13>)
RepositoryValue is a docker repository image and tag that will be saved in values.yaml.
```go
type RepositoryValue struct {
Image string `yaml:"image"`
Tag string `yaml:"tag"`
}
```
<a name="Role"></a>
## type [Role](<https://github.com/katenary/katenary/blob/master/internal/generator/rbac.go#L114-L117>)
Role is a kubernetes Role.
```go
type Role struct {
*rbacv1.Role
// contains filtered or unexported fields
}
```
<a name="Role.Filename"></a>
### func \(\*Role\) [Filename](<https://github.com/katenary/katenary/blob/master/internal/generator/rbac.go#L119>)
```go
func (r *Role) Filename() string
```
<a name="Role.Yaml"></a>
### func \(\*Role\) [Yaml](<https://github.com/katenary/katenary/blob/master/internal/generator/rbac.go#L123>)
```go
func (r *Role) Yaml() ([]byte, error)
```
<a name="RoleBinding"></a>
## type [RoleBinding](<https://github.com/katenary/katenary/blob/master/internal/generator/rbac.go#L100-L103>)
RoleBinding is a kubernetes RoleBinding.
```go
type RoleBinding struct {
*rbacv1.RoleBinding
// contains filtered or unexported fields
}
```
<a name="RoleBinding.Filename"></a>
### func \(\*RoleBinding\) [Filename](<https://github.com/katenary/katenary/blob/master/internal/generator/rbac.go#L105>)
```go
func (r *RoleBinding) Filename() string
```
<a name="RoleBinding.Yaml"></a>
### func \(\*RoleBinding\) [Yaml](<https://github.com/katenary/katenary/blob/master/internal/generator/rbac.go#L109>)
```go
func (r *RoleBinding) Yaml() ([]byte, error)
```
<a name="Secret"></a>
## type [Secret](<https://github.com/katenary/katenary/blob/master/internal/generator/secret.go#L22-L25>)
Secret is a kubernetes Secret.
Implements the DataMap interface.
```go
type Secret struct {
*corev1.Secret
// contains filtered or unexported fields
}
```
<a name="NewSecret"></a>
### func [NewSecret](<https://github.com/katenary/katenary/blob/master/internal/generator/secret.go#L28>)
```go
func NewSecret(service types.ServiceConfig, appName string) *Secret
```
NewSecret creates a new Secret from a compose service
<a name="Secret.AddData"></a>
### func \(\*Secret\) [AddData](<https://github.com/katenary/katenary/blob/master/internal/generator/secret.go#L70>)
```go
func (s *Secret) AddData(key, value string)
```
AddData adds a key value pair to the secret.
<a name="Secret.Filename"></a>
### func \(\*Secret\) [Filename](<https://github.com/katenary/katenary/blob/master/internal/generator/secret.go#L86>)
```go
func (s *Secret) Filename() string
```
Filename returns the filename of the secret.
<a name="Secret.SetData"></a>
### func \(\*Secret\) [SetData](<https://github.com/katenary/katenary/blob/master/internal/generator/secret.go#L91>)
```go
func (s *Secret) SetData(data map[string]string)
```
SetData sets the data of the secret.
<a name="Secret.Yaml"></a>
### func \(\*Secret\) [Yaml](<https://github.com/katenary/katenary/blob/master/internal/generator/secret.go#L98>)
```go
func (s *Secret) Yaml() ([]byte, error)
```
Yaml returns the yaml representation of the secret.
<a name="Service"></a>
## type [Service](<https://github.com/katenary/katenary/blob/master/internal/generator/service.go#L18-L21>)
Service is a kubernetes Service.
```go
type Service struct {
*v1.Service `yaml:",inline"`
// contains filtered or unexported fields
}
```
<a name="NewService"></a>
### func [NewService](<https://github.com/katenary/katenary/blob/master/internal/generator/service.go#L24>)
```go
func NewService(service types.ServiceConfig, appName string) *Service
```
NewService creates a new Service from a compose service.
<a name="Service.AddPort"></a>
### func \(\*Service\) [AddPort](<https://github.com/katenary/katenary/blob/master/internal/generator/service.go#L53>)
```go
func (s *Service) AddPort(port types.ServicePortConfig, serviceName ...string)
```
AddPort adds a port to the service.
<a name="Service.Filename"></a>
### func \(\*Service\) [Filename](<https://github.com/katenary/katenary/blob/master/internal/generator/service.go#L74>)
```go
func (s *Service) Filename() string
```
Filename returns the filename of the service.
<a name="Service.Yaml"></a>
### func \(\*Service\) [Yaml](<https://github.com/katenary/katenary/blob/master/internal/generator/service.go#L79>)
```go
func (s *Service) Yaml() ([]byte, error)
```
Yaml returns the yaml representation of the service.
<a name="ServiceAccount"></a>
## type [ServiceAccount](<https://github.com/katenary/katenary/blob/master/internal/generator/rbac.go#L132-L135>)
ServiceAccount is a kubernetes ServiceAccount.
```go
type ServiceAccount struct {
*corev1.ServiceAccount
// contains filtered or unexported fields
}
```
<a name="ServiceAccount.Filename"></a>
### func \(\*ServiceAccount\) [Filename](<https://github.com/katenary/katenary/blob/master/internal/generator/rbac.go#L137>)
```go
func (r *ServiceAccount) Filename() string
```
<a name="ServiceAccount.Yaml"></a>
### func \(\*ServiceAccount\) [Yaml](<https://github.com/katenary/katenary/blob/master/internal/generator/rbac.go#L141>)
```go
func (r *ServiceAccount) Yaml() ([]byte, error)
```
<a name="TLS"></a>
## type [TLS](<https://github.com/katenary/katenary/blob/master/internal/generator/values.go#L23-L26>)
```go
type TLS struct {
Enabled bool `yaml:"enabled"`
SecretName string `yaml:"secretName"`
}
```
<a name="Value"></a>
## type [Value](<https://github.com/katenary/katenary/blob/master/internal/generator/values.go#L39-L50>)
Value will be saved in values.yaml. It contains configuration for all deployment and services.
```go
type Value struct {
Repository *RepositoryValue `yaml:"repository,omitempty"`
Persistence map[string]*PersistenceValue `yaml:"persistence,omitempty"`
Ingress *IngressValue `yaml:"ingress,omitempty"`
Environment map[string]any `yaml:"environment,omitempty"`
Replicas *uint32 `yaml:"replicas,omitempty"`
CronJob *CronJobValue `yaml:"cronjob,omitempty"`
NodeSelector map[string]string `yaml:"nodeSelector"`
Resources map[string]any `yaml:"resources"`
ImagePullPolicy string `yaml:"imagePullPolicy,omitempty"`
ServiceAccount string `yaml:"serviceAccount"`
}
```
<a name="NewValue"></a>
### func [NewValue](<https://github.com/katenary/katenary/blob/master/internal/generator/values.go#L57>)
```go
func NewValue(service types.ServiceConfig, main ...bool) *Value
```
NewValue creates a new Value from a compose service. The value contains the necessary information to deploy the service \(image, tag, replicas, etc.\).
If \`main\` is true, the tag will be empty because it will be set in the helm chart appVersion.
<a name="Value.AddIngress"></a>
### func \(\*Value\) [AddIngress](<https://github.com/katenary/katenary/blob/master/internal/generator/values.go#L90>)
```go
func (v *Value) AddIngress(host, path string)
```
<a name="Value.AddPersistence"></a>
### func \(\*Value\) [AddPersistence](<https://github.com/katenary/katenary/blob/master/internal/generator/values.go#L104>)
```go
func (v *Value) AddPersistence(volumeName string)
```
AddPersistence adds persistence configuration to the Value.
<a name="VolumeClaim"></a>
## type [VolumeClaim](<https://github.com/katenary/katenary/blob/master/internal/generator/volume.go#L18-L23>)
VolumeClaim is a kubernetes VolumeClaim. This is a PersistentVolumeClaim.
```go
type VolumeClaim struct {
*v1.PersistentVolumeClaim
// contains filtered or unexported fields
}
```
<a name="NewVolumeClaim"></a>
### func [NewVolumeClaim](<https://github.com/katenary/katenary/blob/master/internal/generator/volume.go#L26>)
```go
func NewVolumeClaim(service types.ServiceConfig, volumeName, appName string) *VolumeClaim
```
NewVolumeClaim creates a new VolumeClaim from a compose service.
<a name="VolumeClaim.Filename"></a>
### func \(\*VolumeClaim\) [Filename](<https://github.com/katenary/katenary/blob/master/internal/generator/volume.go#L62>)
```go
func (v *VolumeClaim) Filename() string
```
Filename returns the suggested filename for a VolumeClaim.
<a name="VolumeClaim.Yaml"></a>
### func \(\*VolumeClaim\) [Yaml](<https://github.com/katenary/katenary/blob/master/internal/generator/volume.go#L67>)
```go
func (v *VolumeClaim) Yaml() ([]byte, error)
```
Yaml marshals a VolumeClaim into yaml.
<a name="Yaml"></a>
## type [Yaml](<https://github.com/katenary/katenary/blob/master/internal/generator/types.go#L10-L13>)
Yaml is a kubernetes object that can be converted to yaml.
```go
type Yaml interface {
Yaml() ([]byte, error)
Filename() string
}
```
Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)

View File

@@ -0,0 +1,28 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->
# extrafiles
```go
import "github.com/katenary/katenary/internal/generator/extrafiles"
```
Package extrafiles provides function to generate the Chart files that are not objects. Like README.md and notes.txt...
## func [NotesFile](<https://github.com/katenary/katenary/blob/master/internal/generator/extrafiles/notes.go#L13>)
```go
func NotesFile(services []string) string
```
NotesFile returns the content of the note.txt file.
<a name="ReadMeFile"></a>
## func [ReadMeFile](<https://github.com/katenary/katenary/blob/master/internal/generator/extrafiles/readme.go#L46>)
```go
func ReadMeFile(charname, description string, values map[string]any) string
```
ReadMeFile returns the content of the README.md file.
Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)

View File

@@ -0,0 +1,67 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->
# katenaryfile
```go
import "github.com/katenary/katenary/internal/generator/katenaryfile"
```
Package katenaryfile is a package for reading and writing katenary files.
A katenary file, named "katenary.yml" or "katenary.yaml", is a file where you can define the configuration of the conversion avoiding the use of labels in the compose file.
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/katenary/katenary/blob/master/internal/generator/katenaryfile/main.go#L137>)
```go
func GenerateSchema() string
```
GenerateSchema generates the schema for the katenary.yaml file.
<a name="OverrideWithConfig"></a>
## func [OverrideWithConfig](<https://github.com/katenary/katenary/blob/master/internal/generator/katenaryfile/main.go#L49>)
```go
func OverrideWithConfig(project *types.Project)
```
OverrideWithConfig overrides the project with the katenary.yaml file. It will set the labels of the services with the values from the katenary.yaml file. It work in memory, so it will not modify the original project.
<a name="Service"></a>
## type [Service](<https://github.com/katenary/katenary/blob/master/internal/generator/katenaryfile/main.go#L27-L44>)
Service is a struct that contains the service configuration for katenary
```go
type Service struct {
MainApp *bool `yaml:"main-app,omitempty" json:"main-app,omitempty" jsonschema:"title=Is this service the main application"`
Values []StringOrMap `yaml:"values,omitempty" json:"values,omitempty" jsonschema:"description=Environment variables to be set in values.yaml with or without a description"`
Secrets *labelstructs.Secrets `yaml:"secrets,omitempty" json:"secrets,omitempty" jsonschema:"title=Secrets,description=Environment variables to be set as secrets"`
Ports *labelstructs.Ports `yaml:"ports,omitempty" json:"ports,omitempty" jsonschema:"title=Ports,description=Ports to be exposed in services"`
Ingress *labelstructs.Ingress `yaml:"ingress,omitempty" json:"ingress,omitempty" jsonschema:"title=Ingress,description=Ingress configuration"`
HealthCheck *labelstructs.HealthCheck `yaml:"health-check,omitempty" json:"health-check,omitempty" jsonschema:"title=Health Check,description=Health check configuration that respects the kubernetes api"`
SamePod *string `yaml:"same-pod,omitempty" json:"same-pod,omitempty" jsonschema:"title=Same Pod,description=Service that should be in the same pod"`
Description *string `yaml:"description,omitempty" json:"description,omitempty" jsonschema:"title=Description,description=Description of the service that will be injected in the values.yaml file"`
Ignore *bool `yaml:"ignore,omitempty" json:"ignore,omitempty" jsonschema:"title=Ignore,description=Ignore the service in the conversion"`
Dependencies []labelstructs.Dependency `yaml:"dependencies,omitempty" json:"dependencies,omitempty" jsonschema:"title=Dependencies,description=Services that should be injected in the Chart.yaml file"`
ConfigMapFiles *labelstructs.ConfigMapFiles `yaml:"configmap-files,omitempty" json:"configmap-files,omitempty" jsonschema:"title=ConfigMap Files,description=Files that should be injected as ConfigMap"`
MapEnv *labelstructs.MapEnv `yaml:"map-env,omitempty" json:"map-env,omitempty" jsonschema:"title=Map Env,description=Map environment variables to another value"`
CronJob *labelstructs.CronJob `yaml:"cron-job,omitempty" json:"cron-job,omitempty" jsonschema:"title=Cron Job,description=Cron Job configuration"`
EnvFrom *labelstructs.EnvFrom `yaml:"env-from,omitempty" json:"env-from,omitempty" jsonschema:"title=Env From,description=Inject environment variables from another service"`
ExchangeVolumes []*labelstructs.ExchangeVolume `yaml:"exchange-volumes,omitempty" json:"exchange-volumes,omitempty" jsonschema:"title=Exchange Volumes,description=Exchange volumes between services"`
ValuesFrom *labelstructs.ValueFrom `yaml:"values-from,omitempty" json:"values-from,omitempty" jsonschema:"title=Values From,description=Inject values from another service (secret or configmap environment variables)"`
}
```
<a name="StringOrMap"></a>
## type [StringOrMap](<https://github.com/katenary/katenary/blob/master/internal/generator/katenaryfile/main.go#L24>)
StringOrMap is a struct that can be either a string or a map of strings. It's a helper struct to unmarshal the katenary.yaml file and produce the schema
```go
type StringOrMap any
```
Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)

View File

@@ -0,0 +1,110 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->
# labels
```go
import "github.com/katenary/katenary/internal/generator/labels"
```
Package labels provides functionality to parse and manipulate labels.
## Constants
<a name="KatenaryLabelPrefix"></a>
```go
const KatenaryLabelPrefix = "katenary.v3"
```
<a name="GetLabelHelp"></a>
## func [GetLabelHelp](<https://github.com/katenary/katenary/blob/master/internal/generator/labels/katenaryLabels.go#L88>)
```go
func GetLabelHelp(asMarkdown bool) string
```
GetLabelHelp return the help for the labels.
<a name="GetLabelHelpFor"></a>
## func [GetLabelHelpFor](<https://github.com/katenary/katenary/blob/master/internal/generator/labels/katenaryLabels.go#L97>)
```go
func GetLabelHelpFor(labelname string, asMarkdown bool) string
```
GetLabelHelpFor returns the help for a specific label.
<a name="GetLabelNames"></a>
## func [GetLabelNames](<https://github.com/katenary/katenary/blob/master/internal/generator/labels/katenaryLabels.go#L72>)
```go
func GetLabelNames() []string
```
GetLabelNames returns a sorted list of all katenary label names.
<a name="Prefix"></a>
## func [Prefix](<https://github.com/katenary/katenary/blob/master/internal/generator/labels/katenaryLabels.go#L235>)
```go
func Prefix() string
```
<a name="Help"></a>
## type [Help](<https://github.com/katenary/katenary/blob/master/internal/generator/labels/katenaryLabels.go#L64-L69>)
Help is the documentation of a label.
```go
type Help struct {
Short string `yaml:"short"`
Long string `yaml:"long"`
Example string `yaml:"example"`
Type string `yaml:"type"`
}
```
<a name="Label"></a>
## type [Label](<https://github.com/katenary/katenary/blob/master/internal/generator/labels/katenaryLabels.go#L57>)
Label is a katenary label to find in compose files.
```go
type Label = string
```
<a name="LabelMainApp"></a>Known labels.
```go
const (
LabelMainApp Label = KatenaryLabelPrefix + "/main-app"
LabelValues Label = KatenaryLabelPrefix + "/values"
LabelSecrets Label = KatenaryLabelPrefix + "/secrets"
LabelPorts Label = KatenaryLabelPrefix + "/ports"
LabelIngress Label = KatenaryLabelPrefix + "/ingress"
LabelMapEnv Label = KatenaryLabelPrefix + "/map-env"
LabelHealthCheck Label = KatenaryLabelPrefix + "/health-check"
LabelSamePod Label = KatenaryLabelPrefix + "/same-pod"
LabelDescription Label = KatenaryLabelPrefix + "/description"
LabelIgnore Label = KatenaryLabelPrefix + "/ignore"
LabelDependencies Label = KatenaryLabelPrefix + "/dependencies"
LabelConfigMapFiles Label = KatenaryLabelPrefix + "/configmap-files"
LabelCronJob Label = KatenaryLabelPrefix + "/cronjob"
LabelEnvFrom Label = KatenaryLabelPrefix + "/env-from"
LabelExchangeVolume Label = KatenaryLabelPrefix + "/exchange-volumes"
LabelValuesFrom Label = KatenaryLabelPrefix + "/values-from"
)
```
<a name="LabelName"></a>
### func [LabelName](<https://github.com/katenary/katenary/blob/master/internal/generator/labels/katenaryLabels.go#L59>)
```go
func LabelName(name string) Label
```
Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)

View File

@@ -0,0 +1,246 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->
# labelstructs
```go
import "github.com/katenary/katenary/internal/generator/labels/labelstructs"
```
Package labelstructs is a package that contains the structs used to represent the labels in the yaml files.
## type [ConfigMapFiles](<https://github.com/katenary/katenary/blob/master/internal/generator/labels/labelstructs/configMap.go#L5>)
```go
type ConfigMapFiles []string
```
<a name="ConfigMapFileFrom"></a>
### func [ConfigMapFileFrom](<https://github.com/katenary/katenary/blob/master/internal/generator/labels/labelstructs/configMap.go#L7>)
```go
func ConfigMapFileFrom(data string) (ConfigMapFiles, error)
```
<a name="CronJob"></a>
## type [CronJob](<https://github.com/katenary/katenary/blob/master/internal/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/katenary/katenary/blob/master/internal/generator/labels/labelstructs/cronJob.go#L12>)
```go
func CronJobFrom(data string) (*CronJob, error)
```
<a name="Dependency"></a>
## type [Dependency](<https://github.com/katenary/katenary/blob/master/internal/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/katenary/katenary/blob/master/internal/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/katenary/katenary/blob/master/internal/generator/labels/labelstructs/envFrom.go#L5>)
```go
type EnvFrom []string
```
<a name="EnvFromFrom"></a>
### func [EnvFromFrom](<https://github.com/katenary/katenary/blob/master/internal/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/katenary/katenary/blob/master/internal/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/katenary/katenary/blob/master/internal/generator/labels/labelstructs/exchangeVolume.go#L12>)
```go
func NewExchangeVolumes(data string) ([]*ExchangeVolume, error)
```
<a name="HealthCheck"></a>
## type [HealthCheck](<https://github.com/katenary/katenary/blob/master/internal/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/katenary/katenary/blob/master/internal/generator/labels/labelstructs/probes.go#L16>)
```go
func ProbeFrom(data string) (*HealthCheck, error)
```
<a name="Ingress"></a>
## type [Ingress](<https://github.com/katenary/katenary/blob/master/internal/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/katenary/katenary/blob/master/internal/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/katenary/katenary/blob/master/internal/generator/labels/labelstructs/mapenv.go#L5>)
```go
type MapEnv map[string]string
```
<a name="MapEnvFrom"></a>
### func [MapEnvFrom](<https://github.com/katenary/katenary/blob/master/internal/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/katenary/katenary/blob/master/internal/generator/labels/labelstructs/ports.go#L5>)
```go
type Ports []uint32
```
<a name="PortsFrom"></a>
### func [PortsFrom](<https://github.com/katenary/katenary/blob/master/internal/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/katenary/katenary/blob/master/internal/generator/labels/labelstructs/secrets.go#L5>)
```go
type Secrets []string
```
<a name="SecretsFrom"></a>
### func [SecretsFrom](<https://github.com/katenary/katenary/blob/master/internal/generator/labels/labelstructs/secrets.go#L7>)
```go
func SecretsFrom(data string) (Secrets, error)
```
<a name="TLS"></a>
## type [TLS](<https://github.com/katenary/katenary/blob/master/internal/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/katenary/katenary/blob/master/internal/generator/labels/labelstructs/valueFrom.go#L5>)
```go
type ValueFrom map[string]string
```
<a name="GetValueFrom"></a>
### func [GetValueFrom](<https://github.com/katenary/katenary/blob/master/internal/generator/labels/labelstructs/valueFrom.go#L7>)
```go
func GetValueFrom(data string) (*ValueFrom, error)
```
Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)

View File

@@ -0,0 +1,19 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->
# parser
```go
import "github.com/katenary/katenary/internal/parser"
```
Package parser is a wrapper around compose\-go to parse compose files.
## func [Parse](<https://github.com/katenary/katenary/blob/master/internal/parser/main.go#L29>)
```go
func Parse(profiles []string, envFiles []string, dockerComposeFile ...string) (*types.Project, error)
```
Parse compose files and return a project. The project is parsed with dotenv, osenv and profiles.
Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)

View File

@@ -0,0 +1,221 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->
# utils
```go
import "github.com/katenary/katenary/internal/utils"
```
Package utils provides some utility functions used in katenary. It defines some constants and functions used in the whole project.
## Constants
<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/katenary/katenary/blob/master/internal/utils/utils.go#L196>)
```go
func AsResourceName(name string) string
```
AsResourceName returns a resource name with underscores to respect the kubernetes naming convention. It's the opposite of FixedResourceName.
<a name="Confirm"></a>
## func [Confirm](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L164>)
```go
func Confirm(question string, icon ...Icon) bool
```
Confirm asks a question and returns true if the answer is y.
<a name="CountStartingSpaces"></a>
## func [CountStartingSpaces](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L41>)
```go
func CountStartingSpaces(line string) int
```
CountStartingSpaces counts the number of spaces at the beginning of a string.
<a name="EncodeBasicYaml"></a>
## func [EncodeBasicYaml](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L178>)
```go
func EncodeBasicYaml(data any) ([]byte, error)
```
EncodeBasicYaml encodes a basic yaml from an interface.
<a name="FixedResourceName"></a>
## func [FixedResourceName](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L190>)
```go
func FixedResourceName(name string) string
```
FixedResourceName returns a resource name without underscores to respect the kubernetes naming convention.
<a name="GetContainerByName"></a>
## func [GetContainerByName](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L87>)
```go
func GetContainerByName(name string, containers []corev1.Container) (*corev1.Container, int)
```
GetContainerByName returns a container by name and its index in the array. It returns nil, \-1 if not found.
<a name="GetKind"></a>
## func [GetKind](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L54>)
```go
func GetKind(path string) (kind string)
```
GetKind returns the kind of the resource from the file path.
<a name="GetServiceNameByPort"></a>
## func [GetServiceNameByPort](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L77>)
```go
func GetServiceNameByPort(port int) string
```
GetServiceNameByPort returns the service name for a port. It the service name is not found, it returns an empty string.
<a name="GetValuesFromLabel"></a>
## func [GetValuesFromLabel](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L129>)
```go
func GetValuesFromLabel(service types.ServiceConfig, LabelValues string) map[string]*EnvConfig
```
GetValuesFromLabel returns a map of values from a label.
<a name="HashComposefiles"></a>
## func [HashComposefiles](<https://github.com/katenary/katenary/blob/master/internal/utils/hash.go#L12>)
```go
func HashComposefiles(files []string) (string, error)
```
HashComposefiles returns a hash of the compose files.
<a name="Int32Ptr"></a>
## func [Int32Ptr](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L35>)
```go
func Int32Ptr(i int32) *int32
```
Int32Ptr returns a pointer to an int32.
<a name="PathToName"></a>
## func [PathToName](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L106>)
```go
func PathToName(path string) string
```
PathToName converts a path to a kubernetes complient name.
<a name="StrPtr"></a>
## func [StrPtr](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L38>)
```go
func StrPtr(s string) *string
```
StrPtr returns a pointer to a string.
<a name="TplName"></a>
## func [TplName](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L22>)
```go
func TplName(serviceName, appname string, suffix ...string) string
```
TplName returns the name of the kubernetes resource as a template string. It is used in the templates and defined in \_helper.tpl file.
<a name="TplValue"></a>
## func [TplValue](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L97>)
```go
func TplValue(serviceName, variable string, pipes ...string) string
```
TplValue returns a string that can be used in a template to access a value from the values file.
<a name="Warn"></a>
## func [Warn](<https://github.com/katenary/katenary/blob/master/internal/utils/icons.go#L25>)
```go
func Warn(msg ...any)
```
Warn prints a warning message
<a name="WordWrap"></a>
## func [WordWrap](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L159>)
```go
func WordWrap(text string, lineWidth int) string
```
WordWrap wraps a string to a given line width. Warning: it may break the string. You need to check the result.
<a name="Wrap"></a>
## func [Wrap](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L71>)
```go
func Wrap(src, above, below string) string
```
Wrap wraps a string with a string above and below. It will respect the indentation of the src string.
<a name="EnvConfig"></a>
## type [EnvConfig](<https://github.com/katenary/katenary/blob/master/internal/utils/utils.go#L123-L126>)
EnvConfig is a struct to hold the description of an environment variable.
```go
type EnvConfig struct {
Service types.ServiceConfig
Description string
}
```
<a name="Icon"></a>
## type [Icon](<https://github.com/katenary/katenary/blob/master/internal/utils/icons.go#L6>)
Icon is a unicode icon
```go
type Icon string
```
<a name="IconSuccess"></a>Icons used in katenary.
```go
const (
IconSuccess Icon = "✅"
IconFailure Icon = "❌"
IconWarning Icon = "❕"
IconNote Icon = "📝"
IconWorld Icon = "🌐"
IconPlug Icon = "🔌"
IconPackage Icon = "📦"
IconCabinet Icon = "🗄️"
IconInfo Icon = "🔵"
IconSecret Icon = "🔒"
IconConfig Icon = "🔧"
IconDependency Icon = "🔗"
)
```
Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)

View File

@@ -0,0 +1,48 @@
// Install the highlight.js in the documentation. Then
// highlight all the source code.
function hljsInstall() {
const version = "11.9.0";
const theme = "github-dark";
const script = document.createElement("script");
script.src = `//cdnjs.cloudflare.com/ajax/libs/highlight.js/${version}/highlight.min.js`;
script.onload = () => {
const style = document.createElement("link");
style.rel = "stylesheet";
style.href = `//cdnjs.cloudflare.com/ajax/libs/highlight.js/${version}/styles/${theme}.min.css`;
document.head.appendChild(style);
hljs.highlightAll();
};
document.head.appendChild(script);
}
// All images in an .zoomable div is zoomable, that
// meanse that we can click to zoom and unzoom.
// This needs specific CSS (see main.css).
function makeImagesZoomable() {
const zone = document.querySelectorAll(".zoomable");
zone.forEach((z, i) => {
const im = z.querySelectorAll("img,svg");
if (im.length == 0) {
return;
}
const input = document.createElement("input");
input.setAttribute("type", "checkbox");
input.setAttribute("id", `image-zoom-${i}`);
z.appendChild(input);
const label = document.createElement("label");
label.setAttribute("for", `image-zoom-${i}`);
z.appendChild(label);
label.appendChild(im[0]);
});
}
document.addEventListener("DOMContentLoaded", () => {
hljsInstall();
makeImagesZoomable();
});

121
doc/docs/statics/icon.svg Normal file
View File

@@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg211948"
viewBox="0 0 95.440796 85.01416"
height="85.01416"
width="95.440796"
version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata211954">
<rdf:rdf>
<cc:work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:work>
</rdf:rdf>
</metadata>
<defs
id="defs211952" />
<linearGradient
spreadMethod="pad"
y2="0.30000001"
x2="-0.1"
y1="1.2"
x1="0.30000001"
id="3d_gradient2-logo-24885591-b378-4c55-b87b-b7d42ed10694">
<stop
id="stop211929"
stop-opacity="1"
stop-color="#ffffff"
offset="0%" />
<stop
id="stop211931"
stop-opacity="1"
stop-color="#000000"
offset="100%" />
</linearGradient>
<linearGradient
gradientTransform="rotate(-30)"
spreadMethod="pad"
y2="0.30000001"
x2="-0.1"
y1="1.2"
x1="0.30000001"
id="3d_gradient3-logo-24885591-b378-4c55-b87b-b7d42ed10694">
<stop
id="stop211934"
stop-opacity="1"
stop-color="#ffffff"
offset="0%" />
<stop
id="stop211936"
stop-opacity="1"
stop-color="#cccccc"
offset="50%" />
<stop
id="stop211938"
stop-opacity="1"
stop-color="#000000"
offset="100%" />
</linearGradient>
<g
id="logo-group"
transform="translate(-394.01147,-211.65063)">
<path
d="m 773.83594,234.02344 c -0.4545,0.0375 -0.92821,0.1629 -1.40821,0.3789 -1.91999,0.864 -2.68851,2.68743 -1.72851,4.60743 l 21.4082,44.35351 0.0957,0.0957 -9.5996,21.02344 c -0.96,2.112 -0.28778,3.74342 1.82421,4.60742 0.576,0.288 1.15252,0.38477 1.72852,0.38477 1.248,0 2.2072,-0.76703 2.7832,-2.20703 l 31.10352,-68.35352 c 0.96,-2.112 0.28778,-3.64772 -1.82422,-4.51172 -2.112,-0.864 -3.64742,-0.28748 -4.60742,1.72852 l -17.85547,39.35937 -18.7207,-39.45507 c -0.648,-1.44 -1.83572,-2.12422 -3.19922,-2.01172 z"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path9" />
<path
d="m 692.07617,233.63477 c -15.26398,0 -26.68945,11.51921 -26.68945,26.7832 0,15.26398 11.13594,26.68945 25.91992,26.68945 8.44799,0 15.64875,-3.84038 19.96875,-9.98437 v 5.85547 c 0,2.11199 1.53614,3.64843 3.74414,3.64843 2.112,0 3.74414,-1.53644 3.74414,-3.64843 v -22.56055 c -0.096,-15.26399 -11.51951,-26.7832 -26.6875,-26.7832 z m 0,6.7207 c 11.03999,0 19.39063,8.63851 19.39063,20.0625 0,11.42399 -8.35064,19.96875 -19.39063,19.96875 -11.03999,0 -19.48828,-8.54476 -19.48828,-19.96875 0,-11.42399 8.44829,-20.0625 19.48828,-20.0625 z"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path8" />
<path
d="m 566.60352,233.63477 c -14.78399,0 -25.15235,11.13521 -25.15235,26.7832 0,15.64798 11.03981,26.68945 26.5918,26.68945 5.95199,0 13.24866,-2.59224 17.47265,-6.24023 1.536,-1.344 1.4406,-3.36079 -0.1914,-4.80078 -1.344,-1.056 -3.26508,-0.9606 -4.70508,0.1914 -2.784,2.4 -7.96818,4.22461 -12.57617,4.22461 -10.65599,0 -18.52799,-7.20007 -19.58399,-17.66406 h 38.78516 c 2.016,0 3.45508,-1.34338 3.45508,-3.35938 0,-15.07198 -9.59972,-25.82421 -24.0957,-25.82421 z m 0,6.62304 c 9.69599,0 16.22359,6.72003 17.18359,16.41602 h -35.13477 c 1.344,-9.69599 8.15919,-16.41602 17.95118,-16.41602 z"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path7" />
<path
d="m 756.875,233.53906 c -6.71999,0 -12.67202,3.16757 -16.41602,8.35156 v -4.1289 c 0,-2.304 -1.34248,-3.64649 -3.64648,-3.64649 -2.304,0 -3.64844,1.34249 -3.64844,3.64649 v 45.2168 c 0,2.30399 1.34444,3.64843 3.64844,3.64843 2.304,0 3.64648,-1.34444 3.64648,-3.64843 v -28.70313 c 0,-8.92799 7.87302,-14.68851 18.625,-13.72852 3.168,0.192 5.75965,0.76867 6.43164,-2.11132 0.768,-3.168 -2.68863,-4.89649 -8.64062,-4.89649 z"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path6" />
<path
d="m 628.04297,233.53906 c -7.10399,0 -13.34271,2.87999 -17.4707,7.58399 v -3.26368 c 0,-2.20799 -1.44044,-3.74414 -3.64844,-3.74414 -2.208,0 -3.74414,1.53615 -3.74414,3.74414 v 45.11915 c 0,2.20799 1.53614,3.64843 3.74414,3.64843 2.208,0 3.64844,-1.44044 3.64844,-3.64843 V 254.5625 c 0,-7.96799 7.19913,-14.01563 16.70312,-14.01563 9.79199,0 17.18359,5.66467 17.18359,17.47266 v 24.95899 c 0,2.11199 1.63215,3.64843 3.74414,3.64843 2.016,0 3.64844,-1.53644 3.64844,-3.64843 v -24.95899 c 0,-15.83998 -10.2726,-24.48047 -23.80859,-24.48047 z"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path5" />
<path
d="m 512.17187,217.41016 c -2.11199,0 -3.64843,1.53614 -3.64843,3.74414 v 14.88086 h -6.24024 c -2.01599,0 -3.35937,1.34367 -3.35937,3.26367 0,1.824 1.34338,3.16797 3.35937,3.16797 h 6.24024 v 25.63281 c 0,10.65599 7.39207,18.43134 17.66406,18.52734 h 2.01562 c 2.304,0 4.03321,-1.53644 4.03321,-3.64843 0,-2.208 -1.44104,-3.74415 -3.45703,-3.74415 h -2.5918 c -5.95199,0 -10.27148,-4.60677 -10.27148,-11.13476 V 242.4668 h 10.84765 c 2.016,0 3.35938,-1.34397 3.35938,-3.16797 0,-1.92 -1.34338,-3.26367 -3.35938,-3.26367 H 515.91602 V 221.1543 c 0,-2.208 -1.53615,-3.74414 -3.74415,-3.74414 z"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path4" />
<image
xlink:href=""
id="container"
x="272"
y="144"
width="480"
height="480"
style="display:none" />
<image
xlink:href=""
id="icon_container"
style="display:none"
x="0"
y="0"
width="0"
height="0" />
<path
style="fill:#388ec7;fill-opacity:1;stroke:none;stroke-width:1.41128"
d="m 488.71354,251.36014 -33.31908,17.65778 -13.63054,6.42952 -13.63053,-6.42952 -33.31907,-17.65778 c 0,5.32502 -2.24747,16.12101 0.77828,20.72433 1.84892,2.81293 6.84753,4.46784 9.82323,6.09432 8.26868,4.51957 16.72019,8.751 24.98932,13.27082 3.1083,1.69895 7.62988,5.16897 11.35877,5.21476 3.70778,0.0455 8.2451,-3.55102 11.35878,-5.19752 8.33909,-4.40967 16.72005,-8.76818 24.98932,-13.28806 3.05286,-1.66866 8.68533,-3.49778 10.14461,-6.69253 2.3911,-5.23485 0.45691,-14.46175 0.45691,-20.12612 m -46.19236,16.00215 c 3.59731,-0.51544 7.48607,-3.47354 10.60152,-5.12097 7.58911,-4.0131 15.20174,-8.00941 22.71755,-12.13797 3.53683,-1.94283 9.05374,-3.7013 11.65911,-6.69847 3.28982,-3.78453 1.93068,-13.45759 -2.58668,-15.74886 -4.7363,-2.40229 -8.80251,1.12086 -12.85868,3.25251 l -21.20306,11.2665 c -2.56736,1.35182 -6.01463,4.01777 -9.08702,4.01777 -3.29073,0 -7.12524,-2.97742 -9.84427,-4.47101 -6.50984,-3.57597 -13.17851,-6.90091 -19.68854,-10.47698 -2.85668,-1.56921 -7.10587,-4.85415 -10.60153,-4.52437 -7.2756,0.6864 -9.69308,11.97712 -5.60111,16.68444 2.43875,2.80547 7.56529,4.49972 10.90188,6.26408 7.83167,4.14137 15.6667,8.28474 23.4748,12.46456 3.21433,1.72068 8.18431,5.79216 12.11603,5.22877 m 30.29007,-45.33762 v -1.39693 l -18.17404,-8.90973 -12.11603,4.65427 -12.87329,-4.72164 -18.93129,8.9771 v 1.39693 l 21.20305,11.17542 9.84427,4.34169 9.84428,-4.33415 z"
id="path1" />
<path
d="m 334.95508,211.65039 c -2.016,0 -3.74414,1.63214 -3.74414,3.74414 v 67.48828 c 0,2.112 1.72814,3.74414 3.74414,3.74414 2.112,0 3.74414,-1.63214 3.74414,-3.74414 v -24.57617 l 8.54492,-8.64062 26.11133,35.32812 c 0.768,1.152 1.82397,1.63281 3.16797,1.63281 2.68799,0 4.5115,-3.36112 2.6875,-5.95312 l -26.5918,-36.28711 26.30469,-26.30469 c 2.304,-2.592 1.24701,-6.43164 -2.20899,-6.43164 -1.152,0 -2.01461,0.38375 -2.97461,1.34375 l -35.04101,35.04102 v -32.64063 c 0,-2.112 -1.63214,-3.74414 -3.74414,-3.74414 z"
style="fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path3" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg211948"
viewBox="0 0 544.44238 97.824005"
height="97.824005"
width="544.44238"
version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata211954">
<rdf:rdf>
<cc:work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:work>
</rdf:rdf>
</metadata>
<defs
id="defs211952" />
<linearGradient
spreadMethod="pad"
y2="0.30000001"
x2="-0.1"
y1="1.2"
x1="0.30000001"
id="3d_gradient2-logo-24885591-b378-4c55-b87b-b7d42ed10694">
<stop
id="stop211929"
stop-opacity="1"
stop-color="#ffffff"
offset="0%" />
<stop
id="stop211931"
stop-opacity="1"
stop-color="#000000"
offset="100%" />
</linearGradient>
<linearGradient
gradientTransform="rotate(-30)"
spreadMethod="pad"
y2="0.30000001"
x2="-0.1"
y1="1.2"
x1="0.30000001"
id="3d_gradient3-logo-24885591-b378-4c55-b87b-b7d42ed10694">
<stop
id="stop211934"
stop-opacity="1"
stop-color="#ffffff"
offset="0%" />
<stop
id="stop211936"
stop-opacity="1"
stop-color="#cccccc"
offset="50%" />
<stop
id="stop211938"
stop-opacity="1"
stop-color="#000000"
offset="100%" />
</linearGradient>
<g
id="logo-group"
transform="translate(-185.54797,-175.3735)">
<image
xlink:href=""
id="container"
x="272"
y="144"
width="480"
height="480"
style="display:none" />
<image
xlink:href=""
id="icon_container"
style="display:none"
x="0"
y="0"
width="0"
height="0" />
<g
id="g18"
transform="translate(-91.179677,-177.97015)">
<path
style="fill:#388ec7;fill-opacity:1;stroke:none;stroke-width:1.41128"
d="m 650.16134,532.23713 -33.31908,17.65778 -13.63054,6.42952 -13.63053,-6.42952 -33.31907,-17.65778 c 0,5.32502 -2.24747,16.12101 0.77828,20.72433 1.84892,2.81293 6.84753,4.46784 9.82323,6.09432 8.26868,4.51957 16.72019,8.751 24.98932,13.27082 3.1083,1.69895 7.62988,5.16897 11.35877,5.21476 3.70778,0.0455 8.2451,-3.55102 11.35878,-5.19752 8.33909,-4.40967 16.72005,-8.76818 24.98932,-13.28806 3.05286,-1.66866 8.68533,-3.49778 10.14461,-6.69253 2.3911,-5.23485 0.45691,-14.46175 0.45691,-20.12612 m -46.19236,16.00215 c 3.59731,-0.51544 7.48607,-3.47354 10.60152,-5.12097 7.58911,-4.0131 15.20174,-8.00941 22.71755,-12.13797 3.53683,-1.94283 9.05374,-3.7013 11.65911,-6.69847 3.28982,-3.78453 1.93068,-13.45759 -2.58668,-15.74886 -4.7363,-2.40229 -8.80251,1.12086 -12.85868,3.25251 l -21.20306,11.2665 c -2.56736,1.35182 -6.01463,4.01777 -9.08702,4.01777 -3.29073,0 -7.12524,-2.97742 -9.84427,-4.47101 -6.50984,-3.57597 -13.17851,-6.90091 -19.68854,-10.47698 -2.85668,-1.56921 -7.10587,-4.85415 -10.60153,-4.52437 -7.2756,0.6864 -9.69308,11.97712 -5.60111,16.68444 2.43875,2.80547 7.56529,4.49972 10.90188,6.26408 7.83167,4.14137 15.6667,8.28474 23.4748,12.46456 3.21433,1.72068 8.18431,5.79216 12.11603,5.22877 m 30.29007,-45.33762 v -1.39693 l -18.17404,-8.90973 -12.11603,4.65427 -12.87329,-4.72164 -18.93129,8.9771 v 1.39693 l 21.20305,11.17542 9.84427,4.34169 9.84428,-4.33415 z"
id="path17" />
<path
d="m 388.93329,654.58136 c 2.112,0 3.744,-1.632 3.744,-3.744 v -24.576 l 8.544,-8.64 26.112,35.328 c 0.768,1.152 1.824,1.632 3.168,1.632 2.688,0 4.512,-3.36 2.688,-5.952 l -26.592,-36.288 26.304,-26.304 c 2.304,-2.592 1.248,-6.432 -2.208,-6.432 -1.152,0 -2.016,0.384 -2.976,1.344 l -35.04,35.04 v -32.64 c 0,-2.112 -1.632,-3.744 -3.744,-3.744 -2.016,0 -3.744,1.632 -3.744,3.744 v 67.488 c 0,2.112 1.728,3.744 3.744,3.744 z m 75.55212,0.48 c 8.448,0 15.648,-3.84 19.968,-9.984 v 5.856 c 0,2.112 1.536,3.648 3.744,3.648 2.112,0 3.744,-1.536 3.744,-3.648 v -22.56 c -0.096,-15.264 -11.52,-26.784 -26.688,-26.784 -15.264,0 -26.688,11.52 -26.688,26.784 0,15.264 11.136,26.688 25.92,26.688 z m 0.768,-6.72 c -11.04,0 -19.488,-8.544 -19.488,-19.968 0,-11.424 8.448,-20.064 19.488,-20.064 11.04,0 19.392,8.64 19.392,20.064 0,11.424 -8.352,19.968 -19.392,19.968 z m 61.63201,6.24 h 2.016 c 2.304,0 4.032,-1.536 4.032,-3.648 0,-2.208 -1.44,-3.744 -3.456,-3.744 h -2.592 c -5.952,0 -10.272,-4.608 -10.272,-11.136 v -25.632 h 10.848 c 2.016,0 3.36,-1.344 3.36,-3.168 0,-1.92 -1.344,-3.264 -3.36,-3.264 h -10.848 v -14.88 c 0,-2.208 -1.536,-3.744 -3.744,-3.744 -2.112,0 -3.648,1.536 -3.648,3.744 v 14.88 h -6.24 c -2.016,0 -3.36,1.344 -3.36,3.264 0,1.824 1.344,3.168 3.36,3.168 h 6.24 v 25.632 c 0,10.656 7.392,18.432 17.664,18.528 z m 41.85604,0.48 c 5.952,0 13.248,-2.592 17.472,-6.24 1.536,-1.344 1.44,-3.36 -0.192,-4.8 -1.344,-1.056 -3.264,-0.96 -4.704,0.192 -2.784,2.4 -7.968,4.224 -12.576,4.224 -10.656,0 -18.528,-7.2 -19.584,-17.664 h 38.784 c 2.016,0 3.456,-1.344 3.456,-3.36 0,-15.072 -9.6,-25.824 -24.096,-25.824 -14.784,0 -25.152,11.136 -25.152,26.784 0,15.648 11.04,26.688 26.592,26.688 z m -1.44,-46.848 c 9.696,0 16.224,6.72 17.184,16.416 h -35.136 c 1.344,-9.696 8.16,-16.416 17.952,-16.416 z m 40.32003,46.368 c 2.208,0 3.648,-1.44 3.648,-3.648 v -28.416 c 0,-7.968 7.2,-14.016 16.704,-14.016 9.792,0 17.184,5.664 17.184,17.472 v 24.96 c 0,2.112 1.632,3.648 3.744,3.648 2.016,0 3.648,-1.536 3.648,-3.648 v -24.96 c 0,-15.84 -10.272,-24.48 -23.808,-24.48 -7.104,0 -13.344,2.88 -17.472,7.584 v -3.264 c 0,-2.208 -1.44,-3.744 -3.648,-3.744 -2.208,0 -3.744,1.536 -3.744,3.744 v 45.12 c 0,2.208 1.536,3.648 3.744,3.648 z m 84.384,0.48 c 8.448,0 15.648,-3.84 19.968,-9.984 v 5.856 c 0,2.112 1.536,3.648 3.744,3.648 2.112,0 3.744,-1.536 3.744,-3.648 v -22.56 c -0.096,-15.264 -11.52,-26.784 -26.688,-26.784 -15.264,0 -26.688,11.52 -26.688,26.784 0,15.264 11.136,26.688 25.92,26.688 z m 0.768,-6.72 c -11.04,0 -19.488,-8.544 -19.488,-19.968 0,-11.424 8.448,-20.064 19.488,-20.064 11.04,0 19.392,8.64 19.392,20.064 0,11.424 -8.352,19.968 -19.392,19.968 z m 44.73601,6.24 c 2.304,0 3.648,-1.344 3.648,-3.648 v -28.704 c 0,-8.928 7.872,-14.688 18.624,-13.728 3.168,0.192 5.76,0.768 6.432,-2.112 0.768,-3.168 -2.688,-4.896 -8.64,-4.896 -6.72,0 -12.672,3.168 -16.416,8.352 v -4.128 c 0,-2.304 -1.344,-3.648 -3.648,-3.648 -2.304,0 -3.648,1.344 -3.648,3.648 v 45.216 c 0,2.304 1.344,3.648 3.648,3.648 z m 49.34401,22.848 c 1.248,0 2.208,-0.768 2.784,-2.208 l 31.104,-68.352 c 0.96,-2.112 0.288,-3.648 -1.824,-4.512 -2.112,-0.864 -3.648,-0.288 -4.608,1.728 l -17.856,39.36 -18.72,-39.456 c -0.864,-1.92 -2.688,-2.496 -4.608,-1.632 -1.92,0.864 -2.688,2.688 -1.728,4.608 l 21.408,44.352 0.096,0.096 -9.6,21.024 c -0.96,2.112 -0.288,3.744 1.824,4.608 0.576,0.288 1.152,0.384 1.728,0.384 z"
id="text18"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
aria-label="Katenary" />
</g>
<path
style="fill:#388ec7;fill-opacity:1;stroke:none;stroke-width:1.41128"
d="m 558.98167,600.50526 -33.31908,17.65778 -13.63054,6.42952 -13.63053,-6.42952 -33.31907,-17.65778 c 0,5.32502 -2.24747,16.12101 0.77828,20.72433 1.84892,2.81293 6.84753,4.46784 9.82323,6.09432 8.26868,4.51957 16.72019,8.751 24.98932,13.27082 3.1083,1.69895 7.62988,5.16897 11.35877,5.21476 3.70778,0.0455 8.2451,-3.55102 11.35878,-5.19752 8.33909,-4.40967 16.72005,-8.76818 24.98932,-13.28806 3.05286,-1.66866 8.68533,-3.49778 10.14461,-6.69253 2.3911,-5.23485 0.45691,-14.46175 0.45691,-20.12612 m -46.19236,16.00215 c 3.59731,-0.51544 7.48607,-3.47354 10.60152,-5.12097 7.58911,-4.0131 15.20174,-8.00941 22.71755,-12.13797 3.53683,-1.94283 9.05374,-3.7013 11.65911,-6.69847 3.28982,-3.78453 1.93068,-13.45759 -2.58668,-15.74886 -4.7363,-2.40229 -8.80251,1.12086 -12.85868,3.25251 l -21.20306,11.2665 c -2.56736,1.35182 -6.01463,4.01777 -9.08702,4.01777 -3.29073,0 -7.12524,-2.97742 -9.84427,-4.47101 -6.50984,-3.57597 -13.17851,-6.90091 -19.68854,-10.47698 -2.85668,-1.56921 -7.10587,-4.85415 -10.60153,-4.52437 -7.2756,0.6864 -9.69308,11.97712 -5.60111,16.68444 2.43875,2.80547 7.56529,4.49972 10.90188,6.26408 7.83167,4.14137 15.6667,8.28474 23.4748,12.46456 3.21433,1.72068 8.18431,5.79216 12.11603,5.22877 m 30.29007,-45.33762 v -1.39693 l -18.17404,-8.90973 -12.11603,4.65427 -12.87329,-4.72164 -18.93129,8.9771 v 1.39693 l 21.20305,11.17542 9.84427,4.34169 9.84428,-4.33415 z"
id="path18" />
<g
id="g19">
<path
style="fill:#388ec7;fill-opacity:1;stroke:none;stroke-width:1.41128"
d="m 280.25003,221.48792 -33.31908,17.65778 -13.63054,6.42952 -13.63053,-6.42952 -33.31907,-17.65778 c 0,5.32502 -2.24747,16.12101 0.77828,20.72433 1.84892,2.81293 6.84753,4.46784 9.82323,6.09432 8.26868,4.51957 16.72019,8.751 24.98932,13.27082 3.1083,1.69895 7.62988,5.16897 11.35877,5.21476 3.70778,0.0455 8.2451,-3.55102 11.35878,-5.19752 8.33909,-4.40967 16.72005,-8.76818 24.98932,-13.28806 3.05286,-1.66866 8.68533,-3.49778 10.14461,-6.69253 2.3911,-5.23485 0.45691,-14.46175 0.45691,-20.12612 m -46.19236,16.00215 c 3.59731,-0.51544 7.48607,-3.47354 10.60152,-5.12097 7.58911,-4.0131 15.20174,-8.00941 22.71755,-12.13797 3.53683,-1.94283 9.05374,-3.7013 11.65911,-6.69847 3.28982,-3.78453 1.93068,-13.45759 -2.58668,-15.74886 -4.7363,-2.40229 -8.80251,1.12086 -12.85868,3.25251 l -21.20306,11.2665 c -2.56736,1.35182 -6.01463,4.01777 -9.08702,4.01777 -3.29073,0 -7.12524,-2.97742 -9.84427,-4.47101 -6.50984,-3.57597 -13.17851,-6.90091 -19.68854,-10.47698 -2.85668,-1.56921 -7.10587,-4.85415 -10.60153,-4.52437 -7.2756,0.6864 -9.69308,11.97712 -5.60111,16.68444 2.43875,2.80547 7.56529,4.49972 10.90188,6.26408 7.83167,4.14137 15.6667,8.28474 23.4748,12.46456 3.21433,1.72068 8.18431,5.79216 12.11603,5.22877 m 30.29007,-45.33762 v -1.39693 l -18.17404,-8.90973 -12.11603,4.65427 -12.87329,-4.72164 -18.93129,8.9771 v 1.39693 l 21.20305,11.17542 9.84427,4.34169 9.84428,-4.33415 z"
id="path19" />
<path
d="m 297.75361,250.3495 c 2.112,0 3.744,-1.632 3.744,-3.744 v -24.576 l 8.544,-8.64 26.112,35.328 c 0.768,1.152 1.824,1.632 3.168,1.632 2.688,0 4.512,-3.36 2.688,-5.952 l -26.592,-36.288 26.304,-26.304 c 2.304,-2.592 1.248,-6.432 -2.208,-6.432 -1.152,0 -2.016,0.384 -2.976,1.344 l -35.04,35.04 v -32.64 c 0,-2.112 -1.632,-3.744 -3.744,-3.744 -2.016,0 -3.744,1.632 -3.744,3.744 v 67.488 c 0,2.112 1.728,3.744 3.744,3.744 z m 75.55211,0.48 c 8.448,0 15.648,-3.84 19.968,-9.984 v 5.856 c 0,2.112 1.536,3.648 3.744,3.648 2.112,0 3.744,-1.536 3.744,-3.648 v -22.56 c -0.096,-15.264 -11.52,-26.784 -26.688,-26.784 -15.264,0 -26.688,11.52 -26.688,26.784 0,15.264 11.136,26.688 25.92,26.688 z m 0.768,-6.72 c -11.04,0 -19.488,-8.544 -19.488,-19.968 0,-11.424 8.448,-20.064 19.488,-20.064 11.04,0 19.392,8.64 19.392,20.064 0,11.424 -8.352,19.968 -19.392,19.968 z m 61.63201,6.24 h 2.016 c 2.304,0 4.032,-1.536 4.032,-3.648 0,-2.208 -1.44,-3.744 -3.456,-3.744 h -2.592 c -5.952,0 -10.272,-4.608 -10.272,-11.136 v -25.632 h 10.848 c 2.016,0 3.36,-1.344 3.36,-3.168 0,-1.92 -1.344,-3.264 -3.36,-3.264 h -10.848 v -14.88 c 0,-2.208 -1.536,-3.744 -3.744,-3.744 -2.112,0 -3.648,1.536 -3.648,3.744 v 14.88 h -6.24 c -2.016,0 -3.36,1.344 -3.36,3.264 0,1.824 1.344,3.168 3.36,3.168 h 6.24 v 25.632 c 0,10.656 7.392,18.432 17.664,18.528 z m 41.85604,0.48 c 5.952,0 13.248,-2.592 17.472,-6.24 1.536,-1.344 1.44,-3.36 -0.192,-4.8 -1.344,-1.056 -3.264,-0.96 -4.704,0.192 -2.784,2.4 -7.968,4.224 -12.576,4.224 -10.656,0 -18.528,-7.2 -19.584,-17.664 h 38.784 c 2.016,0 3.456,-1.344 3.456,-3.36 0,-15.072 -9.6,-25.824 -24.096,-25.824 -14.784,0 -25.152,11.136 -25.152,26.784 0,15.648 11.04,26.688 26.592,26.688 z m -1.44,-46.848 c 9.696,0 16.224,6.72 17.184,16.416 h -35.136 c 1.344,-9.696 8.16,-16.416 17.952,-16.416 z m 40.32003,46.368 c 2.208,0 3.648,-1.44 3.648,-3.648 v -28.416 c 0,-7.968 7.2,-14.016 16.704,-14.016 9.792,0 17.184,5.664 17.184,17.472 v 24.96 c 0,2.112 1.632,3.648 3.744,3.648 2.016,0 3.648,-1.536 3.648,-3.648 v -24.96 c 0,-15.84 -10.272,-24.48 -23.808,-24.48 -7.104,0 -13.344,2.88 -17.472,7.584 v -3.264 c 0,-2.208 -1.44,-3.744 -3.648,-3.744 -2.208,0 -3.744,1.536 -3.744,3.744 v 45.12 c 0,2.208 1.536,3.648 3.744,3.648 z m 84.384,0.48 c 8.448,0 15.648,-3.84 19.968,-9.984 v 5.856 c 0,2.112 1.536,3.648 3.744,3.648 2.112,0 3.744,-1.536 3.744,-3.648 v -22.56 c -0.096,-15.264 -11.52,-26.784 -26.688,-26.784 -15.264,0 -26.688,11.52 -26.688,26.784 0,15.264 11.136,26.688 25.92,26.688 z m 0.768,-6.72 c -11.04,0 -19.488,-8.544 -19.488,-19.968 0,-11.424 8.448,-20.064 19.488,-20.064 11.04,0 19.392,8.64 19.392,20.064 0,11.424 -8.352,19.968 -19.392,19.968 z m 44.73601,6.24 c 2.304,0 3.648,-1.344 3.648,-3.648 v -28.704 c 0,-8.928 7.872,-14.688 18.624,-13.728 3.168,0.192 5.76,0.768 6.432,-2.112 0.768,-3.168 -2.688,-4.896 -8.64,-4.896 -6.72,0 -12.672,3.168 -16.416,8.352 v -4.128 c 0,-2.304 -1.344,-3.648 -3.648,-3.648 -2.304,0 -3.648,1.344 -3.648,3.648 v 45.216 c 0,2.304 1.344,3.648 3.648,3.648 z m 49.34402,22.848 c 1.248,0 2.208,-0.768 2.784,-2.208 l 31.104,-68.352 c 0.96,-2.112 0.288,-3.648 -1.824,-4.512 -2.112,-0.864 -3.648,-0.288 -4.608,1.728 l -17.856,39.36 -18.72,-39.456 c -0.864,-1.92 -2.688,-2.496 -4.608,-1.632 -1.92,0.864 -2.688,2.688 -1.728,4.608 l 21.408,44.352 0.096,0.096 -9.6,21.024 c -0.96,2.112 -0.288,3.744 1.824,4.608 0.576,0.288 1.152,0.384 1.728,0.384 z"
id="text19"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
aria-label="Katenary" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg211948"
viewBox="0 0 489.26056 97.824219"
height="97.824219"
width="489.26056"
version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata211954">
<rdf:rdf>
<cc:work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:work>
</rdf:rdf>
</metadata>
<defs
id="defs211952" />
<linearGradient
spreadMethod="pad"
y2="0.30000001"
x2="-0.1"
y1="1.2"
x1="0.30000001"
id="3d_gradient2-logo-24885591-b378-4c55-b87b-b7d42ed10694">
<stop
id="stop211929"
stop-opacity="1"
stop-color="#ffffff"
offset="0%" />
<stop
id="stop211931"
stop-opacity="1"
stop-color="#000000"
offset="100%" />
</linearGradient>
<linearGradient
gradientTransform="rotate(-30)"
spreadMethod="pad"
y2="0.30000001"
x2="-0.1"
y1="1.2"
x1="0.30000001"
id="3d_gradient3-logo-24885591-b378-4c55-b87b-b7d42ed10694">
<stop
id="stop211934"
stop-opacity="1"
stop-color="#ffffff"
offset="0%" />
<stop
id="stop211936"
stop-opacity="1"
stop-color="#cccccc"
offset="50%" />
<stop
id="stop211938"
stop-opacity="1"
stop-color="#000000"
offset="100%" />
</linearGradient>
<g
id="logo-group"
transform="translate(-331.21094,-211.65039)">
<path
d="m 773.83594,234.02344 c -0.4545,0.0375 -0.92821,0.1629 -1.40821,0.3789 -1.91999,0.864 -2.68851,2.68743 -1.72851,4.60743 l 21.4082,44.35351 0.0957,0.0957 -9.5996,21.02344 c -0.96,2.112 -0.28778,3.74342 1.82421,4.60742 0.576,0.288 1.15252,0.38477 1.72852,0.38477 1.248,0 2.2072,-0.76703 2.7832,-2.20703 l 31.10352,-68.35352 c 0.96,-2.112 0.28778,-3.64772 -1.82422,-4.51172 -2.112,-0.864 -3.64742,-0.28748 -4.60742,1.72852 l -17.85547,39.35937 -18.7207,-39.45507 c -0.648,-1.44 -1.83572,-2.12422 -3.19922,-2.01172 z"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path9" />
<path
d="m 692.07617,233.63477 c -15.26398,0 -26.68945,11.51921 -26.68945,26.7832 0,15.26398 11.13594,26.68945 25.91992,26.68945 8.44799,0 15.64875,-3.84038 19.96875,-9.98437 v 5.85547 c 0,2.11199 1.53614,3.64843 3.74414,3.64843 2.112,0 3.74414,-1.53644 3.74414,-3.64843 v -22.56055 c -0.096,-15.26399 -11.51951,-26.7832 -26.6875,-26.7832 z m 0,6.7207 c 11.03999,0 19.39063,8.63851 19.39063,20.0625 0,11.42399 -8.35064,19.96875 -19.39063,19.96875 -11.03999,0 -19.48828,-8.54476 -19.48828,-19.96875 0,-11.42399 8.44829,-20.0625 19.48828,-20.0625 z"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path8" />
<path
d="m 566.60352,233.63477 c -14.78399,0 -25.15235,11.13521 -25.15235,26.7832 0,15.64798 11.03981,26.68945 26.5918,26.68945 5.95199,0 13.24866,-2.59224 17.47265,-6.24023 1.536,-1.344 1.4406,-3.36079 -0.1914,-4.80078 -1.344,-1.056 -3.26508,-0.9606 -4.70508,0.1914 -2.784,2.4 -7.96818,4.22461 -12.57617,4.22461 -10.65599,0 -18.52799,-7.20007 -19.58399,-17.66406 h 38.78516 c 2.016,0 3.45508,-1.34338 3.45508,-3.35938 0,-15.07198 -9.59972,-25.82421 -24.0957,-25.82421 z m 0,6.62304 c 9.69599,0 16.22359,6.72003 17.18359,16.41602 h -35.13477 c 1.344,-9.69599 8.15919,-16.41602 17.95118,-16.41602 z"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path7" />
<path
d="m 756.875,233.53906 c -6.71999,0 -12.67202,3.16757 -16.41602,8.35156 v -4.1289 c 0,-2.304 -1.34248,-3.64649 -3.64648,-3.64649 -2.304,0 -3.64844,1.34249 -3.64844,3.64649 v 45.2168 c 0,2.30399 1.34444,3.64843 3.64844,3.64843 2.304,0 3.64648,-1.34444 3.64648,-3.64843 v -28.70313 c 0,-8.92799 7.87302,-14.68851 18.625,-13.72852 3.168,0.192 5.75965,0.76867 6.43164,-2.11132 0.768,-3.168 -2.68863,-4.89649 -8.64062,-4.89649 z"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path6" />
<path
d="m 628.04297,233.53906 c -7.10399,0 -13.34271,2.87999 -17.4707,7.58399 v -3.26368 c 0,-2.20799 -1.44044,-3.74414 -3.64844,-3.74414 -2.208,0 -3.74414,1.53615 -3.74414,3.74414 v 45.11915 c 0,2.20799 1.53614,3.64843 3.74414,3.64843 2.208,0 3.64844,-1.44044 3.64844,-3.64843 V 254.5625 c 0,-7.96799 7.19913,-14.01563 16.70312,-14.01563 9.79199,0 17.18359,5.66467 17.18359,17.47266 v 24.95899 c 0,2.11199 1.63215,3.64843 3.74414,3.64843 2.016,0 3.64844,-1.53644 3.64844,-3.64843 v -24.95899 c 0,-15.83998 -10.2726,-24.48047 -23.80859,-24.48047 z"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path5" />
<path
d="m 512.17187,217.41016 c -2.11199,0 -3.64843,1.53614 -3.64843,3.74414 v 14.88086 h -6.24024 c -2.01599,0 -3.35937,1.34367 -3.35937,3.26367 0,1.824 1.34338,3.16797 3.35937,3.16797 h 6.24024 v 25.63281 c 0,10.65599 7.39207,18.43134 17.66406,18.52734 h 2.01562 c 2.304,0 4.03321,-1.53644 4.03321,-3.64843 0,-2.208 -1.44104,-3.74415 -3.45703,-3.74415 h -2.5918 c -5.95199,0 -10.27148,-4.60677 -10.27148,-11.13476 V 242.4668 h 10.84765 c 2.016,0 3.35938,-1.34397 3.35938,-3.16797 0,-1.92 -1.34338,-3.26367 -3.35938,-3.26367 H 515.91602 V 221.1543 c 0,-2.208 -1.53615,-3.74414 -3.74415,-3.74414 z"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path4" />
<image
xlink:href=""
id="container"
x="272"
y="144"
width="480"
height="480"
style="display:none" />
<image
xlink:href=""
id="icon_container"
style="display:none"
x="0"
y="0"
width="0"
height="0" />
<path
style="fill:#388ec7;fill-opacity:1;stroke:none;stroke-width:1.41128"
d="m 488.71354,251.36014 -33.31908,17.65778 -13.63054,6.42952 -13.63053,-6.42952 -33.31907,-17.65778 c 0,5.32502 -2.24747,16.12101 0.77828,20.72433 1.84892,2.81293 6.84753,4.46784 9.82323,6.09432 8.26868,4.51957 16.72019,8.751 24.98932,13.27082 3.1083,1.69895 7.62988,5.16897 11.35877,5.21476 3.70778,0.0455 8.2451,-3.55102 11.35878,-5.19752 8.33909,-4.40967 16.72005,-8.76818 24.98932,-13.28806 3.05286,-1.66866 8.68533,-3.49778 10.14461,-6.69253 2.3911,-5.23485 0.45691,-14.46175 0.45691,-20.12612 m -46.19236,16.00215 c 3.59731,-0.51544 7.48607,-3.47354 10.60152,-5.12097 7.58911,-4.0131 15.20174,-8.00941 22.71755,-12.13797 3.53683,-1.94283 9.05374,-3.7013 11.65911,-6.69847 3.28982,-3.78453 1.93068,-13.45759 -2.58668,-15.74886 -4.7363,-2.40229 -8.80251,1.12086 -12.85868,3.25251 l -21.20306,11.2665 c -2.56736,1.35182 -6.01463,4.01777 -9.08702,4.01777 -3.29073,0 -7.12524,-2.97742 -9.84427,-4.47101 -6.50984,-3.57597 -13.17851,-6.90091 -19.68854,-10.47698 -2.85668,-1.56921 -7.10587,-4.85415 -10.60153,-4.52437 -7.2756,0.6864 -9.69308,11.97712 -5.60111,16.68444 2.43875,2.80547 7.56529,4.49972 10.90188,6.26408 7.83167,4.14137 15.6667,8.28474 23.4748,12.46456 3.21433,1.72068 8.18431,5.79216 12.11603,5.22877 m 30.29007,-45.33762 v -1.39693 l -18.17404,-8.90973 -12.11603,4.65427 -12.87329,-4.72164 -18.93129,8.9771 v 1.39693 l 21.20305,11.17542 9.84427,4.34169 9.84428,-4.33415 z"
id="path1" />
<path
d="m 334.95508,211.65039 c -2.016,0 -3.74414,1.63214 -3.74414,3.74414 v 67.48828 c 0,2.112 1.72814,3.74414 3.74414,3.74414 2.112,0 3.74414,-1.63214 3.74414,-3.74414 v -24.57617 l 8.54492,-8.64062 26.11133,35.32812 c 0.768,1.152 1.82397,1.63281 3.16797,1.63281 2.68799,0 4.5115,-3.36112 2.6875,-5.95312 l -26.5918,-36.28711 26.30469,-26.30469 c 2.304,-2.592 1.24701,-6.43164 -2.20899,-6.43164 -1.152,0 -2.01461,0.38375 -2.97461,1.34375 l -35.04101,35.04102 v -32.64063 c 0,-2.112 -1.63214,-3.74414 -3.74414,-3.74414 z"
style="fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
id="path3" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg211948"
viewBox="0 0 435.98074 184.9017"
height="184.9017"
width="435.98074"
version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata211954">
<rdf:rdf>
<cc:work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:work>
</rdf:rdf>
</metadata>
<defs
id="defs211952" />
<linearGradient
spreadMethod="pad"
y2="0.30000001"
x2="-0.1"
y1="1.2"
x1="0.30000001"
id="3d_gradient2-logo-24885591-b378-4c55-b87b-b7d42ed10694">
<stop
id="stop211929"
stop-opacity="1"
stop-color="#ffffff"
offset="0%" />
<stop
id="stop211931"
stop-opacity="1"
stop-color="#000000"
offset="100%" />
</linearGradient>
<linearGradient
gradientTransform="rotate(-30)"
spreadMethod="pad"
y2="0.30000001"
x2="-0.1"
y1="1.2"
x1="0.30000001"
id="3d_gradient3-logo-24885591-b378-4c55-b87b-b7d42ed10694">
<stop
id="stop211934"
stop-opacity="1"
stop-color="#ffffff"
offset="0%" />
<stop
id="stop211936"
stop-opacity="1"
stop-color="#cccccc"
offset="50%" />
<stop
id="stop211938"
stop-opacity="1"
stop-color="#000000"
offset="100%" />
</linearGradient>
<g
id="logo-group"
transform="translate(-294.00961,-314.5575)">
<image
xlink:href=""
id="container"
x="272"
y="144"
width="480"
height="480"
style="display:none" />
<image
xlink:href=""
id="icon_container"
style="display:none"
x="0"
y="0"
width="0"
height="0" />
<g
id="g18"
transform="translate(-91.179677,-177.97015)">
<path
style="fill:#388ec7;fill-opacity:1;stroke:none;stroke-width:1.41128"
d="m 650.16134,532.23713 -33.31908,17.65778 -13.63054,6.42952 -13.63053,-6.42952 -33.31907,-17.65778 c 0,5.32502 -2.24747,16.12101 0.77828,20.72433 1.84892,2.81293 6.84753,4.46784 9.82323,6.09432 8.26868,4.51957 16.72019,8.751 24.98932,13.27082 3.1083,1.69895 7.62988,5.16897 11.35877,5.21476 3.70778,0.0455 8.2451,-3.55102 11.35878,-5.19752 8.33909,-4.40967 16.72005,-8.76818 24.98932,-13.28806 3.05286,-1.66866 8.68533,-3.49778 10.14461,-6.69253 2.3911,-5.23485 0.45691,-14.46175 0.45691,-20.12612 m -46.19236,16.00215 c 3.59731,-0.51544 7.48607,-3.47354 10.60152,-5.12097 7.58911,-4.0131 15.20174,-8.00941 22.71755,-12.13797 3.53683,-1.94283 9.05374,-3.7013 11.65911,-6.69847 3.28982,-3.78453 1.93068,-13.45759 -2.58668,-15.74886 -4.7363,-2.40229 -8.80251,1.12086 -12.85868,3.25251 l -21.20306,11.2665 c -2.56736,1.35182 -6.01463,4.01777 -9.08702,4.01777 -3.29073,0 -7.12524,-2.97742 -9.84427,-4.47101 -6.50984,-3.57597 -13.17851,-6.90091 -19.68854,-10.47698 -2.85668,-1.56921 -7.10587,-4.85415 -10.60153,-4.52437 -7.2756,0.6864 -9.69308,11.97712 -5.60111,16.68444 2.43875,2.80547 7.56529,4.49972 10.90188,6.26408 7.83167,4.14137 15.6667,8.28474 23.4748,12.46456 3.21433,1.72068 8.18431,5.79216 12.11603,5.22877 m 30.29007,-45.33762 v -1.39693 l -18.17404,-8.90973 -12.11603,4.65427 -12.87329,-4.72164 -18.93129,8.9771 v 1.39693 l 21.20305,11.17542 9.84427,4.34169 9.84428,-4.33415 z"
id="path17" />
<path
d="m 388.93329,654.58136 c 2.112,0 3.744,-1.632 3.744,-3.744 v -24.576 l 8.544,-8.64 26.112,35.328 c 0.768,1.152 1.824,1.632 3.168,1.632 2.688,0 4.512,-3.36 2.688,-5.952 l -26.592,-36.288 26.304,-26.304 c 2.304,-2.592 1.248,-6.432 -2.208,-6.432 -1.152,0 -2.016,0.384 -2.976,1.344 l -35.04,35.04 v -32.64 c 0,-2.112 -1.632,-3.744 -3.744,-3.744 -2.016,0 -3.744,1.632 -3.744,3.744 v 67.488 c 0,2.112 1.728,3.744 3.744,3.744 z m 75.55212,0.48 c 8.448,0 15.648,-3.84 19.968,-9.984 v 5.856 c 0,2.112 1.536,3.648 3.744,3.648 2.112,0 3.744,-1.536 3.744,-3.648 v -22.56 c -0.096,-15.264 -11.52,-26.784 -26.688,-26.784 -15.264,0 -26.688,11.52 -26.688,26.784 0,15.264 11.136,26.688 25.92,26.688 z m 0.768,-6.72 c -11.04,0 -19.488,-8.544 -19.488,-19.968 0,-11.424 8.448,-20.064 19.488,-20.064 11.04,0 19.392,8.64 19.392,20.064 0,11.424 -8.352,19.968 -19.392,19.968 z m 61.63201,6.24 h 2.016 c 2.304,0 4.032,-1.536 4.032,-3.648 0,-2.208 -1.44,-3.744 -3.456,-3.744 h -2.592 c -5.952,0 -10.272,-4.608 -10.272,-11.136 v -25.632 h 10.848 c 2.016,0 3.36,-1.344 3.36,-3.168 0,-1.92 -1.344,-3.264 -3.36,-3.264 h -10.848 v -14.88 c 0,-2.208 -1.536,-3.744 -3.744,-3.744 -2.112,0 -3.648,1.536 -3.648,3.744 v 14.88 h -6.24 c -2.016,0 -3.36,1.344 -3.36,3.264 0,1.824 1.344,3.168 3.36,3.168 h 6.24 v 25.632 c 0,10.656 7.392,18.432 17.664,18.528 z m 41.85604,0.48 c 5.952,0 13.248,-2.592 17.472,-6.24 1.536,-1.344 1.44,-3.36 -0.192,-4.8 -1.344,-1.056 -3.264,-0.96 -4.704,0.192 -2.784,2.4 -7.968,4.224 -12.576,4.224 -10.656,0 -18.528,-7.2 -19.584,-17.664 h 38.784 c 2.016,0 3.456,-1.344 3.456,-3.36 0,-15.072 -9.6,-25.824 -24.096,-25.824 -14.784,0 -25.152,11.136 -25.152,26.784 0,15.648 11.04,26.688 26.592,26.688 z m -1.44,-46.848 c 9.696,0 16.224,6.72 17.184,16.416 h -35.136 c 1.344,-9.696 8.16,-16.416 17.952,-16.416 z m 40.32003,46.368 c 2.208,0 3.648,-1.44 3.648,-3.648 v -28.416 c 0,-7.968 7.2,-14.016 16.704,-14.016 9.792,0 17.184,5.664 17.184,17.472 v 24.96 c 0,2.112 1.632,3.648 3.744,3.648 2.016,0 3.648,-1.536 3.648,-3.648 v -24.96 c 0,-15.84 -10.272,-24.48 -23.808,-24.48 -7.104,0 -13.344,2.88 -17.472,7.584 v -3.264 c 0,-2.208 -1.44,-3.744 -3.648,-3.744 -2.208,0 -3.744,1.536 -3.744,3.744 v 45.12 c 0,2.208 1.536,3.648 3.744,3.648 z m 84.384,0.48 c 8.448,0 15.648,-3.84 19.968,-9.984 v 5.856 c 0,2.112 1.536,3.648 3.744,3.648 2.112,0 3.744,-1.536 3.744,-3.648 v -22.56 c -0.096,-15.264 -11.52,-26.784 -26.688,-26.784 -15.264,0 -26.688,11.52 -26.688,26.784 0,15.264 11.136,26.688 25.92,26.688 z m 0.768,-6.72 c -11.04,0 -19.488,-8.544 -19.488,-19.968 0,-11.424 8.448,-20.064 19.488,-20.064 11.04,0 19.392,8.64 19.392,20.064 0,11.424 -8.352,19.968 -19.392,19.968 z m 44.73601,6.24 c 2.304,0 3.648,-1.344 3.648,-3.648 v -28.704 c 0,-8.928 7.872,-14.688 18.624,-13.728 3.168,0.192 5.76,0.768 6.432,-2.112 0.768,-3.168 -2.688,-4.896 -8.64,-4.896 -6.72,0 -12.672,3.168 -16.416,8.352 v -4.128 c 0,-2.304 -1.344,-3.648 -3.648,-3.648 -2.304,0 -3.648,1.344 -3.648,3.648 v 45.216 c 0,2.304 1.344,3.648 3.648,3.648 z m 49.34401,22.848 c 1.248,0 2.208,-0.768 2.784,-2.208 l 31.104,-68.352 c 0.96,-2.112 0.288,-3.648 -1.824,-4.512 -2.112,-0.864 -3.648,-0.288 -4.608,1.728 l -17.856,39.36 -18.72,-39.456 c -0.864,-1.92 -2.688,-2.496 -4.608,-1.632 -1.92,0.864 -2.688,2.688 -1.728,4.608 l 21.408,44.352 0.096,0.096 -9.6,21.024 c -0.96,2.112 -0.288,3.744 1.824,4.608 0.576,0.288 1.152,0.384 1.728,0.384 z"
id="text18"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
aria-label="Katenary" />
</g>
<path
style="fill:#388ec7;fill-opacity:1;stroke:none;stroke-width:1.41128"
d="m 558.98167,600.50526 -33.31908,17.65778 -13.63054,6.42952 -13.63053,-6.42952 -33.31907,-17.65778 c 0,5.32502 -2.24747,16.12101 0.77828,20.72433 1.84892,2.81293 6.84753,4.46784 9.82323,6.09432 8.26868,4.51957 16.72019,8.751 24.98932,13.27082 3.1083,1.69895 7.62988,5.16897 11.35877,5.21476 3.70778,0.0455 8.2451,-3.55102 11.35878,-5.19752 8.33909,-4.40967 16.72005,-8.76818 24.98932,-13.28806 3.05286,-1.66866 8.68533,-3.49778 10.14461,-6.69253 2.3911,-5.23485 0.45691,-14.46175 0.45691,-20.12612 m -46.19236,16.00215 c 3.59731,-0.51544 7.48607,-3.47354 10.60152,-5.12097 7.58911,-4.0131 15.20174,-8.00941 22.71755,-12.13797 3.53683,-1.94283 9.05374,-3.7013 11.65911,-6.69847 3.28982,-3.78453 1.93068,-13.45759 -2.58668,-15.74886 -4.7363,-2.40229 -8.80251,1.12086 -12.85868,3.25251 l -21.20306,11.2665 c -2.56736,1.35182 -6.01463,4.01777 -9.08702,4.01777 -3.29073,0 -7.12524,-2.97742 -9.84427,-4.47101 -6.50984,-3.57597 -13.17851,-6.90091 -19.68854,-10.47698 -2.85668,-1.56921 -7.10587,-4.85415 -10.60153,-4.52437 -7.2756,0.6864 -9.69308,11.97712 -5.60111,16.68444 2.43875,2.80547 7.56529,4.49972 10.90188,6.26408 7.83167,4.14137 15.6667,8.28474 23.4748,12.46456 3.21433,1.72068 8.18431,5.79216 12.11603,5.22877 m 30.29007,-45.33762 v -1.39693 l -18.17404,-8.90973 -12.11603,4.65427 -12.87329,-4.72164 -18.93129,8.9771 v 1.39693 l 21.20305,11.17542 9.84427,4.34169 9.84428,-4.33415 z"
id="path18" />
<g
id="g19">
<path
style="fill:#388ec7;fill-opacity:1;stroke:none;stroke-width:1.41128"
d="m 280.25003,221.48792 -33.31908,17.65778 -13.63054,6.42952 -13.63053,-6.42952 -33.31907,-17.65778 c 0,5.32502 -2.24747,16.12101 0.77828,20.72433 1.84892,2.81293 6.84753,4.46784 9.82323,6.09432 8.26868,4.51957 16.72019,8.751 24.98932,13.27082 3.1083,1.69895 7.62988,5.16897 11.35877,5.21476 3.70778,0.0455 8.2451,-3.55102 11.35878,-5.19752 8.33909,-4.40967 16.72005,-8.76818 24.98932,-13.28806 3.05286,-1.66866 8.68533,-3.49778 10.14461,-6.69253 2.3911,-5.23485 0.45691,-14.46175 0.45691,-20.12612 m -46.19236,16.00215 c 3.59731,-0.51544 7.48607,-3.47354 10.60152,-5.12097 7.58911,-4.0131 15.20174,-8.00941 22.71755,-12.13797 3.53683,-1.94283 9.05374,-3.7013 11.65911,-6.69847 3.28982,-3.78453 1.93068,-13.45759 -2.58668,-15.74886 -4.7363,-2.40229 -8.80251,1.12086 -12.85868,3.25251 l -21.20306,11.2665 c -2.56736,1.35182 -6.01463,4.01777 -9.08702,4.01777 -3.29073,0 -7.12524,-2.97742 -9.84427,-4.47101 -6.50984,-3.57597 -13.17851,-6.90091 -19.68854,-10.47698 -2.85668,-1.56921 -7.10587,-4.85415 -10.60153,-4.52437 -7.2756,0.6864 -9.69308,11.97712 -5.60111,16.68444 2.43875,2.80547 7.56529,4.49972 10.90188,6.26408 7.83167,4.14137 15.6667,8.28474 23.4748,12.46456 3.21433,1.72068 8.18431,5.79216 12.11603,5.22877 m 30.29007,-45.33762 v -1.39693 l -18.17404,-8.90973 -12.11603,4.65427 -12.87329,-4.72164 -18.93129,8.9771 v 1.39693 l 21.20305,11.17542 9.84427,4.34169 9.84428,-4.33415 z"
id="path19" />
<path
d="m 297.75361,250.3495 c 2.112,0 3.744,-1.632 3.744,-3.744 v -24.576 l 8.544,-8.64 26.112,35.328 c 0.768,1.152 1.824,1.632 3.168,1.632 2.688,0 4.512,-3.36 2.688,-5.952 l -26.592,-36.288 26.304,-26.304 c 2.304,-2.592 1.248,-6.432 -2.208,-6.432 -1.152,0 -2.016,0.384 -2.976,1.344 l -35.04,35.04 v -32.64 c 0,-2.112 -1.632,-3.744 -3.744,-3.744 -2.016,0 -3.744,1.632 -3.744,3.744 v 67.488 c 0,2.112 1.728,3.744 3.744,3.744 z m 75.55211,0.48 c 8.448,0 15.648,-3.84 19.968,-9.984 v 5.856 c 0,2.112 1.536,3.648 3.744,3.648 2.112,0 3.744,-1.536 3.744,-3.648 v -22.56 c -0.096,-15.264 -11.52,-26.784 -26.688,-26.784 -15.264,0 -26.688,11.52 -26.688,26.784 0,15.264 11.136,26.688 25.92,26.688 z m 0.768,-6.72 c -11.04,0 -19.488,-8.544 -19.488,-19.968 0,-11.424 8.448,-20.064 19.488,-20.064 11.04,0 19.392,8.64 19.392,20.064 0,11.424 -8.352,19.968 -19.392,19.968 z m 61.63201,6.24 h 2.016 c 2.304,0 4.032,-1.536 4.032,-3.648 0,-2.208 -1.44,-3.744 -3.456,-3.744 h -2.592 c -5.952,0 -10.272,-4.608 -10.272,-11.136 v -25.632 h 10.848 c 2.016,0 3.36,-1.344 3.36,-3.168 0,-1.92 -1.344,-3.264 -3.36,-3.264 h -10.848 v -14.88 c 0,-2.208 -1.536,-3.744 -3.744,-3.744 -2.112,0 -3.648,1.536 -3.648,3.744 v 14.88 h -6.24 c -2.016,0 -3.36,1.344 -3.36,3.264 0,1.824 1.344,3.168 3.36,3.168 h 6.24 v 25.632 c 0,10.656 7.392,18.432 17.664,18.528 z m 41.85604,0.48 c 5.952,0 13.248,-2.592 17.472,-6.24 1.536,-1.344 1.44,-3.36 -0.192,-4.8 -1.344,-1.056 -3.264,-0.96 -4.704,0.192 -2.784,2.4 -7.968,4.224 -12.576,4.224 -10.656,0 -18.528,-7.2 -19.584,-17.664 h 38.784 c 2.016,0 3.456,-1.344 3.456,-3.36 0,-15.072 -9.6,-25.824 -24.096,-25.824 -14.784,0 -25.152,11.136 -25.152,26.784 0,15.648 11.04,26.688 26.592,26.688 z m -1.44,-46.848 c 9.696,0 16.224,6.72 17.184,16.416 h -35.136 c 1.344,-9.696 8.16,-16.416 17.952,-16.416 z m 40.32003,46.368 c 2.208,0 3.648,-1.44 3.648,-3.648 v -28.416 c 0,-7.968 7.2,-14.016 16.704,-14.016 9.792,0 17.184,5.664 17.184,17.472 v 24.96 c 0,2.112 1.632,3.648 3.744,3.648 2.016,0 3.648,-1.536 3.648,-3.648 v -24.96 c 0,-15.84 -10.272,-24.48 -23.808,-24.48 -7.104,0 -13.344,2.88 -17.472,7.584 v -3.264 c 0,-2.208 -1.44,-3.744 -3.648,-3.744 -2.208,0 -3.744,1.536 -3.744,3.744 v 45.12 c 0,2.208 1.536,3.648 3.744,3.648 z m 84.384,0.48 c 8.448,0 15.648,-3.84 19.968,-9.984 v 5.856 c 0,2.112 1.536,3.648 3.744,3.648 2.112,0 3.744,-1.536 3.744,-3.648 v -22.56 c -0.096,-15.264 -11.52,-26.784 -26.688,-26.784 -15.264,0 -26.688,11.52 -26.688,26.784 0,15.264 11.136,26.688 25.92,26.688 z m 0.768,-6.72 c -11.04,0 -19.488,-8.544 -19.488,-19.968 0,-11.424 8.448,-20.064 19.488,-20.064 11.04,0 19.392,8.64 19.392,20.064 0,11.424 -8.352,19.968 -19.392,19.968 z m 44.73601,6.24 c 2.304,0 3.648,-1.344 3.648,-3.648 v -28.704 c 0,-8.928 7.872,-14.688 18.624,-13.728 3.168,0.192 5.76,0.768 6.432,-2.112 0.768,-3.168 -2.688,-4.896 -8.64,-4.896 -6.72,0 -12.672,3.168 -16.416,8.352 v -4.128 c 0,-2.304 -1.344,-3.648 -3.648,-3.648 -2.304,0 -3.648,1.344 -3.648,3.648 v 45.216 c 0,2.304 1.344,3.648 3.648,3.648 z m 49.34402,22.848 c 1.248,0 2.208,-0.768 2.784,-2.208 l 31.104,-68.352 c 0.96,-2.112 0.288,-3.648 -1.824,-4.512 -2.112,-0.864 -3.648,-0.288 -4.608,1.728 l -17.856,39.36 -18.72,-39.456 c -0.864,-1.92 -2.688,-2.496 -4.608,-1.632 -1.92,0.864 -2.688,2.688 -1.728,4.608 l 21.408,44.352 0.096,0.096 -9.6,21.024 c -0.96,2.112 -0.288,3.744 1.824,4.608 0.576,0.288 1.152,0.384 1.728,0.384 z"
id="text19"
style="font-size:96px;line-height:0;font-family:Comfortaa;-inkscape-font-specification:Comfortaa;fill:#ff7f2a;stroke-width:51.0236;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0"
aria-label="Katenary" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

145
doc/docs/statics/logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

117
doc/docs/statics/main.css Normal file
View File

@@ -0,0 +1,117 @@
:root {
--code-bg-color: var(--lt-color-gray-800);
--code-fg-color: var(--lt-color-gray-300);
--md-primary-fg-color: var(--lt-color-gray-900);
}
[data-md-color-scheme="default"] {
--md-primary-fg-color: var(--md-code-fg-color);
}
div.smile-logo {
display: flex;
font-size: 0.7rem;
}
div.smile-logo img {
width: 100px;
}
button.md-clipboard::after {
transition: all 0.5s ease;
color: var(--lt-color-gray-500);
}
button.md-clipboard:hover::after {
color: var(--md-primary-bg-color);
}
article a,
article a:visited {
color: var(--md-code-hl-number-color) !important;
}
.md-center {
text-align: center;
margin: auto;
}
.md-center img {
max-width: 200px;
}
.md-nav__item .md-nav__link--active {
color: var(--md-code-hl-string-color) !important;
opacity: 0.7;
}
.go-logo {
font-size: 4em;
}
/* HLJS */
pre code.hljs {
background-color: var(--code-bg-color);
color: var(--code-fg-color);
}
table tbody code {
text-align: left;
white-space: nowrap;
font-size: 1em !important;
background-color: transparent !important;
color: var(--md-code-hl-special-color) !important;
}
h3[id*="katenaryio"] {
color: var(--md-code-hl-special-color);
}
#logo {
background-image: url("logo-vertical.svg");
background-repeat: no-repeat;
background-position: center;
background-size: contain;
height: 8em;
width: 100%;
margin: 0 auto 2rem auto;
}
/*Zoomable images*/
.zoomable svg {
background-color: var(--md-default-bg-color);
padding: 1rem;
}
[data-md-color-scheme="slate"] .zoomable svg {
background-color: var(--md-default-bg-color);
}
[data-md-color-scheme="slate"] .zoomable svg .colorize {
fill: var(--md-typeset-color) !important;
}
.zoomable input[type="checkbox"] {
display: none;
}
@media all and (min-width: 1399px) {
.zoomable label > * {
cursor: zoom-in;
transition: all 0.2s ease-in-out;
}
.zoomable input[type="checkbox"]:checked ~ label > * {
transform: scale(2);
cursor: zoom-out;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.3);
z-index: 1;
}
}
#klee {
filter: drop-shadow(0 0 16px var(--md-default-fg-color));
text-align: center;
padding: 1rem;
}
#klee svg {
zoom: 2;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 310 KiB

262
doc/docs/usage.md Normal file
View File

@@ -0,0 +1,262 @@
# Basic Usage
Basically, you can use `katenary` to transpose a docker-compose file (or any compose file compatible with
`podman-compose` and `docker-compose`) to a configurable Helm Chart. This resulting helm chart can be installed with
`helm` command to your Kubernetes cluster.
For very basic compose files, without any specific configuration, Katenary will create a working helm chart using the
simple command line:
```bash
katenary convert
```
This will create a `chart` directory with the helm chart inside.
But, in general, you will need to add a few configurations to help Katenary to transpose the compose file to a working
helm chart.
There are two ways to configure Katenary:
- Using the compose files, adding labels to the services
- Using a specific file named `katenary.yaml`
The Katenary file `katenary.yaml` has benefits over the labels in the compose file:
- you can validate the configuration with a schema, and use completion in your editor
- you separate the configuration and leave the compose file "intact"
- the syntax is a bit simpler, instead of using `katenary.v3/xxx: |-` you can use `xxx: ...`
But: **this implies that you have to maintain two files if the compose file changes.**
For example. With "labels", you should do:
```yaml
# in compose file
services:
webapp:
image: php:7-apache
ports:
- 8080:80
environment:
DB_HOST: database
labels:
katenary.v3/ingress: |-
hostname: myapp.example.com
port: 8080
katenary.v3/map-env: |-
DB_HOST: "{{ .Release.Name }}-database"
```
Using a Katenary file, you can do:
```yaml
# in compose file, no need to add labels
services:
webapp:
image: php:7-apache
ports:
- 8080:80
environment:
DB_HOST: database
# in katenary.yaml
webapp:
ingress:
hostname: myapp.example.com
port: 8080
map-env:
DB_HOST: "{{ .Release.Name }}-database"
```
!!! Warning "YAML in multiline label"
Compose only accept text label. So, to put a complete YAML content in the target label,
you need to use a pipe char (`|` or `|-`) and to **indent** your content.
For example :
```yaml
labels:
# your labels
foo: bar
# katenary labels with multiline
katenary.v3/ingress: |-
hostname: my.website.tld
port: 80
katenary.v3/ports: |-
- 1234
```
Katenary transforms compose services this way:
- Takes the service and create a "Deployment" file
- if a port is declared, Katenary creates a service (`ClusterIP`)
- if a port is exposed, Katenary creates a service (`NodePort`)
- environment variables will be stored inside a `ConfigMap`
- image, tags, and ingresses configuration are also stored in `values.yaml` file
- if named volumes are declared, Katenary create `PersistentVolumeClaims` - not enabled in values file
- `depends_on` needs that the pointed service declared a port. If not, you can use labels to inform Katenary
For any other specific configuration, like binding local files as `ConfigMap`, bind variables, add values with
documentation, etc. You'll need to use labels.
Katenary can also configure containers grouping in pods, declare dependencies, ignore some services, force variables as
secrets, mount files as `configMap`, and many others things. To adapt the helm chart generation, you will need to use
some specific labels.
For more complete label usage, see [the labels page](labels.md).
!!! Info "Overriding file"
It could be sometimes more convinient to separate the
configuration related to Katenary inside a secondary file.
Instead of adding labels inside the `compose.yaml` file,
you can create a file named `compose.katenary.yaml` and
declare your labels inside. Katenary will detect it by
default.
**No need to precise the file in the command line.**
## Make conversion
After having installed `katenary`, the standard usage is to call:
katenary convert
It will search standard compose files in the current directory and try to create a helm chart in "chart" directory.
!!! Info
Katenary uses the compose-go library which respects the Docker and Docker-Compose specification. Keep in mind that
it will find files exactly the same way as `docker-compose` and `podman-compose` do it.
Of course, you can provide others files than the default with (cumulative) `-c` options:
katenary convert -c file1.yaml -c file2.yaml
## Some common labels to use
Katenary proposes a lot of labels to configure the helm chart generation, but some are very important.
!!! Info
For more complete label usage, see [the labels page](labels.md).
### Work with Depends On?
Kubernetes does not provide service or pod starting detection from others pods. But Katenary will create `initContainer`
to make you able to wait for a service to respond. But you'll probably need to adapt a bit the compose file.
See this compose file:
```yaml
version: "3"
services:
webapp:
image: php:8-apache
depends_on:
- database
database:
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: foobar
```
In this case, `webapp` needs to know the `database` port because the `depends_on` points on it and Kubernetes has not
(yet) solution to check the database startup. Katenary wants to create a `initContainer` to hit on the related service.
So, instead of exposing the port in the compose definition, let's declare this to Katenary with labels:
```yaml
version: "3"
services:
webapp:
image: php:8-apache
depends_on:
- database
database:
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: foobar
labels:
katenary.v3/ports: |-
- 3306
```
### Declare ingresses
It's very common to have an Ingress resource on web application to deploy on Kubernetes. It allows exposing the
service to the outside of the cluster (you need to install an ingress controller).
Katenary can create this resource for you. You just need to declare the hostname and the port to bind.
```yaml
services:
webapp:
image: ...
ports: 8080:5050
labels:
katenary.v3/ingress: |-
# the target port is 5050 wich is the "service" port
port: 5050
hostname: myapp.example.com
```
Note that the port to bind is the one used by the container, not the used locally. This is because Katenary create a
service to bind the container itself.
### Map environment to helm values
A lot of framework needs to receive service host or IP in an environment variable to configure the connection. For
example, to connect a PHP application to a database.
With a compose file, there is no problem as Docker/Podman allows resolving the name by container name:
```yaml
services:
webapp:
image: php:7-apache
environment:
DB_HOST: database
database:
image: mariadb
```
Katenary prefixes the services with `{{ .Release.Name }}` (to make it possible to install the application several times
in a namespace), so you need to "remap" the environment variable to the right one.
```yaml
services:
webapp:
image: php:7-apache
environment:
DB_HOST: database
labels:
katenary.v3/mapenv: |-
DB_HOST: "{{ .Release.Name }}-database"
database:
image: mariadb
```
This label can be used to map others environment for any others reason. E.g. to change an informational environment
variable.
```yaml
services:
webapp:
#...
environment:
RUNNING: docker
labels:
katenary.v3/mapenv: |-
RUNNING: kubernetes
```
In the above example, `RUNNING` will be set to `kubernetes` when you'll deploy the application with helm, and it's
`docker` for "Podman" and "Docker" executions.

48
doc/fix.py Normal file
View File

@@ -0,0 +1,48 @@
""" Fix the markdown files to replace code blocs by lists when the code blocs are lists."""
import re
import sys
from typing import Tuple
# get markdown bloc code
re_code = re.compile(r"```(.*?)```", re.DOTALL)
def fix(text: str) -> Tuple[str, bool]:
"""Fix the markdown text to replace code blocs by lists when the code blocs are lists."""
# in the text, get the code blocs
code_blocs = re_code.findall(text)
# for each code bloc, if lines begin by a "-", this is a list. So,
# make it a mkdocs list and remove the block code
fixed = False
for code in code_blocs:
lines = code.split("\n")
lines = [line.strip() for line in lines if line.strip()]
if all(line.startswith("-") for line in lines):
fixed = True
# make a mkdocs list
lines = [f"- {line[1:]}" for line in lines]
# replace the code bloc by the list
text = text.replace(f"```{code}```", "\n".join(lines))
return text, fixed
def main(filename: str):
"""Fix and rewrite the markdown file."""
with open(filename, "r", encoding="utf-8") as f:
text = f.read()
content, fixed = fix(text)
if not fixed:
return
with open(sys.argv[1], "w", encoding="utf-8") as f:
f.write(content)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python fix.py <file>")
sys.exit(1)
main(sys.argv[1])

74
doc/mkdocs.yml Normal file
View File

@@ -0,0 +1,74 @@
site_name: Katenary documentation
docs_dir: ./docs
plugins:
- search
- inline-svg
- manpage:
enabled: !ENV [MANPAGE, false]
preprocess: preprocess.py
pages:
- title: Katenary
header: Katenary helm chart generator
output: share/man/man1/katenary.1
inputs:
- usage.md
- labels.md
theme:
name: material
custom_dir: overrides
logo: statics/logo-bright.svg
favicon: statics/icon.svg
palette:
- scheme: slate
toggle:
icon: material/brightness-4
name: Switch to light mode
- scheme: default
toggle:
icon: material/brightness-7
name: Switch to dark mode
markdown_extensions:
- admonition
- footnotes
- attr_list
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
- pymdownx.highlight:
anchor_linenums: true
use_pygments: false
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
extra_css:
- statics/main.css
extra_javascript:
- statics/addons.js
copyright: Copyright &copy; 2021 - 2024 - Katenary authors
extra:
generator: false
social:
- icon: fontawesome/brands/github
link: https://github.com/katenary/katenary
nav:
- "Home": index.md
- usage.md
- labels.md
- Behind the scene:
- coding.md
- dependencies.md
- FAQ: faq.md
- Go Packages:
- packages/cmd/katenary.md
- packages/internal/parser.md
- packages/internal/utils.md
- Generator:
- Index: packages/internal/generator.md
- ExtraFiles: packages/internal/generator/extrafiles.md
- labels:
- packages/internal/generator/labels.md
- Labelstructs: packages/internal/generator/labels/labelstructs.md
- KatenaryFile: packages/internal/generator/katenaryfile.md

View File

@@ -0,0 +1,56 @@
{#- This file was automatically generated - do not edit -#}
<footer class="md-footer">
{% if page.previous_page or page.next_page %} {% if page.meta and
page.meta.hide %} {% set hidden = "hidden" if "footer" in page.meta.hide %} {%
endif %}
<nav
class="md-footer__inner md-grid"
aria-label="{{ lang.t('footer.title') }}"
{{
hidden
}}
>
{% if page.previous_page %} {% set direction = lang.t("footer.previous") %}
<a
href="{{ page.previous_page.url | url }}"
class="md-footer__link md-footer__link--prev"
aria-label="{{ direction }}: {{ page.previous_page.title | e }}"
rel="prev"
>
<div class="md-footer__button md-icon">
{% include ".icons/material/arrow-left.svg" %}
</div>
<div class="md-footer__title">
<div class="md-ellipsis">
<span class="md-footer__direction"> {{ direction }} </span>
{{ page.previous_page.title }}
</div>
</div>
</a>
{% endif %} {% if page.next_page %} {% set direction = lang.t("footer.next")
%}
<a
href="{{ page.next_page.url | url }}"
class="md-footer__link md-footer__link--next"
aria-label="{{ direction }}: {{ page.next_page.title | e }}"
rel="next"
>
<div class="md-footer__title">
<div class="md-ellipsis">
<span class="md-footer__direction"> {{ direction }} </span>
{{ page.next_page.title }}
</div>
</div>
<div class="md-footer__button md-icon">
{% include ".icons/material/arrow-right.svg" %}
</div>
</a>
{% endif %}
</nav>
{% endif %}
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
{% include "partials/copyright.html" %} {% if config.extra.social %} {%
include "partials/social.html" %} {% endif %}
</div>
</footer>

22
doc/preprocess.py Normal file
View File

@@ -0,0 +1,22 @@
"""Called by mkdocs to preprocess files and build manpages"""
from bs4 import BeautifulSoup, Tag
def to_remove(tag: Tag) -> bool:
"""Removes images, SVGs, links containing images or SVGs, and permalinks from the BeautifulSoup object."""
if tag.name in {"img", "svg"}:
return True
# remove links containing images or SVGs
if tag.name == "a" and tag.img and to_remove(tag.img):
return True
# remove permalinks
if tag.name == "a" and "headerlink" in tag.get("class", ()):
return True
return False
def preprocess(soup: BeautifulSoup, output: str) -> None:
"""Preprocess the BeautifulSoup object to remove unwanted elements."""
for element in soup.find_all(to_remove):
element.decompose()

9
doc/requirements.txt Normal file
View File

@@ -0,0 +1,9 @@
mkdocs==1.*
Jinja2==3.*
MarkupSafe==3.*
pymdown-extensions==10.*
mkdocs-material==9.*
mkdocs-material-extensions==1.*
mkdocs-plugin-inline-svg-mod
beautifulsoup4==4.*
mkdocs-manpage[preprocess]

View File

@@ -1,10 +0,0 @@
# Basic example
This is a basic example of what can do Katenary with standard docker-compose file.
In this example:
- `depends_on` yield a `initContainer` in the webapp ddeployment to wait for database
- so we need to declare the listened port inside `database` container as we don't use it with docker-compose- also, we needed to declare that `DB_HOST` is actually a service name using `mapenv` label
Take a look on [chart/basic](chart/basic) directory to see what `katenary convert` command has generated.

View File

@@ -1,8 +0,0 @@
# Create on 2022-02-17T10:27:30+01:00
# Katenary command line: katenary convert
apiVersion: v2
appVersion: 0.0.1
description: A helm chart for basic
name: basic
type: application
version: 0.1.0

View File

@@ -1,8 +0,0 @@
Congratulations,
Your application is now deployed. This may take a while to be up and responding.
{{ if .Values.webapp.ingress.enabled -}}
- webapp is accessible on : http://{{ .Values.webapp.ingress.host }}
{{- end }}

View File

@@ -1,39 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}-database'
labels:
katenary.io/component: database
katenary.io/project: basic
katenary.io/release: '{{ .Release.Name }}'
annotations:
katenary.io/docker-compose-sha1: b9f12bb7d1e97901c1d7680394209525763f6640
katenary.io/version: master-3619cc4
spec:
replicas: 1
selector:
matchLabels:
katenary.io/component: database
katenary.io/release: '{{ .Release.Name }}'
template:
metadata:
labels:
katenary.io/component: database
katenary.io/release: '{{ .Release.Name }}'
spec:
containers:
- name: database
image: '{{ .Values.database.image }}'
ports:
- name: database
containerPort: 3306
env:
- name: MARIADB_PASSWORD
value: foo
- name: MARIADB_DATABASE
value: myapp
- name: MARIADB_ROOT_PASSWORD
value: foobar
- name: MARIADB_USER
value: foo

View File

@@ -1,19 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}-database'
labels:
katenary.io/component: database
katenary.io/project: basic
katenary.io/release: '{{ .Release.Name }}'
annotations:
katenary.io/docker-compose-sha1: b9f12bb7d1e97901c1d7680394209525763f6640
katenary.io/version: master-3619cc4
spec:
selector:
katenary.io/component: database
katenary.io/release: '{{ .Release.Name }}'
ports:
- protocol: TCP
port: 3306
targetPort: 3306

View File

@@ -1,48 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}-webapp'
labels:
katenary.io/component: webapp
katenary.io/project: basic
katenary.io/release: '{{ .Release.Name }}'
annotations:
katenary.io/docker-compose-sha1: b9f12bb7d1e97901c1d7680394209525763f6640
katenary.io/version: master-3619cc4
spec:
replicas: 1
selector:
matchLabels:
katenary.io/component: webapp
katenary.io/release: '{{ .Release.Name }}'
template:
metadata:
labels:
katenary.io/component: webapp
katenary.io/release: '{{ .Release.Name }}'
spec:
initContainers:
- name: check-database
image: busybox
command:
- sh
- -c
- |-
OK=0
echo "Checking database port"
while [ $OK != 1 ]; do
echo -n "."
nc -z {{ .Release.Name }}-database 3306 2>&1 >/dev/null && OK=1 || sleep 1
done
echo
echo "Done"
containers:
- name: webapp
image: '{{ .Values.webapp.image }}'
ports:
- name: webapp
containerPort: 80
env:
- name: DB_HOST
value: '{{ .Release.Name }}-database'

View File

@@ -1,34 +0,0 @@
{{- if .Values.webapp.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: '{{ .Release.Name }}-webapp'
labels:
katenary.io/component: webapp
katenary.io/project: basic
katenary.io/release: '{{ .Release.Name }}'
annotations:
katenary.io/docker-compose-sha1: b9f12bb7d1e97901c1d7680394209525763f6640
katenary.io/version: master-3619cc4
spec:
{{- if and .Values.webapp.ingress.class (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: '{{ .Values.webapp.ingress.class }}'
{{- end }}
rules:
- host: '{{ .Values.webapp.ingress.host }}'
http:
paths:
- path: /
pathType: Prefix
backend:
{{- if semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion }}
service:
name: '{{ .Release.Name }}-webapp'
port:
number: 80
{{- else }}
serviceName: '{{ .Release.Name }}-webapp'
servicePort: 80
{{- end }}
{{- end -}}

View File

@@ -1,19 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}-webapp'
labels:
katenary.io/component: webapp
katenary.io/project: basic
katenary.io/release: '{{ .Release.Name }}'
annotations:
katenary.io/docker-compose-sha1: b9f12bb7d1e97901c1d7680394209525763f6640
katenary.io/version: master-3619cc4
spec:
selector:
katenary.io/component: webapp
katenary.io/release: '{{ .Release.Name }}'
ports:
- protocol: TCP
port: 80
targetPort: 80

View File

@@ -1,8 +0,0 @@
database:
image: mariadb:10
webapp:
image: php:7-apache
ingress:
class: nginx
enabled: false
host: webapp.basic.tld

View File

@@ -1,31 +0,0 @@
version: "3"
# this example is absolutely not working, it's an example to see how it is converted
# by Katenary
services:
webapp:
image: php:7-apache
environment:
DB_HOST: database
ports:
- "8080:80"
labels:
# expose an ingress
katenary.io/ingress: 80
# DB_HOST is actually a service name
katenary.io/mapenv: |
DB_HOST: {{ .Release.Name }}-database
depends_on:
- database
database:
image: mariadb:10
environment:
MARIADB_ROOT_PASSWORD: foobar
MARIADB_USER: foo
MARIADB_PASSWORD: foo
MARIADB_DATABASE: myapp
labels:
# because we don't provide "ports" or "expose", alert katenary
# to use the mysql port for service declaration
katenary.io/ports: 3306

View File

@@ -1,9 +0,0 @@
# Example with Ghost
[Ghost](https://ghost.org/) is a simple but powerfull blog engine. It is very nice to test some behaviors with Docker or Podman.
The given `docker-compose.yaml` file here declares a stand-alone blog service. To help using it, we use [Patwae](https://pathwae.net) reverse-proxy to listend http://ghost.example.localhost
The problem to solve is that the `url` environment variable correspond to the Ingress host when we will convert it to Helm Chart. So, we use the `mapenv` label to declare that `url` is actually `{{ .Values.blog.ingress.host }}` value.
Note that we also `ignore` pathwae because we don't need it in our Helm Chart.

View File

@@ -1,8 +0,0 @@
# Create on 2022-05-05T14:16:27+02:00
# Katenary command line: /tmp/go-build669507924/b001/exe/main convert
apiVersion: v2
appVersion: 0.0.1
description: A helm chart for ghost
name: ghost
type: application
version: 0.1.0

View File

@@ -1,8 +0,0 @@
Congratulations,
Your application is now deployed. This may take a while to be up and responding.
{{ if .Values.blog.ingress.enabled -}}
- blog is accessible on : http://{{ .Values.blog.ingress.host }}
{{- end }}

View File

@@ -1,33 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}-blog'
labels:
katenary.io/component: blog
katenary.io/project: ghost
katenary.io/release: '{{ .Release.Name }}'
annotations:
katenary.io/docker-compose-sha1: 0c2bbf548ff569c3dc5d77dc158e98bbe86fb5d4
katenary.io/version: master
spec:
replicas: 1
selector:
matchLabels:
katenary.io/component: blog
katenary.io/release: '{{ .Release.Name }}'
template:
metadata:
labels:
katenary.io/component: blog
katenary.io/release: '{{ .Release.Name }}'
spec:
containers:
- name: blog
image: '{{ .Values.blog.image }}'
ports:
- name: blog
containerPort: 2368
env:
- name: url
value: http://{{ .Values.blog.ingress.host }}

View File

@@ -1,42 +0,0 @@
{{- if .Values.blog.ingress.enabled -}}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: '{{ .Release.Name }}-blog'
labels:
katenary.io/component: blog
katenary.io/project: ghost
katenary.io/release: '{{ .Release.Name }}'
annotations:
katenary.io/docker-compose-sha1: 0c2bbf548ff569c3dc5d77dc158e98bbe86fb5d4
katenary.io/version: master
spec:
{{- if and .Values.blog.ingress.class (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: '{{ .Values.blog.ingress.class }}'
{{- end }}
rules:
- host: '{{ .Values.blog.ingress.host }}'
http:
paths:
- path: /
{{- if semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion }}
pathType: Prefix
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }}
service:
name: '{{ .Release.Name }}-blog'
port:
number: 2368
{{- else }}
serviceName: '{{ .Release.Name }}-blog'
servicePort: 2368
{{- end }}
{{- end -}}

View File

@@ -1,19 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}-blog'
labels:
katenary.io/component: blog
katenary.io/project: ghost
katenary.io/release: '{{ .Release.Name }}'
annotations:
katenary.io/docker-compose-sha1: 0c2bbf548ff569c3dc5d77dc158e98bbe86fb5d4
katenary.io/version: master
spec:
selector:
katenary.io/component: blog
katenary.io/release: '{{ .Release.Name }}'
ports:
- protocol: TCP
port: 2368
targetPort: 2368

View File

@@ -1,6 +0,0 @@
blog:
image: ghost
ingress:
class: nginx
enabled: false
host: blog.ghost.tld

View File

@@ -1,30 +0,0 @@
version: "3"
services:
blog:
image: ghost
environment:
# this is OK for local test, but not with Helm
# because the URL depends on Ingress
url: http://ghost.example.localhost
labels:
katenary.io/ports: 2368
katenary.io/ingress: 2368
# ... so we declare that "url" is actually
# the ingress host
katenary.io/mapenv: |
url: http://{{ .Values.blog.ingress.host }}
proxy:
# A simple proxy for localhost
image: quay.io/pathwae/proxy
environment:
CONFIG: |
ghost.example.localhost:
to: http://blog:2368
ports:
- 80:80
labels:
# we don't want this in Helm because we will use
# an ingress
katenary.io/ignore: true

View File

@@ -1,13 +0,0 @@
# Make it possible to bind several containers in one pod
In this example, we need to make nginx and php-fpm to run inside the same "pod". The reason is that we configured FPM to listen an unix socket instead of the 9000 port.
Because NGinx will need to connect to the unix socket wich is a file, both containers should share the same node and work together.
So, in the docker-compose file, we need to declare:
- `katenary.io/empty-dirs: socket` where `socket` is the "volume name", this will avoid the creation of a PVC
- `katenary.io/same-pod: http` in `php` container to declare that this will be added in the `containers` section of the `http` deployment
You can note that we also use `configmap-volumes` to declare our configuration as `configMap`.
Take a look on [chart/same-pod](chart/same-pod) directory to see the result of the `katenary convert` command.

View File

@@ -1,8 +0,0 @@
# Create on 2022-02-17T11:36:02+01:00
# Katenary command line: katenary convert --force
apiVersion: v2
appVersion: 0.0.1
description: A helm chart for same-pod
name: same-pod
type: application
version: 0.1.0

View File

@@ -1,8 +0,0 @@
Congratulations,
Your application is now deployed. This may take a while to be up and responding.
{{ if .Values.http.ingress.enabled -}}
- http is accessible on : http://{{ .Values.http.ingress.host }}
{{- end }}

View File

@@ -1,23 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: '{{ .Release.Name }}-config-nginx-http'
labels:
katenary.io/component: ""
katenary.io/project: same-pod
katenary.io/release: '{{ .Release.Name }}'
annotations:
katenary.io/docker-compose-sha1: 74e67695bfdbb829f15531321e158808018280e0
katenary.io/version: master-bf44d44
data:
default.conf: |
upstream _php {
server unix:/sock/fpm.sock;
}
server {
listen 80;
location ~ ^/index\.php(/|$) {
fastcgi_pass _php;
include fastcgi_params;
}
}

View File

@@ -1,30 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: '{{ .Release.Name }}-config-php-php'
labels:
katenary.io/component: ""
katenary.io/project: same-pod
katenary.io/release: '{{ .Release.Name }}'
annotations:
katenary.io/docker-compose-sha1: 74e67695bfdbb829f15531321e158808018280e0
katenary.io/version: master-bf44d44
data:
www.conf: |
[www]
user = www-data
group = www-data
listen = /sock/fpm.sock
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
access.log = /proc/self/fd/2
log_limit = 8192
clear_env = no
catch_workers_output = yes
decorate_workers_output = no

View File

@@ -1,52 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: '{{ .Release.Name }}-http'
labels:
katenary.io/component: http
katenary.io/project: same-pod
katenary.io/release: '{{ .Release.Name }}'
annotations:
katenary.io/docker-compose-sha1: 74e67695bfdbb829f15531321e158808018280e0
katenary.io/version: master-bf44d44
spec:
replicas: 1
selector:
matchLabels:
katenary.io/component: http
katenary.io/release: '{{ .Release.Name }}'
template:
metadata:
labels:
katenary.io/component: http
katenary.io/release: '{{ .Release.Name }}'
spec:
containers:
- name: http
image: '{{ .Values.http.image }}'
ports:
- name: http
containerPort: 80
volumeMounts:
- mountPath: /sock
name: sock
- mountPath: /etc/nginx/conf.d
name: config-nginx
- name: php
image: '{{ .Values.php.image }}'
volumeMounts:
- mountPath: /sock
name: sock
- mountPath: /usr/local/etc/php-fpm.d/www.conf
name: config-php
subPath: www.conf
volumes:
- emptyDir: {}
name: sock
- configMap:
name: '{{ .Release.Name }}-config-nginx-http'
name: config-nginx
- configMap:
name: '{{ .Release.Name }}-config-php-php'
name: config-php

View File

@@ -1,34 +0,0 @@
{{- if .Values.http.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: '{{ .Release.Name }}-http'
labels:
katenary.io/component: http
katenary.io/project: same-pod
katenary.io/release: '{{ .Release.Name }}'
annotations:
katenary.io/docker-compose-sha1: 74e67695bfdbb829f15531321e158808018280e0
katenary.io/version: master-bf44d44
spec:
{{- if and .Values.http.ingress.class (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: '{{ .Values.http.ingress.class }}'
{{- end }}
rules:
- host: '{{ .Values.http.ingress.host }}'
http:
paths:
- path: /
pathType: Prefix
backend:
{{- if semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion }}
service:
name: '{{ .Release.Name }}-http'
port:
number: 80
{{- else }}
serviceName: '{{ .Release.Name }}-http'
servicePort: 80
{{- end }}
{{- end -}}

View File

@@ -1,19 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: '{{ .Release.Name }}-http'
labels:
katenary.io/component: http
katenary.io/project: same-pod
katenary.io/release: '{{ .Release.Name }}'
annotations:
katenary.io/docker-compose-sha1: 74e67695bfdbb829f15531321e158808018280e0
katenary.io/version: master-bf44d44
spec:
selector:
katenary.io/component: http
katenary.io/release: '{{ .Release.Name }}'
ports:
- protocol: TCP
port: 80
targetPort: 80

View File

@@ -1,8 +0,0 @@
http:
image: nginx:alpine
ingress:
class: nginx
enabled: false
host: http.same-pod.tld
php:
image: php:fpm

View File

@@ -1,10 +0,0 @@
upstream _php {
server unix:/sock/fpm.sock;
}
server {
listen 80;
location ~ ^/index\.php(/|$) {
fastcgi_pass _php;
include fastcgi_params;
}
}

View File

@@ -1,17 +0,0 @@
[www]
user = www-data
group = www-data
listen = /sock/fpm.sock
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
access.log = /proc/self/fd/2
log_limit = 8192
clear_env = no
catch_workers_output = yes
decorate_workers_output = no

View File

@@ -1,38 +0,0 @@
version: "3"
services:
http:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- "sock:/sock"
- "./config/nginx:/etc/nginx/conf.d:z"
labels:
# the "sock" volume will need to be shared to the same pod, so let's
# declare that this is not a PVC
katenary.io/empty-dirs: sock
# use ./config/nginx as a configMap
katenary.io/configmap-volumes: ./config/nginx
# declare an ingress
katenary.io/ingress: 80
php:
image: php:fpm
volumes:
- "sock:/sock"
- "./config/php/www.conf:/usr/local/etc/php-fpm.d/www.conf:z"
labels:
# fpm will need to use a unix socket shared
# with nginx (http service above), so we want here
# make a single pod containing nginx and php
katenary.io/same-pod: http
# use the ./config/php files as a configMap
katenary.io/configmap-volumes: ./config/php/www.conf
volumes:
sock:

View File

@@ -1,892 +0,0 @@
package generator
import (
"fmt"
"io/ioutil"
"katenary/compose"
"katenary/helm"
"katenary/logger"
"log"
"net/url"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"github.com/compose-spec/compose-go/types"
"gopkg.in/yaml.v3"
)
type EnvVal = helm.EnvValue
const (
ICON_PACKAGE = "📦"
ICON_SERVICE = "🔌"
ICON_SECRET = "🔏"
ICON_CONF = "📝"
ICON_STORE = "⚡"
ICON_INGRESS = "🌐"
)
// Values is kept in memory to create a values.yaml file.
var (
Values = make(map[string]map[string]interface{})
VolumeValues = make(map[string]map[string]map[string]EnvVal)
EmptyDirs = []string{}
servicesMap = make(map[string]int)
locker = &sync.Mutex{}
dependScript = `
OK=0
echo "Checking __service__ port"
while [ $OK != 1 ]; do
echo -n "."
nc -z ` + helm.ReleaseNameTpl + `-__service__ __port__ 2>&1 >/dev/null && OK=1 || sleep 1
done
echo
echo "Done"
`
madeDeployments = make(map[string]helm.Deployment, 0)
)
// Create a Deployment for a given compose.Service. It returns a list chan
// of HelmFileGenerator which will be used to generate the files (deployment, secrets, configMap...).
func CreateReplicaObject(name string, s types.ServiceConfig, linked map[string]types.ServiceConfig) HelmFileGenerator {
ret := make(chan HelmFile, runtime.NumCPU())
// there is a bug woth typs.ServiceConfig if we use the pointer. So we need to dereference it.
go buildDeployment(name, &s, linked, ret)
return ret
}
// This function will try to yied deployment and services based on a service from the compose file structure.
func buildDeployment(name string, s *types.ServiceConfig, linked map[string]types.ServiceConfig, fileGeneratorChan HelmFileGenerator) {
logger.Magenta(ICON_PACKAGE+" Generating deployment for ", name)
deployment := helm.NewDeployment(name)
newContainerForDeployment(name, name, deployment, s, fileGeneratorChan)
// Add selectors
selectors := buildSelector(name, s)
deployment.Spec.Selector = map[string]interface{}{
"matchLabels": selectors,
}
deployment.Spec.Template.Metadata.Labels = selectors
// Now, the linked services (same pod)
for lname, link := range linked {
newContainerForDeployment(name, lname, deployment, &link, fileGeneratorChan)
// append ports and expose ports to the deployment,
// to be able to generate them in the Service file
if len(link.Ports) > 0 || len(link.Expose) > 0 {
s.Ports = append(s.Ports, link.Ports...)
s.Expose = append(s.Expose, link.Expose...)
}
}
// Remove duplicates in volumes
volumes := make([]map[string]interface{}, 0)
done := make(map[string]bool)
for _, vol := range deployment.Spec.Template.Spec.Volumes {
name := vol["name"].(string)
if _, ok := done[name]; ok {
continue
} else {
done[name] = true
volumes = append(volumes, vol)
}
}
deployment.Spec.Template.Spec.Volumes = volumes
// Then, create Services and possible Ingresses for ingress labels, "ports" and "expose" section
if len(s.Ports) > 0 || len(s.Expose) > 0 {
for _, s := range generateServicesAndIngresses(name, s) {
if s != nil {
fileGeneratorChan <- s
}
}
}
// add the volumes in Values
if len(VolumeValues[name]) > 0 {
AddValues(name, map[string]EnvVal{"persistence": VolumeValues[name]})
}
// the deployment is ready, give it
fileGeneratorChan <- deployment
// and then, we can say that it's the end
fileGeneratorChan <- nil
}
// prepareContainer assigns image, command, env, and labels to a container.
func prepareContainer(container *helm.Container, service *types.ServiceConfig, servicename string) {
// if there is no image name, this should fail!
if service.Image == "" {
log.Fatal(ICON_PACKAGE+" No image name for service ", servicename)
}
// Get the image tag
imageParts := strings.Split(service.Image, ":")
tag := ""
if len(imageParts) == 2 {
container.Image = imageParts[0]
tag = imageParts[1]
}
vtag := ".Values." + servicename + ".repository.tag"
container.Image = `{{ .Values.` + servicename + `.repository.image }}` +
`{{ if ne ` + vtag + ` "" }}:{{ ` + vtag + ` }}{{ end }}`
container.Command = service.Command
AddValues(servicename, map[string]EnvVal{
"repository": map[string]EnvVal{
"image": imageParts[0],
"tag": tag,
},
})
prepareProbes(servicename, service, container)
generateContainerPorts(service, servicename, container)
}
// Create a service (k8s).
func generateServicesAndIngresses(name string, s *types.ServiceConfig) []HelmFile {
ret := make([]HelmFile, 0) // can handle helm.Service or helm.Ingress
logger.Magenta(ICON_SERVICE+" Generating service for ", name)
ks := helm.NewService(name)
for _, p := range s.Ports {
target := int(p.Target)
ks.Spec.Ports = append(ks.Spec.Ports, helm.NewServicePort(target, target))
}
ks.Spec.Selector = buildSelector(name, s)
ret = append(ret, ks)
if v, ok := s.Labels[helm.LABEL_INGRESS]; ok {
port, err := strconv.Atoi(v)
if err != nil {
log.Fatalf("The given port \"%v\" as ingress port in \"%s\" service is not an integer\n", v, name)
}
logger.Cyanf(ICON_INGRESS+" Create an ingress for port %d on %s service\n", port, name)
ing := createIngress(name, port, s)
ret = append(ret, ing)
}
if len(s.Expose) > 0 {
logger.Magenta(ICON_SERVICE+" Generating service for ", name+"-external")
ks := helm.NewService(name + "-external")
ks.Spec.Type = "NodePort"
for _, expose := range s.Expose {
p, _ := strconv.Atoi(expose)
ks.Spec.Ports = append(ks.Spec.Ports, helm.NewServicePort(p, p))
}
ks.Spec.Selector = buildSelector(name, s)
ret = append(ret, ks)
}
return ret
}
// Create an ingress.
func createIngress(name string, port int, s *types.ServiceConfig) *helm.Ingress {
ingress := helm.NewIngress(name)
annotations := map[string]string{}
ingressVal := map[string]interface{}{
"class": "nginx",
"host": name + "." + helm.Appname + ".tld",
"enabled": false,
"annotations": annotations,
}
// add Annotations in values
AddValues(name, map[string]EnvVal{"ingress": ingressVal})
ingress.Spec.Rules = []helm.IngressRule{
{
Host: fmt.Sprintf("{{ .Values.%s.ingress.host }}", name),
Http: helm.IngressHttp{
Paths: []helm.IngressPath{{
Path: "/",
PathType: "Prefix",
Backend: &helm.IngressBackend{
Service: helm.IngressService{
Name: helm.ReleaseNameTpl + "-" + name,
Port: map[string]interface{}{
"number": port,
},
},
},
}},
},
},
}
ingress.SetIngressClass(name)
return ingress
}
// Build the selector for the service.
func buildSelector(name string, s *types.ServiceConfig) map[string]string {
return map[string]string{
"katenary.io/component": name,
"katenary.io/release": helm.ReleaseNameTpl,
}
}
// buildConfigMapFromPath generates a ConfigMap from a path.
func buildConfigMapFromPath(name, path string) *helm.ConfigMap {
stat, err := os.Stat(path)
if err != nil {
return nil
}
files := make(map[string]string, 0)
if stat.IsDir() {
found, _ := filepath.Glob(path + "/*")
for _, f := range found {
if s, err := os.Stat(f); err != nil || s.IsDir() {
if err != nil {
fmt.Fprintf(os.Stderr, "An error occured reading volume path %s\n", err.Error())
} else {
logger.ActivateColors = true
logger.Yellowf("Warning, %s is a directory, at this time we only "+
"can create configmap for first level file list\n", f)
logger.ActivateColors = false
}
continue
}
_, filename := filepath.Split(f)
c, _ := ioutil.ReadFile(f)
files[filename] = string(c)
}
}
cm := helm.NewConfigMap(name, GetRelPath(path))
cm.Data = files
return cm
}
// generateContainerPorts add the container ports of a service.
func generateContainerPorts(s *types.ServiceConfig, name string, container *helm.Container) {
exists := make(map[int]string)
for _, port := range s.Ports {
portName := name
for _, n := range exists {
if name == n {
portName = fmt.Sprintf("%s-%d", name, port.Target)
}
}
container.Ports = append(container.Ports, &helm.ContainerPort{
Name: portName,
ContainerPort: int(port.Target),
})
exists[int(port.Target)] = name
}
// manage the "expose" section to be a NodePort in Kubernetes
for _, expose := range s.Expose {
port, _ := strconv.Atoi(expose)
if _, exist := exists[port]; exist {
continue
}
container.Ports = append(container.Ports, &helm.ContainerPort{
Name: name,
ContainerPort: port,
})
}
}
// prepareVolumes add the volumes of a service.
func prepareVolumes(deployment, name string, s *types.ServiceConfig, container *helm.Container, fileGeneratorChan HelmFileGenerator) []map[string]interface{} {
volumes := make([]map[string]interface{}, 0)
mountPoints := make([]interface{}, 0)
configMapsVolumes := make([]string, 0)
if v, ok := s.Labels[helm.LABEL_VOL_CM]; ok {
configMapsVolumes = strings.Split(v, ",")
for i, cm := range configMapsVolumes {
configMapsVolumes[i] = strings.TrimSpace(cm)
}
}
for _, vol := range s.Volumes {
volname := vol.Source
volepath := vol.Target
if volname == "" {
logger.ActivateColors = true
logger.Yellowf("Warning, volume source to %s is empty for %s -- skipping\n", volepath, name)
logger.ActivateColors = false
continue
}
isConfigMap := false
for _, cmVol := range configMapsVolumes {
if GetRelPath(volname) == cmVol {
isConfigMap = true
break
}
}
// local volume cannt be mounted
if !isConfigMap && (strings.HasPrefix(volname, ".") || strings.HasPrefix(volname, "/")) {
logger.ActivateColors = true
logger.Redf("You cannot, at this time, have local volume in %s deployment\n", name)
logger.ActivateColors = false
continue
}
if isConfigMap {
// check if the volname path points on a file, if so, we need to add subvolume to the interface
stat, err := os.Stat(volname)
if err != nil {
logger.ActivateColors = true
logger.Redf("An error occured reading volume path %s\n", err.Error())
logger.ActivateColors = false
continue
}
pointToFile := ""
if !stat.IsDir() {
pointToFile = filepath.Base(volname)
volname = filepath.Dir(volname)
}
// the volume is a path and it's explicitally asked to be a configmap in labels
cm := buildConfigMapFromPath(name, volname)
cm.K8sBase.Metadata.Name = helm.ReleaseNameTpl + "-" + name + "-" + PathToName(volname)
// build a configmapRef for this volume
volumes = append(volumes, map[string]interface{}{
"name": volname,
"configMap": map[string]string{
"name": cm.K8sBase.Metadata.Name,
},
})
if len(pointToFile) > 0 {
mountPoints = append(mountPoints, map[string]interface{}{
"name": volname,
"mountPath": volepath,
"subPath": pointToFile,
})
} else {
mountPoints = append(mountPoints, map[string]interface{}{
"name": volname,
"mountPath": volepath,
})
}
if cm != nil {
fileGeneratorChan <- cm
}
} else {
// rmove minus sign from volume name
volname = strings.ReplaceAll(volname, "-", "")
isEmptyDir := false
for _, v := range EmptyDirs {
v = strings.ReplaceAll(v, "-", "")
if v == volname {
volumes = append(volumes, map[string]interface{}{
"name": volname,
"emptyDir": map[string]string{},
})
mountPoints = append(mountPoints, map[string]interface{}{
"name": volname,
"mountPath": volepath,
})
container.VolumeMounts = append(container.VolumeMounts, mountPoints...)
isEmptyDir = true
break
}
}
if isEmptyDir {
continue
}
volumes = append(volumes, map[string]interface{}{
"name": volname,
"persistentVolumeClaim": map[string]string{
"claimName": helm.ReleaseNameTpl + "-" + volname,
},
})
mountPoints = append(mountPoints, map[string]interface{}{
"name": volname,
"mountPath": volepath,
})
logger.Yellow(ICON_STORE+" Generate volume values", volname, "for container named", name, "in deployment", deployment)
AddVolumeValues(deployment, volname, map[string]EnvVal{
"enabled": false,
"capacity": "1Gi",
})
if pvc := helm.NewPVC(deployment, volname); pvc != nil {
fileGeneratorChan <- pvc
}
}
}
// add the volume in the container and return the volume definition to add in Deployment
container.VolumeMounts = append(container.VolumeMounts, mountPoints...)
return volumes
}
// prepareInitContainers add the init containers of a service.
func prepareInitContainers(name string, s *types.ServiceConfig, container *helm.Container) []*helm.Container {
// We need to detect others services, but we probably not have parsed them yet, so
// we will wait for them for a while.
initContainers := make([]*helm.Container, 0)
for dp := range s.DependsOn {
c := helm.NewContainer("check-"+dp, "busybox", nil, s.Labels)
command := strings.ReplaceAll(strings.TrimSpace(dependScript), "__service__", dp)
foundPort := -1
locker.Lock()
if defaultPort, ok := servicesMap[dp]; !ok {
logger.Redf("Error while getting port for service %s\n", dp)
os.Exit(1)
} else {
foundPort = defaultPort
}
locker.Unlock()
if foundPort == -1 {
log.Fatalf(
"ERROR, the %s service is waiting for %s port number, "+
"but it is never discovered. You must declare at least one port in "+
"the \"ports\" section of the service in the docker-compose file",
name,
dp,
)
}
command = strings.ReplaceAll(command, "__port__", strconv.Itoa(foundPort))
c.Command = []string{
"sh",
"-c",
command,
}
initContainers = append(initContainers, c)
}
return initContainers
}
// prepareProbes generate http/tcp/command probes for a service.
func prepareProbes(name string, s *types.ServiceConfig, container *helm.Container) {
// first, check if there a label for the probe
if check, ok := s.Labels[helm.LABEL_HEALTHCHECK]; ok {
check = strings.TrimSpace(check)
p := helm.NewProbeFromService(s)
// get the port of the "url" check
if checkurl, err := url.Parse(check); err == nil {
if err == nil {
container.LivenessProbe = buildProtoProbe(p, checkurl)
}
} else {
// it's a command
container.LivenessProbe = p
container.LivenessProbe.Exec = &helm.Exec{
Command: []string{
"sh",
"-c",
check,
},
}
}
return // label overrides everything
}
// if not, we will use the default one
if s.HealthCheck != nil {
container.LivenessProbe = buildCommandProbe(s)
}
}
// buildProtoProbe builds a probe from a url that can be http or tcp.
func buildProtoProbe(probe *helm.Probe, u *url.URL) *helm.Probe {
port, err := strconv.Atoi(u.Port())
if err != nil {
port = 80
}
path := "/"
if u.Path != "" {
path = u.Path
}
switch u.Scheme {
case "http", "https":
probe.HttpGet = &helm.HttpGet{
Path: path,
Port: port,
}
case "tcp":
probe.TCP = &helm.TCP{
Port: port,
}
default:
logger.Redf("Error while parsing healthcheck url %s\n", u.String())
os.Exit(1)
}
return probe
}
func buildCommandProbe(s *types.ServiceConfig) *helm.Probe {
// Get the first element of the command from ServiceConfig
first := s.HealthCheck.Test[0]
p := helm.NewProbeFromService(s)
switch first {
case "CMD", "CMD-SHELL":
// CMD or CMD-SHELL
p.Exec = &helm.Exec{
Command: s.HealthCheck.Test[1:],
}
return p
default:
// badly made but it should work...
p.Exec = &helm.Exec{
Command: []string(s.HealthCheck.Test),
}
return p
}
}
// prepareEnvFromFiles generate configMap or secrets from environment files.
func prepareEnvFromFiles(name string, s *types.ServiceConfig, container *helm.Container, fileGeneratorChan HelmFileGenerator) {
// prepare secrets
secretsFiles := make([]string, 0)
if v, ok := s.Labels[helm.LABEL_ENV_SECRET]; ok {
secretsFiles = strings.Split(v, ",")
}
// manage environment files (env_file in compose)
for _, envfile := range s.EnvFile {
f := PathToName(envfile)
f = strings.ReplaceAll(f, ".env", "")
isSecret := false
for _, s := range secretsFiles {
s = strings.TrimSpace(s)
if s == envfile {
isSecret = true
}
}
var store helm.InlineConfig
if !isSecret {
logger.Bluef(ICON_CONF+" Generating configMap from %s\n", envfile)
store = helm.NewConfigMap(name, envfile)
} else {
logger.Bluef(ICON_SECRET+" Generating secret from %s\n", envfile)
store = helm.NewSecret(name, envfile)
}
envfile = filepath.Join(compose.GetCurrentDir(), envfile)
if err := store.AddEnvFile(envfile); err != nil {
logger.ActivateColors = true
logger.Red(err.Error())
logger.ActivateColors = false
os.Exit(2)
}
section := "configMapRef"
if isSecret {
section = "secretRef"
}
container.EnvFrom = append(container.EnvFrom, map[string]map[string]string{
section: {
"name": store.Metadata().Name,
},
})
// read the envfile and remove them from the container environment or secret
envs := readEnvFile(envfile)
for varname := range envs {
if !isSecret {
// remove varname from container
for i, s := range container.Env {
if s.Name == varname {
container.Env = append(container.Env[:i], container.Env[i+1:]...)
i--
}
}
}
}
if store != nil {
fileGeneratorChan <- store.(HelmFile)
}
}
}
// AddValues adds values to the values.yaml map.
func AddValues(servicename string, values map[string]EnvVal) {
locker.Lock()
defer locker.Unlock()
if _, ok := Values[servicename]; !ok {
Values[servicename] = make(map[string]interface{})
}
for k, v := range values {
Values[servicename][k] = v
}
}
// AddVolumeValues add a volume to the values.yaml map for the given deployment name.
func AddVolumeValues(deployment string, volname string, values map[string]EnvVal) {
locker.Lock()
defer locker.Unlock()
if _, ok := VolumeValues[deployment]; !ok {
VolumeValues[deployment] = make(map[string]map[string]EnvVal)
}
VolumeValues[deployment][volname] = values
}
func readEnvFile(envfilename string) map[string]EnvVal {
env := make(map[string]EnvVal)
content, err := ioutil.ReadFile(envfilename)
if err != nil {
logger.ActivateColors = true
logger.Red(err.Error())
logger.ActivateColors = false
os.Exit(2)
}
// each value is on a separate line with KEY=value
lines := strings.Split(string(content), "\n")
for _, line := range lines {
if strings.Contains(line, "=") {
kv := strings.SplitN(line, "=", 2)
env[kv[0]] = kv[1]
}
}
return env
}
// applyEnvMapLabel will get all LABEL_MAP_ENV to rebuild the env map with tpl.
func applyEnvMapLabel(s *types.ServiceConfig, c *helm.Container) {
locker.Lock()
defer locker.Unlock()
mapenv, ok := s.Labels[helm.LABEL_MAP_ENV]
if !ok {
return
}
// the mapenv is a YAML string
var envmap map[string]EnvVal
err := yaml.Unmarshal([]byte(mapenv), &envmap)
if err != nil {
logger.ActivateColors = true
logger.Red(err.Error())
logger.ActivateColors = false
return
}
// add in envmap
for k, v := range envmap {
vstring := fmt.Sprintf("%v", v)
s.Environment[k] = &vstring
touched := false
if c.Env != nil {
c.Env = make([]*helm.Value, 0)
}
for _, env := range c.Env {
if env.Name == k {
env.Value = v
touched = true
}
}
if !touched {
c.Env = append(c.Env, &helm.Value{Name: k, Value: v})
}
}
}
// setEnvToValues will set the environment variables to the values.yaml map.
func setEnvToValues(name string, s *types.ServiceConfig, c *helm.Container) {
// crete the "environment" key
env := make(map[string]EnvVal)
for k, v := range s.Environment {
env[k] = v
}
if len(env) == 0 {
return
}
AddValues(name, map[string]EnvVal{"environment": env})
for k := range env {
v := "{{ tpl .Values." + name + ".environment." + k + " . }}"
s.Environment[k] = &v
touched := false
for _, c := range c.Env {
if c.Name == k {
c.Value = v
touched = true
}
}
if !touched {
c.Env = append(c.Env, &helm.Value{Name: k, Value: v})
}
}
}
func setSecretVar(name string, s *types.ServiceConfig, c *helm.Container) *helm.Secret {
locker.Lock()
defer locker.Unlock()
// get the list of secret vars
secretvars, ok := s.Labels[helm.LABEL_SECRETVARS]
if !ok {
return nil
}
store := helm.NewSecret(name, "")
for _, secretvar := range strings.Split(secretvars, ",") {
secretvar = strings.TrimSpace(secretvar)
// get the value from env
_, ok := s.Environment[secretvar]
if !ok {
continue
}
// add the secret
store.AddEnv(secretvar, ".Values."+name+".environment."+secretvar)
for i, env := range c.Env {
if env.Name == secretvar {
c.Env = append(c.Env[:i], c.Env[i+1:]...)
i--
}
}
// remove env from ServiceConfig
delete(s.Environment, secretvar)
}
return store
}
// Generate a container in deployment with all needed objects (volumes, secrets, env, ...).
// The deployName shoud be the name of the deployment, we cannot get it from Metadata as this is a variable name.
func newContainerForDeployment(deployName, containerName string, deployment *helm.Deployment, s *types.ServiceConfig, fileGeneratorChan HelmFileGenerator) *helm.Container {
container := helm.NewContainer(containerName, s.Image, s.Environment, s.Labels)
applyEnvMapLabel(s, container)
if secretFile := setSecretVar(containerName, s, container); secretFile != nil {
fileGeneratorChan <- secretFile
container.EnvFrom = append(container.EnvFrom, map[string]map[string]string{
"secretRef": {
"name": secretFile.Metadata().Name,
},
})
}
setEnvToValues(containerName, s, container)
prepareContainer(container, s, containerName)
prepareEnvFromFiles(deployName, s, container, fileGeneratorChan)
// add the container in deployment
if deployment.Spec.Template.Spec.Containers == nil {
deployment.Spec.Template.Spec.Containers = make([]*helm.Container, 0)
}
deployment.Spec.Template.Spec.Containers = append(
deployment.Spec.Template.Spec.Containers,
container,
)
// add the volumes
if deployment.Spec.Template.Spec.Volumes == nil {
deployment.Spec.Template.Spec.Volumes = make([]map[string]interface{}, 0)
}
// manage LABEL_VOLUMEFROM
addVolumeFrom(deployment, container, s)
// and then we can add other volumes
deployment.Spec.Template.Spec.Volumes = append(
deployment.Spec.Template.Spec.Volumes,
prepareVolumes(deployName, containerName, s, container, fileGeneratorChan)...,
)
// add init containers
if deployment.Spec.Template.Spec.InitContainers == nil {
deployment.Spec.Template.Spec.InitContainers = make([]*helm.Container, 0)
}
deployment.Spec.Template.Spec.InitContainers = append(
deployment.Spec.Template.Spec.InitContainers,
prepareInitContainers(containerName, s, container)...,
)
return container
}
// addVolumeFrom takes the LABEL_VOLUMEFROM to get volumes from another container. This can only work with
// container that has got LABEL_SAMEPOD as we need to get the volumes from another container in the same deployment.
func addVolumeFrom(deployment *helm.Deployment, container *helm.Container, s *types.ServiceConfig) {
labelfrom, ok := s.Labels[helm.LABEL_VOLUMEFROM]
if !ok {
return
}
// decode Yaml from the label
var volumesFrom map[string]map[string]string
err := yaml.Unmarshal([]byte(labelfrom), &volumesFrom)
if err != nil {
logger.ActivateColors = true
logger.Red(err.Error())
logger.ActivateColors = false
return
}
// for each declared volume "from", we will find it from the deployment volumes and add it to the container.
// Then, to avoid duplicates, we will remove it from the ServiceConfig object.
for name, volumes := range volumesFrom {
for volumeName := range volumes {
initianame := volumeName
volumeName = PathToName(volumeName)
// get the volume from the deployment container "name"
var ctn *helm.Container
for _, c := range deployment.Spec.Template.Spec.Containers {
if c.Name == name {
ctn = c
break
}
}
if ctn == nil {
logger.ActivateColors = true
logger.Redf("VolumeFrom: container %s not found", name)
logger.ActivateColors = false
continue
}
// get the volume from the container
for _, v := range ctn.VolumeMounts {
switch v := v.(type) {
case map[string]interface{}:
if v["name"] == volumeName {
if container.VolumeMounts == nil {
container.VolumeMounts = make([]interface{}, 0)
}
// make a copy of the volume mount and then add it to the VolumeMounts
var mountpoint = make(map[string]interface{})
for k, v := range v {
mountpoint[k] = v
}
container.VolumeMounts = append(container.VolumeMounts, mountpoint)
// remove the volume from the ServiceConfig
for i, vol := range s.Volumes {
if vol.Source == initianame {
s.Volumes = append(s.Volumes[:i], s.Volumes[i+1:]...)
i--
break
}
}
}
}
}
}
}
}

View File

@@ -1,397 +0,0 @@
package generator
import (
"io/ioutil"
"katenary/compose"
"katenary/helm"
"katenary/logger"
"os"
"path/filepath"
"strings"
"testing"
"github.com/compose-spec/compose-go/cli"
)
const DOCKER_COMPOSE_YML = `version: '3'
services:
# first service, very simple
http:
image: nginx
ports:
- "80:80"
# second service, with environment variables
http2:
image: nginx
environment:
SOME_ENV_VAR: some_value
ANOTHER_ENV_VAR: another_value
# third service with ingress label
web:
image: nginx
ports:
- "80:80"
labels:
katenary.io/ingress: 80
web2:
image: nginx
command: ["/bin/sh", "-c", "while true; do echo hello; sleep 1; done"]
# fourth service is a php service depending on database
php:
image: php:7.2-apache
depends_on:
- database
environment:
SOME_ENV_VAR: some_value
ANOTHER_ENV_VAR: another_value
DB_HOST: database
labels:
katenary.io/mapenv: |
DB_HOST: {{ .Release.Name }}-database
database:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: database
MYSQL_USER: user
MYSQL_PASSWORD: password
volumes:
- data:/var/lib/mysql
labels:
katenary.io/ports: 3306
# try to deploy 2 services but one is in the same pod than the other
http3:
image: nginx
http4:
image: nginx
labels:
katenary.io/same-pod: http3
# unmapped volumes
novol:
image: nginx
volumes:
- /tmp/data
labels:
katenary.io/ports: 80
# use = sign for environment variables
eqenv:
image: nginx
environment:
- SOME_ENV_VAR=some_value
- ANOTHER_ENV_VAR=another_value
# use environment file
useenvfile:
image: nginx
env_file:
- config/env
volumes:
data:
`
var defaultCliFiles = cli.DefaultFileNames
var TMP_DIR = ""
var TMPWORK_DIR = ""
func init() {
logger.NOLOG = len(os.Getenv("NOLOG")) < 1
}
func setUp(t *testing.T) (string, *compose.Parser) {
// cleanup "made" files
helm.ResetMadePVC()
cli.DefaultFileNames = defaultCliFiles
// create a temporary directory
tmp, err := os.MkdirTemp(os.TempDir(), "katenary-test-")
if err != nil {
t.Fatal(err)
}
tmpwork, err := os.MkdirTemp(os.TempDir(), "katenary-test-work-")
if err != nil {
t.Fatal(err)
}
composefile := filepath.Join(tmpwork, "docker-compose.yaml")
p := compose.NewParser(composefile, DOCKER_COMPOSE_YML)
// create envfile for "useenvfile" service
err = os.Mkdir(filepath.Join(tmpwork, "config"), 0777)
if err != nil {
t.Fatal(err)
}
envfile := filepath.Join(tmpwork, "config", "env")
fp, err := os.Create(envfile)
if err != nil {
t.Fatal("MKFILE", err)
}
fp.WriteString("FILEENV1=some_value\n")
fp.WriteString("FILEENV2=another_value\n")
fp.Close()
TMP_DIR = tmp
TMPWORK_DIR = tmpwork
p.Parse("testapp")
Generate(p, "test-0", "testapp", "1.2.3", "4.5.6", DOCKER_COMPOSE_YML, tmp)
return tmp, p
}
func tearDown() {
if len(TMP_DIR) > 0 {
os.RemoveAll(TMP_DIR)
}
if len(TMPWORK_DIR) > 0 {
os.RemoveAll(TMPWORK_DIR)
}
}
// Check if the web2 service has got a command.
func TestCommand(t *testing.T) {
tmp, p := setUp(t)
defer tearDown()
for _, service := range p.Data.Services {
name := service.Name
if name == "web2" {
// Ensure that the command is correctly set
// The command should be a string array
path := filepath.Join(tmp, "templates", name+".deployment.yaml")
path = filepath.Join(tmp, "templates", name+".deployment.yaml")
fp, _ := os.Open(path)
defer fp.Close()
lines, _ := ioutil.ReadAll(fp)
next := false
commands := make([]string, 0)
for _, line := range strings.Split(string(lines), "\n") {
if strings.Contains(line, "command") {
next = true
continue
}
if next {
commands = append(commands, line)
}
}
ok := 0
for _, command := range commands {
if strings.Contains(command, "- /bin/sh") {
ok++
}
if strings.Contains(command, "- -c") {
ok++
}
if strings.Contains(command, "while true; do") {
ok++
}
}
if ok != 3 {
t.Error("Command is not correctly set")
}
}
}
}
// Check if environment is correctly set.
func TestEnvs(t *testing.T) {
tmp, p := setUp(t)
defer tearDown()
for _, service := range p.Data.Services {
name := service.Name
if name == "php" {
// the "DB_HOST" environment variable inside the template must be set to '{{ .Release.Name }}-database'
path := filepath.Join(tmp, "templates", name+".deployment.yaml")
// read the file and find the DB_HOST variable
matched := false
fp, _ := os.Open(path)
defer fp.Close()
lines, _ := ioutil.ReadAll(fp)
next := false
for _, line := range strings.Split(string(lines), "\n") {
if !next && strings.Contains(line, "name: DB_HOST") {
next = true
continue
} else if next && strings.Contains(line, "value:") {
matched = true
if !strings.Contains(line, "{{ tpl .Values.php.environment.DB_HOST . }}") {
t.Error("DB_HOST variable should be set to {{ tpl .Values.php.environment.DB_HOST . }}", line, string(lines))
}
break
}
}
if !matched {
t.Error("DB_HOST variable not found in ", path)
t.Log(string(lines))
}
}
}
}
// Check if the same pod is not deployed twice.
func TestSamePod(t *testing.T) {
tmp, p := setUp(t)
defer tearDown()
for _, service := range p.Data.Services {
name := service.Name
path := filepath.Join(tmp, "templates", name+".deployment.yaml")
if _, found := service.Labels[helm.LABEL_SAMEPOD]; found {
// fail if the service has a deployment
if _, err := os.Stat(path); err == nil {
t.Error("Service ", name, " should not have a deployment")
}
continue
}
// others should have a deployment file
t.Log("Checking ", name, " deployment file")
_, err := os.Stat(path)
if err != nil {
t.Fatal(err)
}
}
}
// Check if the ports are correctly set.
func TestPorts(t *testing.T) {
tmp, p := setUp(t)
defer tearDown()
for _, service := range p.Data.Services {
name := service.Name
path := ""
// if the service has a port found in helm.LABEL_PORT or ports, so the service file should exist
hasPort := false
if _, found := service.Labels[helm.LABEL_PORT]; found {
hasPort = true
}
if service.Ports != nil {
hasPort = true
}
if hasPort {
path = filepath.Join(tmp, "templates", name+".service.yaml")
t.Log("Checking ", name, " service file")
_, err := os.Stat(path)
if err != nil {
t.Error(err)
}
}
}
}
// Check if the volumes are correctly set.
func TestPVC(t *testing.T) {
tmp, p := setUp(t)
defer tearDown()
for _, service := range p.Data.Services {
name := service.Name
path := filepath.Join(tmp, "templates", name+"-data.pvc.yaml")
// the "database" service should have a pvc file in templates (name-data.pvc.yaml)
if name == "database" {
path = filepath.Join(tmp, "templates", name+"-data.pvc.yaml")
t.Log("Checking ", name, " pvc file")
_, err := os.Stat(path)
if err != nil {
list, _ := filepath.Glob(tmp + "/templates/*")
t.Log(list)
t.Fatal(err)
}
}
}
}
//Check if web service has got a ingress.
func TestIngress(t *testing.T) {
tmp, p := setUp(t)
defer tearDown()
for _, service := range p.Data.Services {
name := service.Name
path := filepath.Join(tmp, "templates", name+".ingress.yaml")
// the "web" service should have a ingress file in templates (name.ingress.yaml)
if name == "web" {
path = filepath.Join(tmp, "templates", name+".ingress.yaml")
t.Log("Checking ", name, " ingress file")
_, err := os.Stat(path)
if err != nil {
t.Fatal(err)
}
}
}
}
// Check unmapped volumes
func TestUnmappedVolumes(t *testing.T) {
tmp, p := setUp(t)
defer tearDown()
for _, service := range p.Data.Services {
name := service.Name
if name == "novol" {
path := filepath.Join(tmp, "templates", name+".deployment.yaml")
fp, _ := os.Open(path)
defer fp.Close()
lines, _ := ioutil.ReadAll(fp)
for _, line := range strings.Split(string(lines), "\n") {
if strings.Contains(line, "novol-data") {
t.Error("novol service should not have a volume")
}
}
}
}
}
// Check if service using equal sign for environment works
func TestEqualSignOnEnv(t *testing.T) {
tmp, p := setUp(t)
defer tearDown()
// if the name is eqenv, the service should habe environment
for _, service := range p.Data.Services {
name := service.Name
if name == "eqenv" {
path := filepath.Join(tmp, "templates", name+".deployment.yaml")
fp, _ := os.Open(path)
defer fp.Close()
lines, _ := ioutil.ReadAll(fp)
match := 0
for _, line := range strings.Split(string(lines), "\n") {
// we must find the line with the environment variable name
if strings.Contains(line, "SOME_ENV_VAR") {
// we must find the line with the environment variable value
match++
}
if strings.Contains(line, "ANOTHER_ENV_VAR") {
// we must find the line with the environment variable value
match++
}
}
if match != 4 { // because the value points on .Values...
t.Error("eqenv service should have 2 environment variables")
t.Log(string(lines))
}
}
}
}

View File

@@ -1,22 +0,0 @@
package generator
import (
"katenary/compose"
"regexp"
"strings"
)
// replaceChars replaces some chars in a string.
const replaceChars = `[^a-zA-Z0-9._]`
// GetRelPath return the relative path from the root of the project.
func GetRelPath(path string) string {
return strings.Replace(path, compose.GetCurrentDir(), ".", 1)
}
// PathToName transform a path to a yaml name.
func PathToName(path string) string {
path = strings.TrimPrefix(GetRelPath(path), "./")
path = regexp.MustCompile(replaceChars).ReplaceAllString(path, "-")
return path
}

View File

@@ -1,231 +0,0 @@
package generator
import (
"katenary/compose"
"katenary/generator/writers"
"katenary/helm"
"log"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
"github.com/compose-spec/compose-go/types"
"gopkg.in/yaml.v3"
)
// HelmFile represents a helm file from helm package that has got some necessary methods
// to generate a helm file.
type HelmFile interface {
GetType() string
GetPathRessource() string
}
// HelmFileGenerator is a chanel of HelmFile.
type HelmFileGenerator chan HelmFile
var PrefixRE = regexp.MustCompile(`\{\{.*\}\}-?`)
func portExists(port int, ports []types.ServicePortConfig) bool {
for _, p := range ports {
if p.Target == uint32(port) {
log.Println("portExists:", port, p.Target)
return true
}
}
return false
}
// Generate get a parsed compose file, and generate the helm files.
func Generate(p *compose.Parser, katernayVersion, appName, appVersion, chartVersion, composeFile, dirName string) {
// make the appname global (yes... ugly but easy)
helm.Appname = appName
helm.Version = katernayVersion
templatesDir := filepath.Join(dirName, "templates")
// try to create the directory
err := os.MkdirAll(templatesDir, 0755)
if err != nil {
log.Fatal(err)
}
generators := make(map[string]HelmFileGenerator)
// remove skipped services from the parsed data
for i, service := range p.Data.Services {
if v, ok := service.Labels[helm.LABEL_IGNORE]; !ok || v != "true" {
continue
}
p.Data.Services = append(p.Data.Services[:i], p.Data.Services[i+1:]...)
i--
// find this service in others as "depends_on" and remove it
for _, service2 := range p.Data.Services {
delete(service2.DependsOn, service.Name)
}
}
for i, service := range p.Data.Services {
n := service.Name
// if the service port is declared in labels, add it to the service.
if ports, ok := service.Labels[helm.LABEL_PORT]; ok {
if service.Ports == nil {
service.Ports = make([]types.ServicePortConfig, 0)
}
for _, port := range strings.Split(ports, ",") {
target, err := strconv.Atoi(port)
if err != nil {
log.Fatal(err)
}
if portExists(target, service.Ports) {
continue
}
service.Ports = append(service.Ports, types.ServicePortConfig{
Target: uint32(target),
})
}
}
// find port and store it in servicesMap
for _, port := range service.Ports {
target := int(port.Target)
if target != 0 {
servicesMap[n] = target
break
}
}
// manage emptyDir volumes
if empty, ok := service.Labels[helm.LABEL_EMPTYDIRS]; ok {
//split empty list by coma
emptyDirs := strings.Split(empty, ",")
for i, emptyDir := range emptyDirs {
emptyDirs[i] = strings.TrimSpace(emptyDir)
}
//append them in EmptyDirs
EmptyDirs = append(EmptyDirs, emptyDirs...)
}
p.Data.Services[i] = service
}
// for all services in linked map, and not in samePods map, generate the service
for _, s := range p.Data.Services {
name := s.Name
// do not make a deployment for services declared to be in the same pod than another
if _, ok := s.Labels[helm.LABEL_SAMEPOD]; ok {
continue
}
// find services that is in the same pod
linked := make(map[string]types.ServiceConfig, 0)
for _, service := range p.Data.Services {
n := service.Name
if linkname, ok := service.Labels[helm.LABEL_SAMEPOD]; ok && linkname == name {
linked[n] = service
}
}
generators[name] = CreateReplicaObject(name, s, linked)
}
// to generate notes, we need to keep an Ingresses list
ingresses := make(map[string]*helm.Ingress)
for n, generator := range generators { // generators is a map : name -> generator
for helmFile := range generator { // generator is a chan
if helmFile == nil { // generator finished
break
}
kind := helmFile.(helm.Kinded).Get()
kind = strings.ToLower(kind)
// Add a SHA inside the generated file, it's only
// to make it easy to check it the compose file corresponds to the
// generated helm chart
helmFile.(helm.Signable).BuildSHA(composeFile)
// Some types need special fixes in yaml generation
switch c := helmFile.(type) {
case *helm.Storage:
// For storage, we need to add a "condition" to activate it
writers.BuildStorage(c, n, templatesDir)
case *helm.Deployment:
// for the deployment, we need to fix persitence volumes
// to be activated only when the storage is "enabled",
// either we use an "emptyDir"
writers.BuildDeployment(c, n, templatesDir)
case *helm.Service:
// Change the type for service if it's an "exposed" port
writers.BuildService(c, n, templatesDir)
case *helm.Ingress:
// we need to make ingresses "activable" from values
ingresses[n] = c // keep it to generate notes
writers.BuildIngress(c, n, templatesDir)
case *helm.ConfigMap, *helm.Secret:
// there could be several files, so let's force the filename
name := c.(helm.Named).Name() + "-" + c.GetType()
suffix := c.GetPathRessource()
suffix = PathToName(suffix)
name += suffix
name = PrefixRE.ReplaceAllString(name, "")
writers.BuildConfigMap(c, kind, n, name, templatesDir)
default:
fname := filepath.Join(templatesDir, n+"."+kind+".yaml")
fp, err := os.Create(fname)
if err != nil {
log.Fatal(err)
}
defer fp.Close()
enc := yaml.NewEncoder(fp)
enc.SetIndent(writers.IndentSize)
enc.Encode(c)
}
}
}
// Create the values.yaml file
valueFile, err := os.Create(filepath.Join(dirName, "values.yaml"))
if err != nil {
log.Fatal(err)
}
defer valueFile.Close()
enc := yaml.NewEncoder(valueFile)
enc.SetIndent(writers.IndentSize)
enc.Encode(Values)
// Create tht Chart.yaml file
chartFile, err := os.Create(filepath.Join(dirName, "Chart.yaml"))
if err != nil {
log.Fatal(err)
}
defer chartFile.Close()
chartFile.WriteString(`# Create on ` + time.Now().Format(time.RFC3339) + "\n")
chartFile.WriteString(`# Katenary command line: ` + strings.Join(os.Args, " ") + "\n")
enc = yaml.NewEncoder(chartFile)
enc.SetIndent(writers.IndentSize)
enc.Encode(map[string]interface{}{
"apiVersion": "v2",
"name": appName,
"description": "A helm chart for " + appName,
"type": "application",
"version": chartVersion,
"appVersion": appVersion,
})
// And finally, create a NOTE.txt file
noteFile, err := os.Create(filepath.Join(templatesDir, "NOTES.txt"))
if err != nil {
log.Fatal(err)
}
defer noteFile.Close()
noteFile.WriteString(helm.GenerateNotesFile(ingresses))
}

View File

@@ -1,18 +0,0 @@
package writers
import (
"os"
"path/filepath"
"gopkg.in/yaml.v3"
)
// BuildConfigMap writes the configMap.
func BuildConfigMap(c interface{}, kind, servicename, name, templatesDir string) {
fname := filepath.Join(templatesDir, name+"."+kind+".yaml")
fp, _ := os.Create(fname)
enc := yaml.NewEncoder(fp)
enc.SetIndent(IndentSize)
enc.Encode(c)
fp.Close()
}

View File

@@ -1,44 +0,0 @@
package writers
import (
"bytes"
"katenary/helm"
"os"
"path/filepath"
"strings"
"gopkg.in/yaml.v3"
)
// BuildDeployment builds a deployment.
func BuildDeployment(deployment *helm.Deployment, name, templatesDir string) {
kind := "deployment"
fname := filepath.Join(templatesDir, name+"."+kind+".yaml")
fp, _ := os.Create(fname)
buffer := bytes.NewBuffer(nil)
enc := yaml.NewEncoder(buffer)
enc.SetIndent(IndentSize)
enc.Encode(deployment)
_content := string(buffer.Bytes())
content := strings.Split(string(_content), "\n")
dataname := ""
component := deployment.Spec.Selector["matchLabels"].(map[string]string)[helm.K+"/component"]
n := 0 // will be count of lines only on "persistentVolumeClaim" line, to indent "else" and "end" at the right place
for _, line := range content {
if strings.Contains(line, "name:") {
dataname = strings.Split(line, ":")[1]
dataname = strings.TrimSpace(dataname)
} else if strings.Contains(line, "persistentVolumeClaim") {
n = CountSpaces(line)
line = strings.Repeat(" ", n) + "{{- if .Values." + component + ".persistence." + dataname + ".enabled }}\n" + line
} else if strings.Contains(line, "claimName") {
spaces := strings.Repeat(" ", n)
line += "\n" + spaces + "{{ else }}"
line += "\n" + spaces + "emptyDir: {}"
line += "\n" + spaces + "{{- end }}"
}
fp.WriteString(line + "\n")
}
fp.Close()
}

View File

@@ -1,101 +0,0 @@
package writers
import (
"bytes"
"katenary/helm"
"os"
"path/filepath"
"strings"
"gopkg.in/yaml.v3"
)
const (
classAndVersionCondition = `{{- if and .Values.__name__.ingress.class (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}` + "\n"
versionCondition118 = `{{- if semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion }}` + "\n"
versionCondition119 = `{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }}` + "\n"
apiVersion = `{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}`
)
// BuildIngress generates the ingress yaml file with conditions.
func BuildIngress(ingress *helm.Ingress, name, templatesDir string) {
// Set the backend for 1.18
for _, b := range ingress.Spec.Rules {
for _, p := range b.Http.Paths {
p.Backend.ServiceName = p.Backend.Service.Name
if n, ok := p.Backend.Service.Port["number"]; ok {
p.Backend.ServicePort = n
}
}
}
kind := "ingress"
buffer := bytes.NewBuffer(nil)
fname := filepath.Join(templatesDir, name+"."+kind+".yaml")
enc := yaml.NewEncoder(buffer)
enc.SetIndent(IndentSize)
buffer.WriteString("{{- if .Values." + name + ".ingress.enabled -}}\n")
enc.Encode(ingress)
buffer.WriteString("{{- end -}}")
fp, err := os.Create(fname)
if err != nil {
panic(err)
}
defer fp.Close()
content := string(buffer.Bytes())
lines := strings.Split(content, "\n")
backendHit := false
for _, l := range lines {
// apiVersion is a pain...
if strings.Contains(l, "apiVersion:") {
l = apiVersion
}
// add annotations linked to the Values
if strings.Contains(l, "annotations:") {
n := CountSpaces(l) + IndentSize
l += "\n" + strings.Repeat(" ", n) + "{{- range $k, $v := .Values.__name__.ingress.annotations }}\n"
l += strings.Repeat(" ", n) + "{{ $k }}: {{ $v }}\n"
l += strings.Repeat(" ", n) + "{{- end }}"
l = strings.ReplaceAll(l, "__name__", name)
}
// pathTyype is ony for 1.19+
if strings.Contains(l, "pathType:") {
n := CountSpaces(l)
l = strings.Repeat(" ", n) + versionCondition118 +
l + "\n" +
strings.Repeat(" ", n) + "{{- end }}"
}
if strings.Contains(l, "ingressClassName") {
// should be set only if the version of Kubernetes is 1.18-0 or higher
cond := strings.ReplaceAll(classAndVersionCondition, "__name__", name)
l = ` ` + cond + l + "\n" + ` {{- end }}`
}
// manage the backend format following the Kubernetes 1.19-0 version or higher
if strings.Contains(l, "service:") {
n := CountSpaces(l)
l = strings.Repeat(" ", n) + versionCondition119 + l
}
if strings.Contains(l, "serviceName:") || strings.Contains(l, "servicePort:") {
n := CountSpaces(l)
if !backendHit {
l = strings.Repeat(" ", n) + "{{- else }}\n" + l
} else {
l = l + "\n" + strings.Repeat(" ", n) + "{{- end }}\n"
}
backendHit = true
}
fp.WriteString(l + "\n")
}
}

View File

@@ -1,24 +0,0 @@
package writers
import (
"katenary/helm"
"os"
"path/filepath"
"gopkg.in/yaml.v3"
)
// BuildService writes the service (external or not).
func BuildService(service *helm.Service, name, templatesDir string) {
kind := "service"
suffix := ""
if service.Spec.Type == "NodePort" {
suffix = "-external"
}
fname := filepath.Join(templatesDir, name+suffix+"."+kind+".yaml")
fp, _ := os.Create(fname)
enc := yaml.NewEncoder(fp)
enc.SetIndent(IndentSize)
enc.Encode(service)
fp.Close()
}

View File

@@ -1,32 +0,0 @@
package writers
import (
"katenary/helm"
"log"
"os"
"path/filepath"
"gopkg.in/yaml.v3"
)
// BuildStorage writes the persistentVolumeClaim.
func BuildStorage(storage *helm.Storage, name, templatesDir string) {
kind := "pvc"
name = storage.Metadata.Labels[helm.K+"/component"]
pvcname := storage.Metadata.Labels[helm.K+"/pvc-name"]
fname := filepath.Join(templatesDir, name+"-"+pvcname+"."+kind+".yaml")
fp, err := os.Create(fname)
if err != nil {
log.Fatal(err)
}
defer fp.Close()
volname := storage.K8sBase.Metadata.Labels[helm.K+"/pvc-name"]
fp.WriteString("{{ if .Values." + name + ".persistence." + volname + ".enabled }}\n")
enc := yaml.NewEncoder(fp)
enc.SetIndent(IndentSize)
if err := enc.Encode(storage); err != nil {
log.Fatal(err)
}
fp.WriteString("{{- end -}}")
}

View File

@@ -1,17 +0,0 @@
package writers
// IndentSize set the indentation size for yaml output. Could ba changed by command line argument.
var IndentSize = 2
// CountSpaces returns the number of spaces from the begining of the line.
func CountSpaces(line string) int {
var spaces int
for _, char := range line {
if char == ' ' {
spaces++
} else {
break
}
}
return spaces
}

65
go.mod
View File

@@ -1,16 +1,57 @@
module katenary
module github.com/katenary/katenary
go 1.16
go 1.25
require (
github.com/compose-spec/compose-go v1.2.4
github.com/distribution/distribution/v3 v3.0.0-20220505155552-985711c1f414 // indirect
github.com/kr/pretty v0.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/spf13/cobra v1.4.0
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
golang.org/x/mod v0.5.1
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
github.com/compose-spec/compose-go v1.20.2
github.com/invopop/jsonschema v0.13.0
github.com/mitchellh/go-wordwrap v1.0.1
github.com/spf13/cobra v1.9.1
github.com/thediveo/netdb v1.1.2
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.33.4
k8s.io/apimachinery v0.33.4
sigs.k8s.io/yaml v1.6.0
)
require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mailru/easyjson v0.9.0 // 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/reflect2 v1.0.2 // 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/spf13/pflag v1.0.7 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // 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-20250813145105-42675adae3e6 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
)

374
go.sum
View File

@@ -1,192 +1,92 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.43.16/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/compose-spec/compose-go v1.2.4 h1:nzTFqM8+2J7Veao5Pq5U451thinv3U1wChIvcjX59/A=
github.com/compose-spec/compose-go v1.2.4/go.mod h1:pAy7Mikpeft4pxkFU565/DRHEbDfR84G6AQuiL+Hdg8=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
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/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/compose-spec/compose-go v1.20.2 h1:u/yfZHn4EaHGdidrZycWpxXgFffjYULlTbRfJ51ykjQ=
github.com/compose-spec/compose-go v1.20.2/go.mod h1:+MdqXV4RA7wdFsahh/Kb8U0pAJqkg7mr4PM9tFKU8RM=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e/go.mod h1:xpWTC2KnJMiDLkoawhsPQcXjvwATEBcbq0xevG2YR9M=
github.com/distribution/distribution/v3 v3.0.0-20220504180456-7a6b9e3042bd h1:KRoLSsR7wZ4H2dueR/O6BGBIXDxfOxUVmaMiu1QiQPw=
github.com/distribution/distribution/v3 v3.0.0-20220504180456-7a6b9e3042bd/go.mod h1:qLi7jGj1b5TUaYTB3ekkHiocxHJi8+3CFhXM54MGKBs=
github.com/distribution/distribution/v3 v3.0.0-20220505155552-985711c1f414 h1:KfVB1Z5fm10trO24Rn5Zzocd8sTm5k/gS24ijxQ1aJU=
github.com/distribution/distribution/v3 v3.0.0-20220505155552-985711c1f414/go.mod h1:2oyLKljQFnsI1tzJxjUg4GI+HEpDfzFP3LrGM04rKg0=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
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/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
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.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
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.13.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/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
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/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
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/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
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/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/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
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.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
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.10.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/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/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -194,111 +94,75 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 h1:SbTAbRFnd5kjQXbczszQ0hdk3ctwYf3qBNH9jIsGclE=
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
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.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/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-20200202094626-16171245cfb2/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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
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.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk=
gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
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/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
k8s.io/api v0.33.4 h1:oTzrFVNPXBjMu0IlpA2eDDIU49jsuEorGHB4cvKupkk=
k8s.io/api v0.33.4/go.mod h1:VHQZ4cuxQ9sCUMESJV5+Fe8bGnqAARZ08tSTdHWfeAc=
k8s.io/apimachinery v0.33.4 h1:SOf/JW33TP0eppJMkIgQ+L6atlDiP/090oaX0y9pd9s=
k8s.io/apimachinery v0.33.4/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
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-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
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.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -1,126 +0,0 @@
package helm
import (
"errors"
"fmt"
"io/ioutil"
"strings"
)
// InlineConfig is made to represent a configMap or a secret
type InlineConfig interface {
AddEnvFile(filename string) error
AddEnv(key, val string) error
Metadata() *Metadata
}
// ConfigMap is made to represent a configMap with data.
type ConfigMap struct {
*K8sBase `yaml:",inline"`
Data map[string]string `yaml:"data"`
}
// NewConfigMap returns a new initialzed ConfigMap.
func NewConfigMap(name, path string) *ConfigMap {
base := NewBase()
base.ApiVersion = "v1"
base.Kind = "ConfigMap"
base.Metadata.Name = ReleaseNameTpl + "-" + name
base.Metadata.Labels[K+"/component"] = name
if path != "" {
base.Metadata.Labels[K+"/path"] = path
}
return &ConfigMap{
K8sBase: base,
Data: make(map[string]string),
}
}
// Metadata returns the metadata of the configMap.
func (c *ConfigMap) Metadata() *Metadata {
return c.K8sBase.Metadata
}
// AddEnvFile adds an environment file to the configMap.
func (c *ConfigMap) AddEnvFile(file string) error {
content, err := ioutil.ReadFile(file)
if err != nil {
return err
}
lines := strings.Split(string(content), "\n")
for _, l := range lines {
l = strings.TrimSpace(l)
if len(l) == 0 {
continue
}
parts := strings.SplitN(l, "=", 2)
if len(parts) < 2 {
return errors.New("The environment file " + file + " is not valid")
}
c.Data[parts[0]] = parts[1]
}
return nil
}
func (c *ConfigMap) AddEnv(key, val string) error {
c.Data[key] = val
return nil
}
// Secret is made to represent a secret with data.
type Secret struct {
*K8sBase `yaml:",inline"`
Data map[string]string `yaml:"data"`
}
// NewSecret returns a new initialzed Secret.
func NewSecret(name, path string) *Secret {
base := NewBase()
base.ApiVersion = "v1"
base.Kind = "Secret"
base.Metadata.Name = ReleaseNameTpl + "-" + name
base.Metadata.Labels[K+"/component"] = name
if path != "" {
base.Metadata.Labels[K+"/path"] = path
}
return &Secret{
K8sBase: base,
Data: make(map[string]string),
}
}
// AddEnvFile adds an environment file to the secret.
func (s *Secret) AddEnvFile(file string) error {
content, err := ioutil.ReadFile(file)
if err != nil {
return err
}
lines := strings.Split(string(content), "\n")
for _, l := range lines {
l = strings.TrimSpace(l)
if len(l) == 0 {
continue
}
parts := strings.SplitN(l, "=", 2)
if len(parts) < 2 {
return errors.New("The environment file " + file + " is not valid")
}
s.Data[parts[0]] = fmt.Sprintf(`{{ "%s" | b64enc }}`, parts[1])
}
return nil
}
// Metadata returns the metadata of the secret.
func (s *Secret) Metadata() *Metadata {
return s.K8sBase.Metadata
}
// AddEnv adds an environment variable to the secret.
func (s *Secret) AddEnv(key, val string) error {
s.Data[key] = fmt.Sprintf(`{{ %s | b64enc }}`, val)
return nil
}

View File

@@ -1,65 +0,0 @@
package helm
import (
"katenary/logger"
"strings"
"github.com/compose-spec/compose-go/types"
)
type EnvValue interface{}
// ContainerPort represent a port mapping.
type ContainerPort struct {
Name string
ContainerPort int `yaml:"containerPort"`
}
// Value represent a environment variable with name and value.
type Value struct {
Name string `yaml:"name"`
Value EnvValue `yaml:"value"`
}
// Container represent a container with name, image, and environment variables. It is used in Deployment.
type Container struct {
Name string `yaml:"name,omitempty"`
Image string `yaml:"image"`
Ports []*ContainerPort `yaml:"ports,omitempty"`
Env []*Value `yaml:"env,omitempty"`
EnvFrom []map[string]map[string]string `yaml:"envFrom,omitempty"`
Command []string `yaml:"command,omitempty"`
VolumeMounts []interface{} `yaml:"volumeMounts,omitempty"`
LivenessProbe *Probe `yaml:"livenessProbe,omitempty"`
}
// NewContainer creates a new container with name, image, labels and environment variables.
func NewContainer(name, image string, environment types.MappingWithEquals, labels map[string]string) *Container {
container := &Container{
Image: image,
Name: name,
EnvFrom: make([]map[string]map[string]string, 0),
}
// find bound environment variable to a service
toServices := make([]string, 0)
if bound, ok := labels[LABEL_ENV_SERVICE]; ok {
toServices = strings.Split(bound, ",")
}
if len(toServices) > 0 {
// warn, it's deprecated now
logger.ActivateColors = true
logger.Yellowf(
"[deprecated] in \"%s\" service: label %s is deprecated and **ignored**, please use %s instead\n"+
"e.g.\n"+
" labels:\n"+
" FOO: {{ .Release.Name }}-fooservice\n",
name,
LABEL_ENV_SERVICE,
LABEL_MAP_ENV,
)
logger.ActivateColors = false
}
return container
}

View File

@@ -1,39 +0,0 @@
package helm
// Deployment is a k8s deployment.
type Deployment struct {
*K8sBase `yaml:",inline"`
Spec *DepSpec `yaml:"spec"`
}
func NewDeployment(name string) *Deployment {
d := &Deployment{K8sBase: NewBase(), Spec: NewDepSpec()}
d.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + name
d.K8sBase.ApiVersion = "apps/v1"
d.K8sBase.Kind = "Deployment"
d.K8sBase.Metadata.Labels[K+"/component"] = name
return d
}
type DepSpec struct {
Replicas int `yaml:"replicas"`
Selector map[string]interface{} `yaml:"selector"`
Template PodTemplate `yaml:"template"`
}
func NewDepSpec() *DepSpec {
return &DepSpec{
Replicas: 1,
}
}
type PodSpec struct {
InitContainers []*Container `yaml:"initContainers,omitempty"`
Containers []*Container `yaml:"containers"`
Volumes []map[string]interface{} `yaml:"volumes,omitempty"`
}
type PodTemplate struct {
Metadata Metadata `yaml:"metadata"`
Spec PodSpec `yaml:"spec"`
}

View File

@@ -1,54 +0,0 @@
package helm
// Ingress is the kubernetes ingress object.
type Ingress struct {
*K8sBase `yaml:",inline"`
Spec IngressSpec
}
func NewIngress(name string) *Ingress {
i := &Ingress{}
i.K8sBase = NewBase()
i.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + name
i.K8sBase.Kind = "Ingress"
i.ApiVersion = "networking.k8s.io/v1"
i.K8sBase.Metadata.Labels[K+"/component"] = name
return i
}
func (i *Ingress) SetIngressClass(name string) {
class := "{{ .Values." + name + ".ingress.class }}"
i.Spec.IngressClassName = class
}
type IngressSpec struct {
IngressClassName string `yaml:"ingressClassName,omitempty"`
Rules []IngressRule
}
type IngressRule struct {
Host string
Http IngressHttp
}
type IngressHttp struct {
Paths []IngressPath
}
type IngressPath struct {
Path string
PathType string `yaml:"pathType"`
Backend *IngressBackend
}
type IngressBackend struct {
Service IngressService
ServiceName string `yaml:"serviceName"` // for kubernetes version < 1.18
ServicePort interface{} `yaml:"servicePort"` // for kubernetes version < 1.18
}
type IngressService struct {
Name string `yaml:"name"`
Port map[string]interface{} `yaml:"port"`
}

View File

@@ -1,73 +0,0 @@
package helm
import (
"crypto/sha1"
"fmt"
"io/ioutil"
"strings"
)
// Metadata is the metadata for a kubernetes object.
type Metadata struct {
Name string `yaml:"name,omitempty"`
Labels map[string]string `yaml:"labels"`
Annotations map[string]string `yaml:"annotations,omitempty"`
}
func NewMetadata() *Metadata {
return &Metadata{
Name: "",
Labels: make(map[string]string),
Annotations: make(map[string]string),
}
}
// K8sBase is the base for all kubernetes objects.
type K8sBase struct {
ApiVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
Metadata *Metadata `yaml:"metadata"`
}
// NewBase is a factory for creating a new base object with metadata, labels and annotations set to the default.
func NewBase() *K8sBase {
b := &K8sBase{
Metadata: NewMetadata(),
}
// add some information of the build
b.Metadata.Labels[K+"/project"] = "{{ .Chart.Name }}"
b.Metadata.Labels[K+"/release"] = ReleaseNameTpl
b.Metadata.Annotations[K+"/version"] = Version
return b
}
func (k *K8sBase) BuildSHA(filename string) {
c, _ := ioutil.ReadFile(filename)
//sum := sha256.Sum256(c)
sum := sha1.Sum(c)
k.Metadata.Annotations[K+"/docker-compose-sha1"] = fmt.Sprintf("%x", string(sum[:]))
}
// Get returns the Kind.
func (k *K8sBase) Get() string {
return k.Kind
}
// Name returns the name of the object from Metadata.
func (k *K8sBase) Name() string {
return k.Metadata.Name
}
func (k *K8sBase) GetType() string {
if n, ok := k.Metadata.Labels[K+"/type"]; ok {
return n
}
return strings.ToLower(k.Kind)
}
func (k *K8sBase) GetPathRessource() string {
if p, ok := k.Metadata.Labels[K+"/path"]; ok {
return p
}
return ""
}

View File

@@ -1,61 +0,0 @@
package helm
import (
"bytes"
"html/template"
)
const ReleaseNameTpl = "{{ .Release.Name }}"
const (
LABEL_MAP_ENV = K + "/mapenv"
LABEL_ENV_SECRET = K + "/secret-envfiles"
LABEL_PORT = K + "/ports"
LABEL_INGRESS = K + "/ingress"
LABEL_VOL_CM = K + "/configmap-volumes"
LABEL_HEALTHCHECK = K + "/healthcheck"
LABEL_SAMEPOD = K + "/same-pod"
LABEL_VOLUMEFROM = K + "/volume-from"
LABEL_EMPTYDIRS = K + "/empty-dirs"
LABEL_IGNORE = K + "/ignore"
LABEL_SECRETVARS = K + "/secret-vars"
//deprecated: use LABEL_MAP_ENV instead
LABEL_ENV_SERVICE = K + "/env-to-service"
)
// GetLabelsDocumentation returns the documentation for the labels.
func GetLabelsDocumentation() string {
t, _ := template.New("labels").Parse(`
# Labels
{{.LABEL_IGNORE | printf "%-33s"}}: ignore the container, it will not yied any object in the helm chart
{{.LABEL_SECRETVARS | printf "%-33s"}}: secret variables to push on a secret file
{{.LABEL_ENV_SECRET | printf "%-33s"}}: set the given file names as a secret instead of configmap
{{.LABEL_MAP_ENV | printf "%-33s"}}: map environment variable to a template string (yaml style)
{{.LABEL_PORT | printf "%-33s"}}: set the ports to expose as a service (coma separated)
{{.LABEL_INGRESS | printf "%-33s"}}: set the port to expose in an ingress (coma separated)
{{.LABEL_VOL_CM | printf "%-33s"}}: specifies that the volumes points on a configmap (coma separated)
{{.LABEL_SAMEPOD | printf "%-33s"}}: specifies that the pod should be deployed in the same pod than the given service name
{{.LABEL_VOLUMEFROM | printf "%-33s"}}: specifies that the volumes to be mounted from the given service (yaml style)
{{.LABEL_EMPTYDIRS | printf "%-33s"}}: specifies that the given volume names should be "emptyDir" instead of persistentVolumeClaim (coma separated)
{{.LABEL_HEALTHCHECK | printf "%-33s"}}: specifies that the container should be monitored by a healthcheck, **it overrides the docker-compose healthcheck**.
{{ printf "%-34s" ""}} You can use these form of label values:
{{ printf "%-35s" ""}}- "http://[not used address][:port][/path]" to specify an http healthcheck
{{ printf "%-35s" ""}}- "tcp://[not used address]:port" to specify a tcp healthcheck
{{ printf "%-35s" ""}}- other string is condidered as a "command" healthcheck
`)
buff := bytes.NewBuffer(nil)
t.Execute(buff, map[string]string{
"LABEL_ENV_SECRET": LABEL_ENV_SECRET,
"LABEL_PORT": LABEL_PORT,
"LABEL_INGRESS": LABEL_INGRESS,
"LABEL_VOL_CM": LABEL_VOL_CM,
"LABEL_HEALTHCHECK": LABEL_HEALTHCHECK,
"LABEL_SAMEPOD": LABEL_SAMEPOD,
"LABEL_VOLUMEFROM": LABEL_VOLUMEFROM,
"LABEL_EMPTYDIRS": LABEL_EMPTYDIRS,
"LABEL_IGNORE": LABEL_IGNORE,
"LABEL_MAP_ENV": LABEL_MAP_ENV,
"LABEL_SECRETVARS": LABEL_SECRETVARS,
})
return buff.String()
}

View File

@@ -1,25 +0,0 @@
package helm
import "strings"
var NOTES = `
Congratulations,
Your application is now deployed. This may take a while to be up and responding.
__list__
`
// GenerateNotesFile generates the notes file for the helm chart.
func GenerateNotesFile(ingressess map[string]*Ingress) string {
list := make([]string, 0)
for name, ing := range ingressess {
for _, r := range ing.Spec.Rules {
list = append(list, "{{ if .Values."+name+".ingress.enabled -}}\n- "+name+" is accessible on : http://"+r.Host+"\n{{- end }}")
}
}
return strings.ReplaceAll(NOTES, "__list__", strings.Join(list, "\n"))
}

View File

@@ -1,104 +0,0 @@
package helm
import (
"time"
"github.com/compose-spec/compose-go/types"
)
// Probe is a struct that can be used to create a Liveness or Readiness probe.
type Probe struct {
HttpGet *HttpGet `yaml:"httpGet,omitempty"`
Exec *Exec `yaml:"exec,omitempty"`
TCP *TCP `yaml:"tcp,omitempty"`
Period float64 `yaml:"periodSeconds"`
InitialDelay float64 `yaml:"initialDelaySeconds"`
Success uint64 `yaml:"successThreshold"`
Failure uint64 `yaml:"failureThreshold"`
}
// Create a new Probe object that can be apply to HttpProbe or TCPProbe.
func NewProbe(period, initialDelaySeconds float64, success, failure uint64) *Probe {
probe := &Probe{
Period: period,
Success: success,
Failure: failure,
InitialDelay: initialDelaySeconds,
}
// fix default values from
// https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
if period == 0 {
probe.Period = 10
}
if success == 0 {
probe.Success = 1
}
if failure == 0 {
probe.Failure = 3
}
return probe
}
// NewProbeWithDuration creates a new Probe object with the given duration from types.
func NewProbeWithDuration(period, initialDelaySeconds *types.Duration, success, failure *uint64) *Probe {
if period == nil {
d := types.Duration(0 * time.Second)
period = &d
}
if initialDelaySeconds == nil {
d := types.Duration(0 * time.Second)
initialDelaySeconds = &d
}
if success == nil {
s := uint64(0)
success = &s
}
if failure == nil {
f := uint64(0)
failure = &f
}
p, err := time.ParseDuration(period.String())
if err != nil {
p = time.Second * 10
}
i, err := time.ParseDuration(initialDelaySeconds.String())
if err != nil {
i = time.Second * 0
}
return NewProbe(p.Seconds(), i.Seconds(), *success, *failure)
}
// NewProbeFromService creates a new Probe object from a ServiceConfig.
func NewProbeFromService(s *types.ServiceConfig) *Probe {
if s == nil || s.HealthCheck == nil {
return NewProbe(0, 0, 0, 0)
}
return NewProbeWithDuration(s.HealthCheck.Interval, s.HealthCheck.StartPeriod, nil, s.HealthCheck.Retries)
}
// HttpGet is a Probe configuration to check http health.
type HttpGet struct {
Path string `yaml:"path"`
Port int `yaml:"port"`
}
// Execis a Probe configuration to check exec health.
type Exec struct {
Command []string `yaml:"command"`
}
// TCP is a Probe configuration to check tcp health.
type TCP struct {
Port int `yaml:"port"`
}

View File

@@ -1,51 +0,0 @@
package helm
// Service is a Kubernetes service.
type Service struct {
*K8sBase `yaml:",inline"`
Spec *ServiceSpec `yaml:"spec"`
}
// NewService creates a new initialized service.
func NewService(name string) *Service {
s := &Service{
K8sBase: NewBase(),
Spec: NewServiceSpec(),
}
s.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + name
s.K8sBase.Kind = "Service"
s.K8sBase.ApiVersion = "v1"
s.K8sBase.Metadata.Labels[K+"/component"] = name
return s
}
// ServicePort is a port on a service.
type ServicePort struct {
Protocol string `yaml:"protocol"`
Port int `yaml:"port"`
TargetPort int `yaml:"targetPort"`
}
// NewServicePort creates a new initialized service port.
func NewServicePort(port, target int) *ServicePort {
return &ServicePort{
Protocol: "TCP",
Port: port,
TargetPort: port,
}
}
// ServiceSpec is the spec for a service.
type ServiceSpec struct {
Selector map[string]string
Ports []*ServicePort
Type string `yaml:"type,omitempty"`
}
// NewServiceSpec creates a new initialized service spec.
func NewServiceSpec() *ServiceSpec {
return &ServiceSpec{
Selector: make(map[string]string),
Ports: make([]*ServicePort, 0),
}
}

View File

@@ -1,54 +0,0 @@
package helm
import "sync"
var (
made = make(map[string]bool)
locker = sync.Mutex{}
)
// ResetMadePVC resets the cache of made PVCs.
// Useful in tests only.
func ResetMadePVC() {
locker.Lock()
defer locker.Unlock()
made = make(map[string]bool)
}
// Storage is a struct for a PersistentVolumeClaim.
type Storage struct {
*K8sBase `yaml:",inline"`
Spec *PVCSpec
}
// NewPVC creates a new PersistentVolumeClaim object.
func NewPVC(name, storageName string) *Storage {
locker.Lock()
defer locker.Unlock()
if _, ok := made[name+storageName]; ok {
return nil
}
made[name+storageName] = true
pvc := &Storage{}
pvc.K8sBase = NewBase()
pvc.K8sBase.Kind = "PersistentVolumeClaim"
pvc.K8sBase.Metadata.Labels[K+"/pvc-name"] = storageName
pvc.K8sBase.ApiVersion = "v1"
pvc.K8sBase.Metadata.Name = ReleaseNameTpl + "-" + storageName
pvc.K8sBase.Metadata.Labels[K+"/component"] = name
pvc.Spec = &PVCSpec{
Resouces: map[string]interface{}{
"requests": map[string]string{
"storage": "{{ .Values." + name + ".persistence." + storageName + ".capacity }}",
},
},
AccessModes: []string{"ReadWriteOnce"},
}
return pvc
}
// PVCSpec is a struct for a PersistentVolumeClaim spec.
type PVCSpec struct {
Resouces map[string]interface{} `yaml:"resources"`
AccessModes []string `yaml:"accessModes"`
}

Some files were not shown because too many files have changed in this diff Show More