From daf8907f148ee2f486a3c4d1661fa3cae1fcc526 Mon Sep 17 00:00:00 2001 From: Patrice Ferlet Date: Mon, 28 Mar 2022 11:43:17 +0200 Subject: [PATCH] Place the full command to "cmd" package This changed some variables access and the Makefile to build from "./cmd" direcoty. --- Makefile | 2 +- cmd/main.go | 258 ++++++++++++++++++++++-------------------- cmd/utils.go | 140 +++++++++++++++++++++++ main.go | 150 ------------------------ update/main.go | 4 +- update/update_test.go | 5 +- 6 files changed, 279 insertions(+), 280 deletions(-) create mode 100644 cmd/utils.go delete mode 100644 main.go diff --git a/Makefile b/Makefile index 7c14928..af51d4d 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ CTN:=$(shell which podman 2>&1 1>/dev/null && echo "podman" || echo "docker") PREFIX=~/.local GO=container -BLD_CMD=go build -o katenary -ldflags="-X 'main.Version=$(VERSION)'" . +BLD_CMD=go build -o katenary -ldflags="-X 'main.Version=$(VERSION)'" ./cmd/*.go GOOS=linux GOARCH=amd64 diff --git a/cmd/main.go b/cmd/main.go index 421f38b..31dc74d 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,141 +1,151 @@ -package cmd +package main import ( - "errors" "fmt" - "katenary/compose" - "katenary/generator" - "os" - "os/exec" - "path/filepath" - "strings" + "katenary/generator/writers" + "katenary/helm" + "katenary/update" + "strconv" + + "github.com/spf13/cobra" ) -var ( - composeFiles = []string{"docker-compose.yaml", "docker-compose.yml"} - ComposeFile = "" - AppName = "MyApp" - ChartsDir = "chart" - Version = "master" - AppVersion = "0.0.1" -) +var Version = "master" // changed at compile time + +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 -f docker-compose.yml + katenary convert -f docker-compose.yml -o ./charts + +In case of, check the help of each command using: + katenary --help +or + "katenary help " +` func init() { - FindComposeFile() - SetAppName() - SetAppVersion() + // apply the version to the "update" package + update.Version = Version } -func FindComposeFile() bool { - for _, file := range composeFiles { - if _, err := os.Stat(file); err == nil { - ComposeFile = file - return true - } - } - return false -} +func main() { -// 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") + // The base command + rootCmd := &cobra.Command{ + Use: "katenary", + Long: longHelp, + Short: "Katenary is a tool to convert docker-compose files to Helm Charts", } - // check if "git" executable is callable - if _, err := exec.LookPath("git"); err != nil { - return defaulVersion, errors.New("git executable not found") + // to display the version + versionCmd := &cobra.Command{ + Use: "version", + Short: "Display version", + Run: func(c *cobra.Command, args []string) { c.Println(Version) }, } - // 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 - 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 + // 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() + 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, force) + }, } - return defaulVersion, errors.New("git log failed") -} - -func Convert(composeFile, appVersion, appName, chartDir string, force bool) { - composeFound := FindComposeFile() - _, err := os.Stat(ComposeFile) - if !composeFound && err != nil { - fmt.Println("No compose file found") - os.Exit(1) - } - - 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) - } - - // Parse the compose file now - p := compose.NewParser(ComposeFile) - p.Parse(appName) - - // start generator - generator.Generate(p, Version, appName, appVersion, ComposeFile, dirname) - + convertCmd.Flags().BoolP( + "force", "f", false, "force overwrite of existing output files") + convertCmd.Flags().StringP( + "app-version", "a", AppVersion, "app 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.AddCommand( + versionCmd, + convertCmd, + showLabelsCmd, + updateCmd, + ) + + // 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) } diff --git a/cmd/utils.go b/cmd/utils.go new file mode 100644 index 0000000..3b68e7d --- /dev/null +++ b/cmd/utils.go @@ -0,0 +1,140 @@ +package main + +import ( + "errors" + "fmt" + "katenary/compose" + "katenary/generator" + "os" + "os/exec" + "path/filepath" + "strings" +) + +var ( + composeFiles = []string{"docker-compose.yaml", "docker-compose.yml"} + ComposeFile = "" + AppName = "MyApp" + ChartsDir = "chart" + AppVersion = "0.0.1" +) + +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 + 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 string, force bool) { + composeFound := FindComposeFile() + _, err := os.Stat(ComposeFile) + if !composeFound && err != nil { + fmt.Println("No compose file found") + os.Exit(1) + } + + 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) + } + + // Parse the compose file now + p := compose.NewParser(ComposeFile) + p.Parse(appName) + + // start generator + generator.Generate(p, Version, appName, appVersion, ComposeFile, dirname) + +} diff --git a/main.go b/main.go deleted file mode 100644 index 8a5ffbb..0000000 --- a/main.go +++ /dev/null @@ -1,150 +0,0 @@ -package main - -import ( - "fmt" - "katenary/cmd" - "katenary/generator/writers" - "katenary/helm" - "katenary/update" - "strconv" - - "github.com/spf13/cobra" -) - -var Version = "master" // changed at compile time - -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 -f docker-compose.yml - katenary convert -f docker-compose.yml -o ./charts - -In case of, check the help of each command using: - katenary --help -or - "katenary help " -` - -func main() { - - // set the version to "cmd" package - yes... it's ugly - cmd.Version = Version - - // The base command - rootCmd := &cobra.Command{ - Use: "katenary", - Long: longHelp, - Short: "Katenary is a tool to convert docker-compose files to Helm 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/" + - cmd.ChartsDir + "/" + cmd.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() - chartDir := c.Flag("output-dir").Value.String() - indentation, err := strconv.Atoi(c.Flag("indent-size").Value.String()) - if err != nil { - writers.IndentSize = indentation - } - cmd.Convert(composeFile, appversion, appName, chartDir, force) - }, - } - convertCmd.Flags().BoolP( - "force", "f", false, "force overwrite of existing output files") - convertCmd.Flags().StringP( - "app-version", "a", cmd.AppVersion, "app version") - convertCmd.Flags().StringP( - "compose-file", "c", cmd.ComposeFile, "docker compose file") - convertCmd.Flags().StringP( - "app-name", "n", cmd.AppName, "application name") - convertCmd.Flags().StringP( - "output-dir", "o", cmd.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.AddCommand( - versionCmd, - convertCmd, - showLabelsCmd, - updateCmd, - ) - - // 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 cmd.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) -} diff --git a/update/main.go b/update/main.go index 15ed586..3aa693e 100644 --- a/update/main.go +++ b/update/main.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "katenary/cmd" "net/http" "os" "runtime" @@ -13,6 +12,7 @@ import ( ) var exe, _ = os.Executable() +var Version = "master" // reset by cmd/main.go // Asset is a github asset from release url. type Asset struct { @@ -63,7 +63,7 @@ func CheckLatestVersion() (string, []Asset, error) { } // if the current version is the same as the latest version, don't update - if cmd.Version == release.TagName { + if Version == release.TagName { fmt.Println("You are using the latest version") return "", nil, errors.New("You are using the latest version") } diff --git a/update/update_test.go b/update/update_test.go index 69b8180..ec97e8a 100644 --- a/update/update_test.go +++ b/update/update_test.go @@ -2,15 +2,14 @@ package update import ( "fmt" - "katenary/cmd" "os" "testing" ) func TestDownloadLatestRelease(t *testing.T) { - // Change the cmd.Version to "v0.0.0" to test the fallback to the latest release - cmd.Version = "v0.0.0" + // Reset the version to test the latest release + Version = "0.0.0" // change "exe" to /tmp/test-katenary exe = "/tmp/test-katenary"