Skip to content

Commit

Permalink
refactor(local): improve deploy/undeploy with upgrade check
Browse files Browse the repository at this point in the history
  • Loading branch information
pinglin committed Oct 13, 2023
1 parent 6d8856c commit b6f4297
Show file tree
Hide file tree
Showing 14 changed files with 354 additions and 348 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ require (
go.opentelemetry.io/otel/metric v0.33.0 // indirect
go.opentelemetry.io/otel/trace v1.11.1 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/mod v0.13.0
golang.org/x/net v0.15.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.13.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -403,8 +403,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
Expand Down
221 changes: 144 additions & 77 deletions pkg/cmd/local/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@ package local
import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
"time"

"github.com/MakeNowJust/heredoc"
"github.com/cli/safeexec"
"github.com/mgutz/ansi"
"github.com/spf13/cobra"

"github.com/instill-ai/cli/internal/build"
"github.com/instill-ai/cli/internal/config"
"github.com/instill-ai/cli/pkg/cmd/factory"
"github.com/instill-ai/cli/pkg/cmd/instance"
"github.com/instill-ai/cli/pkg/cmdutil"
"github.com/instill-ai/cli/pkg/iostreams"
Expand All @@ -35,17 +33,18 @@ type DeployOptions struct {
Config config.Config
MainExecutable string
Interactive bool
Path string
Force bool
Upgrade bool
checkForUpdate func(ExecDep, string, string, string) (*releaseInfo, error)
isDeployed func(ExecDep) error
isDeployed func(ExecDep, string) error
}

// NewDeployCmd creates a new command
func NewDeployCmd(f *cmdutil.Factory, runF func(*DeployOptions) error) *cobra.Command {
opts := &DeployOptions{
IO: f.IOStreams,
checkForUpdate: checkForUpdate,
isDeployed: isDeployed,
isDeployed: isProjectDeployed,
}

cmd := &cobra.Command{
Expand Down Expand Up @@ -73,19 +72,17 @@ func NewDeployCmd(f *cmdutil.Factory, runF func(*DeployOptions) error) *cobra.Co
return runDeploy(opts)
},
}
d, err := os.UserHomeDir()
if err != nil {
logger.Error("Couldn't get Home directory", err)
}
opts.Path = filepath.Join(d, ".local", "instill") + string(os.PathSeparator)

cmd.Flags().BoolVarP(&opts.Force, "force", "f", false, "Force to deploy a new local Instill Core instance")
cmd.Flags().BoolVarP(&opts.Upgrade, "upgrade", "u", false, "Upgrade Instill Core instance to the latest version")
cmd.MarkFlagsMutuallyExclusive("force", "upgrade")

return cmd
}

func runDeploy(opts *DeployOptions) error {

var err error
path := opts.Path
start := time.Now()

// check the deps
Expand All @@ -101,91 +98,169 @@ func runDeploy(opts *DeployOptions) error {
}
}

// check existing local deployment
if err := opts.isDeployed(opts.Exec); err == nil {
p(opts.IO, "A local Instill Core deployment detected")
for i := range projs {
prj := strings.ToLower(projs[i])
if opts.OS != nil {
err = opts.OS.Chdir(filepath.Join(path, prj))
// download the latest version of the projects, if local repos are not present
for _, proj := range projs {
projDirPath := filepath.Join(LocalInstancePath, proj)
_, err = os.Stat(projDirPath)
if os.IsNotExist(err) {
if latestVersion, err := execCmd(opts.Exec, "bash", "-c", fmt.Sprintf("curl https://api.github.com/repos/instill-ai/%s/releases | jq -r 'map(select(.prerelease)) | first | .tag_name'", proj)); err == nil {
latestVersion = strings.Trim(latestVersion, "\n")
if out, err := execCmd(opts.Exec, "bash", "-c",
fmt.Sprintf("git clone --depth 1 -b %s -c advice.detachedHead=false https://github.com/instill-ai/%s.git %s", latestVersion, proj, projDirPath)); err != nil {
return fmt.Errorf("ERROR: cannot clone %s, %w:\n%s", proj, err, out)
}
if _, err := opts.checkForUpdate(opts.Exec, filepath.Join(config.StateDir(), fmt.Sprintf("%s.yml", proj)), fmt.Sprintf("instill-ai/%s", proj), latestVersion); err != nil {
return fmt.Errorf("ERROR: cannot check for update %s, %w:\n%s", proj, err, latestVersion)
}
} else {
err = os.Chdir(filepath.Join(path, prj))
}
if err != nil {
return fmt.Errorf("ERROR: can't open the destination, %w", err)
return fmt.Errorf("ERROR: cannot find latest release version of %s, %w:\n%s", proj, err, latestVersion)
}
}
}

if currentVersion, err := execCmd(opts.Exec, "bash", "-c", "git name-rev --tags --name-only $(git rev-parse HEAD)"); err == nil {
newRelease, _ := opts.checkForUpdate(opts.Exec, filepath.Join(config.StateDir(), fmt.Sprintf("%s.yml", prj)), fmt.Sprintf("instill-ai/%s", prj), currentVersion)
if newRelease != nil {
cmdFactory := factory.New(build.Version)
stderr := cmdFactory.IOStreams.ErrOut
fmt.Fprintf(stderr, "\n\n%s %s → %s\n",
ansi.Color(fmt.Sprintf("A new release of Instill %s is available:", prj), "yellow"),
ansi.Color(currentVersion, "cyan"),
ansi.Color(newRelease.Version, "cyan"))
fmt.Fprintf(stderr, "%s\n\n",
ansi.Color(newRelease.URL, "yellow"))
if opts.Force {
p(opts.IO, "Tear down Instill Core instance if existing...")
for _, proj := range projs {
projDirPath := filepath.Join(LocalInstancePath, proj)
_, err = os.Stat(projDirPath)
if !os.IsNotExist(err) {
if opts.OS != nil {
if err = opts.OS.Chdir(projDirPath); err != nil {
return err
}
} else {
if err = os.Chdir(projDirPath); err != nil {
return err
}
}
if out, err := execCmd(opts.Exec, "bash", "-c", "make down"); err != nil {
return fmt.Errorf("ERROR: cannot force tearing down %s, %w:\n%s", proj, err, out)
}
}
}
return nil
}

p(opts.IO, "Download the latest Instill Core to: %s", path)
for i := range projs {
prj := strings.ToLower(projs[i])
_, err = os.Stat(filepath.Join(path, prj))
if os.IsNotExist(err) {
if latestVersion, err := execCmd(opts.Exec, "bash", "-c", fmt.Sprintf("curl https://api.github.com/repos/instill-ai/%s/releases | jq -r 'map(select(.prerelease)) | first | .tag_name'", prj)); err == nil {
latestVersion = strings.Trim(latestVersion, "\n")
if out, err := execCmd(opts.Exec, "bash", "-c",
fmt.Sprintf("git clone --depth 1 -b %s -c advice.detachedHead=false https://github.com/instill-ai/%s.git %s", latestVersion, prj, filepath.Join(path, prj))); err != nil {
return fmt.Errorf("ERROR: cant clone %s, %w:\n%s", prj, err, out)
} else if opts.Upgrade {
hasNewVersion := false
for _, proj := range projs {
projDirPath := filepath.Join(LocalInstancePath, proj)
_, err = os.Stat(projDirPath)
if !os.IsNotExist(err) {
if opts.OS != nil {
if err = opts.OS.Chdir(projDirPath); err != nil {
return err
}
} else {
if err = os.Chdir(projDirPath); err != nil {
return err
}
}
if currentVersion, err := execCmd(opts.Exec, "bash", "-c", "git name-rev --tags --name-only $(git rev-parse HEAD)"); err == nil {
currentVersion = strings.Trim(currentVersion, "\n")
if newRelease, err := opts.checkForUpdate(opts.Exec, filepath.Join(config.StateDir(), fmt.Sprintf("%s.yml", proj)), fmt.Sprintf("instill-ai/%s", proj), currentVersion); err != nil {
return fmt.Errorf("ERROR: cannot check for update %s, %w:\n%s", proj, err, currentVersion)
} else if newRelease != nil {
p(opts.IO, "Upgrade %s to %s...", proj, newRelease.Version)
hasNewVersion = true
}
} else {
return fmt.Errorf("ERROR: cannot find current release version of %s, %w:\n%s", proj, err, currentVersion)
}
_, _ = checkForUpdate(opts.Exec, filepath.Join(config.StateDir(), fmt.Sprintf("%s.yml", prj)), fmt.Sprintf("instill-ai/%s", prj), latestVersion)
} else {
return fmt.Errorf("ERROR: cant find latest release version of %s, %w:\n%s", prj, err, latestVersion)
}
}
}

p(opts.IO, "Launch Instill Core")
for i := range projs {
prj := strings.ToLower(projs[i])
if opts.OS != nil {
err = opts.OS.Chdir(filepath.Join(path, prj))
if hasNewVersion {
p(opts.IO, "Tear down Instill Core instance if existing...")
for _, proj := range projs {
projDirPath := filepath.Join(LocalInstancePath, proj)
_, err = os.Stat(projDirPath)
if !os.IsNotExist(err) {
if opts.OS != nil {
if err = opts.OS.Chdir(projDirPath); err != nil {
return err
}
} else {
if err = os.Chdir(projDirPath); err != nil {
return err
}
}
if out, err := execCmd(opts.Exec, "bash", "-c", "make down"); err != nil {
return fmt.Errorf("ERROR: cannot force tearing down %s, %w:\n%s", proj, err, out)
}
}

if dir, err := os.ReadDir(projDirPath); err == nil {
for _, d := range dir {
if os.RemoveAll(path.Join([]string{projDirPath, d.Name()}...)); err != nil {
return fmt.Errorf("ERROR: cannot remove %s, %w", projDirPath, err)
}
}
} else {
return fmt.Errorf("ERROR: cannot read %s, %w", projDirPath, err)
}

if latestVersion, err := execCmd(opts.Exec, "bash", "-c", fmt.Sprintf("curl https://api.github.com/repos/instill-ai/%s/releases | jq -r 'map(select(.prerelease)) | first | .tag_name'", proj)); err == nil {
latestVersion = strings.Trim(latestVersion, "\n")
if out, err := execCmd(opts.Exec, "bash", "-c",
fmt.Sprintf("git clone --depth 1 -b %s -c advice.detachedHead=false https://github.com/instill-ai/%s.git %s", latestVersion, proj, projDirPath)); err != nil {
return fmt.Errorf("ERROR: cannot clone %s, %w:\n%s", proj, err, out)
}
} else {
return fmt.Errorf("ERROR: cannot find latest release version of %s, %w:\n%s", proj, err, latestVersion)
}
}
} else {
err = os.Chdir(filepath.Join(path, prj))
p(opts.IO, "No upgrade available")
return nil
}
if err != nil {
return fmt.Errorf("ERROR: can't open the destination, %w", err)

} else {
for _, proj := range projs {
if err := opts.isDeployed(opts.Exec, proj); err == nil {
p(opts.IO, "A local Instill Core deployment detected")
return nil
}
}
}

p(opts.IO, "Launch Instill Core...")
for _, proj := range projs {
projDirPath := filepath.Join(LocalInstancePath, proj)
_, err = os.Stat(projDirPath)
if !os.IsNotExist(err) {
if opts.OS != nil {
err = opts.OS.Chdir(projDirPath)
} else {
err = os.Chdir(projDirPath)
}
if err != nil {
return fmt.Errorf("ERROR: cannot open the directory: %w", err)
}
}

if currentVersion, err := execCmd(opts.Exec, "bash", "-c", "git name-rev --tags --name-only $(git rev-parse HEAD)"); err == nil {
currentVersion = strings.Trim(currentVersion, "\n")
p(opts.IO, "Spin up Instill %s %s...", projs[i], currentVersion)
p(opts.IO, "Spin up %s %s...", proj, currentVersion)
if out, err := execCmd(opts.Exec, "bash", "-c", "make all"); err != nil {
return fmt.Errorf("ERROR: %s spin-up failed, %w\n%s", prj, err, out)
return fmt.Errorf("ERROR: %s spin-up failed, %w\n%s", proj, err, out)
}
} else {
return fmt.Errorf("ERROR: cant get current tag %s, %w:\n%s", prj, err, currentVersion)
return fmt.Errorf("ERROR: cannot get current tag %s, %w:\n%s", proj, err, currentVersion)
}
}

// print a summary
elapsed := time.Since(start)
p(opts.IO, "")
p(opts.IO, `
Instill Core console available at http://localhost:3000
After changing your password, run "$ inst auth login".
Instill Core console available at http://localhost:3000
User: %s
Password: %s
User: %s
Password: %s
Deployed in %.0fs to %s
`,
DefUsername, DefPassword, elapsed.Seconds(), path)
After changing your password, run "$ inst auth login" with your new password.
Deployed in %.0fs to %s
`,
DefUsername, DefPassword, elapsed.Seconds(), LocalInstancePath)

err = registerInstance(opts)
if err != nil {
Expand All @@ -197,14 +272,6 @@ func runDeploy(opts *DeployOptions) error {

func registerInstance(opts *DeployOptions) error {
// register the new instance
err := opts.Config.Set("", ConfigKeyPath, opts.Path)
if err != nil {
return fmt.Errorf("ERROR: saving config, %w", err)
}
err = opts.Config.Write()
if err != nil {
return fmt.Errorf("ERROR: saving config, %w", err)
}
exists, err := instance.IsInstanceAdded(opts.Config, "localhost:8080")
if err != nil {
return err
Expand Down
Loading

0 comments on commit b6f4297

Please sign in to comment.