Skip to content

Commit

Permalink
create dependency type to allow for composition
Browse files Browse the repository at this point in the history
  • Loading branch information
jyecusch committed Nov 4, 2024
1 parent cb7de74 commit 04f28be
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 58 deletions.
2 changes: 1 addition & 1 deletion cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ var buildCmd = &cobra.Command{
}

func init() {
rootCmd.AddCommand(tui.AddDependencyCheck(buildCmd, &tui.ContainerToolDependency{}))
rootCmd.AddCommand(tui.AddDependencyCheck(buildCmd, tui.RequireContainerBuilder))
}
2 changes: 1 addition & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,5 +237,5 @@ func init() {
false,
"disable browser opening for local dashboard, note: in CI mode the browser opening feature is disabled",
)
rootCmd.AddCommand(tui.AddDependencyCheck(runCmd, &tui.ContainerToolDependency{}))
rootCmd.AddCommand(tui.AddDependencyCheck(runCmd, tui.RequireContainerBuilder))
}
2 changes: 1 addition & 1 deletion cmd/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ func init() {
newStackCmd.Flags().BoolVarP(&forceNewStack, "force", "f", false, "force stack creation.")

// Update Stack (Up)
stackCmd.AddCommand(tui.AddDependencyCheck(stackUpdateCmd, &tui.ContainerToolDependency{}))
stackCmd.AddCommand(tui.AddDependencyCheck(stackUpdateCmd, tui.RequireContainerBuilder))
stackUpdateCmd.Flags().StringVarP(&envFile, "env-file", "e", "", "--env-file config/.my-env")
stackUpdateCmd.Flags().BoolVarP(&forceStack, "force", "f", false, "force override previous deployment")
tui.CheckErr(AddOptions(stackUpdateCmd, false))
Expand Down
6 changes: 3 additions & 3 deletions pkg/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (d *Docker) Build(dockerfile, srcPath, imageTag string, buildArgs map[strin
// If docker is available, create a buildx builder
var builder *BuildxBuilder

if a, _ := tui.DockerAvailable(); a {
if err := tui.DockerAvailable(); err == nil {
var err error

builder, err = d.createBuildxBuilder()
Expand Down Expand Up @@ -187,8 +187,8 @@ func (d *Docker) Build(dockerfile, srcPath, imageTag string, buildArgs map[strin
// The args should be compatible with either docker or podman
baseCommand := "docker"

if a, _ := tui.DockerAvailable(); !a {
if b, _ := tui.PodmanAvailable(); b {
if err := tui.DockerAvailable(); err != nil {
if err := tui.PodmanAvailable(); err == nil {
baseCommand = "podman"
} else {
return errors.New("Docker or Podman is required, see https://docs.docker.com/engine/install/ for docker installation instructions")
Expand Down
151 changes: 99 additions & 52 deletions pkg/view/tui/dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,94 +17,152 @@
package tui

import (
"errors"
"os/exec"

"github.com/spf13/cobra"
)

type DependencyError struct {
details string
assist string
}

func (d *DependencyError) Error() string {
return d.details
}

func (d *DependencyError) Assist() string {
return d.assist
}

type Dependency = func() *DependencyError

func atLeastOne(d ...Dependency) Dependency {
return func() *DependencyError {
if len(d) == 0 {
return nil
}

// Extract the preference dependency
primary := d[0]()
if primary == nil {
return nil
}

// Check the rest of the dependencies
for _, dep := range d[1:] {
if dep() == nil {
return nil
}
}

return primary
}
}

var (
dockerAvailable bool
dockerBuildxAvailable bool
podmanAvailable bool
)

func DockerAvailable() (bool, error) {
func DockerAvailable() error {
if dockerAvailable {
return true, nil
return nil
}

err := exec.Command("docker", "version").Run()
if err == nil {
dockerAvailable = true
if err != nil {
return err
}

return dockerAvailable, err
dockerAvailable = true

return nil
}

func DockerBuildxAvailable() (bool, error) {
func DockerBuildxAvailable() error {
if dockerBuildxAvailable {
return true, nil
return nil
}

err := exec.Command("docker", "buildx", "version").Run()
if err == nil {
dockerBuildxAvailable = true
if err != nil {
return err
}

return dockerBuildxAvailable, err
dockerBuildxAvailable = true

return nil
}

func PodmanAvailable() (bool, error) {
func PodmanAvailable() error {
if podmanAvailable {
return true, nil
return nil
}

err := exec.Command("podman", "version").Run()
if err == nil {
podmanAvailable = true
if err != nil {
return err
}

return podmanAvailable, err
}
podmanAvailable = true

type Dependency interface {
// Check if the dependency is met
Check() error
// If the dependency is not met, provide a message to the user to assist in installing it
Assist() string
return nil
}

type ContainerToolDependency struct {
message string
}
func RequireDocker() *DependencyError {
if dockerAvailable {
return nil
}

func (c *ContainerToolDependency) Check() error {
_, err := DockerAvailable()
err := DockerAvailable()
if err != nil {
_, podErr := PodmanAvailable()
if podErr != nil {
c.message = "Docker or Podman is required, see https://docs.docker.com/engine/install/ for docker installation instructions"
return err
} else {
// Use podman
return nil
depErr := DependencyError{
details: err.Error(),
assist: "Docker is required, see https://docs.docker.com/engine/install/ for docker installation instructions",
}

return &depErr
}

_, err = DockerBuildxAvailable()
err = DockerBuildxAvailable()
if err != nil {
c.message = "docker buildx is required to run this command. For installation instructions see: https://github.com/docker/buildx"
return err
depErr := DependencyError{
details: err.Error(),
assist: "docker buildx is required to run this command. For installation instructions see: https://github.com/docker/buildx",
}

return &depErr
}

dockerBuildxAvailable = true

return nil
}

func (c *ContainerToolDependency) Assist() string {
return c.message
func RequirePodman() *DependencyError {
if podmanAvailable {
return nil
}

err := PodmanAvailable()
if err != nil {
depErr := DependencyError{
details: err.Error(),
assist: "Podman is required, see https://docs.docker.com/engine/install/ for docker installation instructions",
}

return &depErr
}

podmanAvailable = true

return nil
}

var RequireContainerBuilder = atLeastOne(RequireDocker, RequirePodman)

// AddDependencyCheck - Wraps a cobra command with a pre-run that
// will check for dependencies
func AddDependencyCheck(cmd *cobra.Command, deps ...Dependency) *cobra.Command {
Expand All @@ -121,21 +179,10 @@ func checkDependencies(deps ...Dependency) error {
return nil
}

missing := make([]Dependency, 0)

for _, p := range deps {
err := p.Check()
err := p()
if err != nil {
missing = append(missing, p)
}
}

if len(missing) > 0 {
for _, p := range missing {
err := errors.New(p.Assist())
if err != nil {
return err
}
return err
}
}

Expand Down

0 comments on commit 04f28be

Please sign in to comment.