Last active
March 5, 2020 17:53
-
-
Save clarkmcc/7b2db9c5529f8812070db2560adcdb54 to your computer and use it in GitHub Desktop.
This package is a dependency installer for Golang. Dependencies can be registered with the startup.Engine, checked, and installed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package startup | |
import ( | |
"fmt" | |
"log" | |
"sync" | |
) | |
const BufferedChan int = 1 | |
const FailedToInstallDependency = "failed to install dependency" | |
const FailedToDownloadFile = "failed to download file" | |
type Dependency interface { | |
Check() bool | |
Install(chan error, interface{}) | |
IsCritical() bool | |
GetFilename() string | |
GetFilePath() string | |
} | |
type File struct { | |
Filename string | |
URL string | |
Critical bool | |
} | |
type Zip struct { | |
Filename string | |
Destination string | |
URL string | |
Critical bool | |
} | |
type Dca struct { | |
Filename string | |
URL string | |
Arch string | |
Overwrite bool | |
} | |
type EngineOpts struct { | |
Dependencies []Dependency | |
} | |
// The engine holds a slice of dependencies that it has the ability to check and install when needed | |
type Engine struct { | |
Opts *EngineOpts | |
Dependencies []Dependency | |
} | |
// Returns a new instance of a startup engine | |
func NewStartupEngine(opts *EngineOpts) *Engine { | |
e := &Engine{ | |
Opts: opts, | |
} | |
if opts == nil { | |
e.Dependencies = []Dependency{} | |
} else { | |
e.Dependencies = opts.Dependencies | |
} | |
return e | |
} | |
// Adds a dependency to the engine struct | |
func (e *Engine) RegisterDependency(dep Dependency) *Engine { | |
e.Dependencies = append(e.Dependencies, dep) | |
return e | |
} | |
// Adds a slice of dependencies to the engine struct | |
func (e *Engine) RegisterDependencies(deps []Dependency) *Engine { | |
for _, dep := range deps { | |
e.Dependencies = append(e.Dependencies, dep) | |
} | |
return e | |
} | |
// Checks for the existence of an individual dependency and installs it if necessary | |
func (e *Engine) CheckDependency(dep Dependency) error { | |
log.Printf("checking that dependency %s exists", dep.GetFilename()) | |
exists := dep.Check() | |
if !exists { | |
// if its not a critical dependency, just download and install it in the background | |
if !dep.IsCritical() { | |
wg := &sync.WaitGroup{} | |
wg.Add(1) | |
go dep.Install(make(chan error, BufferedChan), wg) | |
wg.Wait() | |
} else { | |
ec := make(chan error, BufferedChan) | |
dep.Install(ec, nil) | |
select { | |
case e := <-ec: | |
return fmt.Errorf("%v: %v", FailedToInstallDependency, e) | |
default: | |
log.Printf("successfully installed dependency: %s", dep.GetFilename()) | |
return nil | |
} | |
} | |
} | |
return nil | |
} | |
// Checks for the existence of an individual dependency and installs it if necessary | |
func (e *Engine) CheckDependencyChan(dep Dependency, msgChan chan string) { | |
msgChan <- fmt.Sprintf("checking that dependency %s exists", dep.GetFilename()) | |
exists := dep.Check() | |
if !exists { | |
msgChan <- fmt.Sprintf("dependency %s not existing or should be overwritten", dep.GetFilename()) | |
if !dep.IsCritical() { | |
wg := &sync.WaitGroup{} | |
wg.Add(1) | |
go dep.Install(make(chan error, BufferedChan), wg) | |
wg.Wait() | |
} else { | |
ec := make(chan error, BufferedChan) | |
dep.Install(ec, nil) | |
select { | |
case e := <-ec: | |
msgChan <- fmt.Sprintf("%v: %v", FailedToInstallDependency, e) | |
default: | |
msgChan <- fmt.Sprintf("successfully installed dependency: %s", dep.GetFilename()) | |
} | |
} | |
} | |
} | |
// Checks for the existence all of the dependencies registered to a startup engine | |
func (e *Engine) CheckAllDependencies() []error { | |
var errors []error | |
for _, dep := range e.Dependencies { | |
err := e.CheckDependency(dep) | |
if err != nil { | |
errors = append(errors, err) | |
} | |
} | |
return errors | |
} | |
// Checks for the existence all of the dependencies registered to a startup engine and sends logs via the channel | |
func (e *Engine) CheckAllDependenciesChan(msgChan chan string) { | |
for _, dep := range e.Dependencies { | |
e.CheckDependencyChan(dep, msgChan) | |
} | |
close(msgChan) | |
} | |
func (d *File) Check() bool { | |
return util.Exists(d.Filename) | |
} | |
func (d *File) Install(ec chan error, wgs interface{}) { | |
if wgs != nil { | |
defer wgs.(*sync.WaitGroup).Done() | |
} | |
err := util.DownloadFile(d.Filename, d.URL) | |
if err != nil { | |
ec <- fmt.Errorf(fmt.Sprintf("%v: %v", FailedToDownloadFile, err)) | |
} | |
} | |
func (d *File) IsCritical() bool { | |
return d.Critical | |
} | |
func (d *File) GetFilename() string { | |
return d.Filename | |
} | |
func (d *File) GetFilePath() string { | |
return d.Filename | |
} | |
func (d *Zip) Check() bool { | |
return util.Exists(d.Destination) | |
} | |
func (d *Zip) Install(ec chan error, wgs interface{}) { | |
if wgs != nil { | |
defer wgs.(*sync.WaitGroup).Done() | |
} | |
err := util.DownloadFile(d.Filename, d.URL) | |
if err != nil { | |
ec <- fmt.Errorf(fmt.Sprintf("%v: %v", FailedToDownloadFile, err)) | |
return | |
} | |
defer os.Remove(d.Filename) | |
_, err = util.Unzip(d.Filename, d.Destination) | |
if err != nil { | |
ec <- fmt.Errorf(fmt.Sprintf("%v: %v", FailedToInstallDependency, err)) | |
return | |
} | |
} | |
func (d *Zip) IsCritical() bool { | |
return d.Critical | |
} | |
func (d *Zip) GetFilename() string { | |
return d.Filename | |
} | |
func (d *Zip) GetFilePath() string { | |
return d.Destination | |
} | |
func (d *Dca) Check() bool { | |
if d.Overwrite { | |
return false | |
} | |
return util.Exists(d.Filename) | |
} | |
func (d *Dca) Install(ec chan error, wgs interface{}) { | |
if wgs != nil { | |
defer wgs.(*sync.WaitGroup).Done() | |
} | |
err := util.DownloadFile(d.Filename, d.URL) | |
if err != nil { | |
ec <- fmt.Errorf(fmt.Sprintf("%v: %v", FailedToDownloadFile, err)) | |
} | |
} | |
func (d *Dca) IsCritical() bool { | |
return true | |
} | |
func (d *Dca) GetFilename() string { | |
return d.Filename | |
} | |
func (d *Dca) GetFilePath() string { | |
return d.Filename | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment