From a44b40e9457316b1c2e30cf5af6daccabc98240d Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Thu, 23 Apr 2020 16:31:38 -0400 Subject: [PATCH 01/67] add finder interface and SitesEnabled Signed-off-by: Jason McCallister --- internal/find/find.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/internal/find/find.go b/internal/find/find.go index 105ddef9..eb523dfe 100644 --- a/internal/find/find.go +++ b/internal/find/find.go @@ -1,6 +1,7 @@ package find import ( + "bufio" "bytes" "encoding/csv" "fmt" @@ -10,6 +11,13 @@ import ( "github.com/craftcms/nitro/config" ) + +// Finder is an interface the wraps the exec.Command Output function +// it is used by this package to parse output of the exec.Command +type Finder interface { + Output() ([]byte, error) +} + // Mounts will take a name of a machine and the output of an exec.Command as a slice of bytes // and return a slice of config mounts that has a source and destination or an error. This is // used to match if the machine has any mounts. The args passed to multipass are expected to @@ -65,3 +73,27 @@ func ContainersToCreate(machine string, cfg config.Config) ([]config.Database, e return dbs, nil } + +// SitesEnabled takes a finder which is a command executed +// by the multipass cli tool that outputs the contents +// (symlinks) or sites-enabled and returns sites. +func SitesEnabled(f Finder) ([]config.Site, error) { + out, err := f.Output() + if err != nil { + return nil, err + } + + // parse the out + var sites []config.Site + sc := bufio.NewScanner(strings.NewReader(string(out))) + for sc.Scan() { + if l := sc.Text(); l != "" { + path := strings.Split(strings.TrimSpace(sc.Text()), "/") + if h := path[len(path)-1]; h != "default" { + sites = append(sites, config.Site{Hostname: h}) + } + } + } + + return sites, nil +} From 88ff072eabe5c23786e0fc564fe7e4df0cefd064 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Thu, 23 Apr 2020 16:40:41 -0400 Subject: [PATCH 02/67] skip empty sites as well Signed-off-by: Jason McCallister --- internal/find/find.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/find/find.go b/internal/find/find.go index eb523dfe..4495a8c6 100644 --- a/internal/find/find.go +++ b/internal/find/find.go @@ -11,7 +11,6 @@ import ( "github.com/craftcms/nitro/config" ) - // Finder is an interface the wraps the exec.Command Output function // it is used by this package to parse output of the exec.Command type Finder interface { @@ -88,8 +87,8 @@ func SitesEnabled(f Finder) ([]config.Site, error) { sc := bufio.NewScanner(strings.NewReader(string(out))) for sc.Scan() { if l := sc.Text(); l != "" { - path := strings.Split(strings.TrimSpace(sc.Text()), "/") - if h := path[len(path)-1]; h != "default" { + sp := strings.Split(strings.TrimSpace(sc.Text()), "/") + if h := sp[len(sp)-1]; (h != "default") || (h != "") { sites = append(sites, config.Site{Hostname: h}) } } From 2843bb27646b2a16066aada0d9678dbfd6e2c634 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Thu, 23 Apr 2020 16:48:15 -0400 Subject: [PATCH 03/67] remove or Signed-off-by: Jason McCallister --- internal/find/find.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/find/find.go b/internal/find/find.go index 4495a8c6..f287d507 100644 --- a/internal/find/find.go +++ b/internal/find/find.go @@ -88,7 +88,7 @@ func SitesEnabled(f Finder) ([]config.Site, error) { for sc.Scan() { if l := sc.Text(); l != "" { sp := strings.Split(strings.TrimSpace(sc.Text()), "/") - if h := sp[len(sp)-1]; (h != "default") || (h != "") { + if h := sp[len(sp)-1]; h != "default" { sites = append(sites, config.Site{Hostname: h}) } } From 30c1475313a1d691d1a61f3d9bf7e3b6bf00da75 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Thu, 23 Apr 2020 17:01:07 -0400 Subject: [PATCH 04/67] wip rename command Signed-off-by: Jason McCallister --- internal/cmd/rename.go | 85 ++++++++++++++++++++++++++++++++++++++++++ internal/cmd/root.go | 1 + 2 files changed, 86 insertions(+) create mode 100644 internal/cmd/rename.go diff --git a/internal/cmd/rename.go b/internal/cmd/rename.go new file mode 100644 index 00000000..5963ac8e --- /dev/null +++ b/internal/cmd/rename.go @@ -0,0 +1,85 @@ +package cmd + +import ( + "errors" + "fmt" + "os/exec" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/craftcms/nitro/config" + "github.com/craftcms/nitro/internal/find" + "github.com/craftcms/nitro/internal/nitro" + "github.com/craftcms/nitro/internal/prompt" +) + +var renameCommand = &cobra.Command{ + Use: "rename", + Short: "Rename a site", + RunE: func(cmd *cobra.Command, args []string) error { + machine := flagMachineName + + var configFile config.Config + if err := viper.Unmarshal(&configFile); err != nil { + return err + } + + configSites := configFile.GetSites() + + if len(configSites) == 0 { + return errors.New("there are no sites to rename") + } + + i, _ := prompt.Select("Select site to rename", configFile.SitesAsList()) + + siteToRename := configSites[i] + + path, err := exec.LookPath("multipass") + if err != nil { + return err + } + + _, err = find.SitesEnabled( + exec.Command(path, []string{"exec", machine, "--", "find", "/etc/nginx/sites-enabled/", "-maxdepth", "1", "-type", "l"}...), + ) + if err != nil { + return err + } + + var actions []nitro.Action + + // remove the symlink + removeSymlinkAction, err := nitro.RemoveSymlink(machine, siteToRename.Hostname) + if err != nil { + return err + } + actions = append(actions, *removeSymlinkAction) + + copyTemplateAction, err := nitro.CopyNginxTemplate(machine, siteToRename.Hostname) + if err != nil { + return err + } + actions = append(actions, *copyTemplateAction) + + // TODO add PHP back to the config file and add to apply + changeNginxVariablesAction, err := nitro.ChangeTemplateVariables(machine, siteToRename.Webroot, siteToRename.Hostname, "7.4", siteToRename.Aliases) + if err != nil { + return err + } + actions = append(actions, *changeNginxVariablesAction...) + + // restart nginx + restartNginxAction, err := nitro.NginxReload(machine) + if err != nil { + return err + } + actions = append(actions, *restartNginxAction) + + for _, action := range actions { + fmt.Println(action.Args) + } + + return nil + }, +} diff --git a/internal/cmd/root.go b/internal/cmd/root.go index e7974eb7..dc91fe20 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -43,6 +43,7 @@ func init() { editCommand, importCommand, hostsCommand, + renameCommand, ) xdebugCommand.AddCommand(xdebugOnCommand, xdebugOffCommand, xdebugConfigureCommand) } From d0b6e864b209ff3f5f59580401c02ed8360a072f Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Thu, 23 Apr 2020 17:13:57 -0400 Subject: [PATCH 05/67] hardcode bionic in launch Signed-off-by: Jason McCallister --- internal/nitro/launch.go | 2 +- internal/nitro/launch_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/nitro/launch.go b/internal/nitro/launch.go index bd9f5ca2..8597f675 100644 --- a/internal/nitro/launch.go +++ b/internal/nitro/launch.go @@ -37,6 +37,6 @@ func Launch(name string, cpus int, memory, disk, input string) (*Action, error) Type: "launch", UseSyscall: false, Input: input, - Args: []string{"launch", "--name", name, "--cpus", strconv.Itoa(cpus), "--mem", memory, "--disk", disk, "--cloud-init", "-"}, + Args: []string{"launch", "--name", name, "--cpus", strconv.Itoa(cpus), "--mem", memory, "--disk", disk, "bionic", "--cloud-init", "-"}, }, nil } diff --git a/internal/nitro/launch_test.go b/internal/nitro/launch_test.go index 9305da86..9b3738dd 100644 --- a/internal/nitro/launch_test.go +++ b/internal/nitro/launch_test.go @@ -32,7 +32,7 @@ func TestLaunch(t *testing.T) { Type: "launch", UseSyscall: false, Input: "someinput", - Args: []string{"launch", "--name", "machine", "--cpus", "4", "--mem", "2G", "--disk", "20G", "--cloud-init", "-"}, + Args: []string{"launch", "--name", "machine", "--cpus", "4", "--mem", "2G", "--disk", "20G", "bionic", "--cloud-init", "-"}, }, wantErr: false, }, From 8cc64c3befae661c96643c64228aef20de70cd78 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Fri, 24 Apr 2020 08:32:13 -0400 Subject: [PATCH 06/67] move actions into task package Signed-off-by: Jason McCallister --- internal/cmd/remove.go | 29 ++--------------------------- internal/task/remove.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 27 deletions(-) create mode 100644 internal/task/remove.go diff --git a/internal/cmd/remove.go b/internal/cmd/remove.go index b32c0970..e8b9cedf 100644 --- a/internal/cmd/remove.go +++ b/internal/cmd/remove.go @@ -14,6 +14,7 @@ import ( "github.com/craftcms/nitro/internal/nitro" "github.com/craftcms/nitro/internal/prompt" "github.com/craftcms/nitro/internal/sudo" + "github.com/craftcms/nitro/internal/task" ) var removeCommand = &cobra.Command{ @@ -76,7 +77,7 @@ var removeCommand = &cobra.Command{ } // END HACK - actions, err := removeActions(machine, *mount, site) + actions, err := task.Remove(machine, *mount, site) if err != nil { return err } @@ -107,29 +108,3 @@ var removeCommand = &cobra.Command{ return sudo.RunCommand(nitro, machine, "hosts", "remove", site.Hostname) }, } - -func removeActions(name string, mount config.Mount, site config.Site) ([]nitro.Action, error) { - var actions []nitro.Action - - // unmount - unmountAction, err := nitro.UnmountDir(name, mount.Dest) - if err != nil { - return nil, err - } - actions = append(actions, *unmountAction) - - // remove nginx symlink - removeSymlinkAction, err := nitro.RemoveSymlink(name, site.Hostname) - if err != nil { - return nil, err - } - actions = append(actions, *removeSymlinkAction) - - restartNginxAction, err := nitro.NginxReload(name) - if err != nil { - return nil, err - } - actions = append(actions, *restartNginxAction) - - return actions, nil -} diff --git a/internal/task/remove.go b/internal/task/remove.go new file mode 100644 index 00000000..716b965d --- /dev/null +++ b/internal/task/remove.go @@ -0,0 +1,32 @@ +package task + +import ( + "github.com/craftcms/nitro/config" + "github.com/craftcms/nitro/internal/nitro" +) + +func Remove(name string, mount config.Mount, site config.Site) ([]nitro.Action, error) { + var actions []nitro.Action + + // unmount + unmountAction, err := nitro.UnmountDir(name, mount.Dest) + if err != nil { + return nil, err + } + actions = append(actions, *unmountAction) + + // remove nginx symlink + removeSymlinkAction, err := nitro.RemoveSymlink(name, site.Hostname) + if err != nil { + return nil, err + } + actions = append(actions, *removeSymlinkAction) + + restartNginxAction, err := nitro.NginxReload(name) + if err != nil { + return nil, err + } + actions = append(actions, *restartNginxAction) + + return actions, nil +} From 42e7584381c83b0784d8aba8b142b0279247f5d2 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Fri, 24 Apr 2020 08:32:19 -0400 Subject: [PATCH 07/67] reorder Signed-off-by: Jason McCallister --- internal/cmd/rename.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/cmd/rename.go b/internal/cmd/rename.go index 5963ac8e..8b436b7a 100644 --- a/internal/cmd/rename.go +++ b/internal/cmd/rename.go @@ -20,6 +20,11 @@ var renameCommand = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { machine := flagMachineName + path, err := exec.LookPath("multipass") + if err != nil { + return err + } + var configFile config.Config if err := viper.Unmarshal(&configFile); err != nil { return err @@ -35,11 +40,6 @@ var renameCommand = &cobra.Command{ siteToRename := configSites[i] - path, err := exec.LookPath("multipass") - if err != nil { - return err - } - _, err = find.SitesEnabled( exec.Command(path, []string{"exec", machine, "--", "find", "/etc/nginx/sites-enabled/", "-maxdepth", "1", "-type", "l"}...), ) From 84480481981f9238bbead0e2b2b78f6da81b27ab Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Fri, 24 Apr 2020 08:39:07 -0400 Subject: [PATCH 08/67] move add to task pkg Signed-off-by: Jason McCallister --- internal/cmd/add.go | 36 ++--------------------------------- internal/task/add.go | 45 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 34 deletions(-) create mode 100644 internal/task/add.go diff --git a/internal/cmd/add.go b/internal/cmd/add.go index 02032ad9..d6aa8605 100644 --- a/internal/cmd/add.go +++ b/internal/cmd/add.go @@ -12,6 +12,7 @@ import ( "github.com/craftcms/nitro/internal/helpers" "github.com/craftcms/nitro/internal/nitro" "github.com/craftcms/nitro/internal/sudo" + "github.com/craftcms/nitro/internal/task" "github.com/craftcms/nitro/internal/webroot" "github.com/craftcms/nitro/validate" ) @@ -131,40 +132,7 @@ var addCommand = &cobra.Command{ return nil } - var actions []nitro.Action - // mount the directory - m := configFile.Mounts[len(configFile.Mounts)-1] - mountAction, err := nitro.MountDir(machine, m.AbsSourcePath(), m.Dest) - if err != nil { - return err - } - actions = append(actions, *mountAction) - - // copy the nginx template - copyTemplateAction, err := nitro.CopyNginxTemplate(machine, site.Hostname) - if err != nil { - return err - } - actions = append(actions, *copyTemplateAction) - - // copy the nginx template - changeNginxVariablesAction, err := nitro.ChangeTemplateVariables(machine, site.Webroot, site.Hostname, configFile.PHP, site.Aliases) - if err != nil { - return err - } - actions = append(actions, *changeNginxVariablesAction...) - - createSymlinkAction, err := nitro.CreateSiteSymllink(machine, site.Hostname) - if err != nil { - return err - } - actions = append(actions, *createSymlinkAction) - - restartNginxAction, err := nitro.NginxReload(machine) - if err != nil { - return err - } - actions = append(actions, *restartNginxAction) + actions, err := task.Add(machine, configFile, site) if flagDebug { for _, action := range actions { diff --git a/internal/task/add.go b/internal/task/add.go new file mode 100644 index 00000000..1f9c8e41 --- /dev/null +++ b/internal/task/add.go @@ -0,0 +1,45 @@ +package task + +import ( + "github.com/craftcms/nitro/config" + "github.com/craftcms/nitro/internal/nitro" +) + +func Add(machine string, configFile config.Config, site config.Site) ([]nitro.Action, error) { + var actions []nitro.Action + // mount the directory + m := configFile.Mounts[len(configFile.Mounts)-1] + mountAction, err := nitro.MountDir(machine, m.AbsSourcePath(), m.Dest) + if err != nil { + return nil, err + } + actions = append(actions, *mountAction) + + // copy the nginx template + copyTemplateAction, err := nitro.CopyNginxTemplate(machine, site.Hostname) + if err != nil { + return nil, err + } + actions = append(actions, *copyTemplateAction) + + // copy the nginx template + changeNginxVariablesAction, err := nitro.ChangeTemplateVariables(machine, site.Webroot, site.Hostname, configFile.PHP, site.Aliases) + if err != nil { + return nil, err + } + actions = append(actions, *changeNginxVariablesAction...) + + createSymlinkAction, err := nitro.CreateSiteSymllink(machine, site.Hostname) + if err != nil { + return nil, err + } + actions = append(actions, *createSymlinkAction) + + restartNginxAction, err := nitro.NginxReload(machine) + if err != nil { + return nil, err + } + actions = append(actions, *restartNginxAction) + + return actions, nil +} \ No newline at end of file From ebaa658fb324b8295fb5c67432cf4a91eee011d4 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Fri, 24 Apr 2020 08:59:09 -0400 Subject: [PATCH 09/67] cleanup Signed-off-by: Jason McCallister --- internal/cmd/init.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/init.go b/internal/cmd/init.go index 39aa9e60..4fe56445 100644 --- a/internal/cmd/init.go +++ b/internal/cmd/init.go @@ -71,8 +71,8 @@ var initCommand = &cobra.Command{ if strings.Contains(engine, "postgres") { port = "5432" } - // TODO check if the port has already been used and +1 it + // TODO check if the port has already been used and +1 it cfg.Databases = []config.Database{ { Engine: engine, From 6e8be7b46d1d9dc9145e0ef5353f728a7244039e Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Fri, 24 Apr 2020 09:13:37 -0400 Subject: [PATCH 10/67] add find.PHPVersion Signed-off-by: Jason McCallister --- internal/find/find.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/internal/find/find.go b/internal/find/find.go index f287d507..d390da01 100644 --- a/internal/find/find.go +++ b/internal/find/find.go @@ -96,3 +96,32 @@ func SitesEnabled(f Finder) ([]config.Site, error) { return sites, nil } + +// PHPVersion is used to get the "current" or "default" version +// of PHP that is installed. It expects exec.Command to sent +// "multipass", "exec", machine, "--", "php", "--version" +func PHPVersion(f Finder) (string, error) { + out, err := f.Output() + if err != nil { + return "", err + } + + var version string + sc := bufio.NewScanner(strings.NewReader(string(out))) + c := 0 + for sc.Scan() { + if c > 0 { + break + } + + if l := sc.Text(); l != "" { + sp := strings.Split(strings.TrimSpace(sc.Text()), " ") + full := strings.Split(sp[1], ".") + version = fmt.Sprintf("%s.%s", full[0], full[1]) + } + + c = c + 1 + } + + return version, nil +} From b6a01af33bafcfacd19e5096a98684a825c70f80 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Fri, 24 Apr 2020 09:31:41 -0400 Subject: [PATCH 11/67] added rename command Signed-off-by: Jason McCallister --- config/config.go | 12 ++++++ internal/cmd/rename.go | 86 +++++++++++++++++++++++++++++------------ internal/task/rename.go | 38 ++++++++++++++++++ 3 files changed, 111 insertions(+), 25 deletions(-) create mode 100644 internal/task/rename.go diff --git a/config/config.go b/config/config.go index b232ac3b..aac9ab0d 100644 --- a/config/config.go +++ b/config/config.go @@ -106,6 +106,18 @@ func (c *Config) AddMount(m Mount) error { return nil } +func (c *Config) RenameSite(site Site, hostname string) error { + for _, s := range c.Sites { + if site.Hostname == s.Hostname { + s.Hostname = hostname + + return nil + } + } + + return errors.New("unable to locate the site with the hostname: " + site.Hostname) +} + func (c *Config) RemoveSite(hostname string) error { for i := len(c.Sites) - 1; i >= 0; i-- { site := c.Sites[i] diff --git a/internal/cmd/rename.go b/internal/cmd/rename.go index 8b436b7a..74caf1ce 100644 --- a/internal/cmd/rename.go +++ b/internal/cmd/rename.go @@ -5,6 +5,7 @@ import ( "fmt" "os/exec" + "github.com/manifoldco/promptui" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -12,6 +13,9 @@ import ( "github.com/craftcms/nitro/internal/find" "github.com/craftcms/nitro/internal/nitro" "github.com/craftcms/nitro/internal/prompt" + "github.com/craftcms/nitro/internal/sudo" + "github.com/craftcms/nitro/internal/task" + "github.com/craftcms/nitro/validate" ) var renameCommand = &cobra.Command{ @@ -20,11 +24,6 @@ var renameCommand = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { machine := flagMachineName - path, err := exec.LookPath("multipass") - if err != nil { - return err - } - var configFile config.Config if err := viper.Unmarshal(&configFile); err != nil { return err @@ -36,48 +35,85 @@ var renameCommand = &cobra.Command{ return errors.New("there are no sites to rename") } + // ask to select a site i, _ := prompt.Select("Select site to rename", configFile.SitesAsList()) - siteToRename := configSites[i] + site := configSites[i] - _, err = find.SitesEnabled( - exec.Command(path, []string{"exec", machine, "--", "find", "/etc/nginx/sites-enabled/", "-maxdepth", "1", "-type", "l"}...), - ) + // ask for the new hostname + var hostname string + hostnamePrompt := promptui.Prompt{ + Label: fmt.Sprintf("What should the new hostname be? [current: %s]", site.Hostname), + Validate: validate.Hostname, + } + + hostnameEntered, err := hostnamePrompt.Run() if err != nil { return err } - var actions []nitro.Action + switch hostnameEntered { + case "": + hostname = site.Hostname + default: + hostname = hostnameEntered + } - // remove the symlink - removeSymlinkAction, err := nitro.RemoveSymlink(machine, siteToRename.Hostname) + path, err := exec.LookPath("multipass") if err != nil { return err } - actions = append(actions, *removeSymlinkAction) - copyTemplateAction, err := nitro.CopyNginxTemplate(machine, siteToRename.Hostname) - if err != nil { + php, err := find.PHPVersion( + exec.Command(path, []string{"exec", machine, "--", "php", "--version"}...), + ) + + actions, err := task.Rename(machine, php, site) + + if site.Hostname == hostname { + return errors.New("the new and original hostnames match, nothing to do") + } + + // update the config + if err := configFile.RenameSite(site, hostname); err != nil { return err } - actions = append(actions, *copyTemplateAction) - // TODO add PHP back to the config file and add to apply - changeNginxVariablesAction, err := nitro.ChangeTemplateVariables(machine, siteToRename.Webroot, siteToRename.Hostname, "7.4", siteToRename.Aliases) - if err != nil { + // save the file + if !flagDebug { + if err := configFile.Save(viper.ConfigFileUsed()); err != nil { + return err + } + } + + if flagDebug { + for _, action := range actions { + fmt.Println(action.Args) + } + + return nil + } + + if err := nitro.Run(nitro.NewMultipassRunner("multipass"), actions); err != nil { return err } - actions = append(actions, *changeNginxVariablesAction...) - // restart nginx - restartNginxAction, err := nitro.NginxReload(machine) + fmt.Println(fmt.Sprintf("Ok, we renamed the site %s to %s. We are now going to update the hosts file...", site.Hostname, hostname)) + + nitro, err := exec.LookPath("nitro") if err != nil { return err } - actions = append(actions, *restartNginxAction) - for _, action := range actions { - fmt.Println(action.Args) + // remove the site + if err := sudo.RunCommand(nitro, machine, "hosts", "remove", site.Hostname); err != nil { + fmt.Println("Error removing", site.Hostname, "from the hosts file") + return err + } + + if err := sudo.RunCommand(nitro, machine, "hosts"); err != nil { + fmt.Println("Error adding", hostname, "to the hosts file") + return err } return nil diff --git a/internal/task/rename.go b/internal/task/rename.go new file mode 100644 index 00000000..9977c940 --- /dev/null +++ b/internal/task/rename.go @@ -0,0 +1,38 @@ +package task + +import ( + "github.com/craftcms/nitro/config" + "github.com/craftcms/nitro/internal/nitro" +) + +func Rename(machine, php string, site config.Site) ([]nitro.Action, error) { + var actions []nitro.Action + + // remove the symlink + removeSymlinkAction, err := nitro.RemoveSymlink(machine, site.Hostname) + if err != nil { + return nil, err + } + actions = append(actions, *removeSymlinkAction) + + copyTemplateAction, err := nitro.CopyNginxTemplate(machine, site.Hostname) + if err != nil { + return nil, err + } + actions = append(actions, *copyTemplateAction) + + changeNginxVariablesAction, err := nitro.ChangeTemplateVariables(machine, site.Webroot, site.Hostname, php, site.Aliases) + if err != nil { + return nil, err + } + actions = append(actions, *changeNginxVariablesAction...) + + // restart nginx + restartNginxAction, err := nitro.NginxReload(machine) + if err != nil { + return nil, err + } + actions = append(actions, *restartNginxAction) + + return actions, nil +} From 7e3f1fad2cfb865bd86596d5cc9050165b03c360 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Fri, 24 Apr 2020 10:09:53 -0400 Subject: [PATCH 12/67] add test for rename site Signed-off-by: Jason McCallister --- config/config.go | 8 ++--- config/config_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/config/config.go b/config/config.go index aac9ab0d..a5ad5b15 100644 --- a/config/config.go +++ b/config/config.go @@ -107,10 +107,10 @@ func (c *Config) AddMount(m Mount) error { } func (c *Config) RenameSite(site Site, hostname string) error { - for _, s := range c.Sites { - if site.Hostname == s.Hostname { - s.Hostname = hostname - + for i, s := range c.Sites { + if s.Hostname == site.Hostname { + w := strings.Replace(s.Webroot, s.Hostname, hostname, 1) + c.Sites[i] = Site{Hostname: hostname, Webroot: w} return nil } } diff --git a/config/config_test.go b/config/config_test.go index da29b8d9..1c2756e3 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -538,3 +538,74 @@ func TestConfig_RemoveSite1(t *testing.T) { }) } } + +func TestConfig_RenameSite(t *testing.T) { + type fields struct { + Mounts []Mount + Databases []Database + Sites []Site + } + type args struct { + site Site + hostname string + } + tests := []struct { + name string + fields fields + args args + want []Site + wantErr bool + }{ + { + name: "remove a site my hostname", + args: args{ + site: Site{ + Hostname: "old.test", + Webroot: "/nitro/sites/old.test", + }, + hostname: "new.test", + }, + fields: fields{ + Sites: []Site{ + { + Hostname: "old.test", + Webroot: "/nitro/sites/old.test", + }, + { + Hostname: "keep.test", + Webroot: "/nitro/sites/keep.test", + }, + }, + }, + want: []Site{ + { + Hostname: "new.test", + Webroot: "/nitro/sites/new.test", + }, + { + Hostname: "keep.test", + Webroot: "/nitro/sites/keep.test", + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + Mounts: tt.fields.Mounts, + Databases: tt.fields.Databases, + Sites: tt.fields.Sites, + } + if err := c.RenameSite(tt.args.site, tt.args.hostname); (err != nil) != tt.wantErr { + t.Errorf("RenameSite() error = %v, wantErr %v", err, tt.wantErr) + } + + if tt.want != nil { + if !reflect.DeepEqual(c.Sites, tt.want) { + t.Errorf("RenameSite() got = \n%v, \nwant \n%v", c.Sites, tt.want) + } + } + }) + } +} From 8b8abcaf9fca6664fe0da5f7655c562920eeeef4 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Fri, 24 Apr 2020 17:10:18 -0400 Subject: [PATCH 13/67] break out config and add tests for existing mounts Signed-off-by: Jason McCallister --- config/config.go | 40 +++++++++++++++----------------- config/config_test.go | 12 +++++----- config/database.go | 14 ++++++++++++ config/mount.go | 29 +++++++++++++++++++++++ config/mount_test.go | 53 +++++++++++++++++++++++++++++++++++++++++++ config/site.go | 7 ++++++ 6 files changed, 127 insertions(+), 28 deletions(-) create mode 100644 config/database.go create mode 100644 config/mount.go create mode 100644 config/mount_test.go create mode 100644 config/site.go diff --git a/config/config.go b/config/config.go index a5ad5b15..7bf57051 100644 --- a/config/config.go +++ b/config/config.go @@ -23,28 +23,6 @@ type Config struct { Sites []Site `yaml:"sites,omitempty"` } -type Mount struct { - Source string `yaml:"source"` - Dest string `yaml:"dest"` -} - -type Database struct { - Engine string `yaml:"engine"` - Version string `yaml:"version"` - Port string `yaml:"port"` -} - -type Site struct { - Hostname string `yaml:"hostname"` - Webroot string `yaml:"webroot"` - Aliases []string `yaml:"aliases,omitempty"` -} - -func (m *Mount) AbsSourcePath() string { - home, _ := homedir.Dir() - return strings.Replace(m.Source, "~", home, 1) -} - func (c *Config) AddSite(site Site) error { if len(site.Aliases) == 0 { site.Aliases = nil @@ -111,6 +89,7 @@ func (c *Config) RenameSite(site Site, hostname string) error { if s.Hostname == site.Hostname { w := strings.Replace(s.Webroot, s.Hostname, hostname, 1) c.Sites[i] = Site{Hostname: hostname, Webroot: w} + return nil } } @@ -118,6 +97,23 @@ func (c *Config) RenameSite(site Site, hostname string) error { return errors.New("unable to locate the site with the hostname: " + site.Hostname) } +func (c *Config) RenameMountBySite(site Site) error { + for i, mount := range c.Mounts { + sp := strings.Split(site.Webroot, "/") + siteMount := sp[len(sp)-1] + if strings.Contains(mount.Dest, siteMount) { + c.Mounts[i] = Mount{ + Source: mount.Source, + Dest: siteMount, + } + + return nil + } + } + + return errors.New("unable to find the mount for the site " + site.Hostname) +} + func (c *Config) RemoveSite(hostname string) error { for i := len(c.Sites) - 1; i >= 0; i-- { site := c.Sites[i] diff --git a/config/config_test.go b/config/config_test.go index 1c2756e3..a56cb4f7 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -550,11 +550,11 @@ func TestConfig_RenameSite(t *testing.T) { hostname string } tests := []struct { - name string - fields fields - args args - want []Site - wantErr bool + name string + fields fields + args args + want []Site + wantErr bool }{ { name: "remove a site my hostname", @@ -603,7 +603,7 @@ func TestConfig_RenameSite(t *testing.T) { if tt.want != nil { if !reflect.DeepEqual(c.Sites, tt.want) { - t.Errorf("RenameSite() got = \n%v, \nwant \n%v", c.Sites, tt.want) + t.Errorf("RenameSite() got sites = \n%v, \nwant \n%v", c.Sites, tt.want) } } }) diff --git a/config/database.go b/config/database.go new file mode 100644 index 00000000..8e301ce8 --- /dev/null +++ b/config/database.go @@ -0,0 +1,14 @@ +package config + +import "fmt" + +type Database struct { + Engine string `yaml:"engine"` + Version string `yaml:"version"` + Port string `yaml:"port"` +} + +// Name converts a database into a name used for the container +func (d *Database) Name() string { + return fmt.Sprintf("%s_%s_%s", d.Engine, d.Version, d.Port) +} diff --git a/config/mount.go b/config/mount.go new file mode 100644 index 00000000..e3571dd0 --- /dev/null +++ b/config/mount.go @@ -0,0 +1,29 @@ +package config + +import ( + "strings" + + "github.com/mitchellh/go-homedir" +) + +type Mount struct { + Source string `yaml:"source"` + Dest string `yaml:"dest"` +} + +func (m *Mount) AbsSourcePath() string { + home, _ := homedir.Dir() + return strings.Replace(m.Source, "~", home, 1) +} + +func (m *Mount) Exists(webroot string) bool { + split := strings.Split(webroot, "/") + path := split[:len(split)-1] + dest := strings.Join(path, "/") + + if strings.Contains(m.Dest, dest) { + return true + } + + return false +} diff --git a/config/mount_test.go b/config/mount_test.go new file mode 100644 index 00000000..ef797655 --- /dev/null +++ b/config/mount_test.go @@ -0,0 +1,53 @@ +package config + +import "testing" + +func TestMount_Exists(t *testing.T) { + type fields struct { + Source string + Dest string + } + type args struct { + webroot string + } + tests := []struct { + name string + fields fields + args args + want bool + }{ + { + name: "returns true if the sites mount exists", + fields: fields{ + Source: "./testdata/example-source", + Dest: "/nitro/sites/example", + }, + args: args{ + webroot: "/nitro/sites/example/web", + }, + want: true, + }, + { + name: "returns false if the sites mount does not exist", + fields: fields{ + Source: "./testdata/another-source", + Dest: "/nitro/sites/another", + }, + args: args{ + webroot: "/nitro/sites/example/web", + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Mount{ + Source: tt.fields.Source, + Dest: tt.fields.Dest, + } + if got := m.Exists(tt.args.webroot); got != tt.want { + t.Errorf("Exists() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/config/site.go b/config/site.go new file mode 100644 index 00000000..d40df91a --- /dev/null +++ b/config/site.go @@ -0,0 +1,7 @@ +package config + +type Site struct { + Hostname string `yaml:"hostname"` + Webroot string `yaml:"webroot"` + Aliases []string `yaml:"aliases,omitempty"` +} From ed6b592c3420feadb6142b0baeda689ab29ee925 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Fri, 24 Apr 2020 17:10:53 -0400 Subject: [PATCH 14/67] use db.Name Signed-off-by: Jason McCallister --- internal/cmd/import.go | 2 +- internal/cmd/logs.go | 2 +- internal/find/find.go | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/internal/cmd/import.go b/internal/cmd/import.go index 09d0fa9b..68844dee 100644 --- a/internal/cmd/import.go +++ b/internal/cmd/import.go @@ -47,7 +47,7 @@ var importCommand = &cobra.Command{ } var dbs []string for _, db := range databases { - dbs = append(dbs, fmt.Sprintf("%s_%s_%s", db.Engine, db.Version, db.Port)) + dbs = append(dbs, db.Name()) } databaseContainerName := promptui.Select{ Label: "Select database", diff --git a/internal/cmd/logs.go b/internal/cmd/logs.go index 3368ceb1..b4de7448 100644 --- a/internal/cmd/logs.go +++ b/internal/cmd/logs.go @@ -69,7 +69,7 @@ var logsCommand = &cobra.Command{ } var dbs []string for _, db := range databases { - dbs = append(dbs, fmt.Sprintf("%s_%s_%s", db.Engine, db.Version, db.Port)) + dbs = append(dbs, db.Name()) } databaseContainerName := promptui.Select{ Label: "Select database", diff --git a/internal/find/find.go b/internal/find/find.go index d390da01..96170cd7 100644 --- a/internal/find/find.go +++ b/internal/find/find.go @@ -57,9 +57,7 @@ func ContainersToCreate(machine string, cfg config.Config) ([]config.Database, e var dbs []config.Database for _, db := range cfg.Databases { - container := fmt.Sprintf("%s_%s_%s", db.Engine, db.Version, db.Port) - - c := exec.Command(path, []string{"exec", machine, "--", "sudo", "bash", "/opt/nitro/scripts/docker-container-exists.sh", container}...) + c := exec.Command(path, []string{"exec", machine, "--", "sudo", "bash", "/opt/nitro/scripts/docker-container-exists.sh", db.Name()}...) output, err := c.Output() if err != nil { return nil, err From e52765ba55280403e4f128c65151a9aa5d240a8c Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Fri, 24 Apr 2020 21:43:55 -0400 Subject: [PATCH 15/67] add IsExact helper to Mount Signed-off-by: Jason McCallister --- config/mount.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/config/mount.go b/config/mount.go index e3571dd0..e5224f75 100644 --- a/config/mount.go +++ b/config/mount.go @@ -27,3 +27,11 @@ func (m *Mount) Exists(webroot string) bool { return false } + +func (m *Mount) IsExact(webroot string) bool { + if m.Dest == webroot { + return true + } + + return false +} From 7c2f2902480f57b51c24e5de91140c1b8a39610e Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Fri, 24 Apr 2020 21:44:23 -0400 Subject: [PATCH 16/67] wip rename Signed-off-by: Jason McCallister --- internal/cmd/rename.go | 39 +++--- internal/task/rename.go | 96 ++++++++++++++- internal/task/rename_test.go | 224 +++++++++++++++++++++++++++++++++++ 3 files changed, 336 insertions(+), 23 deletions(-) create mode 100644 internal/task/rename_test.go diff --git a/internal/cmd/rename.go b/internal/cmd/rename.go index 74caf1ce..a975d0dd 100644 --- a/internal/cmd/rename.go +++ b/internal/cmd/rename.go @@ -35,15 +35,15 @@ var renameCommand = &cobra.Command{ return errors.New("there are no sites to rename") } - // ask to select a site - i, _ := prompt.Select("Select site to rename", configFile.SitesAsList()) + // ask to select a existingSite + i, _ := prompt.Select("Select existingSite to rename", configFile.SitesAsList()) - site := configSites[i] + existingSite := configSites[i] - // ask for the new hostname - var hostname string + // ask for the new newHostname + var newHostname string hostnamePrompt := promptui.Prompt{ - Label: fmt.Sprintf("What should the new hostname be? [current: %s]", site.Hostname), + Label: fmt.Sprintf("What should the new newHostname be? [current: %s]", existingSite.Hostname), Validate: validate.Hostname, } @@ -54,9 +54,13 @@ var renameCommand = &cobra.Command{ switch hostnameEntered { case "": - hostname = site.Hostname + newHostname = existingSite.Hostname default: - hostname = hostnameEntered + newHostname = hostnameEntered + } + + if existingSite.Hostname == newHostname { + return errors.New("the new and original hostnames match, nothing to do") } path, err := exec.LookPath("multipass") @@ -68,14 +72,13 @@ var renameCommand = &cobra.Command{ exec.Command(path, []string{"exec", machine, "--", "php", "--version"}...), ) - actions, err := task.Rename(machine, php, site) + renamedSite := config.Site{Hostname: newHostname, Webroot: existingSite.Webroot} + mount := configFile.FindMountBySiteWebroot(existingSite.Webroot) - if site.Hostname == hostname { - return errors.New("the new and original hostnames match, nothing to do") - } + actions, err := task.Rename(machine, php, existingSite, renamedSite, mount) // update the config - if err := configFile.RenameSite(site, hostname); err != nil { + if err := configFile.RenameSite(existingSite, newHostname); err != nil { return err } @@ -98,21 +101,21 @@ var renameCommand = &cobra.Command{ return err } - fmt.Println(fmt.Sprintf("Ok, we renamed the site %s to %s. We are now going to update the hosts file...", site.Hostname, hostname)) + fmt.Println(fmt.Sprintf("Ok, we renamed the existingSite %s to %s. We are now going to update the hosts file...", existingSite.Hostname, newHostname)) nitro, err := exec.LookPath("nitro") if err != nil { return err } - // remove the site - if err := sudo.RunCommand(nitro, machine, "hosts", "remove", site.Hostname); err != nil { - fmt.Println("Error removing", site.Hostname, "from the hosts file") + // remove the existingSite + if err := sudo.RunCommand(nitro, machine, "hosts", "remove", existingSite.Hostname); err != nil { + fmt.Println("Error removing", existingSite.Hostname, "from the hosts file") return err } if err := sudo.RunCommand(nitro, machine, "hosts"); err != nil { - fmt.Println("Error adding", hostname, "to the hosts file") + fmt.Println("Error adding", newHostname, "to the hosts file") return err } diff --git a/internal/task/rename.go b/internal/task/rename.go index 9977c940..50d4196d 100644 --- a/internal/task/rename.go +++ b/internal/task/rename.go @@ -5,23 +5,93 @@ import ( "github.com/craftcms/nitro/internal/nitro" ) -func Rename(machine, php string, site config.Site) ([]nitro.Action, error) { +func RenameSite(machine, php string, oldSite, newSite config.Site, configFile config.Config) ([]nitro.Action, error) { var actions []nitro.Action + unmount := false + for _, m := range configFile.Mounts { + if m.IsExact(oldSite.Webroot) { + unmount = true + } + } + + if unmount { + // unmount the site + unmountAction, err := nitro.Unmount(machine, oldSite.Hostname) + if err != nil { + return nil, err + } + actions = append(actions, *unmountAction) + + // mount the new directory + mount := configFile.FindMountBySiteWebroot(newSite.Webroot) + mountAction, err := nitro.MountDir(machine, mount.AbsSourcePath(), mount.Dest) + if err != nil { + return nil, err + } + actions = append(actions, *mountAction) + } + + // remove the symlink from the old oldSite + removeSymlinkAction, err := nitro.RemoveSymlink(machine, oldSite.Hostname) + if err != nil { + return nil, err + } + actions = append(actions, *removeSymlinkAction) + + // create a new oldSite config for the new hostname + copyTemplateAction, err := nitro.CopyNginxTemplate(machine, newSite.Hostname) + if err != nil { + return nil, err + } + actions = append(actions, *copyTemplateAction) + + // change the webroot path + actions = append( + actions, + *nitro.ChangeNginxTemplateVariable(machine, newSite.Hostname, "CHANGEWEBROOTDIR", newSite.Webroot), + ) - // remove the symlink - removeSymlinkAction, err := nitro.RemoveSymlink(machine, site.Hostname) + // change the server name variable + actions = append( + actions, + *nitro.ChangeNginxTemplateVariable(machine, newSite.Hostname, "CHANGESERVERNAME", newSite.Hostname), + ) + + // change the PHP version + actions = append( + actions, + *nitro.ChangeNginxTemplateVariable(machine, newSite.Hostname, "CHANGEPHPVERSION", php), + ) + + // reload nginx + reloadNginxAction, err := nitro.NginxReload(machine) + if err != nil { + return nil, err + } + + actions = append(actions, *reloadNginxAction) + + return actions, nil +} + +func Rename(machine, php string, existingSite, renamedSite config.Site, mount *config.Mount) ([]nitro.Action, error) { + var actions []nitro.Action + + // remove the symlink from the old oldSite + removeSymlinkAction, err := nitro.RemoveSymlink(machine, existingSite.Hostname) if err != nil { return nil, err } actions = append(actions, *removeSymlinkAction) - copyTemplateAction, err := nitro.CopyNginxTemplate(machine, site.Hostname) + // create a new oldSite config + copyTemplateAction, err := nitro.CopyNginxTemplate(machine, renamedSite.Hostname) if err != nil { return nil, err } actions = append(actions, *copyTemplateAction) - changeNginxVariablesAction, err := nitro.ChangeTemplateVariables(machine, site.Webroot, site.Hostname, php, site.Aliases) + changeNginxVariablesAction, err := nitro.ChangeTemplateVariables(machine, existingSite.Webroot, existingSite.Hostname, php, existingSite.Aliases) if err != nil { return nil, err } @@ -34,5 +104,21 @@ func Rename(machine, php string, site config.Site) ([]nitro.Action, error) { } actions = append(actions, *restartNginxAction) + if mount != nil { + /// unmount the directory + unMountAction, err := nitro.Unmount(machine, existingSite.Hostname) + if err != nil { + return nil, err + } + actions = append(actions, *unMountAction) + + // mount the new directory + mountAction, err := nitro.Mount(machine, mount.AbsSourcePath(), renamedSite.Hostname) + if err != nil { + return nil, err + } + actions = append(actions, *mountAction) + } + return actions, nil } diff --git a/internal/task/rename_test.go b/internal/task/rename_test.go new file mode 100644 index 00000000..0236d40a --- /dev/null +++ b/internal/task/rename_test.go @@ -0,0 +1,224 @@ +package task + +import ( + "reflect" + "testing" + + "github.com/craftcms/nitro/config" + "github.com/craftcms/nitro/internal/nitro" +) + +func TestRemoveSite(t *testing.T) { + type args struct { + machine string + php string + oldSite config.Site + newSite config.Site + configFile config.Config + } + tests := []struct { + name string + args args + want []nitro.Action + wantErr bool + }{ + { + name: "if there is an exact mount it unmounts it and adds a new mount", + args: args{ + machine: "mymachine", + php: "7.4", + oldSite: config.Site{ + Hostname: "oldhostname.test", + Webroot: "/nitro/sites/oldhostname.test", + }, + newSite: config.Site{ + Hostname: "newhostname.test", + Webroot: "/nitro/sites/newhostname.test", + }, + configFile: config.Config{ + Mounts: []config.Mount{ + { + Source: "./testdata/example-source", + Dest: "/nitro/sites/oldhostname.test", + }, + }, + }, + }, + want: []nitro.Action{ + { + Type: "umount", + UseSyscall: false, + Args: []string{"umount", "mymachine" + ":/app/sites/oldhostname.test"}, + }, + { + Type: "umount", + UseSyscall: false, + Args: []string{"umount", "mymachine" + ":/app/sites/oldhostname.test"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mymachine", "--", "sudo", "rm", "/etc/nginx/sites-enabled/oldhostname.test"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mymachine", "--", "sudo", "cp", "/opt/nitro/nginx/template.conf", "/etc/nginx/sites-available/newhostname.test"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGEWEBROOTDIR|/nitro/sites/newhostname.test|g", "/etc/nginx/sites-available/newhostname.test"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGESERVERNAME|newhostname.test|g", "/etc/nginx/sites-available/newhostname.test"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGEPHPVERSION|7.4|g", "/etc/nginx/sites-available/newhostname.test"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mymachine", "--", "sudo", "service", "nginx", "restart"}, + }, + }, + wantErr: false, + }, + //{ + // name: "if there is a relative mount it does not unmount", + // args: args{ + // machine: "mymachine", + // php: "7.4", + // oldSite: config.Site{ + // Hostname: "oldhostname.test", + // Webroot: "/nitro/sites/oldhostname.test", + // }, + // newSite: config.Site{ + // Hostname: "newhostname.test", + // Webroot: "/nitro/sites/newhostname.test", + // }, + // configFile: config.Config{ + // Mounts: []config.Mount{ + // { + // Source: "./testdata/example-source", + // Dest: "/nitro/sites/", + // }, + // }, + // }, + // }, + // want: []nitro.Action{ + // { + // Type: "exec", + // UseSyscall: false, + // Args: []string{"exec", "mymachine", "--", "sudo", "rm", "/etc/nginx/sites-enabled/oldhostname.test"}, + // }, + // { + // Type: "exec", + // UseSyscall: false, + // Args: []string{"exec", "mymachine", "--", "sudo", "cp", "/opt/nitro/nginx/template.conf", "/etc/nginx/sites-available/newhostname.test"}, + // }, + // { + // Type: "exec", + // UseSyscall: false, + // Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGEWEBROOTDIR|/nitro/sites/newhostname.test|g", "/etc/nginx/sites-available/newhostname.test"}, + // }, + // { + // Type: "exec", + // UseSyscall: false, + // Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGESERVERNAME|newhostname.test|g", "/etc/nginx/sites-available/newhostname.test"}, + // }, + // { + // Type: "exec", + // UseSyscall: false, + // Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGEPHPVERSION|7.4|g", "/etc/nginx/sites-available/newhostname.test"}, + // }, + // { + // Type: "exec", + // UseSyscall: false, + // Args: []string{"exec", "mymachine", "--", "sudo", "service", "nginx", "restart"}, + // }, + // }, + // wantErr: false, + //}, + //{ + // name: "returns an action for mounts that do not have a direct mount", + // args: args{ + // machine: "mymachine", + // php: "7.4", + // oldSite: config.Site{ + // Hostname: "oldhostname.test", + // Webroot: "/nitro/sites/oldhostname.test", + // }, + // newSite: config.Site{ + // Hostname: "newhostname.test", + // Webroot: "/nitro/sites/newhostname.test", + // }, + // configFile: config.Config{ + // Mounts: []config.Mount{ + // { + // Source: "./testdata/example-source", + // Dest: "/nitro/sites/", + // }, + // }, + // }, + // }, + // want: []nitro.Action{ + // { + // Type: "exec", + // UseSyscall: false, + // Args: []string{"exec", "mymachine", "--", "sudo", "rm", "/etc/nginx/sites-enabled/oldhostname.test"}, + // }, + // { + // Type: "exec", + // UseSyscall: false, + // Args: []string{"exec", "mymachine", "--", "sudo", "cp", "/opt/nitro/nginx/template.conf", "/etc/nginx/sites-available/newhostname.test"}, + // }, + // { + // Type: "exec", + // UseSyscall: false, + // Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGEWEBROOTDIR|/nitro/sites/newhostname.test|g", "/etc/nginx/sites-available/newhostname.test"}, + // }, + // { + // Type: "exec", + // UseSyscall: false, + // Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGESERVERNAME|newhostname.test|g", "/etc/nginx/sites-available/newhostname.test"}, + // }, + // { + // Type: "exec", + // UseSyscall: false, + // Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGEPHPVERSION|7.4|g", "/etc/nginx/sites-available/newhostname.test"}, + // }, + // { + // Type: "exec", + // UseSyscall: false, + // Args: []string{"exec", "mymachine", "--", "sudo", "service", "nginx", "restart"}, + // }, + // }, + // wantErr: false, + //}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := RenameSite(tt.args.machine, tt.args.php, tt.args.oldSite, tt.args.newSite, tt.args.configFile) + if (err != nil) != tt.wantErr { + t.Errorf("RenameSite() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if len(got) != len(tt.want) { + t.Errorf("did not get the number of actions we wanted, got %d, wanted: %d", len(got), len(tt.want)) + return + } + + for i, action := range tt.want { + if !reflect.DeepEqual(action, got[i]) { + t.Errorf("RenameSite() got = \n%v, \nwant \n%v", action, tt.want[i]) + } + } + }) + } +} From 55aadff889d7cb605e1c5812938b5b0f716482e2 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Fri, 24 Apr 2020 21:44:51 -0400 Subject: [PATCH 17/67] make varialbe change exported Signed-off-by: Jason McCallister --- internal/nitro/site_add.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/internal/nitro/site_add.go b/internal/nitro/site_add.go index c3951561..d0eb8905 100644 --- a/internal/nitro/site_add.go +++ b/internal/nitro/site_add.go @@ -54,21 +54,19 @@ func ChangeTemplateVariables(name, webroot, hostname, php string, aliases []stri hostname = hostname + " " + strings.Join(aliases, " ") } - actions = append(actions, *changeVariables(name, template, "CHANGEWEBROOTDIR", webroot)) - actions = append(actions, *changeVariables(name, template, "CHANGESERVERNAME", hostname)) - actions = append(actions, *changeVariables(name, template, "CHANGEPHPVERSION", php)) + actions = append(actions, *ChangeNginxTemplateVariable(name, template, "CHANGEWEBROOTDIR", webroot)) + actions = append(actions, *ChangeNginxTemplateVariable(name, template, "CHANGESERVERNAME", hostname)) + actions = append(actions, *ChangeNginxTemplateVariable(name, template, "CHANGEPHPVERSION", php)) return &actions, nil } -func changeVariables(name, site, variable, actual string) *Action { - file := fmt.Sprintf("/etc/nginx/sites-available/%v", site) - +func ChangeNginxTemplateVariable(machine, hostname, variable, actual string) *Action { sedCmd := "s|" + variable + "|" + actual + "|g" return &Action{ Type: "exec", UseSyscall: false, - Args: []string{"exec", name, "--", "sudo", "sed", "-i", sedCmd, file}, + Args: []string{"exec", machine, "--", "sudo", "sed", "-i", sedCmd, fmt.Sprintf("/etc/nginx/sites-available/%v", hostname)}, } } From 9771b93d859b5a696e353489eafd0aeb5b044b50 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Fri, 24 Apr 2020 22:23:55 -0400 Subject: [PATCH 18/67] move GetExpandedMounts to config Signed-off-by: Jason McCallister --- config/config.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/config/config.go b/config/config.go index 7bf57051..cdb490ba 100644 --- a/config/config.go +++ b/config/config.go @@ -36,6 +36,17 @@ func (c *Config) GetSites() []Site { return c.Sites } +// GetExpandedMounts will take all of the mounts in a config file +// and "expand" or get the full path mount source and return +// a slice of mounts +func (c *Config) GetExpandedMounts() []Mount { + var mounts []Mount + for _, m := range c.Mounts { + mounts = append(mounts, Mount{Source: m.AbsSourcePath(), Dest: m.Dest}) + } + return mounts +} + func (c *Config) SitesAsList() []string { var s []string for _, site := range c.Sites { From 1fe940a72c3bfc75740dd1ea4fc5930bc20af30c Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 14:49:20 -0400 Subject: [PATCH 19/67] add tests for checking mounts on config Signed-off-by: Jason McCallister --- config/config.go | 12 ++++ config/config_test.go | 91 ++++++++++++++++++++++++++++-- config/mount.go | 12 ++-- config/mount_test.go | 53 ----------------- config/testdata/new-mount/.gitkeep | 0 5 files changed, 102 insertions(+), 66 deletions(-) delete mode 100644 config/mount_test.go create mode 100644 config/testdata/new-mount/.gitkeep diff --git a/config/config.go b/config/config.go index cdb490ba..87ffc4ae 100644 --- a/config/config.go +++ b/config/config.go @@ -47,6 +47,18 @@ func (c *Config) GetExpandedMounts() []Mount { return mounts } +// MountExists will check if a mount exists by checking if it is an exact +// dest or a parent of an existing dest +func (c *Config) MountExists(dest string) bool { + for _, mount := range c.Mounts { + if mount.IsExact(dest) || mount.IsParent(dest) { + return true + } + } + + return false +} + func (c *Config) SitesAsList() []string { var s []string for _, site := range c.Sites { diff --git a/config/config_test.go b/config/config_test.go index a56cb4f7..1516a1aa 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -550,11 +550,11 @@ func TestConfig_RenameSite(t *testing.T) { hostname string } tests := []struct { - name string - fields fields - args args - want []Site - wantErr bool + name string + fields fields + args args + want []Site + wantErr bool }{ { name: "remove a site my hostname", @@ -609,3 +609,84 @@ func TestConfig_RenameSite(t *testing.T) { }) } } + +func TestConfig_MountExists(t *testing.T) { + type fields struct { + PHP string + CPUs string + Disk string + Memory string + Mounts []Mount + Databases []Database + Sites []Site + } + type args struct { + dest string + } + tests := []struct { + name string + fields fields + args args + want bool + }{ + { + name: "existing mounts return true", + fields: fields{ + Mounts: []Mount{ + { + Source: "./testdata/existing-mount", + Dest: "/nitro/sites/example-site", + }, + }, + }, + args: args{dest: "/nitro/sites/example-site"}, + want: true, + }, + { + name: "non-existing mounts return false", + fields: fields{ + Mounts: []Mount{ + { + Source: "./testdata/existing-mount", + Dest: "/nitro/sites/example-site", + }, + }, + }, + args: args{dest: "/nitro/sites/nonexistent-site"}, + want: false, + }, + { + name: "parent mounts return true", + fields: fields{ + Mounts: []Mount{ + { + Source: "./testdata/test-mount", + Dest: "/nitro/sites", + }, + { + Source: "./testdata/existing-mount", + Dest: "/nitro/sites", + }, + }, + }, + args: args{dest: "/nitro/sites/nonexistent-site"}, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + PHP: tt.fields.PHP, + CPUs: tt.fields.CPUs, + Disk: tt.fields.Disk, + Memory: tt.fields.Memory, + Mounts: tt.fields.Mounts, + Databases: tt.fields.Databases, + Sites: tt.fields.Sites, + } + if got := c.MountExists(tt.args.dest); got != tt.want { + t.Errorf("MountExists() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/config/mount.go b/config/mount.go index e5224f75..849ee67f 100644 --- a/config/mount.go +++ b/config/mount.go @@ -16,20 +16,16 @@ func (m *Mount) AbsSourcePath() string { return strings.Replace(m.Source, "~", home, 1) } -func (m *Mount) Exists(webroot string) bool { - split := strings.Split(webroot, "/") - path := split[:len(split)-1] - dest := strings.Join(path, "/") - - if strings.Contains(m.Dest, dest) { +func (m *Mount) IsExact(dest string) bool { + if m.Dest == dest { return true } return false } -func (m *Mount) IsExact(webroot string) bool { - if m.Dest == webroot { +func (m *Mount) IsParent(dest string) bool { + if strings.Contains(dest, m.Dest) { return true } diff --git a/config/mount_test.go b/config/mount_test.go deleted file mode 100644 index ef797655..00000000 --- a/config/mount_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package config - -import "testing" - -func TestMount_Exists(t *testing.T) { - type fields struct { - Source string - Dest string - } - type args struct { - webroot string - } - tests := []struct { - name string - fields fields - args args - want bool - }{ - { - name: "returns true if the sites mount exists", - fields: fields{ - Source: "./testdata/example-source", - Dest: "/nitro/sites/example", - }, - args: args{ - webroot: "/nitro/sites/example/web", - }, - want: true, - }, - { - name: "returns false if the sites mount does not exist", - fields: fields{ - Source: "./testdata/another-source", - Dest: "/nitro/sites/another", - }, - args: args{ - webroot: "/nitro/sites/example/web", - }, - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - m := &Mount{ - Source: tt.fields.Source, - Dest: tt.fields.Dest, - } - if got := m.Exists(tt.args.webroot); got != tt.want { - t.Errorf("Exists() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/config/testdata/new-mount/.gitkeep b/config/testdata/new-mount/.gitkeep new file mode 100644 index 00000000..e69de29b From 72aa7cbb8cce41b386d1b18025bbef7c01d5678d Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 14:52:38 -0400 Subject: [PATCH 20/67] add apply task with tests Signed-off-by: Jason McCallister --- internal/task/apply.go | 36 +++++ internal/task/apply_test.go | 147 ++++++++++++++++++ .../task/testdata/existing-mount/.gitkeep | 0 internal/task/testdata/new-mount/.gitkeep | 0 4 files changed, 183 insertions(+) create mode 100644 internal/task/apply.go create mode 100644 internal/task/apply_test.go create mode 100644 internal/task/testdata/existing-mount/.gitkeep create mode 100644 internal/task/testdata/new-mount/.gitkeep diff --git a/internal/task/apply.go b/internal/task/apply.go new file mode 100644 index 00000000..13b24fb5 --- /dev/null +++ b/internal/task/apply.go @@ -0,0 +1,36 @@ +package task + +import ( + "github.com/craftcms/nitro/config" + "github.com/craftcms/nitro/internal/nitro" +) + +// Apply is responsible for comparing the current configuration and what information is +// found on a machine such as fromMultipassMounts and sites. Apple will then take the appropriate +// steps to compare are create actions that "normal up" the configuration state. +func Apply(machine string, configFile config.Config, mounts []config.Mount, sites []config.Site, dbs []config.Database, php string) ([]nitro.Action, error) { + var actions []nitro.Action + inMemoryConfig := config.Config{PHP: php, Mounts: mounts, Sites: sites, Databases: dbs} + + for _, mount := range inMemoryConfig.Mounts { + if !configFile.MountExists(mount.Dest) { + unmountAction, err := nitro.UnmountDir(machine, mount.Dest) + if err != nil { + return nil, err + } + actions = append(actions, *unmountAction) + } + } + + for _, mount := range configFile.Mounts { + if !inMemoryConfig.MountExists(mount.Dest) { + mountAction, err := nitro.MountDir(machine, mount.AbsSourcePath(), mount.Dest) + if err != nil { + return nil, err + } + actions = append(actions, *mountAction) + } + } + + return actions, nil +} diff --git a/internal/task/apply_test.go b/internal/task/apply_test.go new file mode 100644 index 00000000..9f5230df --- /dev/null +++ b/internal/task/apply_test.go @@ -0,0 +1,147 @@ +package task + +import ( + "reflect" + "testing" + + "github.com/craftcms/nitro/config" + "github.com/craftcms/nitro/internal/nitro" +) + +func TestApply(t *testing.T) { + type args struct { + machine string + configFile config.Config + fromMultipassMounts []config.Mount + sites []config.Site + dbs []config.Database + php string + } + tests := []struct { + name string + args args + want []nitro.Action + wantErr bool + }{ + { + name: "new mounts return actions to create mounts", + args: args{ + machine: "mytestmachine", + configFile: config.Config{ + Mounts: []config.Mount{ + { + Source: "./testdata/existing-mount", + Dest: "/nitro/sites/example-site", + }, + { + Source: "./testdata/new-mount", + Dest: "/nitro/sites/new-site", + }, + }, + }, + fromMultipassMounts: []config.Mount{ + { + Source: "./testdata/existing-mount", + Dest: "/nitro/sites/example-site", + }, + }, + }, + want: []nitro.Action{ + { + Type: "mount", + UseSyscall: false, + Args: []string{"mount", "./testdata/new-mount", "mytestmachine:/nitro/sites/new-site"}, + }, + }, + wantErr: false, + }, + { + name: "removed mounts return actions to remove mounts", + args: args{ + machine: "mytestmachine", + configFile: config.Config{ + Mounts: []config.Mount{ + { + Source: "./testdata/new-mount", + Dest: "/nitro/sites/new-site", + }, + }, + }, + fromMultipassMounts: []config.Mount{ + { + Source: "./testdata/new-mount", + Dest: "/nitro/sites/new-site", + }, + { + Source: "./testdata/existing-mount", + Dest: "/nitro/sites/example-site", + }, + }, + php: "", + }, + want: []nitro.Action{ + { + Type: "umount", + UseSyscall: false, + Args: []string{"umount", "mytestmachine:/nitro/sites/example-site"}, + }, + }, + wantErr: false, + }, + { + name: "renamed mounts get removed and added", + args: args{ + machine: "mytestmachine", + configFile: config.Config{ + Mounts: []config.Mount{ + { + Source: "./testdata/new-mount", + Dest: "/nitro/sites/new-site", + }, + }, + }, + fromMultipassMounts: []config.Mount{ + { + Source: "./testdata/existing-mount", + Dest: "/nitro/sites/existing-site", + }, + }, + }, + want: []nitro.Action{ + { + Type: "umount", + UseSyscall: false, + Args: []string{"umount", "mytestmachine:/nitro/sites/existing-site"}, + }, + { + Type: "mount", + UseSyscall: false, + Args: []string{"mount", "./testdata/new-mount", "mytestmachine:/nitro/sites/new-site"}, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Apply(tt.args.machine, tt.args.configFile, tt.args.fromMultipassMounts, tt.args.sites, tt.args.dbs, tt.args.php) + if (err != nil) != tt.wantErr { + t.Errorf("Apply() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if len(tt.want) != len(got) { + t.Errorf("expected the number of actions to be equal for Apply(); got %d, want %d", len(got), len(tt.want)) + return + } + + if tt.want != nil { + for i, action := range tt.want { + if !reflect.DeepEqual(action, got[i]) { + t.Errorf("Apply() got = \n%v, \nwant \n%v", got[i], tt.want[i]) + } + } + } + }) + } +} diff --git a/internal/task/testdata/existing-mount/.gitkeep b/internal/task/testdata/existing-mount/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/internal/task/testdata/new-mount/.gitkeep b/internal/task/testdata/new-mount/.gitkeep new file mode 100644 index 00000000..e69de29b From 5605d53dd776d03413a97112484172a69b4bf044 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 15:55:01 -0400 Subject: [PATCH 21/67] add new sites to test mount Signed-off-by: Jason McCallister --- config/config.go | 10 +++ config/config_test.go | 70 +++++++++++++++++++ config/site.go | 8 +++ internal/task/apply.go | 26 ++++++++ internal/task/apply_test.go | 130 ++++++++++++++++++++++++++++++++++++ 5 files changed, 244 insertions(+) diff --git a/config/config.go b/config/config.go index 87ffc4ae..f6435f6c 100644 --- a/config/config.go +++ b/config/config.go @@ -59,6 +59,16 @@ func (c *Config) MountExists(dest string) bool { return false } +func (c *Config) SiteExists(site Site) bool { + for _, s := range c.Sites { + if s.IsExact(site) { + return true + } + } + + return false +} + func (c *Config) SitesAsList() []string { var s []string for _, site := range c.Sites { diff --git a/config/config_test.go b/config/config_test.go index 1516a1aa..bf469713 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -690,3 +690,73 @@ func TestConfig_MountExists(t *testing.T) { }) } } + +func TestConfig_SiteExists(t *testing.T) { + type fields struct { + PHP string + CPUs string + Disk string + Memory string + Mounts []Mount + Databases []Database + Sites []Site + } + type args struct { + site Site + } + tests := []struct { + name string + fields fields + args args + want bool + }{ + { + name: "exact sites return true", + fields: fields{ + Sites: []Site{ + { + Hostname: "iexist.test", + Webroot: "/nitro/sites/iexist.test", + }, + }, + }, + args: args{site: Site{ + Hostname: "iexist.test", + Webroot: "/nitro/sites/iexist.test", + }}, + want: true, + }, + { + name: "exact sites return false", + fields: fields{ + Sites: []Site{ + { + Hostname: "iexist.test", + Webroot: "/nitro/sites/iexist.test", + }, + }, + }, + args: args{site: Site{ + Hostname: "idontexist.test", + Webroot: "/nitro/sites/idontexist.test", + }}, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + PHP: tt.fields.PHP, + CPUs: tt.fields.CPUs, + Disk: tt.fields.Disk, + Memory: tt.fields.Memory, + Mounts: tt.fields.Mounts, + Databases: tt.fields.Databases, + Sites: tt.fields.Sites, + } + if got := c.SiteExists(tt.args.site); got != tt.want { + t.Errorf("SiteExists() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/config/site.go b/config/site.go index d40df91a..55e145f3 100644 --- a/config/site.go +++ b/config/site.go @@ -5,3 +5,11 @@ type Site struct { Webroot string `yaml:"webroot"` Aliases []string `yaml:"aliases,omitempty"` } + +func (s *Site) IsExact(site Site) bool { + if s.Hostname == site.Hostname && s.Webroot == site.Webroot { + return true + } + + return false +} diff --git a/internal/task/apply.go b/internal/task/apply.go index 13b24fb5..81205ca2 100644 --- a/internal/task/apply.go +++ b/internal/task/apply.go @@ -32,5 +32,31 @@ func Apply(machine string, configFile config.Config, mounts []config.Mount, site } } + for _, site := range configFile.Sites { + // find the parent to mount + if !inMemoryConfig.SiteExists(site) { + // copy template + copyTemplateAction, err := nitro.CopyNginxTemplate(machine, site.Hostname) + if err != nil { + return nil, err + } + actions = append(actions, *copyTemplateAction) + + // replace variable + changeNginxVariablesAction, err := nitro.ChangeTemplateVariables(machine, site.Webroot, site.Hostname, configFile.PHP, site.Aliases) + if err != nil { + return nil, err + } + actions = append(actions, *changeNginxVariablesAction...) + + // reload nginx + reloadNginxAction, err := nitro.NginxReload(machine) + if err != nil { + return nil, err + } + actions = append(actions, *reloadNginxAction) + } + } + return actions, nil } diff --git a/internal/task/apply_test.go b/internal/task/apply_test.go index 9f5230df..0ecd52fa 100644 --- a/internal/task/apply_test.go +++ b/internal/task/apply_test.go @@ -23,6 +23,136 @@ func TestApply(t *testing.T) { want []nitro.Action wantErr bool }{ + { + name: "new sites without a parent mount in the config are added to the machine and mounted", + args: args{ + machine: "mytestmachine", + configFile: config.Config{ + PHP: "7.4", + Mounts: []config.Mount{ + { + Source: "./testdata/existing-mount", + Dest: "/nitro/sites/existing-site", + }, + }, + Sites: []config.Site{ + { + Hostname: "existing-site", + Webroot: "/nitro/sites/existing-site", + }, + { + Hostname: "new-site", + Webroot: "/nitro/sites/new-site", + }, + }, + }, + fromMultipassMounts: []config.Mount{ + { + Source: "./testdata/existing-mount", + Dest: "/nitro/sites/existing-site", + }, + }, + sites: []config.Site{ + { + Hostname: "existing-site", + Webroot: "/nitro/sites/existing-site", + }, + }, + }, + want: []nitro.Action{ + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "cp", "/opt/nitro/nginx/template.conf", "/etc/nginx/sites-available/new-site"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "sed", "-i", "s|CHANGEWEBROOTDIR|/nitro/sites/new-site|g", "/etc/nginx/sites-available/new-site"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "sed", "-i", "s|CHANGESERVERNAME|new-site|g", "/etc/nginx/sites-available/new-site"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "sed", "-i", "s|CHANGEPHPVERSION|7.4|g", "/etc/nginx/sites-available/new-site"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "service", "nginx", "restart"}, + }, + }, + wantErr: false, + }, + { + name: "new sites using parent mounts in the config are added to the machine", + args: args{ + machine: "mytestmachine", + configFile: config.Config{ + PHP: "7.4", + Mounts: []config.Mount{ + { + Source: "./testdata/existing-mount", + Dest: "/nitro/sites", + }, + }, + Sites: []config.Site{ + { + Hostname: "existing-site", + Webroot: "/nitro/sites/existing-site", + }, + { + Hostname: "new-site", + Webroot: "/nitro/sites/new-site", + }, + }, + }, + fromMultipassMounts: []config.Mount{ + { + Source: "./testdata/existing-mount", + Dest: "/nitro/sites", + }, + }, + sites: []config.Site{ + { + Hostname: "existing-site", + Webroot: "/nitro/sites/existing-site", + }, + }, + }, + want: []nitro.Action{ + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "cp", "/opt/nitro/nginx/template.conf", "/etc/nginx/sites-available/new-site"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "sed", "-i", "s|CHANGEWEBROOTDIR|/nitro/sites/new-site|g", "/etc/nginx/sites-available/new-site"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "sed", "-i", "s|CHANGESERVERNAME|new-site|g", "/etc/nginx/sites-available/new-site"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "sed", "-i", "s|CHANGEPHPVERSION|7.4|g", "/etc/nginx/sites-available/new-site"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "service", "nginx", "restart"}, + }, + }, + wantErr: false, + }, { name: "new mounts return actions to create mounts", args: args{ From be37b819f0a3dfc473c4743e6aa0c994bedd6003 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 15:55:22 -0400 Subject: [PATCH 22/67] add rename and test Signed-off-by: Jason McCallister --- internal/cmd/apply.go | 27 ++--- internal/task/rename.go | 25 +--- internal/task/rename_test.go | 222 ----------------------------------- 3 files changed, 11 insertions(+), 263 deletions(-) diff --git a/internal/cmd/apply.go b/internal/cmd/apply.go index 4f88b5a8..a4093e74 100644 --- a/internal/cmd/apply.go +++ b/internal/cmd/apply.go @@ -20,6 +20,13 @@ var applyCommand = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { machine := flagMachineName + // load the config file + var configFile config.Config + if err := viper.Unmarshal(&configFile); err != nil { + return err + } + + // ABSTRACT path, err := exec.LookPath("multipass") if err != nil { return err @@ -35,18 +42,7 @@ var applyCommand = &cobra.Command{ if err != nil { return err } - - // load the config file - var configFile config.Config - if err := viper.Unmarshal(&configFile); err != nil { - return err - } - - // get abs path for file sources - var fileMounts []config.Mount - for _, m := range configFile.Mounts { - fileMounts = append(fileMounts, config.Mount{Source: m.AbsSourcePath(), Dest: m.Dest}) - } + // END ABSTRACT // find sites not created var sitesToCreate []config.Site @@ -67,10 +63,10 @@ var applyCommand = &cobra.Command{ return err } - // prompt? + // task.ApplyChanges(cfgfile, attached, sitesNotCreated, dbsToCreate) var actions []nitro.Action - mountActions, err := diff.MountActions(machine, attachedMounts, fileMounts) + mountActions, err := diff.MountActions(machine, attachedMounts, configFile.GetExpandedMounts()) if err != nil { return err } @@ -145,9 +141,6 @@ var applyCommand = &cobra.Command{ return nil } - fmt.Printf("There are %d mounted directories and %d mounts in the config file. Applying changes now...\n", len(attachedMounts), len(fileMounts)) - fmt.Printf("There are %d sites to create and %d sites in the config file. Applying changes now...\n", len(sitesToCreate), len(configFile.Sites)) - if err := nitro.Run(nitro.NewMultipassRunner("multipass"), actions); err != nil { return err } diff --git a/internal/task/rename.go b/internal/task/rename.go index 50d4196d..dbd99a90 100644 --- a/internal/task/rename.go +++ b/internal/task/rename.go @@ -5,31 +5,8 @@ import ( "github.com/craftcms/nitro/internal/nitro" ) -func RenameSite(machine, php string, oldSite, newSite config.Site, configFile config.Config) ([]nitro.Action, error) { +func RenameSite(machine, php string, oldSite, newSite config.Site) ([]nitro.Action, error) { var actions []nitro.Action - unmount := false - for _, m := range configFile.Mounts { - if m.IsExact(oldSite.Webroot) { - unmount = true - } - } - - if unmount { - // unmount the site - unmountAction, err := nitro.Unmount(machine, oldSite.Hostname) - if err != nil { - return nil, err - } - actions = append(actions, *unmountAction) - - // mount the new directory - mount := configFile.FindMountBySiteWebroot(newSite.Webroot) - mountAction, err := nitro.MountDir(machine, mount.AbsSourcePath(), mount.Dest) - if err != nil { - return nil, err - } - actions = append(actions, *mountAction) - } // remove the symlink from the old oldSite removeSymlinkAction, err := nitro.RemoveSymlink(machine, oldSite.Hostname) diff --git a/internal/task/rename_test.go b/internal/task/rename_test.go index 0236d40a..29512d9b 100644 --- a/internal/task/rename_test.go +++ b/internal/task/rename_test.go @@ -1,224 +1,2 @@ package task -import ( - "reflect" - "testing" - - "github.com/craftcms/nitro/config" - "github.com/craftcms/nitro/internal/nitro" -) - -func TestRemoveSite(t *testing.T) { - type args struct { - machine string - php string - oldSite config.Site - newSite config.Site - configFile config.Config - } - tests := []struct { - name string - args args - want []nitro.Action - wantErr bool - }{ - { - name: "if there is an exact mount it unmounts it and adds a new mount", - args: args{ - machine: "mymachine", - php: "7.4", - oldSite: config.Site{ - Hostname: "oldhostname.test", - Webroot: "/nitro/sites/oldhostname.test", - }, - newSite: config.Site{ - Hostname: "newhostname.test", - Webroot: "/nitro/sites/newhostname.test", - }, - configFile: config.Config{ - Mounts: []config.Mount{ - { - Source: "./testdata/example-source", - Dest: "/nitro/sites/oldhostname.test", - }, - }, - }, - }, - want: []nitro.Action{ - { - Type: "umount", - UseSyscall: false, - Args: []string{"umount", "mymachine" + ":/app/sites/oldhostname.test"}, - }, - { - Type: "umount", - UseSyscall: false, - Args: []string{"umount", "mymachine" + ":/app/sites/oldhostname.test"}, - }, - { - Type: "exec", - UseSyscall: false, - Args: []string{"exec", "mymachine", "--", "sudo", "rm", "/etc/nginx/sites-enabled/oldhostname.test"}, - }, - { - Type: "exec", - UseSyscall: false, - Args: []string{"exec", "mymachine", "--", "sudo", "cp", "/opt/nitro/nginx/template.conf", "/etc/nginx/sites-available/newhostname.test"}, - }, - { - Type: "exec", - UseSyscall: false, - Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGEWEBROOTDIR|/nitro/sites/newhostname.test|g", "/etc/nginx/sites-available/newhostname.test"}, - }, - { - Type: "exec", - UseSyscall: false, - Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGESERVERNAME|newhostname.test|g", "/etc/nginx/sites-available/newhostname.test"}, - }, - { - Type: "exec", - UseSyscall: false, - Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGEPHPVERSION|7.4|g", "/etc/nginx/sites-available/newhostname.test"}, - }, - { - Type: "exec", - UseSyscall: false, - Args: []string{"exec", "mymachine", "--", "sudo", "service", "nginx", "restart"}, - }, - }, - wantErr: false, - }, - //{ - // name: "if there is a relative mount it does not unmount", - // args: args{ - // machine: "mymachine", - // php: "7.4", - // oldSite: config.Site{ - // Hostname: "oldhostname.test", - // Webroot: "/nitro/sites/oldhostname.test", - // }, - // newSite: config.Site{ - // Hostname: "newhostname.test", - // Webroot: "/nitro/sites/newhostname.test", - // }, - // configFile: config.Config{ - // Mounts: []config.Mount{ - // { - // Source: "./testdata/example-source", - // Dest: "/nitro/sites/", - // }, - // }, - // }, - // }, - // want: []nitro.Action{ - // { - // Type: "exec", - // UseSyscall: false, - // Args: []string{"exec", "mymachine", "--", "sudo", "rm", "/etc/nginx/sites-enabled/oldhostname.test"}, - // }, - // { - // Type: "exec", - // UseSyscall: false, - // Args: []string{"exec", "mymachine", "--", "sudo", "cp", "/opt/nitro/nginx/template.conf", "/etc/nginx/sites-available/newhostname.test"}, - // }, - // { - // Type: "exec", - // UseSyscall: false, - // Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGEWEBROOTDIR|/nitro/sites/newhostname.test|g", "/etc/nginx/sites-available/newhostname.test"}, - // }, - // { - // Type: "exec", - // UseSyscall: false, - // Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGESERVERNAME|newhostname.test|g", "/etc/nginx/sites-available/newhostname.test"}, - // }, - // { - // Type: "exec", - // UseSyscall: false, - // Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGEPHPVERSION|7.4|g", "/etc/nginx/sites-available/newhostname.test"}, - // }, - // { - // Type: "exec", - // UseSyscall: false, - // Args: []string{"exec", "mymachine", "--", "sudo", "service", "nginx", "restart"}, - // }, - // }, - // wantErr: false, - //}, - //{ - // name: "returns an action for mounts that do not have a direct mount", - // args: args{ - // machine: "mymachine", - // php: "7.4", - // oldSite: config.Site{ - // Hostname: "oldhostname.test", - // Webroot: "/nitro/sites/oldhostname.test", - // }, - // newSite: config.Site{ - // Hostname: "newhostname.test", - // Webroot: "/nitro/sites/newhostname.test", - // }, - // configFile: config.Config{ - // Mounts: []config.Mount{ - // { - // Source: "./testdata/example-source", - // Dest: "/nitro/sites/", - // }, - // }, - // }, - // }, - // want: []nitro.Action{ - // { - // Type: "exec", - // UseSyscall: false, - // Args: []string{"exec", "mymachine", "--", "sudo", "rm", "/etc/nginx/sites-enabled/oldhostname.test"}, - // }, - // { - // Type: "exec", - // UseSyscall: false, - // Args: []string{"exec", "mymachine", "--", "sudo", "cp", "/opt/nitro/nginx/template.conf", "/etc/nginx/sites-available/newhostname.test"}, - // }, - // { - // Type: "exec", - // UseSyscall: false, - // Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGEWEBROOTDIR|/nitro/sites/newhostname.test|g", "/etc/nginx/sites-available/newhostname.test"}, - // }, - // { - // Type: "exec", - // UseSyscall: false, - // Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGESERVERNAME|newhostname.test|g", "/etc/nginx/sites-available/newhostname.test"}, - // }, - // { - // Type: "exec", - // UseSyscall: false, - // Args: []string{"exec", "mymachine", "--", "sudo", "sed", "-i", "s|CHANGEPHPVERSION|7.4|g", "/etc/nginx/sites-available/newhostname.test"}, - // }, - // { - // Type: "exec", - // UseSyscall: false, - // Args: []string{"exec", "mymachine", "--", "sudo", "service", "nginx", "restart"}, - // }, - // }, - // wantErr: false, - //}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := RenameSite(tt.args.machine, tt.args.php, tt.args.oldSite, tt.args.newSite, tt.args.configFile) - if (err != nil) != tt.wantErr { - t.Errorf("RenameSite() error = %v, wantErr %v", err, tt.wantErr) - return - } - - if len(got) != len(tt.want) { - t.Errorf("did not get the number of actions we wanted, got %d, wanted: %d", len(got), len(tt.want)) - return - } - - for i, action := range tt.want { - if !reflect.DeepEqual(action, got[i]) { - t.Errorf("RenameSite() got = \n%v, \nwant \n%v", action, tt.want[i]) - } - } - }) - } -} From a608ab5f55c1f0c1b2653b4fab2866adfe9c81e7 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 16:13:16 -0400 Subject: [PATCH 23/67] add tests to remove left over sites Signed-off-by: Jason McCallister --- internal/task/apply.go | 22 ++++++++++++++++++++++ internal/task/apply_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/internal/task/apply.go b/internal/task/apply.go index 81205ca2..98d2a331 100644 --- a/internal/task/apply.go +++ b/internal/task/apply.go @@ -12,6 +12,7 @@ func Apply(machine string, configFile config.Config, mounts []config.Mount, site var actions []nitro.Action inMemoryConfig := config.Config{PHP: php, Mounts: mounts, Sites: sites, Databases: dbs} + // check if there are mounts we need to remove for _, mount := range inMemoryConfig.Mounts { if !configFile.MountExists(mount.Dest) { unmountAction, err := nitro.UnmountDir(machine, mount.Dest) @@ -22,6 +23,7 @@ func Apply(machine string, configFile config.Config, mounts []config.Mount, site } } + // check if there are mounts we need to create for _, mount := range configFile.Mounts { if !inMemoryConfig.MountExists(mount.Dest) { mountAction, err := nitro.MountDir(machine, mount.AbsSourcePath(), mount.Dest) @@ -32,6 +34,26 @@ func Apply(machine string, configFile config.Config, mounts []config.Mount, site } } + // TODO check if there are sites we need to remove + for _, site := range inMemoryConfig.Sites { + if !configFile.SiteExists(site) { + // remove symlink + removeSymlink, err := nitro.RemoveSymlink(machine, site.Hostname) + if err != nil { + return nil, err + } + actions = append(actions, *removeSymlink) + + // reload nginx + reloadNginxAction, err := nitro.NginxReload(machine) + if err != nil { + return nil, err + } + actions = append(actions, *reloadNginxAction) + } + } + + // check if there are sites we need to make for _, site := range configFile.Sites { // find the parent to mount if !inMemoryConfig.SiteExists(site) { diff --git a/internal/task/apply_test.go b/internal/task/apply_test.go index 0ecd52fa..36f2b329 100644 --- a/internal/task/apply_test.go +++ b/internal/task/apply_test.go @@ -23,6 +23,42 @@ func TestApply(t *testing.T) { want []nitro.Action wantErr bool }{ + { + name: "sites that exist but are not in the config file are removed", + args: args{ + machine: "mytestmachine", + configFile: config.Config{}, + fromMultipassMounts: []config.Mount{ + { + Source: "./testdata/existing/mount", + Dest: "/nitro/sites/leftoversite.test", + }, + }, + sites: []config.Site{ + { + Hostname: "leftoversite.test", + Webroot: "/nitro/sites/leftoversite.test/web", + }, + }, + }, + want: []nitro.Action{ + { + Type: "umount", + UseSyscall: false, + Args: []string{"umount", "mytestmachine:/nitro/sites/leftoversite.test"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "rm", "/etc/nginx/sites-enabled/leftoversite.test"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "service", "nginx", "restart"}, + }, + }, + }, { name: "new sites without a parent mount in the config are added to the machine and mounted", args: args{ From a02221ae0f8cf59cec1913d94d5064a942b2af54 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 16:17:03 -0400 Subject: [PATCH 24/67] remove todo Signed-off-by: Jason McCallister --- internal/task/apply.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/task/apply.go b/internal/task/apply.go index 98d2a331..f22f5b7e 100644 --- a/internal/task/apply.go +++ b/internal/task/apply.go @@ -34,7 +34,7 @@ func Apply(machine string, configFile config.Config, mounts []config.Mount, site } } - // TODO check if there are sites we need to remove + // check if there are sites we need to remove for _, site := range inMemoryConfig.Sites { if !configFile.SiteExists(site) { // remove symlink From 45cfbf86ccccdfc3526201ec7688b1256c7c9112 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 16:40:00 -0400 Subject: [PATCH 25/67] add databases to apply with tests Signed-off-by: Jason McCallister --- config/config.go | 10 ++++++ internal/task/apply.go | 28 ++++++++++++++++ internal/task/apply_test.go | 64 ++++++++++++++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index f6435f6c..31ee0ef1 100644 --- a/config/config.go +++ b/config/config.go @@ -69,6 +69,16 @@ func (c *Config) SiteExists(site Site) bool { return false } +func (c *Config) DatabaseExists(database Database) bool { + for _, d := range c.Databases { + if d.Engine == database.Engine && d.Version == database.Version && d.Port == database.Port { + return true + } + } + + return false +} + func (c *Config) SitesAsList() []string { var s []string for _, site := range c.Sites { diff --git a/internal/task/apply.go b/internal/task/apply.go index f22f5b7e..9d392f92 100644 --- a/internal/task/apply.go +++ b/internal/task/apply.go @@ -80,5 +80,33 @@ func Apply(machine string, configFile config.Config, mounts []config.Mount, site } } + // check if there are databases to remove + for _, database := range inMemoryConfig.Databases { + if !configFile.DatabaseExists(database) { + actions = append(actions, nitro.Action{ + Type: "exec", + UseSyscall: false, + Args: []string{"exec", machine, "--", "docker", "rm", "-v", database.Name()}, + }) + } + } + + // check if there are database to create + for _, database := range configFile.Databases { + if !inMemoryConfig.DatabaseExists(database) { + createVolume, err := nitro.CreateDatabaseVolume(machine, database.Engine, database.Version, database.Port) + if err != nil { + return nil, err + } + actions = append(actions, *createVolume) + + createContainer, err := nitro.CreateDatabaseContainer(machine, database.Engine, database.Version, database.Port) + if err != nil { + return nil, err + } + actions = append(actions, *createContainer) + } + } + return actions, nil } diff --git a/internal/task/apply_test.go b/internal/task/apply_test.go index 36f2b329..1cc13ee7 100644 --- a/internal/task/apply_test.go +++ b/internal/task/apply_test.go @@ -23,6 +23,68 @@ func TestApply(t *testing.T) { want []nitro.Action wantErr bool }{ + { + name: "new databases that are in the config are created", + args: args{ + machine: "mytestmachine", + configFile: config.Config{ + Databases: []config.Database{ + { + Engine: "mysql", + Version: "5.7", + Port: "3306", + }, + }, + }, + dbs: nil, + }, + want: []nitro.Action{ + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "docker", "volume", "create", "mysql_5.7_3306"}, + }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "docker", "run", "-v", "mysql_5.7_3306:/var/lib/mysql", "--name", "mysql_5.7_3306", "-d", "--restart=always", "-p", "3306:3306", "-e", "MYSQL_ROOT_PASSWORD=nitro", "-e", "MYSQL_USER=nitro", "-e", "MYSQL_PASSWORD=nitro", "mysql:5.7"}, + }, + }, + }, + { + name: "databases that are not in the config are removed", + args: args{ + machine: "mytestmachine", + configFile: config.Config{ + Databases: []config.Database{ + { + Engine: "mysql", + Version: "5.7", + Port: "3306", + }, + }, + }, + dbs: []config.Database{ + { + Engine: "mysql", + Version: "5.7", + Port: "3306", + }, + { + Engine: "postgres", + Version: "11", + Port: "5432", + }, + }, + }, + want: []nitro.Action{ + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "docker", "rm", "-v", "postgres_11_5432"}, + }, + }, + }, { name: "sites that exist but are not in the config file are removed", args: args{ @@ -50,7 +112,7 @@ func TestApply(t *testing.T) { { Type: "exec", UseSyscall: false, - Args: []string{"exec", "mytestmachine", "--", "sudo", "rm", "/etc/nginx/sites-enabled/leftoversite.test"}, + Args: []string{"exec", "mytestmachine", "--", "sudo", "rm", "/etc/nginx/sites-enabled/leftoversite.test"}, }, { Type: "exec", From 854eb44e15b1da66544ee7222912a9d343af7dfb Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 16:50:19 -0400 Subject: [PATCH 26/67] add tests for the config file php Signed-off-by: Jason McCallister --- internal/task/apply.go | 10 ++++++++++ internal/task/apply_test.go | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/internal/task/apply.go b/internal/task/apply.go index 9d392f92..85220c14 100644 --- a/internal/task/apply.go +++ b/internal/task/apply.go @@ -108,5 +108,15 @@ func Apply(machine string, configFile config.Config, mounts []config.Mount, site } } + // if the php versions do not match, install the requested version - which makes it the default + if configFile.PHP != php { + installPhp, err := nitro.InstallPackages(machine, configFile.PHP) + if err != nil { + return nil, err + } + actions = append(actions, *installPhp) + + } + return actions, nil } diff --git a/internal/task/apply_test.go b/internal/task/apply_test.go index 1cc13ee7..339de78c 100644 --- a/internal/task/apply_test.go +++ b/internal/task/apply_test.go @@ -23,6 +23,21 @@ func TestApply(t *testing.T) { want []nitro.Action wantErr bool }{ + { + name: "mismatched versions of PHP installs the request version from the config file", + args: args{ + machine: "mytestmachine", + configFile: config.Config{PHP: "7.4"}, + php: "7.2", + }, + want: []nitro.Action{ + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "apt-get", "install", "-y", "php7.4", "php7.4-mbstring", "php7.4-cli", "php7.4-curl", "php7.4-fpm", "php7.4-gd", "php7.4-intl", "php7.4-json", "php7.4-mysql", "php7.4-opcache", "php7.4-pgsql", "php7.4-zip", "php7.4-xml", "php7.4-soap", "php-xdebug", "php-imagick", "blackfire-agent", "blackfire-php"}, + }, + }, + }, { name: "new databases that are in the config are created", args: args{ @@ -156,6 +171,7 @@ func TestApply(t *testing.T) { Webroot: "/nitro/sites/existing-site", }, }, + php: "7.4", }, want: []nitro.Action{ { @@ -221,6 +237,7 @@ func TestApply(t *testing.T) { Webroot: "/nitro/sites/existing-site", }, }, + php: "7.4", }, want: []nitro.Action{ { From 98e14da2fbd3b601571e42cbd16466fd98166223 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 16:52:24 -0400 Subject: [PATCH 27/67] make php a valid config option Signed-off-by: Jason McCallister --- config/config.go | 2 +- config/testdata/configs/full-example.yaml | 1 + config/testdata/configs/golden-full.yaml | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 31ee0ef1..2d99f5ea 100644 --- a/config/config.go +++ b/config/config.go @@ -14,7 +14,7 @@ import ( ) type Config struct { - PHP string `yaml:"-"` + PHP string `yaml:"php"` CPUs string `yaml:"-"` Disk string `yaml:"-"` Memory string `yaml:"-"` diff --git a/config/testdata/configs/full-example.yaml b/config/testdata/configs/full-example.yaml index b68b12aa..fb074429 100644 --- a/config/testdata/configs/full-example.yaml +++ b/config/testdata/configs/full-example.yaml @@ -1,3 +1,4 @@ +php: "7.4" mounts: - source: ~/go/src/github.com/craftcms/nitro/demo-site dest: /nitro/sites/demo-site diff --git a/config/testdata/configs/golden-full.yaml b/config/testdata/configs/golden-full.yaml index 4c08b1ae..cf9741cc 100644 --- a/config/testdata/configs/golden-full.yaml +++ b/config/testdata/configs/golden-full.yaml @@ -1,3 +1,4 @@ +php: "7.4" mounts: - source: ~/go/src/github.com/craftcms/nitro/production-site dest: /nitro/sites/production-site From 8fa951956b1c4f54557474fa305fd0a01c15e878 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 17:35:43 -0400 Subject: [PATCH 28/67] add clean flag to destroy to also remove config file Signed-off-by: Jason McCallister --- internal/cmd/destroy.go | 11 +++++++++++ internal/cmd/flags.go | 1 + 2 files changed, 12 insertions(+) diff --git a/internal/cmd/destroy.go b/internal/cmd/destroy.go index 5e406045..deeff9f7 100644 --- a/internal/cmd/destroy.go +++ b/internal/cmd/destroy.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "os" "os/exec" "github.com/spf13/cobra" @@ -38,6 +39,12 @@ var destroyCommand = &cobra.Command{ return err } + if flagClean { + if err := os.Remove(viper.ConfigFileUsed()); err != nil { + fmt.Println("unable to remove the config:", viper.ConfigFileUsed()) + } + } + if len(domains) == 0 { return nil } @@ -58,3 +65,7 @@ var destroyCommand = &cobra.Command{ return sudo.RunCommand(nitro, machine, cmds...) }, } + +func init() { + destroyCommand.Flags().BoolVar(&flagClean, "clean", false, "remove the config file when destroying the machine") +} diff --git a/internal/cmd/flags.go b/internal/cmd/flags.go index 6db87c15..3d95be4c 100644 --- a/internal/cmd/flags.go +++ b/internal/cmd/flags.go @@ -8,6 +8,7 @@ var ( flagDisk string flagPhpVersion string flagNginxLogsKind string + flagClean bool // flags for the add command flagHostname string From 7192d90d51498511044140c227afc80f60e51758 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 17:36:07 -0400 Subject: [PATCH 29/67] switch apply to use new task, update databases Signed-off-by: Jason McCallister --- internal/cmd/apply.go | 93 ++++++++++--------------------------------- 1 file changed, 20 insertions(+), 73 deletions(-) diff --git a/internal/cmd/apply.go b/internal/cmd/apply.go index a4093e74..de066c84 100644 --- a/internal/cmd/apply.go +++ b/internal/cmd/apply.go @@ -9,14 +9,14 @@ import ( "github.com/spf13/viper" "github.com/craftcms/nitro/config" - "github.com/craftcms/nitro/internal/diff" "github.com/craftcms/nitro/internal/find" "github.com/craftcms/nitro/internal/nitro" + "github.com/craftcms/nitro/internal/task" ) var applyCommand = &cobra.Command{ - Use: "apply", - Short: "Apply changes from config", + Use: "apply", + Short: "Apply changes from config", RunE: func(cmd *cobra.Command, args []string) error { machine := flagMachineName @@ -38,99 +38,46 @@ var applyCommand = &cobra.Command{ return err } - attachedMounts, err := find.Mounts(machine, output) + mounts, err := find.Mounts(machine, output) if err != nil { return err } // END ABSTRACT // find sites not created - var sitesToCreate []config.Site + var sites []config.Site for _, site := range configFile.Sites { - c := exec.Command(path, "exec", machine, "--", "sudo", "bash", "/opt/nitro/scripts/site-exists.sh", site.Hostname) - output, err := c.Output() + output, err := exec.Command(path, "exec", machine, "--", "sudo", "bash", "/opt/nitro/scripts/site-exists.sh", site.Hostname).Output() if err != nil { return err } if !strings.Contains(string(output), "exists") { - sitesToCreate = append(sitesToCreate, site) + sites = append(sites, site) } } - // check for new dbs - dbsToCreate, err := find.ContainersToCreate(machine, configFile) - if err != nil { - return err - } - - // task.ApplyChanges(cfgfile, attached, sitesNotCreated, dbsToCreate) - var actions []nitro.Action - - mountActions, err := diff.MountActions(machine, attachedMounts, configFile.GetExpandedMounts()) - if err != nil { - return err - } - actions = append(actions, mountActions...) - - // create site actions - for _, site := range sitesToCreate { - // TODO abstract this logic into a func that takes mountActions and sites to return the mount action - for _, ma := range mountActions { - // break the string - mnt := strings.Split(ma.Args[2], ":") - - // if the webroot is not of the mounts, then we should create an action - if !strings.Contains(mnt[1], site.Webroot) { - m := configFile.FindMountBySiteWebroot(site.Webroot) - mountAction, err := nitro.MountDir(machine, m.AbsSourcePath(), m.Dest) - if err != nil { - return err - } - actions = append(actions, *mountAction) - } - } - - copyTemplateAction, err := nitro.CopyNginxTemplate(machine, site.Hostname) + // check if a database already exists + var databases []config.Database + for _, db := range configFile.Databases { + database, err := find.ExistingContainer(exec.Command(path, []string{"exec", machine, "--", "sudo", "bash", "/opt/nitro/scripts/docker-container-exists.sh", db.Name()}...), db) if err != nil { return err } - actions = append(actions, *copyTemplateAction) - // copy the nginx template - changeNginxVariablesAction, err := nitro.ChangeTemplateVariables(machine, site.Webroot, site.Hostname, configFile.PHP, site.Aliases) - if err != nil { - return err - } - actions = append(actions, *changeNginxVariablesAction...) - - createSymlinkAction, err := nitro.CreateSiteSymllink(machine, site.Hostname) - if err != nil { - return err + if database != nil { + fmt.Println("Database", db.Name(), "exists, skipping...") + databases = append(databases, *database) } - actions = append(actions, *createSymlinkAction) } - if len(sitesToCreate) > 0 { - restartNginxAction, err := nitro.NginxReload(machine) - if err != nil { - return err - } - actions = append(actions, *restartNginxAction) + php, err := find.PHPVersion(exec.Command(path, "exec", machine, "--", "php", "--version")) + if err != nil { + return err } - // create database actions - for _, database := range dbsToCreate { - volumeAction, err := nitro.CreateDatabaseVolume(machine, database.Engine, database.Version, database.Port) - if err != nil { - return err - } - actions = append(actions, *volumeAction) - - createDatabaseAction, err := nitro.CreateDatabaseContainer(machine, database.Engine, database.Version, database.Port) - if err != nil { - return err - } - actions = append(actions, *createDatabaseAction) + actions, err := task.Apply(machine, configFile, mounts, sites, databases, php) + if err != nil { + return err } if flagDebug { From 8c6ba8de55b9a8df8eb86d93307a5f4c5ab658c8 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 17:36:18 -0400 Subject: [PATCH 30/67] add find existing container func Signed-off-by: Jason McCallister --- internal/find/find.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/internal/find/find.go b/internal/find/find.go index 96170cd7..d15d0814 100644 --- a/internal/find/find.go +++ b/internal/find/find.go @@ -49,6 +49,19 @@ func Mounts(name string, b []byte) ([]config.Mount, error) { return mounts, nil } +func ExistingContainer(f Finder, database config.Database) (*config.Database, error) { + output, err := f.Output() + if err != nil { + return nil, err + } + + if strings.Contains(string(output), "exists") { + return &database, nil + } + + return nil, nil +} + func ContainersToCreate(machine string, cfg config.Config) ([]config.Database, error) { path, err := exec.LookPath("multipass") if err != nil { From e3c55e06cf6a580365073b23bb81bade04927707 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 17:44:08 -0400 Subject: [PATCH 31/67] fix logic for the sites in apply Signed-off-by: Jason McCallister --- internal/cmd/add.go | 52 ++++++++++++++++++++++++++++++++++++++++++- internal/cmd/apply.go | 2 +- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/internal/cmd/add.go b/internal/cmd/add.go index d6aa8605..04136b68 100644 --- a/internal/cmd/add.go +++ b/internal/cmd/add.go @@ -3,12 +3,14 @@ package cmd import ( "fmt" "os/exec" + "strings" "github.com/manifoldco/promptui" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/craftcms/nitro/config" + "github.com/craftcms/nitro/internal/find" "github.com/craftcms/nitro/internal/helpers" "github.com/craftcms/nitro/internal/nitro" "github.com/craftcms/nitro/internal/sudo" @@ -132,7 +134,55 @@ var addCommand = &cobra.Command{ return nil } - actions, err := task.Add(machine, configFile, site) + path, err := exec.LookPath("multipass") + if err != nil { + return err + } + + // find the existingMounts + + c := exec.Command(path, []string{"info", machine, "--format=csv"}...) + output, err := c.Output() + if err != nil { + return err + } + existingMounts, err := find.Mounts(machine, output) + if err != nil { + return err + } + + // find the sites + var sites []config.Site + for _, site := range configFile.Sites { + output, err := exec.Command(path, "exec", machine, "--", "sudo", "bash", "/opt/nitro/scripts/site-exists.sh", site.Hostname).Output() + if err != nil { + return err + } + if strings.Contains(string(output), "exists") { + sites = append(sites, site) + } + } + + // check if a database already exists + var databases []config.Database + for _, db := range configFile.Databases { + database, err := find.ExistingContainer(exec.Command(path, []string{"exec", machine, "--", "sudo", "bash", "/opt/nitro/scripts/docker-container-exists.sh", db.Name()}...), db) + if err != nil { + return err + } + + if database != nil { + fmt.Println("Database", db.Name(), "exists, skipping...") + databases = append(databases, *database) + } + } + + php, err := find.PHPVersion(exec.Command(path, "exec", machine, "--", "php", "--version")) + if err != nil { + return err + } + + actions, err := task.Apply(machine, configFile, existingMounts, sites, databases, php) if flagDebug { for _, action := range actions { diff --git a/internal/cmd/apply.go b/internal/cmd/apply.go index de066c84..41c04c56 100644 --- a/internal/cmd/apply.go +++ b/internal/cmd/apply.go @@ -51,7 +51,7 @@ var applyCommand = &cobra.Command{ if err != nil { return err } - if !strings.Contains(string(output), "exists") { + if strings.Contains(string(output), "exists") { sites = append(sites, site) } } From b34165acb1c0e21474e511a1f800d372df77bba6 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 21:24:58 -0400 Subject: [PATCH 32/67] create symlinks when adding sites Signed-off-by: Jason McCallister --- internal/cmd/apply.go | 10 +++++++++- internal/task/apply.go | 6 ++++++ internal/task/apply_test.go | 10 ++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/internal/cmd/apply.go b/internal/cmd/apply.go index 41c04c56..a96d35f0 100644 --- a/internal/cmd/apply.go +++ b/internal/cmd/apply.go @@ -11,6 +11,7 @@ import ( "github.com/craftcms/nitro/config" "github.com/craftcms/nitro/internal/find" "github.com/craftcms/nitro/internal/nitro" + "github.com/craftcms/nitro/internal/sudo" "github.com/craftcms/nitro/internal/task" ) @@ -94,6 +95,13 @@ var applyCommand = &cobra.Command{ fmt.Println("Applied changes from", viper.ConfigFileUsed()) - return nil + nitro, err := exec.LookPath("nitro") + if err != nil { + return err + } + + fmt.Println("Editing your hosts file") + + return sudo.RunCommand(nitro, machine, "hosts") }, } diff --git a/internal/task/apply.go b/internal/task/apply.go index 85220c14..2e16d903 100644 --- a/internal/task/apply.go +++ b/internal/task/apply.go @@ -71,6 +71,12 @@ func Apply(machine string, configFile config.Config, mounts []config.Mount, site } actions = append(actions, *changeNginxVariablesAction...) + createSymlink, err := nitro.CreateSiteSymllink(machine, site.Hostname) + if err != nil { + return nil, err + } + actions = append(actions, *createSymlink) + // reload nginx reloadNginxAction, err := nitro.NginxReload(machine) if err != nil { diff --git a/internal/task/apply_test.go b/internal/task/apply_test.go index 339de78c..deb8e135 100644 --- a/internal/task/apply_test.go +++ b/internal/task/apply_test.go @@ -194,6 +194,11 @@ func TestApply(t *testing.T) { UseSyscall: false, Args: []string{"exec", "mytestmachine", "--", "sudo", "sed", "-i", "s|CHANGEPHPVERSION|7.4|g", "/etc/nginx/sites-available/new-site"}, }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "ln", "-s", "/etc/nginx/sites-available/new-site", "/etc/nginx/sites-enabled/"}, + }, { Type: "exec", UseSyscall: false, @@ -260,6 +265,11 @@ func TestApply(t *testing.T) { UseSyscall: false, Args: []string{"exec", "mytestmachine", "--", "sudo", "sed", "-i", "s|CHANGEPHPVERSION|7.4|g", "/etc/nginx/sites-available/new-site"}, }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "ln", "-s", "/etc/nginx/sites-available/new-site", "/etc/nginx/sites-enabled/"}, + }, { Type: "exec", UseSyscall: false, From 3f56414df2f6f7daecad75e12c57b9f6a3b03886 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Mon, 27 Apr 2020 21:25:19 -0400 Subject: [PATCH 33/67] update add to call apply Signed-off-by: Jason McCallister --- internal/cmd/add.go | 92 +++------------------------------------------ 1 file changed, 6 insertions(+), 86 deletions(-) diff --git a/internal/cmd/add.go b/internal/cmd/add.go index 04136b68..a0e91285 100644 --- a/internal/cmd/add.go +++ b/internal/cmd/add.go @@ -2,19 +2,13 @@ package cmd import ( "fmt" - "os/exec" - "strings" "github.com/manifoldco/promptui" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/craftcms/nitro/config" - "github.com/craftcms/nitro/internal/find" "github.com/craftcms/nitro/internal/helpers" - "github.com/craftcms/nitro/internal/nitro" - "github.com/craftcms/nitro/internal/sudo" - "github.com/craftcms/nitro/internal/task" "github.com/craftcms/nitro/internal/webroot" "github.com/craftcms/nitro/validate" ) @@ -23,7 +17,11 @@ var addCommand = &cobra.Command{ Use: "add", Short: "Add site to machine", RunE: func(cmd *cobra.Command, args []string) error { - machine := flagMachineName + // load the config + var configFile config.Config + if err := viper.Unmarshal(&configFile); err != nil { + return err + } // if there is no arg, get the current working dir // else get the first arg @@ -88,12 +86,6 @@ var addCommand = &cobra.Command{ // create the vmWebRootPath (e.g. "/nitro/sites/"+ hostName + "/" | webrootName webRootPath := fmt.Sprintf("/nitro/sites/%s/%s", hostname, webrootDir) - // load the config - var configFile config.Config - if err := viper.Unmarshal(&configFile); err != nil { - return err - } - // create a new mount // add the mount to configfile mount := config.Mount{Source: absolutePath, Dest: "/nitro/sites/" + hostname} @@ -134,79 +126,7 @@ var addCommand = &cobra.Command{ return nil } - path, err := exec.LookPath("multipass") - if err != nil { - return err - } - - // find the existingMounts - - c := exec.Command(path, []string{"info", machine, "--format=csv"}...) - output, err := c.Output() - if err != nil { - return err - } - existingMounts, err := find.Mounts(machine, output) - if err != nil { - return err - } - - // find the sites - var sites []config.Site - for _, site := range configFile.Sites { - output, err := exec.Command(path, "exec", machine, "--", "sudo", "bash", "/opt/nitro/scripts/site-exists.sh", site.Hostname).Output() - if err != nil { - return err - } - if strings.Contains(string(output), "exists") { - sites = append(sites, site) - } - } - - // check if a database already exists - var databases []config.Database - for _, db := range configFile.Databases { - database, err := find.ExistingContainer(exec.Command(path, []string{"exec", machine, "--", "sudo", "bash", "/opt/nitro/scripts/docker-container-exists.sh", db.Name()}...), db) - if err != nil { - return err - } - - if database != nil { - fmt.Println("Database", db.Name(), "exists, skipping...") - databases = append(databases, *database) - } - } - - php, err := find.PHPVersion(exec.Command(path, "exec", machine, "--", "php", "--version")) - if err != nil { - return err - } - - actions, err := task.Apply(machine, configFile, existingMounts, sites, databases, php) - - if flagDebug { - for _, action := range actions { - fmt.Println(action.Args) - } - - return nil - } - - if err = nitro.Run(nitro.NewMultipassRunner("multipass"), actions); err != nil { - return err - } - - fmt.Println("Applied the changes and added", hostname, "to", machine) - - // prompt to add hosts file - nitro, err := exec.LookPath("nitro") - if err != nil { - return err - } - - fmt.Println("Adding", site.Hostname, "to your hosts file") - - return sudo.RunCommand(nitro, machine, "hosts") + return applyCommand.RunE(cmd, args) }, } From 5a6f97e17086c836a2cd3213c6c0e2be5d09ca32 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 07:30:49 -0400 Subject: [PATCH 34/67] check for os note Signed-off-by: Jason McCallister --- internal/cmd/apply.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/cmd/apply.go b/internal/cmd/apply.go index a96d35f0..2d3ef5ac 100644 --- a/internal/cmd/apply.go +++ b/internal/cmd/apply.go @@ -102,6 +102,7 @@ var applyCommand = &cobra.Command{ fmt.Println("Editing your hosts file") + // TODO check the current OS and call commands for windows return sudo.RunCommand(nitro, machine, "hosts") }, } From 03d024beab5d7ef7d972870190b5c82185631984 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 08:33:50 -0400 Subject: [PATCH 35/67] add tests for finding database Signed-off-by: Jason McCallister --- config/config_test.go | 74 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/config/config_test.go b/config/config_test.go index bf469713..c2a13bed 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -760,3 +760,77 @@ func TestConfig_SiteExists(t *testing.T) { }) } } + +func TestConfig_DatabaseExists(t *testing.T) { + type fields struct { + PHP string + CPUs string + Disk string + Memory string + Mounts []Mount + Databases []Database + Sites []Site + } + type args struct { + database Database + } + tests := []struct { + name string + fields fields + args args + want bool + }{ + { + name: "can find an existing database", + fields: fields{ + Databases: []Database{ + { + Engine: "mysql", + Version: "5.7", + Port: "3306", + }, + }, + }, + args: args{database: Database{ + Engine: "mysql", + Version: "5.8", + Port: "3306", + }}, + want: false, + }, + { + name: "non-existing databases return false", + fields: fields{ + Databases: []Database{ + { + Engine: "mysql", + Version: "5.7", + Port: "3306", + }, + }, + }, + args: args{database: Database{ + Engine: "mysql", + Version: "5.7", + Port: "3306", + }}, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + PHP: tt.fields.PHP, + CPUs: tt.fields.CPUs, + Disk: tt.fields.Disk, + Memory: tt.fields.Memory, + Mounts: tt.fields.Mounts, + Databases: tt.fields.Databases, + Sites: tt.fields.Sites, + } + if got := c.DatabaseExists(tt.args.database); got != tt.want { + t.Errorf("DatabaseExists() = %v, want %v", got, tt.want) + } + }) + } +} From d254a0fc4033d7f1985418cfab4197e7e8ff6d93 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 08:35:11 -0400 Subject: [PATCH 36/67] add more tests for apply Signed-off-by: Jason McCallister --- internal/task/apply_test.go | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/internal/task/apply_test.go b/internal/task/apply_test.go index deb8e135..0c10045f 100644 --- a/internal/task/apply_test.go +++ b/internal/task/apply_test.go @@ -66,6 +66,50 @@ func TestApply(t *testing.T) { }, }, }, + { + name: "new databases are created but the ones in the config are kept", + args: args{ + machine: "mytestmachine", + configFile: config.Config{ + Databases: []config.Database{ + { + Engine: "mysql", + Version: "5.7", + Port: "3306", + }, + { + Engine: "postgres", + Version: "11", + Port: "5432", + }, + }, + }, + dbs: []config.Database{ + { + Engine: "mysql", + Version: "5.7", + Port: "3306", + }, + { + Engine: "postgres", + Version: "11", + Port: "5432", + }, + { + Engine: "postgres", + Version: "12", + Port: "54321", + }, + }, + }, + want: []nitro.Action{ + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "docker", "rm", "-v", "postgres_12_54321"}, + }, + }, + }, { name: "databases that are not in the config are removed", args: args{ From 7097841302d8caca384c935dc9dd91f6a1ac057e Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 10:08:04 -0400 Subject: [PATCH 37/67] fix an issue with apply and databases Signed-off-by: Jason McCallister --- internal/cmd/apply.go | 20 +++++++------------- internal/find/find.go | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/internal/cmd/apply.go b/internal/cmd/apply.go index 2d3ef5ac..028772e5 100644 --- a/internal/cmd/apply.go +++ b/internal/cmd/apply.go @@ -39,13 +39,14 @@ var applyCommand = &cobra.Command{ return err } + // find mounts that already exist mounts, err := find.Mounts(machine, output) if err != nil { return err } // END ABSTRACT - // find sites not created + // find sites that are created var sites []config.Site for _, site := range configFile.Sites { output, err := exec.Command(path, "exec", machine, "--", "sudo", "bash", "/opt/nitro/scripts/site-exists.sh", site.Hostname).Output() @@ -57,20 +58,13 @@ var applyCommand = &cobra.Command{ } } - // check if a database already exists - var databases []config.Database - for _, db := range configFile.Databases { - database, err := find.ExistingContainer(exec.Command(path, []string{"exec", machine, "--", "sudo", "bash", "/opt/nitro/scripts/docker-container-exists.sh", db.Name()}...), db) - if err != nil { - return err - } - - if database != nil { - fmt.Println("Database", db.Name(), "exists, skipping...") - databases = append(databases, *database) - } + // find all existing databases + databases, err := find.AllDatabases(exec.Command(path, []string{"exec", machine, "--", "docker", "container", "ls", "--format", `'{{ .Names }}'`}...)) + if err != nil { + return err } + // find the current version of php installed php, err := find.PHPVersion(exec.Command(path, "exec", machine, "--", "php", "--version")) if err != nil { return err diff --git a/internal/find/find.go b/internal/find/find.go index d15d0814..d047d297 100644 --- a/internal/find/find.go +++ b/internal/find/find.go @@ -136,3 +136,27 @@ func PHPVersion(f Finder) (string, error) { return version, nil } + +// docker container ls --format '{{.Names}}' +func AllDatabases(f Finder) ([]config.Database, error) { + out, err := f.Output() + if err != nil { + return nil, err + } + + var databases []config.Database + sc := bufio.NewScanner(strings.NewReader(string(out))) + for sc.Scan() { + if strings.Contains(sc.Text(), "mysql") || strings.Contains(sc.Text(), "postgres") { + sp := strings.Split(sc.Text(), "_") + db := config.Database{ + Engine: strings.TrimLeft(sp[0], "'"), + Version: sp[1], + Port: strings.TrimRight(sp[2], "'"), + } + databases = append(databases, db) + } + } + + return databases, nil +} From 8b896f7ecaa0f3d3736ba2ec7d7ab1e3c4f375f0 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 10:10:20 -0400 Subject: [PATCH 38/67] force remove running containers Signed-off-by: Jason McCallister --- internal/task/apply.go | 2 +- internal/task/apply_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/task/apply.go b/internal/task/apply.go index 2e16d903..a2bde35c 100644 --- a/internal/task/apply.go +++ b/internal/task/apply.go @@ -92,7 +92,7 @@ func Apply(machine string, configFile config.Config, mounts []config.Mount, site actions = append(actions, nitro.Action{ Type: "exec", UseSyscall: false, - Args: []string{"exec", machine, "--", "docker", "rm", "-v", database.Name()}, + Args: []string{"exec", machine, "--", "docker", "rm", "-v", database.Name(), "-f"}, }) } } diff --git a/internal/task/apply_test.go b/internal/task/apply_test.go index 0c10045f..fd1363ed 100644 --- a/internal/task/apply_test.go +++ b/internal/task/apply_test.go @@ -106,7 +106,7 @@ func TestApply(t *testing.T) { { Type: "exec", UseSyscall: false, - Args: []string{"exec", "mytestmachine", "--", "docker", "rm", "-v", "postgres_12_54321"}, + Args: []string{"exec", "mytestmachine", "--", "docker", "rm", "-v", "postgres_12_54321", "-f"}, }, }, }, @@ -140,7 +140,7 @@ func TestApply(t *testing.T) { { Type: "exec", UseSyscall: false, - Args: []string{"exec", "mytestmachine", "--", "docker", "rm", "-v", "postgres_11_5432"}, + Args: []string{"exec", "mytestmachine", "--", "docker", "rm", "-v", "postgres_11_5432", "-f"}, }, }, }, From fdf24029b58a983afec06646ec047bc25feec745 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 10:31:22 -0400 Subject: [PATCH 39/67] replace prompt library and update remove to use apply Signed-off-by: Jason McCallister --- go.mod | 1 + go.sum | 3 ++ internal/cmd/remove.go | 72 +++++++++++++++++++++++++++++------------- 3 files changed, 54 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index 8b136560..c6702161 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.6.2 github.com/stretchr/testify v1.5.1 // indirect + github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8 github.com/txn2/txeh v1.3.0 gopkg.in/yaml.v2 v2.2.8 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c diff --git a/go.sum b/go.sum index c3acb387..3a64b07e 100644 --- a/go.sum +++ b/go.sum @@ -134,6 +134,8 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8 h1:RB0v+/pc8oMzPsN97aZYEwNuJ6ouRJ2uhjxemJ9zvrY= +github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8/go.mod h1:IlWNj9v/13q7xFbaK4mbyzMNwrZLaWSHx/aibKIZuIg= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/txn2/txeh v1.3.0 h1:vnbv63htVMZCaQgLqVBxKvj2+HHHFUzNW7I183zjg3E= github.com/txn2/txeh v1.3.0/go.mod h1:O7M6gUTPeMF+vsa4c4Ipx3JDkOYrruB1Wry8QRsMcw8= @@ -147,6 +149,7 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ 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= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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= diff --git a/internal/cmd/remove.go b/internal/cmd/remove.go index e8b9cedf..7843fef6 100644 --- a/internal/cmd/remove.go +++ b/internal/cmd/remove.go @@ -4,16 +4,15 @@ import ( "bytes" "errors" "fmt" - "os/exec" + "os" + "strings" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/tcnksm/go-input" "gopkg.in/yaml.v3" "github.com/craftcms/nitro/config" - "github.com/craftcms/nitro/internal/nitro" - "github.com/craftcms/nitro/internal/prompt" - "github.com/craftcms/nitro/internal/sudo" "github.com/craftcms/nitro/internal/task" ) @@ -34,9 +33,27 @@ var removeCommand = &cobra.Command{ return errors.New("there are no sites to remove") } - i, _ := prompt.Select("Select site to remove", configFile.SitesAsList()) + ui := &input.UI{ + Writer: os.Stdout, + Reader: os.Stdin, + } - site := sites[i] + selectedSite, err := ui.Select("Select a site to remove:", configFile.SitesAsList(), &input.Options{ + Required: true, + }) + if err != nil { + return err + } + + var site config.Site + for i, s := range sites { + if s.Hostname == selectedSite { + site = sites[i] + } + } + if site.Hostname == "" { + return errors.New("error selecting a site") + } // find the mount mount := configFile.FindMountBySiteWebroot(site.Webroot) @@ -60,9 +77,13 @@ var removeCommand = &cobra.Command{ if err := viper.ReadConfig(bytes.NewBuffer(c)); err != nil { return err } - if err := viper.WriteConfigAs(viper.ConfigFileUsed()); err != nil { - return err + + if !flagDebug { + if err := viper.WriteConfigAs(viper.ConfigFileUsed()); err != nil { + return err + } } + // unmarshal the messy config into a config var messyConfig config.Config if err := viper.Unmarshal(&messyConfig); err != nil { @@ -82,29 +103,36 @@ var removeCommand = &cobra.Command{ return err } + applyChanges := false + answer, err := ui.Ask("Apply changes from config now?", &input.Options{ + Default: "y", + Required: true, + Loop: true, + }) + if err != nil { + return err + } + + if strings.ContainsAny(answer, "y") { + applyChanges = true + } + // save the config if flagDebug { + if applyChanges { + fmt.Println("Ok, applying changes from the config file...") + } for _, a := range actions { fmt.Println(a.Args) } return nil } - if err := nitro.Run(nitro.NewMultipassRunner("multipass"), actions); err != nil { - fmt.Println("Failed to remove the site:", err) - return err + if applyChanges { + fmt.Println("Ok, applying changes from the config file...") + return applyCommand.RunE(cmd, args) } - fmt.Println("Removed the site from your config and applied the changes.") - - // prompt to remove hosts file - nitro, err := exec.LookPath("nitro") - if err != nil { - return err - } - - fmt.Println("Removing site from your hosts file") - - return sudo.RunCommand(nitro, machine, "hosts", "remove", site.Hostname) + return nil }, } From 84090c84f30c141808d5394705e2cc9d50357b8f Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 15:17:54 -0400 Subject: [PATCH 40/67] =?UTF-8?q?don=E2=80=99t=20error=20on=20no=20sites?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jason McCallister --- internal/cmd/hosts.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/cmd/hosts.go b/internal/cmd/hosts.go index 3a286666..d436db1e 100644 --- a/internal/cmd/hosts.go +++ b/internal/cmd/hosts.go @@ -32,15 +32,16 @@ var hostsCommand = &cobra.Command{ ip := nitro.IP(machine, nitro.NewMultipassRunner("multipass")) // get all of the sites from the config file - if !viper.IsSet("sites") { - return errors.New("unable to read sites from " + viper.ConfigFileUsed()) - } - var sites []config.Site if err := viper.UnmarshalKey("sites", &sites); err != nil { return err } + if sites == nil { + fmt.Println("There are no sites in the config file to remove") + return nil + } + var domains []string for _, site := range sites { domains = append(domains, site.Hostname) From 565d403530c3c7c612a72a415ca20a9255b98111 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 15:18:09 -0400 Subject: [PATCH 41/67] update rmeove and rename to use apply Signed-off-by: Jason McCallister --- internal/cmd/remove.go | 48 +++------------------------ internal/cmd/rename.go | 68 +++++++++++---------------------------- internal/prompt/prompt.go | 63 +++++++++++++++++------------------- 3 files changed, 54 insertions(+), 125 deletions(-) diff --git a/internal/cmd/remove.go b/internal/cmd/remove.go index 7843fef6..deacf82b 100644 --- a/internal/cmd/remove.go +++ b/internal/cmd/remove.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "os" - "strings" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -13,15 +12,13 @@ import ( "gopkg.in/yaml.v3" "github.com/craftcms/nitro/config" - "github.com/craftcms/nitro/internal/task" + "github.com/craftcms/nitro/internal/prompt" ) var removeCommand = &cobra.Command{ Use: "remove", Short: "Manage your nitro sites", RunE: func(cmd *cobra.Command, args []string) error { - machine := flagMachineName - var configFile config.Config if err := viper.Unmarshal(&configFile); err != nil { return err @@ -38,22 +35,12 @@ var removeCommand = &cobra.Command{ Reader: os.Stdin, } - selectedSite, err := ui.Select("Select a site to remove:", configFile.SitesAsList(), &input.Options{ - Required: true, - }) + var site config.Site + _, i, err := prompt.Select(ui, "Select a site to remove:", sites[0].Hostname, configFile.SitesAsList()) if err != nil { return err } - - var site config.Site - for i, s := range sites { - if s.Hostname == selectedSite { - site = sites[i] - } - } - if site.Hostname == "" { - return errors.New("error selecting a site") - } + site = sites[i] // find the mount mount := configFile.FindMountBySiteWebroot(site.Webroot) @@ -98,36 +85,11 @@ var removeCommand = &cobra.Command{ } // END HACK - actions, err := task.Remove(machine, *mount, site) + applyChanges, err := prompt.Verify(ui, "Apply changes from config now?", "y") if err != nil { return err } - applyChanges := false - answer, err := ui.Ask("Apply changes from config now?", &input.Options{ - Default: "y", - Required: true, - Loop: true, - }) - if err != nil { - return err - } - - if strings.ContainsAny(answer, "y") { - applyChanges = true - } - - // save the config - if flagDebug { - if applyChanges { - fmt.Println("Ok, applying changes from the config file...") - } - for _, a := range actions { - fmt.Println(a.Args) - } - return nil - } - if applyChanges { fmt.Println("Ok, applying changes from the config file...") return applyCommand.RunE(cmd, args) diff --git a/internal/cmd/rename.go b/internal/cmd/rename.go index a975d0dd..2acb5642 100644 --- a/internal/cmd/rename.go +++ b/internal/cmd/rename.go @@ -3,18 +3,15 @@ package cmd import ( "errors" "fmt" - "os/exec" + "os" "github.com/manifoldco/promptui" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/tcnksm/go-input" "github.com/craftcms/nitro/config" - "github.com/craftcms/nitro/internal/find" - "github.com/craftcms/nitro/internal/nitro" "github.com/craftcms/nitro/internal/prompt" - "github.com/craftcms/nitro/internal/sudo" - "github.com/craftcms/nitro/internal/task" "github.com/craftcms/nitro/validate" ) @@ -22,23 +19,30 @@ var renameCommand = &cobra.Command{ Use: "rename", Short: "Rename a site", RunE: func(cmd *cobra.Command, args []string) error { - machine := flagMachineName - var configFile config.Config if err := viper.Unmarshal(&configFile); err != nil { return err } - configSites := configFile.GetSites() + sites := configFile.GetSites() - if len(configSites) == 0 { + if len(sites) == 0 { return errors.New("there are no sites to rename") } + ui := &input.UI{ + Writer: os.Stdout, + Reader: os.Stdin, + } + // ask to select a existingSite - i, _ := prompt.Select("Select existingSite to rename", configFile.SitesAsList()) + var existingSite config.Site + _, i, err := prompt.Select(ui, "Select a site to rename:", "1", configFile.SitesAsList()) + if err != nil { + return err + } - existingSite := configSites[i] + existingSite = sites[i] // ask for the new newHostname var newHostname string @@ -63,20 +67,6 @@ var renameCommand = &cobra.Command{ return errors.New("the new and original hostnames match, nothing to do") } - path, err := exec.LookPath("multipass") - if err != nil { - return err - } - - php, err := find.PHPVersion( - exec.Command(path, []string{"exec", machine, "--", "php", "--version"}...), - ) - - renamedSite := config.Site{Hostname: newHostname, Webroot: existingSite.Webroot} - mount := configFile.FindMountBySiteWebroot(existingSite.Webroot) - - actions, err := task.Rename(machine, php, existingSite, renamedSite, mount) - // update the config if err := configFile.RenameSite(existingSite, newHostname); err != nil { return err @@ -89,34 +79,14 @@ var renameCommand = &cobra.Command{ } } - if flagDebug { - for _, action := range actions { - fmt.Println(action.Args) - } - - return nil - } - - if err := nitro.Run(nitro.NewMultipassRunner("multipass"), actions); err != nil { - return err - } - - fmt.Println(fmt.Sprintf("Ok, we renamed the existingSite %s to %s. We are now going to update the hosts file...", existingSite.Hostname, newHostname)) - - nitro, err := exec.LookPath("nitro") + applyChanges, err := prompt.Verify(ui, "Apply changes from config now?", "y") if err != nil { return err } - // remove the existingSite - if err := sudo.RunCommand(nitro, machine, "hosts", "remove", existingSite.Hostname); err != nil { - fmt.Println("Error removing", existingSite.Hostname, "from the hosts file") - return err - } - - if err := sudo.RunCommand(nitro, machine, "hosts"); err != nil { - fmt.Println("Error adding", newHostname, "to the hosts file") - return err + if applyChanges { + fmt.Println("Ok, applying changes from the config file...") + return applyCommand.RunE(cmd, args) } return nil diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 95966dfa..957cacbe 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -1,24 +1,13 @@ package prompt import ( + "errors" + "strings" + "github.com/manifoldco/promptui" + "github.com/tcnksm/go-input" ) -func Ask(label, def string, validator promptui.ValidateFunc) (string, error) { - p := promptui.Prompt{ - Label: label, - Default: def, - Validate: validator, - } - - v, err := p.Run() - if err != nil { - return "", err - } - - return v, nil -} - func AskWithDefault(label, def string, validator promptui.ValidateFunc) (string, error) { p := promptui.Prompt{ Label: label + " [" + def + "]", @@ -38,20 +27,28 @@ func AskWithDefault(label, def string, validator promptui.ValidateFunc) (string, return v, nil } -func SelectWithDefault(label, def string, options []string) (int, string) { - p := promptui.Select{ - Label: label + " [" + def + "]", - Items: options, +// Select is responsible for providing a list of options to remove +func Select(ui *input.UI, query, def string, list []string) (string, int, error) { + selected, err := ui.Select(query, list, &input.Options{ + Required: true, + Default: def, + }) + if err != nil { + return "", 0, err } - i, selected, _ := p.Run() + for i, s := range list { + if s == selected { + return s, i, nil + } + } - return i, selected + return "", 0, errors.New("unable to find the selected option") } -func Select(label string, options []string) (int, string) { +func SelectWithDefault(label, def string, options []string) (int, string) { p := promptui.Select{ - Label: label, + Label: label + " [" + def + "]", Items: options, } @@ -60,19 +57,19 @@ func Select(label string, options []string) (int, string) { return i, selected } -func Verify(label string) bool { - verify := promptui.Prompt{ - Label: label, - } - - answer, err := verify.Run() +func Verify(ui *input.UI, query, def string) (bool, error) { + a, err := ui.Ask(query, &input.Options{ + Default: def, + Required: true, + Loop: true, + }) if err != nil { - return false + return false, err } - if answer == "" { - return true + if strings.ContainsAny(a, "y") { + return true, nil } - return false + return false, nil } From 5a66ad80aa1c19dfc74e386b6c169d6ee72b2e50 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 15:37:06 -0400 Subject: [PATCH 42/67] remove old prompt from add Signed-off-by: Jason McCallister --- internal/cmd/add.go | 46 ++++++++++----------------------------- internal/cmd/rename.go | 14 ++++++------ internal/prompt/prompt.go | 13 +++++++++++ 3 files changed, 32 insertions(+), 41 deletions(-) diff --git a/internal/cmd/add.go b/internal/cmd/add.go index a0e91285..4545a3c1 100644 --- a/internal/cmd/add.go +++ b/internal/cmd/add.go @@ -2,15 +2,16 @@ package cmd import ( "fmt" + "os" - "github.com/manifoldco/promptui" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/tcnksm/go-input" "github.com/craftcms/nitro/config" "github.com/craftcms/nitro/internal/helpers" + "github.com/craftcms/nitro/internal/prompt" "github.com/craftcms/nitro/internal/webroot" - "github.com/craftcms/nitro/validate" ) var addCommand = &cobra.Command{ @@ -31,27 +32,20 @@ var addCommand = &cobra.Command{ return err } + ui := &input.UI{ + Writer: os.Stdout, + Reader: os.Stdin, + } + // prompt for the hostname if --hostname == "" // else get the name of the current directory (e.g. nitro) var hostname string switch flagHostname { case "": - hostnamePrompt := promptui.Prompt{ - Label: fmt.Sprintf("What should the hostname be? [%s]", directoryName), - Validate: validate.Hostname, - } - - hostnameEntered, err := hostnamePrompt.Run() + hostname, err = prompt.Ask(ui, "What should the hostname be?", directoryName, true) if err != nil { return err } - - switch hostnameEntered { - case "": - hostname = directoryName - default: - hostname = hostnameEntered - } default: hostname = helpers.RemoveTrailingSlash(flagHostname) } @@ -65,20 +59,11 @@ var addCommand = &cobra.Command{ if err != nil { return err } - webRootPrompt := promptui.Prompt{ - Label: fmt.Sprintf("Where is the webroot? [%s]", foundDir), - } - webrootEntered, err := webRootPrompt.Run() + webrootDir, err = prompt.Ask(ui, "Where is the webroot?", foundDir, true) if err != nil { return err } - switch webrootEntered { - case "": - webrootDir = foundDir - default: - webrootDir = webrootEntered - } default: webrootDir = flagWebroot } @@ -108,19 +93,12 @@ var addCommand = &cobra.Command{ fmt.Printf("%s has been added to nitro.yaml", hostname) - applyPrompt := promptui.Prompt{ - Label: "Apply changes now? [y]", - } - - apply, err := applyPrompt.Run() + applyChanges, err := prompt.Verify(ui, "Apply changes from config now?", "y") if err != nil { return err } - if apply == "" { - apply = "y" - } - if apply != "y" { + if !applyChanges { fmt.Println("You can apply new nitro.yaml changes later by running `nitro apply`.") return nil diff --git a/internal/cmd/rename.go b/internal/cmd/rename.go index 2acb5642..55da34b9 100644 --- a/internal/cmd/rename.go +++ b/internal/cmd/rename.go @@ -35,19 +35,19 @@ var renameCommand = &cobra.Command{ Reader: os.Stdin, } - // ask to select a existingSite - var existingSite config.Site + // ask to select a site + var site config.Site _, i, err := prompt.Select(ui, "Select a site to rename:", "1", configFile.SitesAsList()) if err != nil { return err } - existingSite = sites[i] + site = sites[i] // ask for the new newHostname var newHostname string hostnamePrompt := promptui.Prompt{ - Label: fmt.Sprintf("What should the new newHostname be? [current: %s]", existingSite.Hostname), + Label: fmt.Sprintf("What should the new newHostname be? [current: %s]", site.Hostname), Validate: validate.Hostname, } @@ -58,17 +58,17 @@ var renameCommand = &cobra.Command{ switch hostnameEntered { case "": - newHostname = existingSite.Hostname + newHostname = site.Hostname default: newHostname = hostnameEntered } - if existingSite.Hostname == newHostname { + if site.Hostname == newHostname { return errors.New("the new and original hostnames match, nothing to do") } // update the config - if err := configFile.RenameSite(existingSite, newHostname); err != nil { + if err := configFile.RenameSite(site, newHostname); err != nil { return err } diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 957cacbe..a905623b 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -27,6 +27,19 @@ func AskWithDefault(label, def string, validator promptui.ValidateFunc) (string, return v, nil } +func Ask(ui *input.UI, query, def string, req bool) (string, error) { + a, err := ui.Ask(query, &input.Options{ + Default: def, + Required: req, + Loop: true, + }) + if err != nil { + return "", err + } + + return a, nil +} + // Select is responsible for providing a list of options to remove func Select(ui *input.UI, query, def string, list []string) (string, int, error) { selected, err := ui.Select(query, list, &input.Options{ From 1800c20393b2ac23c47360dc6d71f3b42e8f521a Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 15:40:47 -0400 Subject: [PATCH 43/67] switch hostname to new prompt Signed-off-by: Jason McCallister --- internal/cmd/rename.go | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/internal/cmd/rename.go b/internal/cmd/rename.go index 55da34b9..55f21c18 100644 --- a/internal/cmd/rename.go +++ b/internal/cmd/rename.go @@ -5,14 +5,12 @@ import ( "fmt" "os" - "github.com/manifoldco/promptui" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/tcnksm/go-input" "github.com/craftcms/nitro/config" "github.com/craftcms/nitro/internal/prompt" - "github.com/craftcms/nitro/validate" ) var renameCommand = &cobra.Command{ @@ -37,32 +35,18 @@ var renameCommand = &cobra.Command{ // ask to select a site var site config.Site - _, i, err := prompt.Select(ui, "Select a site to rename:", "1", configFile.SitesAsList()) + _, i, err := prompt.Select(ui, "Select a site to rename:", sites[0].Hostname, configFile.SitesAsList()) if err != nil { return err } - site = sites[i] // ask for the new newHostname var newHostname string - hostnamePrompt := promptui.Prompt{ - Label: fmt.Sprintf("What should the new newHostname be? [current: %s]", site.Hostname), - Validate: validate.Hostname, - } - - hostnameEntered, err := hostnamePrompt.Run() + newHostname, err = prompt.Ask(ui, "What should the new hostname be?", site.Hostname, true) if err != nil { return err } - - switch hostnameEntered { - case "": - newHostname = site.Hostname - default: - newHostname = hostnameEntered - } - if site.Hostname == newHostname { return errors.New("the new and original hostnames match, nothing to do") } From ab827f2618f8dd3f89e0413981ca25e6e1044ab5 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 15:47:33 -0400 Subject: [PATCH 44/67] remove the old prompt from logs Signed-off-by: Jason McCallister --- internal/cmd/logs.go | 45 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/internal/cmd/logs.go b/internal/cmd/logs.go index b4de7448..013fa617 100644 --- a/internal/cmd/logs.go +++ b/internal/cmd/logs.go @@ -3,14 +3,15 @@ package cmd import ( "errors" "fmt" - "strings" + "os" - "github.com/manifoldco/promptui" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/tcnksm/go-input" "github.com/craftcms/nitro/config" "github.com/craftcms/nitro/internal/nitro" + "github.com/craftcms/nitro/internal/prompt" ) var logsCommand = &cobra.Command{ @@ -21,14 +22,12 @@ var logsCommand = &cobra.Command{ // define the flags opts := []string{"nginx", "database", "docker"} - - logType := promptui.Select{ - Label: "Select the type of logs to view", - Items: opts, - Size: len(opts), + ui := &input.UI{ + Writer: os.Stdout, + Reader: os.Stdin, } - _, kind, err := logType.Run() + kind, _, err := prompt.Select(ui, "Select the type of logs to view", "nginx", opts) if err != nil { return err } @@ -36,26 +35,15 @@ var logsCommand = &cobra.Command{ var actions []nitro.Action switch kind { case "docker": - validate := func(input string) error { - if input == "" { - return errors.New("container machine cannot be empty") - } - if strings.Contains(input, " ") { - return errors.New("container names cannot contain spaces") - } - return nil - } - - containerNamePrompt := promptui.Prompt{ - Label: "Enter container machine", - Validate: validate, - } - - containerName, err := containerNamePrompt.Run() + containerName, err := prompt.Ask(ui, "Enter container name:", "", true) if err != nil { return err } + if containerName == "" { + return errors.New("container name cannot be empty") + } + dockerLogsAction, err := nitro.LogsDocker(machine, containerName) if err != nil { return err @@ -71,15 +59,16 @@ var logsCommand = &cobra.Command{ for _, db := range databases { dbs = append(dbs, db.Name()) } - databaseContainerName := promptui.Select{ - Label: "Select database", - Items: dbs, + + if len(dbs) == 0 { + return errors.New("there are no databases to view logs from") } - _, containerName, err := databaseContainerName.Run() + containerName, _, err := prompt.Select(ui, "Select database", dbs[0], dbs) if err != nil { return err } + dockerLogsAction, err := nitro.LogsDocker(machine, containerName) if err != nil { return err From 8fdb3b71dbce738c7d78cfdeae65d5e281fd56f8 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 16:11:20 -0400 Subject: [PATCH 45/67] update import to use new prompt Signed-off-by: Jason McCallister --- internal/cmd/import.go | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/internal/cmd/import.go b/internal/cmd/import.go index 68844dee..a23b9af6 100644 --- a/internal/cmd/import.go +++ b/internal/cmd/import.go @@ -6,15 +6,16 @@ import ( "os" "strings" - "github.com/manifoldco/promptui" "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/tcnksm/go-input" "github.com/craftcms/nitro/config" "github.com/craftcms/nitro/internal/helpers" "github.com/craftcms/nitro/internal/nitro" "github.com/craftcms/nitro/internal/normalize" + "github.com/craftcms/nitro/internal/prompt" ) var importCommand = &cobra.Command{ @@ -49,16 +50,17 @@ var importCommand = &cobra.Command{ for _, db := range databases { dbs = append(dbs, db.Name()) } - databaseContainerName := promptui.Select{ - Label: "Select database", - Items: dbs, + ui := &input.UI{ + Writer: os.Stdout, + Reader: os.Stdin, } - _, containerName, err := databaseContainerName.Run() - if err != nil { - return err + if len(dbs) == 0 { + return errors.New("there are no databases that we can import the file into") } + containerName, _, err := prompt.Select(ui, "Select a database to import the file into", dbs[0], dbs) + var actions []nitro.Action // syntax is strange, see this issue: https://github.com/canonical/multipass/issues/1165#issuecomment-548763143 @@ -84,22 +86,12 @@ var importCommand = &cobra.Command{ fmt.Printf("Importing %q into %q (large files may take a while)...\n", filename, containerName) - return nitro.Run(nitro.NewMultipassRunner("multipass"), actions) - }, -} + if err := nitro.Run(nitro.NewMultipassRunner("multipass"), actions); err != nil { + return err + } -func fileExists(filename string) bool { - info, err := os.Stat(filename) - if os.IsNotExist(err) { - return false - } - return !info.IsDir() -} + fmt.Println("Successfully imported the database file into", containerName) -func dirExists(dir string) bool { - info, err := os.Stat(dir) - if os.IsExist(err) { - return false - } - return info.IsDir() + return nil + }, } From 8ecec158c80a5d5cbf8a52771cbdffecd761ff52 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 16:17:24 -0400 Subject: [PATCH 46/67] completely remove old prompt package Signed-off-by: Jason McCallister --- go.mod | 1 - go.sum | 17 ----------------- internal/cmd/init.go | 26 +++++++++++++++++++++----- internal/prompt/prompt.go | 31 ------------------------------- 4 files changed, 21 insertions(+), 54 deletions(-) diff --git a/go.mod b/go.mod index c6702161..a9a0742b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/craftcms/nitro go 1.14 require ( - github.com/manifoldco/promptui v0.7.0 github.com/mitchellh/go-homedir v1.1.0 github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.6.2 diff --git a/go.sum b/go.sum index 3a64b07e..0ad0a9b0 100644 --- a/go.sum +++ b/go.sum @@ -8,12 +8,6 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 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/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 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= @@ -59,8 +53,6 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= -github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -71,17 +63,9 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN 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/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= -github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/manifoldco/promptui v0.7.0 h1:3l11YT8tm9MnwGFQ4kETwkzpAwY2Jt9lCrumCUW4+z4= -github.com/manifoldco/promptui v0.7.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= -github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -166,7 +150,6 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h 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/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/internal/cmd/init.go b/internal/cmd/init.go index 4fe56445..38d40878 100644 --- a/internal/cmd/init.go +++ b/internal/cmd/init.go @@ -3,12 +3,14 @@ package cmd import ( "errors" "fmt" + "os" "strconv" "strings" "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/tcnksm/go-input" "github.com/craftcms/nitro/config" "github.com/craftcms/nitro/internal/nitro" @@ -27,6 +29,11 @@ var initCommand = &cobra.Command{ return errors.New("using a config file already") } + ui := &input.UI{ + Writer: os.Stdout, + Reader: os.Stdin, + } + // we don't have a config file // set the config file var cfg config.Config @@ -41,30 +48,39 @@ var initCommand = &cobra.Command{ cfg.CPUs = hardCodedCpus // ask how much memory - memory, err := prompt.AskWithDefault("How much memory should we assign?", "4G", nil) + memory, err := prompt.Ask(ui, "How much memory should we assign?", "4G", true) if err != nil { return err } cfg.Memory = memory // how much disk space - disk, err := prompt.AskWithDefault("How much disk space should the machine have?", "40G", nil) + disk, err := prompt.Ask(ui, "How much disk space should the machine have?", "40G", true) if err != nil { return err } cfg.Disk = disk // which version of PHP - _, php := prompt.SelectWithDefault("Which version of PHP should we install?", "7.4", nitro.PHPVersions) + php, _, err := prompt.Select(ui, "Which version of PHP should we install?", "7.4", nitro.PHPVersions) + if err != nil { + return err + } cfg.PHP = php // what database engine? - _, engine := prompt.SelectWithDefault("Which database engine should we setup?", "mysql", nitro.DBEngines) + engine, _, err := prompt.Select(ui, "Which database engine should we setup?", "mysql", nitro.DBEngines) + if err != nil { + return err + } // which version should we use? versions := nitro.DBVersions[engine] defaultVersion := versions[0] - _, version := prompt.SelectWithDefault("Select a version of "+engine+" to use:", defaultVersion, versions) + version, _, err := prompt.Select(ui, "Select a version of "+engine+" to use:", defaultVersion, versions) + if err != nil { + return err + } // get the port for the engine port := "3306" diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index a905623b..79386514 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -4,29 +4,9 @@ import ( "errors" "strings" - "github.com/manifoldco/promptui" "github.com/tcnksm/go-input" ) -func AskWithDefault(label, def string, validator promptui.ValidateFunc) (string, error) { - p := promptui.Prompt{ - Label: label + " [" + def + "]", - Validate: validator, - } - - v, err := p.Run() - if err != nil { - return "", err - } - - switch v { - case "": - v = def - } - - return v, nil -} - func Ask(ui *input.UI, query, def string, req bool) (string, error) { a, err := ui.Ask(query, &input.Options{ Default: def, @@ -59,17 +39,6 @@ func Select(ui *input.UI, query, def string, list []string) (string, int, error) return "", 0, errors.New("unable to find the selected option") } -func SelectWithDefault(label, def string, options []string) (int, string) { - p := promptui.Select{ - Label: label + " [" + def + "]", - Items: options, - } - - i, selected, _ := p.Run() - - return i, selected -} - func Verify(ui *input.UI, query, def string) (bool, error) { a, err := ui.Ask(query, &input.Options{ Default: def, From a67cc1adcec6477421f55629e14de0237696af72 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Tue, 28 Apr 2020 16:32:58 -0400 Subject: [PATCH 47/67] new line Signed-off-by: Jason McCallister --- internal/cmd/add.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/add.go b/internal/cmd/add.go index 4545a3c1..ff6c790e 100644 --- a/internal/cmd/add.go +++ b/internal/cmd/add.go @@ -91,7 +91,7 @@ var addCommand = &cobra.Command{ } } - fmt.Printf("%s has been added to nitro.yaml", hostname) + fmt.Printf("%s has been added to nitro.yaml\n", hostname) applyChanges, err := prompt.Verify(ui, "Apply changes from config now?", "y") if err != nil { From 8bf541855089710fd2d19fbbd587898fd236b220 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 10:35:27 -0400 Subject: [PATCH 48/67] always read in config during apply command Signed-off-by: Jason McCallister --- internal/cmd/apply.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/cmd/apply.go b/internal/cmd/apply.go index 028772e5..62eeee0e 100644 --- a/internal/cmd/apply.go +++ b/internal/cmd/apply.go @@ -21,6 +21,11 @@ var applyCommand = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { machine := flagMachineName + // always read the config file so its updated from any previous commands + if err := viper.ReadInConfig(); err != nil { + return err + } + // load the config file var configFile config.Config if err := viper.Unmarshal(&configFile); err != nil { From 1bbdd2e3c55612882da989f7ec18ce64416b529e Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 10:58:29 -0400 Subject: [PATCH 49/67] notes Signed-off-by: Jason McCallister --- internal/cmd/apply.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/cmd/apply.go b/internal/cmd/apply.go index 62eeee0e..3999ab43 100644 --- a/internal/cmd/apply.go +++ b/internal/cmd/apply.go @@ -26,6 +26,8 @@ var applyCommand = &cobra.Command{ return err } + // TODO check if a machine exists, if not launch it + // load the config file var configFile config.Config if err := viper.Unmarshal(&configFile); err != nil { From 1b77a5454213fdec2d546afe06ec172fc2477ebd Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 11:02:44 -0400 Subject: [PATCH 50/67] make error more useful Signed-off-by: Jason McCallister --- internal/cmd/init.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/init.go b/internal/cmd/init.go index 38d40878..721a1b37 100644 --- a/internal/cmd/init.go +++ b/internal/cmd/init.go @@ -26,7 +26,7 @@ var initCommand = &cobra.Command{ if viper.ConfigFileUsed() != "" { // TODO prompt for the confirmation of re initing the machine - return errors.New("using a config file already") + return errors.New("existing config file: " + viper.ConfigFileUsed()) } ui := &input.UI{ From c4348848cb3cbf8e64f375cf9889dc011408aaaa Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 11:41:25 -0400 Subject: [PATCH 51/67] remove old code Signed-off-by: Jason McCallister --- internal/task/add.go | 45 ---------------- internal/task/remove.go | 32 ----------- internal/task/rename.go | 101 ----------------------------------- internal/task/rename_test.go | 2 - 4 files changed, 180 deletions(-) delete mode 100644 internal/task/add.go delete mode 100644 internal/task/remove.go delete mode 100644 internal/task/rename.go delete mode 100644 internal/task/rename_test.go diff --git a/internal/task/add.go b/internal/task/add.go deleted file mode 100644 index 1f9c8e41..00000000 --- a/internal/task/add.go +++ /dev/null @@ -1,45 +0,0 @@ -package task - -import ( - "github.com/craftcms/nitro/config" - "github.com/craftcms/nitro/internal/nitro" -) - -func Add(machine string, configFile config.Config, site config.Site) ([]nitro.Action, error) { - var actions []nitro.Action - // mount the directory - m := configFile.Mounts[len(configFile.Mounts)-1] - mountAction, err := nitro.MountDir(machine, m.AbsSourcePath(), m.Dest) - if err != nil { - return nil, err - } - actions = append(actions, *mountAction) - - // copy the nginx template - copyTemplateAction, err := nitro.CopyNginxTemplate(machine, site.Hostname) - if err != nil { - return nil, err - } - actions = append(actions, *copyTemplateAction) - - // copy the nginx template - changeNginxVariablesAction, err := nitro.ChangeTemplateVariables(machine, site.Webroot, site.Hostname, configFile.PHP, site.Aliases) - if err != nil { - return nil, err - } - actions = append(actions, *changeNginxVariablesAction...) - - createSymlinkAction, err := nitro.CreateSiteSymllink(machine, site.Hostname) - if err != nil { - return nil, err - } - actions = append(actions, *createSymlinkAction) - - restartNginxAction, err := nitro.NginxReload(machine) - if err != nil { - return nil, err - } - actions = append(actions, *restartNginxAction) - - return actions, nil -} \ No newline at end of file diff --git a/internal/task/remove.go b/internal/task/remove.go deleted file mode 100644 index 716b965d..00000000 --- a/internal/task/remove.go +++ /dev/null @@ -1,32 +0,0 @@ -package task - -import ( - "github.com/craftcms/nitro/config" - "github.com/craftcms/nitro/internal/nitro" -) - -func Remove(name string, mount config.Mount, site config.Site) ([]nitro.Action, error) { - var actions []nitro.Action - - // unmount - unmountAction, err := nitro.UnmountDir(name, mount.Dest) - if err != nil { - return nil, err - } - actions = append(actions, *unmountAction) - - // remove nginx symlink - removeSymlinkAction, err := nitro.RemoveSymlink(name, site.Hostname) - if err != nil { - return nil, err - } - actions = append(actions, *removeSymlinkAction) - - restartNginxAction, err := nitro.NginxReload(name) - if err != nil { - return nil, err - } - actions = append(actions, *restartNginxAction) - - return actions, nil -} diff --git a/internal/task/rename.go b/internal/task/rename.go deleted file mode 100644 index dbd99a90..00000000 --- a/internal/task/rename.go +++ /dev/null @@ -1,101 +0,0 @@ -package task - -import ( - "github.com/craftcms/nitro/config" - "github.com/craftcms/nitro/internal/nitro" -) - -func RenameSite(machine, php string, oldSite, newSite config.Site) ([]nitro.Action, error) { - var actions []nitro.Action - - // remove the symlink from the old oldSite - removeSymlinkAction, err := nitro.RemoveSymlink(machine, oldSite.Hostname) - if err != nil { - return nil, err - } - actions = append(actions, *removeSymlinkAction) - - // create a new oldSite config for the new hostname - copyTemplateAction, err := nitro.CopyNginxTemplate(machine, newSite.Hostname) - if err != nil { - return nil, err - } - actions = append(actions, *copyTemplateAction) - - // change the webroot path - actions = append( - actions, - *nitro.ChangeNginxTemplateVariable(machine, newSite.Hostname, "CHANGEWEBROOTDIR", newSite.Webroot), - ) - - // change the server name variable - actions = append( - actions, - *nitro.ChangeNginxTemplateVariable(machine, newSite.Hostname, "CHANGESERVERNAME", newSite.Hostname), - ) - - // change the PHP version - actions = append( - actions, - *nitro.ChangeNginxTemplateVariable(machine, newSite.Hostname, "CHANGEPHPVERSION", php), - ) - - // reload nginx - reloadNginxAction, err := nitro.NginxReload(machine) - if err != nil { - return nil, err - } - - actions = append(actions, *reloadNginxAction) - - return actions, nil -} - -func Rename(machine, php string, existingSite, renamedSite config.Site, mount *config.Mount) ([]nitro.Action, error) { - var actions []nitro.Action - - // remove the symlink from the old oldSite - removeSymlinkAction, err := nitro.RemoveSymlink(machine, existingSite.Hostname) - if err != nil { - return nil, err - } - actions = append(actions, *removeSymlinkAction) - - // create a new oldSite config - copyTemplateAction, err := nitro.CopyNginxTemplate(machine, renamedSite.Hostname) - if err != nil { - return nil, err - } - actions = append(actions, *copyTemplateAction) - - changeNginxVariablesAction, err := nitro.ChangeTemplateVariables(machine, existingSite.Webroot, existingSite.Hostname, php, existingSite.Aliases) - if err != nil { - return nil, err - } - actions = append(actions, *changeNginxVariablesAction...) - - // restart nginx - restartNginxAction, err := nitro.NginxReload(machine) - if err != nil { - return nil, err - } - actions = append(actions, *restartNginxAction) - - if mount != nil { - /// unmount the directory - unMountAction, err := nitro.Unmount(machine, existingSite.Hostname) - if err != nil { - return nil, err - } - actions = append(actions, *unMountAction) - - // mount the new directory - mountAction, err := nitro.Mount(machine, mount.AbsSourcePath(), renamedSite.Hostname) - if err != nil { - return nil, err - } - actions = append(actions, *mountAction) - } - - return actions, nil -} diff --git a/internal/task/rename_test.go b/internal/task/rename_test.go deleted file mode 100644 index 29512d9b..00000000 --- a/internal/task/rename_test.go +++ /dev/null @@ -1,2 +0,0 @@ -package task - From 3f39c1ff2cbe8ecc26d16f36bf7870f8ebf96f73 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 11:47:43 -0400 Subject: [PATCH 52/67] account for existing configs during init Signed-off-by: Jason McCallister --- internal/cmd/init.go | 98 +++++++++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 33 deletions(-) diff --git a/internal/cmd/init.go b/internal/cmd/init.go index 721a1b37..36527bcc 100644 --- a/internal/cmd/init.go +++ b/internal/cmd/init.go @@ -1,7 +1,6 @@ package cmd import ( - "errors" "fmt" "os" "strconv" @@ -24,9 +23,10 @@ var initCommand = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { machine := flagMachineName + existingConfig := false if viper.ConfigFileUsed() != "" { - // TODO prompt for the confirmation of re initing the machine - return errors.New("existing config file: " + viper.ConfigFileUsed()) + fmt.Println("Using an existing config:", viper.ConfigFileUsed()) + existingConfig = true } ui := &input.UI{ @@ -62,43 +62,75 @@ var initCommand = &cobra.Command{ cfg.Disk = disk // which version of PHP - php, _, err := prompt.Select(ui, "Which version of PHP should we install?", "7.4", nitro.PHPVersions) - if err != nil { - return err - } - cfg.PHP = php + if !existingConfig { + php, _, err := prompt.Select(ui, "Which version of PHP should we install?", "7.4", nitro.PHPVersions) + if err != nil { + return err + } + cfg.PHP = php + } else { + cfg.PHP = config.GetString("php", flagPhpVersion) - // what database engine? - engine, _, err := prompt.Select(ui, "Which database engine should we setup?", "mysql", nitro.DBEngines) - if err != nil { - return err + // double check from the major update + if cfg.PHP == "" { + cfg.PHP = "7.4" + } } - // which version should we use? - versions := nitro.DBVersions[engine] - defaultVersion := versions[0] - version, _, err := prompt.Select(ui, "Select a version of "+engine+" to use:", defaultVersion, versions) - if err != nil { - return err - } + if !existingConfig { + // what database engine? + engine, _, err := prompt.Select(ui, "Which database engine should we setup?", "mysql", nitro.DBEngines) + if err != nil { + return err + } - // get the port for the engine - port := "3306" - if strings.Contains(engine, "postgres") { - port = "5432" + // which version should we use? + versions := nitro.DBVersions[engine] + defaultVersion := versions[0] + version, _, err := prompt.Select(ui, "Select a version of "+engine+" to use:", defaultVersion, versions) + if err != nil { + return err + } + + // get the port for the engine + port := "3306" + if strings.Contains(engine, "postgres") { + port = "5432" + } + + cfg.Databases = []config.Database{ + { + Engine: engine, + Version: version, + Port: port, + }, + } + } else { + var databases []config.Database + if err := viper.UnmarshalKey("databases", &databases); err != nil { + return err + } + + if databases != nil { + cfg.Databases = databases + } } - // TODO check if the port has already been used and +1 it - cfg.Databases = []config.Database{ - { - Engine: engine, - Version: version, - Port: port, - }, + if len(cfg.Databases) > 0 { + if err := validate.DatabaseConfig(cfg.Databases); err != nil { + return err + } } - if err := validate.DatabaseConfig(cfg.Databases); err != nil { - return err + var mounts []config.Mount + var sites []config.Site + if existingConfig { + if err := viper.UnmarshalKey("mounts", &mounts); err != nil { + return err + } + if err := viper.UnmarshalKey("sites", &sites); err != nil { + return err + } } // save the config file @@ -110,7 +142,7 @@ var initCommand = &cobra.Command{ return err } - actions, err := createActions(machine, memory, disk, cpuInt, php, cfg.Databases, nil, nil) + actions, err := createActions(machine, memory, disk, cpuInt, cfg.PHP, cfg.Databases, mounts, sites) if err != nil { return err } From 4f5f8dd3528823f4096a75f0594ea68f9b8c683e Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 11:47:50 -0400 Subject: [PATCH 53/67] semantics Signed-off-by: Jason McCallister --- internal/nitro/info.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/nitro/info.go b/internal/nitro/info.go index 851bb3cc..d846a12b 100644 --- a/internal/nitro/info.go +++ b/internal/nitro/info.go @@ -3,14 +3,14 @@ package nitro import "errors" // Info will display the machine information based on a name -func Info(name string) (*Action, error) { - if name == "" { +func Info(machine string) (*Action, error) { + if machine == "" { return nil, errors.New("missing machine name") } return &Action{ Type: "info", UseSyscall: false, - Args: []string{"info", name}, + Args: []string{"info", machine}, }, nil } From fe8ef98c557038374c9f3272ccc48bf91be4ef3e Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 11:48:00 -0400 Subject: [PATCH 54/67] better output for context Signed-off-by: Jason McCallister --- internal/cmd/context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/context.go b/internal/cmd/context.go index ef1d9961..e5454c2d 100644 --- a/internal/cmd/context.go +++ b/internal/cmd/context.go @@ -24,7 +24,7 @@ var contextCommand = &cobra.Command{ return err } - fmt.Println("Using config file:", configFile) + fmt.Println("Using config:", configFile) fmt.Println("------") fmt.Print(string(data)) return nil From d648a8e229221d811fbf03cadcf24a16fd97647c Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 11:48:57 -0400 Subject: [PATCH 55/67] tidy Signed-off-by: Jason McCallister --- go.sum | 1 + 1 file changed, 1 insertion(+) diff --git a/go.sum b/go.sum index 0ad0a9b0..c57acda7 100644 --- a/go.sum +++ b/go.sum @@ -181,4 +181,5 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099 h1:XJP7lxbSxWLOMNdBE4B/STaqVy6L73o0knwj2vIlxnw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From be9a7a898144e1f214559d4c0ae3e1cd770af7d6 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 12:13:36 -0400 Subject: [PATCH 56/67] dont save config when using existing during init Signed-off-by: Jason McCallister --- internal/cmd/destroy.go | 1 + internal/cmd/init.go | 16 +++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/internal/cmd/destroy.go b/internal/cmd/destroy.go index deeff9f7..15b1cd50 100644 --- a/internal/cmd/destroy.go +++ b/internal/cmd/destroy.go @@ -46,6 +46,7 @@ var destroyCommand = &cobra.Command{ } if len(domains) == 0 { + fmt.Println("Permanently removed", machine) return nil } diff --git a/internal/cmd/init.go b/internal/cmd/init.go index 36527bcc..fa403fd1 100644 --- a/internal/cmd/init.go +++ b/internal/cmd/init.go @@ -133,13 +133,15 @@ var initCommand = &cobra.Command{ } } - // save the config file - home, err := homedir.Dir() - if err != nil { - return err - } - if err := cfg.SaveAs(home, machine); err != nil { - return err + // save the config file if it does not exist + if !existingConfig { + home, err := homedir.Dir() + if err != nil { + return err + } + if err := cfg.SaveAs(home, machine); err != nil { + return err + } } actions, err := createActions(machine, memory, disk, cpuInt, cfg.PHP, cfg.Databases, mounts, sites) From 07c20821ebbff1ffae9ca6e27acf7b5f27ad5f9d Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 14:17:21 -0400 Subject: [PATCH 57/67] =?UTF-8?q?don=E2=80=99t=20use=20syscall=20on=20wind?= =?UTF-8?q?ows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jason McCallister --- internal/nitro/redis.go | 12 ++++++++++-- internal/nitro/ssh.go | 13 +++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/internal/nitro/redis.go b/internal/nitro/redis.go index 6e7fc859..2a5c05e5 100644 --- a/internal/nitro/redis.go +++ b/internal/nitro/redis.go @@ -1,15 +1,23 @@ package nitro -import "errors" +import ( + "errors" + "runtime" +) func Redis(name string) (*Action, error) { if name == "" { return nil, errors.New("name cannot be empty") } + syscall := true + if runtime.GOOS == "windows" { + syscall = false + } + return &Action{ Type: "exec", - UseSyscall: true, + UseSyscall: syscall, Args: []string{"exec", name, "--", "redis-cli"}, }, nil } diff --git a/internal/nitro/ssh.go b/internal/nitro/ssh.go index 4d667678..deb83c6d 100644 --- a/internal/nitro/ssh.go +++ b/internal/nitro/ssh.go @@ -1,15 +1,24 @@ package nitro -import "github.com/craftcms/nitro/validate" +import ( + "runtime" + + "github.com/craftcms/nitro/validate" +) func SSH(name string) (*Action, error) { if err := validate.MachineName(name); err != nil { return nil, err } + syscall := true + if runtime.GOOS == "windows" { + syscall = false + } + return &Action{ Type: "shell", - UseSyscall: true, + UseSyscall: syscall, Args: []string{"shell", name}, }, nil } From 9d1cfc1b0e197c0b48d0b3065b76e02590f13153 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 14:18:05 -0400 Subject: [PATCH 58/67] check for existing sites and mounts in add Signed-off-by: Jason McCallister --- internal/cmd/add.go | 16 ++++++++++++---- internal/cmd/apply.go | 2 -- internal/nitro/docker.go | 11 ++++++----- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/internal/cmd/add.go b/internal/cmd/add.go index ff6c790e..4e307a05 100644 --- a/internal/cmd/add.go +++ b/internal/cmd/add.go @@ -74,15 +74,23 @@ var addCommand = &cobra.Command{ // create a new mount // add the mount to configfile mount := config.Mount{Source: absolutePath, Dest: "/nitro/sites/" + hostname} - if err := configFile.AddMount(mount); err != nil { - return err + if configFile.MountExists(mount.Dest) { + fmt.Println(mount.Source, "is already mounted at", mount.Dest) + } else { + if err := configFile.AddMount(mount); err != nil { + return err + } } // create a new site // add site to config file site := config.Site{Hostname: hostname, Webroot: webRootPath} - if err := configFile.AddSite(site); err != nil { - return err + if configFile.SiteExists(site) { + fmt.Println(site.Hostname, "has already been set") + } else { + if err := configFile.AddSite(site); err != nil { + return err + } } if !flagDebug { diff --git a/internal/cmd/apply.go b/internal/cmd/apply.go index 3999ab43..62eeee0e 100644 --- a/internal/cmd/apply.go +++ b/internal/cmd/apply.go @@ -26,8 +26,6 @@ var applyCommand = &cobra.Command{ return err } - // TODO check if a machine exists, if not launch it - // load the config file var configFile config.Config if err := viper.Unmarshal(&configFile); err != nil { diff --git a/internal/nitro/docker.go b/internal/nitro/docker.go index 9ed4db51..7ffad6af 100644 --- a/internal/nitro/docker.go +++ b/internal/nitro/docker.go @@ -9,7 +9,7 @@ import ( // CreateDatabaseContainer is responsible for the creation of a new Docker database and will // assign a volume and port based on the arguments. Validation of port collisions should occur // outside of this func and this will only validate engines and versions. -func CreateDatabaseContainer(name, engine, version, port string) (*Action, error) { +func CreateDatabaseContainer(machine, engine, version, port string) (*Action, error) { if err := validate.DatabaseEngineAndVersion(engine, version); err != nil { return nil, err } @@ -33,13 +33,13 @@ func CreateDatabaseContainer(name, engine, version, port string) (*Action, error volume := containerVolume(engine, version, port) volumeMount := fmt.Sprintf("%s:%s", volume, containerPath) - // build the container name based on engine, version, and port + // build the container machine based on engine, version, and port containerName := containerName(engine, version, port) // create the port mapping portMapping := fmt.Sprintf("%v:%v", port, containerPort) - args := []string{"exec", name, "--", "docker", "run", "-v", volumeMount, "--name", containerName, "-d", "--restart=always", "-p", portMapping} + args := []string{"exec", machine, "--", "docker", "run", "-v", volumeMount, "--name", containerName, "-d", "--restart=always", "-p", portMapping} // append the env vars args = append(args, containerEnvVars...) @@ -54,7 +54,8 @@ func CreateDatabaseContainer(name, engine, version, port string) (*Action, error }, nil } -func CreateDatabaseVolume(name, engine, version, port string) (*Action, error) { +// CreateDatabaseVolume will make a database vaolume to ensure that data is persisted during reboots. +func CreateDatabaseVolume(machine, engine, version, port string) (*Action, error) { if err := validate.DatabaseEngineAndVersion(engine, version); err != nil { return nil, err } @@ -64,7 +65,7 @@ func CreateDatabaseVolume(name, engine, version, port string) (*Action, error) { return &Action{ Type: "exec", UseSyscall: false, - Args: []string{"exec", name, "--", "docker", "volume", "create", volume}, + Args: []string{"exec", machine, "--", "docker", "volume", "create", volume}, }, nil } From 84f8be7f5d0ccc81a867da72e19504b766d4ae81 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 14:56:12 -0400 Subject: [PATCH 59/67] add skips apply if there is nothing to do Signed-off-by: Jason McCallister --- internal/cmd/add.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/internal/cmd/add.go b/internal/cmd/add.go index 4e307a05..e2e41a08 100644 --- a/internal/cmd/add.go +++ b/internal/cmd/add.go @@ -49,12 +49,12 @@ var addCommand = &cobra.Command{ default: hostname = helpers.RemoveTrailingSlash(flagHostname) } - - // look for the www,public,public_html,www using the absolutePath variable + // set the webrootName var (e.g. web) var webrootDir string switch flagWebroot { case "": + // look for the www,public,public_html,www using the absolutePath variable foundDir, err := webroot.Find(absolutePath) if err != nil { return err @@ -72,18 +72,21 @@ var addCommand = &cobra.Command{ webRootPath := fmt.Sprintf("/nitro/sites/%s/%s", hostname, webrootDir) // create a new mount - // add the mount to configfile + skipMount := true mount := config.Mount{Source: absolutePath, Dest: "/nitro/sites/" + hostname} if configFile.MountExists(mount.Dest) { - fmt.Println(mount.Source, "is already mounted at", mount.Dest) + fmt.Println(mount.Source, "is already mounted at", mount.Dest, ". Using that instead of creating a new mount.") } else { + // add the mount to configfile if err := configFile.AddMount(mount); err != nil { return err } + skipMount = false } // create a new site // add site to config file + skipSite := true site := config.Site{Hostname: hostname, Webroot: webRootPath} if configFile.SiteExists(site) { fmt.Println(site.Hostname, "has already been set") @@ -91,6 +94,12 @@ var addCommand = &cobra.Command{ if err := configFile.AddSite(site); err != nil { return err } + skipSite = false + } + + if skipMount && skipSite { + fmt.Println("There are no changes to apply, skipping...") + return nil } if !flagDebug { From 1455122e10e3a339619654a4883d22cd997d7159 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 15:04:15 -0400 Subject: [PATCH 60/67] semantics Signed-off-by: Jason McCallister --- internal/cmd/add.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/add.go b/internal/cmd/add.go index e2e41a08..deb97002 100644 --- a/internal/cmd/add.go +++ b/internal/cmd/add.go @@ -49,7 +49,7 @@ var addCommand = &cobra.Command{ default: hostname = helpers.RemoveTrailingSlash(flagHostname) } - + // set the webrootName var (e.g. web) var webrootDir string switch flagWebroot { From 5d96b1584de27514893712202c8f57ff1581a5c6 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 15:46:08 -0400 Subject: [PATCH 61/67] add script to set database user permissions Signed-off-by: Jason McCallister --- internal/cmd/config.go | 23 +++++++++++++++++++++++ internal/cmd/init.go | 6 ++++++ internal/nitro/docker.go | 10 ++++++++++ internal/task/apply.go | 6 ++++++ internal/task/apply_test.go | 5 +++++ 5 files changed, 50 insertions(+) diff --git a/internal/cmd/config.go b/internal/cmd/config.go index f74c201c..f8d18cdf 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -39,6 +39,29 @@ write_files: else cat "$filename" | pv | docker exec -i "$container" psql -U nitro -d "$database" fi + - path: /opt/nitro/scripts/docker-set-database-user-permissions.sh + content: | + #!/usr/bin/env bash + container="$1" + engine="$2" + + if [ -z "$container" ]; then + echo "you must provide a container name" + exit 1 + fi + + if [ -z "$engine" ]; then + echo "you must provide a database engine (e.g. mysql or postgres)" + exit 1 + fi + + if [ "$engine" == "mysql" ]; then + docker exec -it "$container" mysql -uroot -pnitro -e "GRANT ALL ON *.* TO 'nitro'@'%'; FLUSH PRIVILEGES;" + echo "setting root permissions on user nitro" + else + docker exec -it "$container" psql -U nitro -c "ALTER USER nitro WITH SUPERUSER;" + echo "setting superuser permissions on user nitro" + fi - path: /opt/nitro/nginx/template.conf content: | server { diff --git a/internal/cmd/init.go b/internal/cmd/init.go index fa403fd1..9ee0633c 100644 --- a/internal/cmd/init.go +++ b/internal/cmd/init.go @@ -231,6 +231,12 @@ func createActions(machine, memory, disk string, cpus int, phpVersion string, da return nil, err } actions = append(actions, *createDatabaseAction) + + setUserPermissions, err := nitro.SetDatabaseUserPermissions(machine, database) + if err != nil { + return nil, err + } + actions = append(actions, *setUserPermissions) } var siteErrs []error diff --git a/internal/nitro/docker.go b/internal/nitro/docker.go index 7ffad6af..0bbb762a 100644 --- a/internal/nitro/docker.go +++ b/internal/nitro/docker.go @@ -3,6 +3,7 @@ package nitro import ( "fmt" + "github.com/craftcms/nitro/config" "github.com/craftcms/nitro/validate" ) @@ -54,6 +55,15 @@ func CreateDatabaseContainer(machine, engine, version, port string) (*Action, er }, nil } +// SetDatabaseUserPermissions is used to set all permissions on the nitro user for a database +func SetDatabaseUserPermissions(machine string, database config.Database) (*Action, error) { + return &Action{ + Type: "exec", + UseSyscall: false, + Args: []string{"exec", machine, "--", "sudo", "bash", "/opt/nitro/scripts/docker-set-database-user-permissions.sh", database.Name(), database.Engine}, + }, nil +} + // CreateDatabaseVolume will make a database vaolume to ensure that data is persisted during reboots. func CreateDatabaseVolume(machine, engine, version, port string) (*Action, error) { if err := validate.DatabaseEngineAndVersion(engine, version); err != nil { diff --git a/internal/task/apply.go b/internal/task/apply.go index a2bde35c..68258676 100644 --- a/internal/task/apply.go +++ b/internal/task/apply.go @@ -111,6 +111,12 @@ func Apply(machine string, configFile config.Config, mounts []config.Mount, site return nil, err } actions = append(actions, *createContainer) + + setUserPermissions, err := nitro.SetDatabaseUserPermissions(machine, database) + if err != nil { + return nil, err + } + actions = append(actions, *setUserPermissions) } } diff --git a/internal/task/apply_test.go b/internal/task/apply_test.go index fd1363ed..3c63e10a 100644 --- a/internal/task/apply_test.go +++ b/internal/task/apply_test.go @@ -64,6 +64,11 @@ func TestApply(t *testing.T) { UseSyscall: false, Args: []string{"exec", "mytestmachine", "--", "docker", "run", "-v", "mysql_5.7_3306:/var/lib/mysql", "--name", "mysql_5.7_3306", "-d", "--restart=always", "-p", "3306:3306", "-e", "MYSQL_ROOT_PASSWORD=nitro", "-e", "MYSQL_USER=nitro", "-e", "MYSQL_PASSWORD=nitro", "mysql:5.7"}, }, + { + Type: "exec", + UseSyscall: false, + Args: []string{"exec", "mytestmachine", "--", "sudo", "bash", "/opt/nitro/scripts/docker-set-database-user-permissions.sh", "mysql_5.7_3306", "mysql"}, + }, }, }, { From ab80ce0f1b4cf1c8dca6ff4d7df5174bea54ca5f Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 16:11:10 -0400 Subject: [PATCH 62/67] create databases on import Signed-off-by: Jason McCallister --- internal/cmd/config.go | 10 ++++++---- internal/cmd/import.go | 9 +++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/internal/cmd/config.go b/internal/cmd/config.go index f8d18cdf..2585c006 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -35,9 +35,11 @@ write_files: engine="$4" if [ "$engine" == "mysql" ]; then - cat "$filename" | pv | docker exec -i "$container" mysql -unitro -pnitro "$database" --init-command="SET autocommit=0;" + docker exec "$container" mysql -uroot -pnitro -e "CREATE DATABASE IF NOT EXISTS $database; GRANT ALL ON $database.* TO 'nitro'@'%'; FLUSH PRIVILEGES;" + cat "$filename" | pv | docker exec "$container" mysql -unitro -pnitro "$database" --init-command="SET autocommit=0;" else - cat "$filename" | pv | docker exec -i "$container" psql -U nitro -d "$database" + docker exec "$container" psql -U nitro -c "CREATE DATABASE IF NOT EXISTS $database OWNER nitro;" + cat "$filename" | pv | docker exec "$container" psql -U nitro -d "$database" fi - path: /opt/nitro/scripts/docker-set-database-user-permissions.sh content: | @@ -56,10 +58,10 @@ write_files: fi if [ "$engine" == "mysql" ]; then - docker exec -it "$container" mysql -uroot -pnitro -e "GRANT ALL ON *.* TO 'nitro'@'%'; FLUSH PRIVILEGES;" + docker exec "$container" mysql -uroot -pnitro -e "GRANT ALL ON *.* TO 'nitro'@'%'; FLUSH PRIVILEGES;" echo "setting root permissions on user nitro" else - docker exec -it "$container" psql -U nitro -c "ALTER USER nitro WITH SUPERUSER;" + docker exec "$container" psql -U nitro -c "ALTER USER nitro WITH SUPERUSER;" echo "setting superuser permissions on user nitro" fi - path: /opt/nitro/nginx/template.conf diff --git a/internal/cmd/import.go b/internal/cmd/import.go index a23b9af6..b91bda82 100644 --- a/internal/cmd/import.go +++ b/internal/cmd/import.go @@ -59,7 +59,12 @@ var importCommand = &cobra.Command{ return errors.New("there are no databases that we can import the file into") } - containerName, _, err := prompt.Select(ui, "Select a database to import the file into", dbs[0], dbs) + containerName, _, err := prompt.Select(ui, "Select a database engine to import the file into", dbs[0], dbs) + + databaseName, err := prompt.Ask(ui, "What is the database name?", "", true) + if err != nil { + return err + } var actions []nitro.Action @@ -76,7 +81,7 @@ var importCommand = &cobra.Command{ engine = "postgres" } - importArgs := []string{"exec", machine, "--", "bash", "/opt/nitro/scripts/docker-exec-import.sh", containerName, "nitro", filename, engine} + importArgs := []string{"exec", machine, "--", "bash", "/opt/nitro/scripts/docker-exec-import.sh", containerName, databaseName, filename, engine} dockerExecAction := nitro.Action{ Type: "exec", UseSyscall: false, From eb840ab44ed9e81e174216ea56fbccaeffc3eeef Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 16:16:21 -0400 Subject: [PATCH 63/67] break import commands to sep lines Signed-off-by: Jason McCallister --- internal/cmd/config.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/cmd/config.go b/internal/cmd/config.go index 2585c006..a1bc469d 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -35,7 +35,9 @@ write_files: engine="$4" if [ "$engine" == "mysql" ]; then - docker exec "$container" mysql -uroot -pnitro -e "CREATE DATABASE IF NOT EXISTS $database; GRANT ALL ON $database.* TO 'nitro'@'%'; FLUSH PRIVILEGES;" + docker exec "$container" mysql -uroot -pnitro -e "CREATE DATABASE IF NOT EXISTS $database;" + docker exec "$container" mysql -uroot -pnitro -e "GRANT ALL ON $database.* TO 'nitro'@'%';" + docker exec "$container" mysql -uroot -pnitro -e "FLUSH PRIVILEGES;" cat "$filename" | pv | docker exec "$container" mysql -unitro -pnitro "$database" --init-command="SET autocommit=0;" else docker exec "$container" psql -U nitro -c "CREATE DATABASE IF NOT EXISTS $database OWNER nitro;" @@ -58,7 +60,8 @@ write_files: fi if [ "$engine" == "mysql" ]; then - docker exec "$container" mysql -uroot -pnitro -e "GRANT ALL ON *.* TO 'nitro'@'%'; FLUSH PRIVILEGES;" + docker exec "$container" mysql -uroot -pnitro -e "GRANT ALL ON *.* TO 'nitro'@'%';" + docker exec "$container" mysql -uroot -pnitro -e "FLUSH PRIVILEGES;" echo "setting root permissions on user nitro" else docker exec "$container" psql -U nitro -c "ALTER USER nitro WITH SUPERUSER;" From 2356fa628a38070bfcf5a4361b2d13ee6bd52485 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 17:06:37 -0400 Subject: [PATCH 64/67] change log Signed-off-by: Jason McCallister --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4ecee70..4c102e42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,27 @@ ## Unreleased +## Added +- Added the `rename` command to allow users to quickly rename sites in Nitro. + +## Changed +- The command `destroy` now has a `--clean` option which will delete a configuration file after destroying the machine. +- The database user nitro now has root privileges for `mysql` and `postgres` databases. +- Removed the sometime buggy prompt package in favor of a simple package to ask for user input on certain commands. +- Added the `php` option back to the config file +- All commands that perform configuration changes (e.g. `add`, `remove`, and `rename`) now use the same logic as the `apply` command. +- When importing a database using the `import` command, users asked for the database name which nitro will create if it does not exist. +- Added a lot of tests and code cleanup for future work. +- The `apply` command will automatically update our hosts file. +- The `destroy` command will now remove any sites in the machine configuration from the hosts file. +- The `init` command will use an existing configuration file and recreate the entire environment. Also, the existing file will not be overwritten when using this command. +- Commands now output more _statuses_ where possible to provide the user more feedback. + +## Fixed +- When using the `add` command, the config file checks for duplicate sites and mounts and displays. [#86](https://github.com/craftcms/nitro/issues/86) +- Fixed an issue when using some commands on Windows. [#88](https://github.com/craftcms/nitro/issues/88) +- Fixed an issue in the `apply` command that would not detect new changes to + ## 0.10.0 - 2020-04-23 > **Warning:** This release contains breaking changes. See the [upgrade notes](UPGRADE.md#upgrading-to-nitro-0100) From a0b5597b90c98efe6e85e07445b1f43771eaa222 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 17:08:36 -0400 Subject: [PATCH 65/67] wait for mysql to be ready before continuing Signed-off-by: Jason McCallister --- internal/cmd/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/cmd/config.go b/internal/cmd/config.go index a1bc469d..884807d8 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -60,6 +60,7 @@ write_files: fi if [ "$engine" == "mysql" ]; then + docker exec "$container" bash -c "while ! mysqladmin ping -h 127.0.0.1 -uroot -pnitro; do echo 'waiting...'; sleep 1; done" docker exec "$container" mysql -uroot -pnitro -e "GRANT ALL ON *.* TO 'nitro'@'%';" docker exec "$container" mysql -uroot -pnitro -e "FLUSH PRIVILEGES;" echo "setting root permissions on user nitro" From 153718538b6254d53b9dffb30266c86cb6b091c4 Mon Sep 17 00:00:00 2001 From: Jason McCallister Date: Wed, 29 Apr 2020 17:24:53 -0400 Subject: [PATCH 66/67] update changelog for 0.11.0 Signed-off-by: Jason McCallister --- CHANGELOG.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c102e42..a831b628 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,26 +2,26 @@ ## Unreleased +## 0.11.0 - 2020-04-29 + ## Added -- Added the `rename` command to allow users to quickly rename sites in Nitro. +- Added the `rename` command to allow users to quickly rename sites. ## Changed -- The command `destroy` now has a `--clean` option which will delete a configuration file after destroying the machine. -- The database user nitro now has root privileges for `mysql` and `postgres` databases. -- Removed the sometime buggy prompt package in favor of a simple package to ask for user input on certain commands. -- Added the `php` option back to the config file -- All commands that perform configuration changes (e.g. `add`, `remove`, and `rename`) now use the same logic as the `apply` command. -- When importing a database using the `import` command, users asked for the database name which nitro will create if it does not exist. -- Added a lot of tests and code cleanup for future work. -- The `apply` command will automatically update our hosts file. -- The `destroy` command will now remove any sites in the machine configuration from the hosts file. -- The `init` command will use an existing configuration file and recreate the entire environment. Also, the existing file will not be overwritten when using this command. +- The `destroy` command now has a `--clean` option which will delete a config file after destroying the machine. +- The `nitro` database user now has root privileges for `mysql` and `postgres` databases. [#79](https://github.com/craftcms/nitro/issues/79) +- Added the `php` option back to the config file. +- All commands that perform config changes (e.g. `add`, `remove`, and `rename`) now use the same logic as the `apply` command. +- When importing a database using the `import` command, users will be prompted for the database name which will be created if it does not exist. +- The `apply` command will automatically update the machine's hosts file. +- The `destroy` command will now remove any sites in the machine config from the hosts file. +- The `init` command will use an existing config file and recreate the entire environment. - Commands now output more _statuses_ where possible to provide the user more feedback. ## Fixed -- When using the `add` command, the config file checks for duplicate sites and mounts and displays. [#86](https://github.com/craftcms/nitro/issues/86) +- When using the `add` command, the config file checks for duplicate sites and mounts. [#86](https://github.com/craftcms/nitro/issues/86) - Fixed an issue when using some commands on Windows. [#88](https://github.com/craftcms/nitro/issues/88) -- Fixed an issue in the `apply` command that would not detect new changes to +- Fixed an issue in the `apply` command that would not detect new changes to the config file. ## 0.10.0 - 2020-04-23 From 856caea1e5ef298d098f69bbf1ff89794e514b2e Mon Sep 17 00:00:00 2001 From: Brad Bell Date: Wed, 29 Apr 2020 14:43:21 -0700 Subject: [PATCH 67/67] Cleanup loose files on failure. --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index aa544f5c..28d5d805 100755 --- a/install.sh +++ b/install.sh @@ -60,7 +60,7 @@ checkHash() { # Make sure the file names match up. if [ "$4" != "$checkResultFileName" ]; then - # rm "$1" + rm "$1" echo "Checksums do not match. Exiting." exit 1 fi