27 Commits

Author SHA1 Message Date
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
957cc4bcf6 Merge pull request #87 from metal3d/develop
Add schema to the root, fix test coverage
2024-11-26 17:55:46 +01:00
6b301f3171 Merge pull request #86 from metal3d/develop
Add "values-from" and more tests
2024-11-26 16:29:14 +01:00
5f20585fb2 Merge pull request #85 from metal3d/develop
test(codecov): remove codecov
2024-11-26 09:10:48 +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
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
315f5da970 Merge pull request #79 from metal3d/develop
Activable tls
2024-11-09 13:57:35 +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
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
10b9342607 Update README.md
Alert on v3 version
2024-04-23 14:37:19 +02:00
15 changed files with 254 additions and 257 deletions

View File

@@ -9,3 +9,4 @@ updates:
directory: "/" # Location of package manifests
schedule:
interval: "daily"
target-branch: develop

View File

@@ -260,7 +260,7 @@ return {
settings = {
yaml = {
schemas = {
["https://raw.githubusercontent.com/metal3d/katenary/refs/heads/master/katenary.json"] = "katenary.yaml",
["https://raw.githubusercontent.com/metal3d/katenary/master/katenary.json"] = "katenary.yaml",
},
},
},
@@ -276,12 +276,12 @@ Use this address to validate the `katenary.yaml` file in VSCode:
```json
{
"yaml.schemas": {
"https://raw.githubusercontent.com/metal3d/katenary/refs/heads/master/katenary.json": "katenary.yaml"
"https://raw.githubusercontent.com/metal3d/katenary/master/katenary.json": "katenary.yaml"
}
}
```
> You can, of course, replace the `refs/heads/master` with a specific tag or branch.
> You can, of course, replace the `master` with a specific tag or branch.
## What a name…

View File

@@ -141,11 +141,11 @@ func generateConvertCommand() *cobra.Command {
convertCmd := &cobra.Command{
Use: "convert",
Short: "Converts a docker-compose file to a Helm Chart",
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) error {
if givenAppVersion != "" {
appVersion = &givenAppVersion
}
generator.Convert(generator.ConvertOptions{
return generator.Convert(generator.ConvertOptions{
Force: force,
OutputDir: outputDir,
Profiles: profiles,

View File

@@ -38,7 +38,7 @@ var Version = "master" // changed at compile time
## func [Convert](<https://github.com/metal3d/katenary/blob/develop/generator/converter.go#L93>)
```go
func Convert(config ConvertOptions, dockerComposeFile ...string)
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.
@@ -119,7 +119,7 @@ type ChartTemplate struct {
```
<a name="ConfigMap"></a>
## type [ConfigMap](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L35-L40>)
## type [ConfigMap](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L37-L42>)
ConfigMap is a kubernetes ConfigMap. Implements the DataMap interface.
@@ -131,7 +131,7 @@ type ConfigMap struct {
```
<a name="NewConfigMap"></a>
### func [NewConfigMap](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L44>)
### func [NewConfigMap](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L46>)
```go
func NewConfigMap(service types.ServiceConfig, appName string, forFile bool) *ConfigMap
@@ -140,7 +140,7 @@ func NewConfigMap(service types.ServiceConfig, appName string, forFile bool) *Co
NewConfigMap creates a new ConfigMap from a compose service. The appName is the name of the application taken from the project name. The ConfigMap is filled by environment variables and labels "map\-env".
<a name="NewConfigMapFromDirectory"></a>
### func [NewConfigMapFromDirectory](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L117>)
### func [NewConfigMapFromDirectory](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L119>)
```go
func NewConfigMapFromDirectory(service types.ServiceConfig, appName, path string) *ConfigMap
@@ -148,8 +148,17 @@ func NewConfigMapFromDirectory(service types.ServiceConfig, appName, path string
NewConfigMapFromDirectory creates a new ConfigMap from a compose service. This path is the path to the file or directory. If the path is a directory, all files in the directory are added to the ConfigMap. Each subdirectory are ignored. Note that the Generate\(\) function will create the subdirectories ConfigMaps.
<a name="ConfigMap.AddBinaryData"></a>
### func \(\*ConfigMap\) [AddBinaryData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L155>)
```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/metal3d/katenary/blob/develop/generator/configMap.go#L148>)
### func \(\*ConfigMap\) [AddData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L150>)
```go
func (c *ConfigMap) AddData(key, value string)
@@ -158,25 +167,25 @@ func (c *ConfigMap) AddData(key, value string)
AddData adds a key value pair to the configmap. Append or overwrite the value if the key already exists.
<a name="ConfigMap.AppendDir"></a>
### func \(\*ConfigMap\) [AppendDir](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L154>)
### func \(\*ConfigMap\) [AppendDir](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L164>)
```go
func (c *ConfigMap) AppendDir(path string)
func (c *ConfigMap) AppendDir(path string) error
```
AddFile adds files from given path to the configmap. It is not recursive, to add all files in a directory, you need to call this function for each subdirectory.
<a name="ConfigMap.AppendFile"></a>
### func \(\*ConfigMap\) [AppendFile](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L189>)
### func \(\*ConfigMap\) [AppendFile](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L211>)
```go
func (c *ConfigMap) AppendFile(path string)
func (c *ConfigMap) AppendFile(path string) error
```
<a name="ConfigMap.Filename"></a>
### func \(\*ConfigMap\) [Filename](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L207>)
### func \(\*ConfigMap\) [Filename](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L235>)
```go
func (c *ConfigMap) Filename() string
@@ -185,7 +194,7 @@ func (c *ConfigMap) Filename() string
Filename returns the filename of the configmap. If the configmap is used for files, the filename contains the path.
<a name="ConfigMap.SetData"></a>
### func \(\*ConfigMap\) [SetData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L217>)
### func \(\*ConfigMap\) [SetData](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L245>)
```go
func (c *ConfigMap) SetData(data map[string]string)
@@ -194,7 +203,7 @@ func (c *ConfigMap) SetData(data map[string]string)
SetData sets the data of the configmap. It replaces the entire data.
<a name="ConfigMap.Yaml"></a>
### func \(\*ConfigMap\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L222>)
### func \(\*ConfigMap\) [Yaml](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L250>)
```go
func (c *ConfigMap) Yaml() ([]byte, error)
@@ -421,7 +430,7 @@ func (d *Deployment) Yaml() ([]byte, error)
Yaml returns the yaml representation of the deployment.
<a name="FileMapUsage"></a>
## type [FileMapUsage](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L19>)
## type [FileMapUsage](<https://github.com/metal3d/katenary/blob/develop/generator/configMap.go#L21>)
FileMapUsage is the usage of the filemap.

View File

@@ -196,13 +196,13 @@ type Icon string
const (
IconSuccess Icon = "✅"
IconFailure Icon = "❌"
IconWarning Icon = "⚠️'"
IconWarning Icon = ""
IconNote Icon = "📝"
IconWorld Icon = "🌐"
IconPlug Icon = "🔌"
IconPackage Icon = "📦"
IconCabinet Icon = "🗄️"
IconInfo Icon = ""
IconInfo Icon = "🔵"
IconSecret Icon = "🔒"
IconConfig Icon = "🔧"
IconDependency Icon = "🔗"

View File

@@ -1,6 +1,7 @@
package generator
import (
"fmt"
"katenary/generator/labels"
"katenary/generator/labels/labelStructs"
"katenary/utils"
@@ -9,6 +10,7 @@ import (
"path/filepath"
"regexp"
"strings"
"unicode/utf8"
"github.com/compose-spec/compose-go/types"
corev1 "k8s.io/api/core/v1"
@@ -149,58 +151,84 @@ func (c *ConfigMap) AddData(key, value string) {
c.Data[key] = value
}
// AddBinaryData adds binary data to the configmap. Append or overwrite the value if the key already exists.
func (c *ConfigMap) AddBinaryData(key string, value []byte) {
if c.BinaryData == nil {
c.BinaryData = make(map[string][]byte)
}
c.BinaryData[key] = value
}
// AddFile adds files from given path to the configmap. It is not recursive, to add all files in a directory,
// you need to call this function for each subdirectory.
func (c *ConfigMap) AppendDir(path string) {
func (c *ConfigMap) AppendDir(path string) error {
// read all files in the path and add them to the configmap
stat, err := os.Stat(path)
if err != nil {
log.Fatalf("Path %s does not exist\n", path)
return fmt.Errorf("Path %s does not exist, %w\n", path, err)
}
// recursively read all files in the path and add them to the configmap
if stat.IsDir() {
files, err := os.ReadDir(path)
if err != nil {
log.Fatal(err)
return err
}
for _, file := range files {
if file.IsDir() {
utils.Warn("Subdirectories are ignored for the moment, skipping", filepath.Join(path, file.Name()))
continue
}
path := filepath.Join(path, file.Name())
content, err := os.ReadFile(path)
if err != nil {
log.Fatal(err)
return err
}
// remove the path from the file
filename := filepath.Base(path)
c.AddData(filename, string(content))
if utf8.Valid(content) {
c.AddData(filename, string(content))
} else {
c.AddBinaryData(filename, content)
}
}
} else {
// add the file to the configmap
content, err := os.ReadFile(path)
if err != nil {
log.Fatal(err)
return err
}
filename := filepath.Base(path)
if utf8.Valid(content) {
c.AddData(filename, string(content))
} else {
c.AddBinaryData(filename, content)
}
c.AddData(filepath.Base(path), string(content))
}
return nil
}
func (c *ConfigMap) AppendFile(path string) {
func (c *ConfigMap) AppendFile(path string) error {
// read all files in the path and add them to the configmap
stat, err := os.Stat(path)
if err != nil {
log.Fatalf("Path %s does not exist\n", path)
return fmt.Errorf("Path %s doesn not exists, %w", path, err)
}
// recursively read all files in the path and add them to the configmap
if !stat.IsDir() {
// add the file to the configmap
content, err := os.ReadFile(path)
if err != nil {
log.Fatal(err)
return err
}
c.AddData(filepath.Base(path), string(content))
if utf8.Valid(content) {
c.AddData(filepath.Base(path), string(content))
} else {
c.AddBinaryData(filepath.Base(path), content)
}
}
return nil
}
// Filename returns the filename of the configmap. If the configmap is used for files, the filename contains the path.

View File

@@ -6,6 +6,7 @@ import (
"os"
"testing"
"github.com/compose-spec/compose-go/types"
v1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml"
)
@@ -73,3 +74,19 @@ services:
t.Errorf("Expected FOO to be baz, got %s", v)
}
}
func TestAppendBadFile(t *testing.T) {
cm := NewConfigMap(types.ServiceConfig{}, "app", true)
err := cm.AppendFile("foo")
if err == nil {
t.Errorf("Expected error, got nil")
}
}
func TestAppendBadDir(t *testing.T) {
cm := NewConfigMap(types.ServiceConfig{}, "app", true)
err := cm.AppendDir("foo")
if err == nil {
t.Errorf("Expected error, got nil")
}
}

View File

@@ -90,7 +90,7 @@ var keyRegExp = regexp.MustCompile(`^\s*[^#]+:.*`)
// Convert a compose (docker, podman...) project to a helm chart.
// It calls Generate() to generate the chart and then write it to the disk.
func Convert(config ConvertOptions, dockerComposeFile ...string) {
func Convert(config ConvertOptions, dockerComposeFile ...string) error {
var (
templateDir = filepath.Join(config.OutputDir, "templates")
helpersPath = filepath.Join(config.OutputDir, "templates", "_helpers.tpl")
@@ -105,7 +105,7 @@ func Convert(config ConvertOptions, dockerComposeFile ...string) {
// go to the root of the project
if err := os.Chdir(filepath.Dir(dockerComposeFile[0])); err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
return err
}
defer os.Chdir(currentDir) // after the generation, go back to the original directory
@@ -118,13 +118,13 @@ func Convert(config ConvertOptions, dockerComposeFile ...string) {
project, err := parser.Parse(config.Profiles, config.EnvFiles, dockerComposeFile...)
if err != nil {
fmt.Println(err)
os.Exit(1)
return err
}
// check older version of labels
if err := checkOldLabels(project); err != nil {
fmt.Println(utils.IconFailure, err)
os.Exit(1)
return err
}
// TODO: use katenary.yaml file here to set the labels
@@ -140,7 +140,7 @@ func Convert(config ConvertOptions, dockerComposeFile ...string) {
)
if !overwrite {
fmt.Println("Aborting")
os.Exit(126) // 126 is the exit code for "Command invoked cannot execute"
return nil
}
}
fmt.Println() // clean line
@@ -150,7 +150,7 @@ func Convert(config ConvertOptions, dockerComposeFile ...string) {
chart, err := Generate(project)
if err != nil {
fmt.Println(err)
os.Exit(1)
return err
}
// if the app version is set from the command line, use it
@@ -194,6 +194,7 @@ func Convert(config ConvertOptions, dockerComposeFile ...string) {
// call helm update if needed
callHelmUpdate(config)
return nil
}
func addChartDoc(values []byte, project *types.Project) []byte {

View File

@@ -48,7 +48,9 @@ func internalCompileTest(t *testing.T, options ...string) string {
AppVersion: appVersion,
ChartVersion: chartVersion,
}
Convert(convertOptions, "compose.yml")
if err := Convert(convertOptions, "compose.yml"); err != nil {
return err.Error()
}
// launch helm lint to check the generated chart
if helmLint(convertOptions) != nil {

View File

@@ -2,8 +2,13 @@ package generator
import (
"fmt"
"image"
"image/color"
"image/png"
"katenary/generator/labels"
"log"
"os"
"path/filepath"
"testing"
v1 "k8s.io/api/apps/v1"
@@ -149,6 +154,140 @@ services:
}
}
func TestBinaryMount(t *testing.T) {
composeFile := `
services:
web:
image: nginx
volumes:
- ./images/foo.png:/var/www/foo
labels:
%[1]s/configmap-files: |-
- ./images/foo.png
`
composeFile = fmt.Sprintf(composeFile, labels.KatenaryLabelPrefix)
tmpDir := setup(composeFile)
log.Println(tmpDir)
defer teardown(tmpDir)
os.Mkdir(filepath.Join(tmpDir, "images"), 0o755)
// create a png image
pngFile := tmpDir + "/images/foo.png"
w, h := 100, 100
img := image.NewRGBA(image.Rect(0, 0, w, h))
red := color.RGBA{255, 0, 0, 255}
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
img.Set(x, y, red)
}
}
blue := color.RGBA{0, 0, 255, 255}
for y := 30; y < 70; y++ {
for x := 30; x < 70; x++ {
img.Set(x, y, blue)
}
}
currentDir, _ := os.Getwd()
os.Chdir(tmpDir)
defer os.Chdir(currentDir)
f, err := os.Create(pngFile)
if err != nil {
t.Fatal(err)
}
png.Encode(f, img)
f.Close()
output := internalCompileTest(t, "-s", "templates/web/deployment.yaml")
d := v1.Deployment{}
yaml.Unmarshal([]byte(output), &d)
volumes := d.Spec.Template.Spec.Volumes
if len(volumes) != 1 {
t.Errorf("Expected 1 volume, got %d", len(volumes))
}
cm := corev1.ConfigMap{}
cmContent, err := helmTemplate(ConvertOptions{
OutputDir: "chart",
}, "-s", "templates/web/statics/images/configmap.yaml")
yaml.Unmarshal([]byte(cmContent), &cm)
if im, ok := cm.BinaryData["foo.png"]; !ok {
t.Errorf("Expected foo.png to be in the configmap")
} else {
if len(im) == 0 {
t.Errorf("Expected image to be non-empty")
}
}
}
func TestGloballyBinaryMount(t *testing.T) {
composeFile := `
services:
web:
image: nginx
volumes:
- ./images:/var/www/foo
labels:
%[1]s/configmap-files: |-
- ./images
`
composeFile = fmt.Sprintf(composeFile, labels.KatenaryLabelPrefix)
tmpDir := setup(composeFile)
log.Println(tmpDir)
defer teardown(tmpDir)
os.Mkdir(filepath.Join(tmpDir, "images"), 0o755)
// create a png image
pngFile := tmpDir + "/images/foo.png"
w, h := 100, 100
img := image.NewRGBA(image.Rect(0, 0, w, h))
red := color.RGBA{255, 0, 0, 255}
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
img.Set(x, y, red)
}
}
blue := color.RGBA{0, 0, 255, 255}
for y := 30; y < 70; y++ {
for x := 30; x < 70; x++ {
img.Set(x, y, blue)
}
}
currentDir, _ := os.Getwd()
os.Chdir(tmpDir)
defer os.Chdir(currentDir)
f, err := os.Create(pngFile)
if err != nil {
t.Fatal(err)
}
png.Encode(f, img)
f.Close()
output := internalCompileTest(t, "-s", "templates/web/deployment.yaml")
d := v1.Deployment{}
yaml.Unmarshal([]byte(output), &d)
volumes := d.Spec.Template.Spec.Volumes
if len(volumes) != 1 {
t.Errorf("Expected 1 volume, got %d", len(volumes))
}
cm := corev1.ConfigMap{}
cmContent, err := helmTemplate(ConvertOptions{
OutputDir: "chart",
}, "-s", "templates/web/statics/images/configmap.yaml")
yaml.Unmarshal([]byte(cmContent), &cm)
if im, ok := cm.BinaryData["foo.png"]; !ok {
t.Errorf("Expected foo.png to be in the configmap")
} else {
if len(im) == 0 {
t.Errorf("Expected image to be non-empty")
}
}
}
func TestBindFrom(t *testing.T) {
composeFile := `
services:

11
go.mod
View File

@@ -10,10 +10,9 @@ require (
github.com/mitchellh/go-wordwrap v1.0.1
github.com/spf13/cobra v1.8.1
github.com/thediveo/netdb v1.1.2
golang.org/x/mod v0.22.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.31.2
k8s.io/apimachinery v0.31.2
k8s.io/api v0.31.3
k8s.io/apimachinery v0.31.3
sigs.k8s.io/yaml v1.4.0
)
@@ -44,8 +43,8 @@ require (
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
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
@@ -53,5 +52,5 @@ require (
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.3 // indirect
)

26
go.sum
View File

@@ -103,18 +103,16 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
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.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-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.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
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=
@@ -134,8 +132,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
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=
@@ -150,17 +148,17 @@ 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.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8=
k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE=
k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4=
k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
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-20241104163129-6fe5fd82f078 h1:jGnCPejIetjiy2gqaJ5V0NLwTpF4wbQ6cZIItJCSHno=
k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.3 h1:sCP7Vv3xx/CWIuTPVN38lUPx0uw0lcLfzaiDa8Ja01A=
sigs.k8s.io/structured-merge-diff/v4 v4.4.3/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

View File

@@ -1,148 +0,0 @@
/* Update package is used to check if a new version of katenary is available.*/
package update
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"runtime"
"time"
"golang.org/x/mod/semver"
)
var (
exe, _ = os.Executable()
Version = "master" // reset by cmd/main.go
)
// Asset is a github asset from release url.
type Asset struct {
Name string `json:"name"`
URL string `json:"browser_download_url"`
}
// CheckLatestVersion check katenary latest version from release and propose to download it
func CheckLatestVersion() (string, []Asset, error) {
githuburl := "https://api.github.com/repos/metal3d/katenary/releases/latest"
// Create a HTTP client with 1s timeout
client := &http.Client{
Timeout: time.Second * 1,
}
// Create a request
req, err := http.NewRequest("GET", githuburl, nil)
if err != nil {
return "", nil, err
}
// Send the request via a client
resp, err := client.Do(req)
if err != nil {
return "", nil, err
}
defer resp.Body.Close()
// Get tag_name from the json response
release := struct {
TagName string `json:"tag_name"`
Assets []Asset `json:"assets"`
PreRelease bool `json:"prerelease"`
}{}
err = json.NewDecoder(resp.Body).Decode(&release)
if err != nil {
return "", nil, err
}
// if it's a prerelease, don't update
if release.PreRelease {
return "", nil, errors.New("prerelease detected, not updating")
}
// no tag, don't update
if release.TagName == "" {
return "", nil, errors.New("no release found")
}
// compare the current version, if the current version is the same or lower than the latest version, don't update
versions := []string{Version, release.TagName}
semver.Sort(versions)
if versions[1] == Version {
return "", nil, errors.New("current version is the latest version")
}
return release.TagName, release.Assets, nil
}
// DownloadLatestVersion will download the latest version of katenary.
func DownloadLatestVersion(assets []Asset) error {
defer func() {
if r := recover(); r != nil {
os.Rename(exe+".old", exe)
}
}()
// Download the latest version
fmt.Println("Downloading the latest version...")
// ok, replace this from the current version to the latest version
err := os.Rename(exe, exe+".old")
if err != nil {
return err
}
// Download the latest version for the current OS
for _, asset := range assets {
switch runtime.GOOS {
case "windows":
if asset.Name == "katenary.exe" {
err = DownloadFile(asset.URL, exe)
}
case "linux":
switch runtime.GOARCH {
case "amd64":
if asset.Name == "katenary-linux-amd64" {
err = DownloadFile(asset.URL, exe)
}
case "arm64":
if asset.Name == "katenary-linux-arm64" {
err = DownloadFile(asset.URL, exe)
}
}
case "darwin":
if asset.Name == "katenary-darwin" {
err = DownloadFile(asset.URL, exe)
}
default:
fmt.Println("Unsupported OS")
err = errors.New("unsupported OS")
}
}
if err == nil {
// remove the old version
os.Remove(exe + ".old")
} else {
// restore the old version
os.Rename(exe+".old", exe)
}
return err
}
// DownloadFile will download a url to a local file. It also ensure that the file is executable.
func DownloadFile(url, exe string) error {
// Download the url binary to exe path
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
fp, err := os.OpenFile(exe, os.O_WRONLY|os.O_CREATE, 0o755)
if err != nil {
return err
}
defer fp.Close()
_, err = io.Copy(fp, resp.Body)
return err
}

View File

@@ -1,49 +0,0 @@
package update
import (
"fmt"
"os"
"testing"
)
func TestDownloadLatestRelease(t *testing.T) {
// Reset the version to test the latest release
Version = "0.0.0"
// change "exe" to /tmp/test-katenary
exe = "/tmp/test-katenary"
defer os.Remove(exe)
// Now call the CheckLatestVersion function
version, assets, err := CheckLatestVersion()
if err != nil {
t.Errorf("Error getting latest version: %s", err)
}
fmt.Println("Version found", version)
// Touch exe binary
f, _ := os.OpenFile(exe, os.O_RDONLY|os.O_CREATE, 0o755)
f.Write(nil)
f.Close()
err = DownloadLatestVersion(assets)
if err != nil {
t.Errorf("Error: %s", err)
}
}
func TestAlreadyUpToDate(t *testing.T) {
Version = "99999.999.99"
exe = "/tmp/test-katenary"
defer os.Remove(exe)
// Call the version check
version, _, err := CheckLatestVersion()
if err == nil {
t.Errorf("Error: %s", err)
}
t.Log("Version is already the most recent", version)
}

View File

@@ -9,13 +9,13 @@ type Icon string
const (
IconSuccess Icon = "✅"
IconFailure Icon = "❌"
IconWarning Icon = "⚠️'"
IconWarning Icon = ""
IconNote Icon = "📝"
IconWorld Icon = "🌐"
IconPlug Icon = "🔌"
IconPackage Icon = "📦"
IconCabinet Icon = "🗄️"
IconInfo Icon = ""
IconInfo Icon = "🔵"
IconSecret Icon = "🔒"
IconConfig Icon = "🔧"
IconDependency Icon = "🔗"