feat(dependencies): Add postinstall process and swagger dependency

This commit is contained in:
Matthieu 'JP' DERASSE 2022-07-15 22:16:59 +00:00
parent f04f5513ab
commit 7c9cc568ab
Signed by: mderasse
GPG Key ID: 55141C777B16A705
8 changed files with 430 additions and 22 deletions

1
README.md Normal file
View File

@ -0,0 +1 @@
# Goguik (name TBD)

View File

@ -31,7 +31,10 @@ func init() {
func runUpgradeAction(cmd *cobra.Command, args []string) {
dependencies := []dependencies.Dependency{dependencies.Golang{}}
dependencies := []dependencies.Dependency{
dependencies.Golang{},
dependencies.Swagger{},
}
for _, dependency := range dependencies {
@ -95,7 +98,7 @@ func runUpgradeAction(cmd *cobra.Command, args []string) {
installDirectory = helpers.IsValidPathInput()
}
log.Infof("The following command will be executed:\n%s", dependency.DescribeInstall(installDirectory))
log.Infof("%s", dependency.DescribeInstall(installDirectory))
log.Info("Do you want to continue ?")
answer := helpers.YesOrNoInput()
if !answer {
@ -111,6 +114,21 @@ func runUpgradeAction(cmd *cobra.Command, args []string) {
return
}
log.Infof("%s", dependency.DescribePostInstall(installDirectory))
log.Info("Do you want to continue ?")
answer = helpers.YesOrNoInput()
if !answer {
log.Warnf("Skipping post installation of %s. Some part of the application might not be able to work correctly!", dependency.GetName())
continue
}
log.Debug("Launching post install")
err = dependency.PostInstall(installDirectory)
if err != nil {
log.Errorf("Fail to execute post install of %s. The following error happen: %s", dependency.GetName(), err.Error())
return
}
log.Infof("%s successfully installed", dependency.GetName())
}
}

17
go.mod
View File

@ -3,11 +3,14 @@ module git.home.m-and-m.ovh/mderasse/boot
go 1.17
require (
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/juju/errors v0.0.0-20220331221717-b38fca44723b // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/cobra v1.4.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect
github.com/blang/semver v3.5.1+incompatible
github.com/juju/errors v1.0.0
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.5.0
)
require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
)

7
go.sum
View File

@ -1,21 +1,28 @@
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/juju/errors v0.0.0-20220331221717-b38fca44723b h1:AxFeSQJfcm2O3ov1wqAkTKYFsnMw2g1B4PkYujfAdkY=
github.com/juju/errors v0.0.0-20220331221717-b38fca44723b/go.mod h1:jMGj9DWF/qbo91ODcfJq6z/RYc3FX3taCBZMCcpI4Ls=
github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM=
github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
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/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
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/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View File

@ -66,7 +66,7 @@ func (g Golang) GetBinaryPath() (string, error) {
return path, nil
}
// GetVersion return the major, minor and patch version of Golang
// GetVersion
func (g Golang) GetVersion() (string, error) {
isInstalled, err := g.IsInstalled()
@ -78,14 +78,14 @@ func (g Golang) GetVersion() (string, error) {
return "", errors.NotFoundf("golang is not installed on the system")
}
golangPath, err := g.GetBinaryPath()
binaryPath, err := g.GetBinaryPath()
if err != nil {
return "", errors.Trace(err)
}
log.Debug("executing go version command")
cmd := exec.Command(golangPath, "version")
cmd := exec.Command(binaryPath, "version")
stdout, err := cmd.Output()
if err != nil {
return "", errors.Trace(err)
@ -105,6 +105,14 @@ func (g Golang) GetVersion() (string, error) {
// IsVersionSupported
func (g Golang) IsVersionSupported() (bool, error) {
isInstalled, err := g.IsInstalled()
if err != nil {
return false, errors.Trace(err)
}
if !isInstalled {
return false, nil
}
version, err := g.GetVersion()
if err != nil {
return false, errors.Trace(err)
@ -130,9 +138,13 @@ func (g Golang) IsVersionSupported() (bool, error) {
// DescribeInstall
func (g Golang) DescribeInstall(path string) string {
description := fmt.Sprintf("rm -rf %s/* ", path)
description = fmt.Sprintf("%s\ncurl %s | tar --strip-components=1 -C %s -zxf -", description, getDownloadUrl(), path)
return description
commands := []string{
"The following commands will be executed",
fmt.Sprintf("rm -rf %s/* ", path),
fmt.Sprintf("curl %s | tar --strip-components=1 -C %s -zxf -", g.getDownloadUrl(), path),
}
return strings.Join(commands, "\n")
}
// Install
@ -143,7 +155,7 @@ func (g Golang) Install(path string) error {
log.Warnf("fail to delete content of directory %s", path)
}
downloadUrl := getDownloadUrl()
downloadUrl := g.getDownloadUrl()
content, err := downloadFile(downloadUrl)
if err != nil {
log.Warnf("fail to download file from %s", downloadUrl)
@ -166,6 +178,95 @@ func (g Golang) Install(path string) error {
return nil
}
// DescribePostInstall
func (g Golang) DescribePostInstall(path string) string {
descriptions := []string{
`For your environment to work correctly, we will add if needed the following environment variable to your .bashrc:
export GOPATH=\"$HOME/go\"`,
fmt.Sprintf(" export GOROOT=\"%s\"", path),
` "PATH=\"$GOROOT/bin:$GOPATH/bin:$PATH\"
You will have to reopen a new terminal to apply the changes or execute the following command:
source $HOME/.bashrc
`,
}
return strings.Join(descriptions, "\n")
}
// PostInstall
func (g Golang) PostInstall(path string) error {
lineBashRc := []string{
"# Golang - Added by goguik",
}
gopath := os.Getenv("GOPATH")
createGoPath := false
if gopath == "" {
lineBashRc = append(lineBashRc, "export GOPATH=\"$HOME/go\"")
createGoPath = true
}
goroot := os.Getenv("GOROOT")
if goroot == "" {
lineBashRc = append(lineBashRc, fmt.Sprintf("export GOROOT=\"%s\"", path))
}
// checking if go binary is found after installation
// if not it's probably not in the path
isInstalled, err := g.IsInstalled()
if err != nil {
return errors.Trace(err)
}
if !isInstalled {
lineBashRc = append(lineBashRc, "PATH=\"$GOROOT/bin:$GOPATH/bin:$PATH\"")
}
homeDir, err := os.UserHomeDir()
if err != nil {
return errors.Trace(err)
}
if len(lineBashRc) > 1 {
log.Debug("Adding env variable to .bashrc")
fh, err := os.OpenFile(
fmt.Sprintf("%s.bashrc", homeDir),
os.O_APPEND|os.O_CREATE|os.O_WRONLY,
0644,
)
if err != nil {
return errors.Trace(err)
}
defer fh.Close()
_, err = fh.WriteString(
fmt.Sprintf("\n\n%s\n", strings.Join(lineBashRc, "\n")),
)
if err != nil {
return errors.Trace(err)
}
}
if createGoPath {
log.Debug("creating gopath directory")
err = helpers.CheckAndCreateDir(fmt.Sprintf("%sgo", homeDir))
if err != nil {
return errors.Trace(err)
}
}
return nil
}
// GetInstallDirectory will try to find the current golang directory. If it doesn't exist or if it's in a
// not userspace directory, it will provide the "default"
// It doesn't mean that the directory is "writable"
@ -183,18 +284,18 @@ func (g Golang) GetInstallDirectory() (string, error) {
// concat default install dir with home and we have our path
if !isInstalled {
return fmt.Sprintf("%s/%s", homeDir, defaultGolangInstallDir), nil
return fmt.Sprintf("%s%s", homeDir, defaultGolangInstallDir), nil
}
// now let's play and find the current install path
golangPath, err := g.GetBinaryPath()
binaryPath, err := g.GetBinaryPath()
if err != nil {
return "", errors.Trace(err)
}
log.Debug("executing go env GOROOT command")
cmd := exec.Command(golangPath, "env", "GOROOT")
cmd := exec.Command(binaryPath, "env", "GOROOT")
stdout, err := cmd.Output()
if err != nil {
return "", errors.Trace(err)
@ -202,13 +303,13 @@ func (g Golang) GetInstallDirectory() (string, error) {
cleanOut := strings.TrimSpace(string(stdout))
if !strings.Contains(cleanOut, homeDir) {
return fmt.Sprintf("%s/%s", homeDir, defaultGolangInstallDir), nil
return fmt.Sprintf("%s%s", homeDir, defaultGolangInstallDir), nil
}
return cleanOut, nil
}
func getDownloadUrl() string {
func (g Golang) getDownloadUrl() string {
return fmt.Sprintf("https://dl.google.com/go/go%s.%s-%s.tar.gz", minimumGolangVersion, runtime.GOOS, runtime.GOARCH)
}

View File

@ -3,12 +3,14 @@ package dependencies
// Dependency
type Dependency interface {
DescribeInstall(path string) string
DescribePostInstall(path string) string
GetBinaryPath() (string, error)
GetInstallDirectory() (string, error)
GetName() string
GetMinimumVersion() string
GetVersion() (string, error)
Install(path string) error
PostInstall(path string) error
IsInstalled() (bool, error)
IsVersionSupported() (bool, error)
}

View File

@ -0,0 +1,276 @@
package dependencies
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
"github.com/blang/semver"
"github.com/juju/errors"
log "github.com/sirupsen/logrus"
)
// minimum minor required for the app to work
const minimumSwaggerVersion = "0.29.0"
// installation directory for fresh install.
// will be prefixed by $HOME
const defaultSwaggerInstallDir = "/bin"
var regexSwaggerVersion = regexp.MustCompile(`^version: v(\d+\.\d+\.\d+)`)
type Swagger struct{}
// regroup all swagger dependencies function
// GetName
func (s Swagger) GetName() string {
return "Go Swagger"
}
// GetMinimumVersion
func (s Swagger) GetMinimumVersion() string {
return minimumSwaggerVersion
}
// IsInstalled
func (s Swagger) IsInstalled() (bool, error) {
_, err := s.GetBinaryPath()
if err != nil && !errors.Is(err, exec.ErrNotFound) {
return false, errors.Trace(err)
} else if err != nil && errors.Is(err, exec.ErrNotFound) {
return false, nil
}
return true, nil
}
// GetBinaryPath
func (s Swagger) GetBinaryPath() (string, error) {
log.Debug("looking for swagger binary")
path, err := exec.LookPath("swagger")
if err != nil {
return "", errors.Trace(err)
}
log.Debug("found swagger binary in", path)
return path, nil
}
// GetVersion
func (s Swagger) GetVersion() (string, error) {
isInstalled, err := s.IsInstalled()
if err != nil {
return "", errors.Trace(err)
}
if !isInstalled {
return "", errors.NotFoundf("swagger is not installed on the system")
}
binaryPath, err := s.GetBinaryPath()
if err != nil {
return "", errors.Trace(err)
}
log.Debug("executing swagger version command")
cmd := exec.Command(binaryPath, "version")
stdout, err := cmd.Output()
if err != nil {
return "", errors.Trace(err)
}
cleanOut := strings.TrimSpace(string(stdout))
log.Debugf("swagger version returned %s", cleanOut)
parseOutput := regexSwaggerVersion.FindStringSubmatch(cleanOut)
if len(parseOutput) != 2 {
return "", errors.NotSupportedf("failed to parse swagger version output: %s", cleanOut)
}
return parseOutput[1], nil
}
// IsVersionSupported
func (s Swagger) IsVersionSupported() (bool, error) {
isInstalled, err := s.IsInstalled()
if err != nil {
return false, errors.Trace(err)
}
if !isInstalled {
return false, nil
}
version, err := s.GetVersion()
if err != nil {
return false, errors.Trace(err)
}
installedVersion, err := semver.Make(version)
if err != nil {
return false, errors.Trace(err)
}
requiredVersion, _ := semver.Make(minimumSwaggerVersion)
if err != nil {
return false, errors.Trace(err)
}
if installedVersion.LT(*&requiredVersion) {
return false, nil
}
return true, nil
}
// DescribeInstall
func (s Swagger) DescribeInstall(path string) string {
commands := []string{
"The following commands will be executed",
fmt.Sprintf("rm -rf %s/swagger ", path),
fmt.Sprintf("wget %s -O %s/swagger", s.getDownloadUrl(), path),
fmt.Sprintf("chmod 644 %s/swagger", path),
}
return strings.Join(commands, "\n")
}
// Install
func (s Swagger) Install(path string) error {
downloadUrl := s.getDownloadUrl()
content, err := downloadFile(downloadUrl)
if err != nil {
log.Warnf("fail to download file from %s", downloadUrl)
return errors.Trace(err)
}
fh, err := os.OpenFile(
fmt.Sprintf("%s/swagger", path),
os.O_RDWR|os.O_CREATE|os.O_TRUNC,
0744,
)
if err != nil {
return errors.Trace(err)
}
defer fh.Close()
bContent, err := ioutil.ReadAll(content)
if err != nil {
return errors.Trace(err)
}
_, err = fh.Write(bContent)
if err != nil {
return errors.Trace(err)
}
return nil
}
// DescribePostInstall
func (s Swagger) DescribePostInstall(path string) string {
return `For your environment to work correctly, we will add if needed the following environment variable to your .bashrc:
PATH=\"$HOME/bin:$PATH\"
You will have to reopen a new terminal to apply the changes or execute the following command:
source $HOME/.bashrc
`
}
// PostInstall
func (s Swagger) PostInstall(path string) error {
lineBashRc := []string{
"# Swagger - Added by goguik",
}
// checking if swagger binary is found after installation
// if not it's probably not in the path
isInstalled, err := s.IsInstalled()
if err != nil {
return errors.Trace(err)
}
if !isInstalled {
lineBashRc = append(lineBashRc, "PATH=\"$HOME/bin:$PATH\"")
}
homeDir, err := os.UserHomeDir()
if err != nil {
return errors.Trace(err)
}
if len(lineBashRc) > 1 {
log.Debug("Adding env variable to .bashrc")
fh, err := os.OpenFile(
fmt.Sprintf("%s.bashrc", homeDir),
os.O_APPEND|os.O_CREATE|os.O_WRONLY,
0644,
)
if err != nil {
return errors.Trace(err)
}
defer fh.Close()
_, err = fh.WriteString(
fmt.Sprintf("\n\n%s\n", strings.Join(lineBashRc, "\n")),
)
if err != nil {
return errors.Trace(err)
}
}
return nil
}
// GetInstallDirectory will try to find the current swagger directory. If it doesn't exist or if it's in a
// not userspace directory, it will provide the "default"
// It doesn't mean that the directory is "writable"
func (s Swagger) GetInstallDirectory() (string, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", errors.Trace(err)
}
isInstalled, err := s.IsInstalled()
if err != nil {
return "", errors.Trace(err)
}
// concat default install dir with home and we have our path
if !isInstalled {
return fmt.Sprintf("%s%s", homeDir, defaultSwaggerInstallDir), nil
}
// now let's play and find the current install path
binaryPath, err := s.GetBinaryPath()
if err != nil {
return "", errors.Trace(err)
}
return strings.TrimSuffix(binaryPath, "/swagger"), nil
}
func (s Swagger) getDownloadUrl() string {
return fmt.Sprintf("https://github.com/go-swagger/go-swagger/releases/download/v%s/swagger_%s_%s", minimumSwaggerVersion, runtime.GOOS, runtime.GOARCH)
}

View File

@ -70,7 +70,7 @@ func unTar(reader io.Reader, subdir string, dest string) error {
if subdir != "" && strings.HasPrefix(filename, subdir) {
filename = strings.Replace(filename, subdir, "", 1)
filename = strings.TrimPrefix(filename, subdir)
if filename == "" {
continue