├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── boxfile.yml ├── commands ├── build.go ├── clean.go ├── commands.go ├── compile.go ├── configure.go ├── console.go ├── deploy.go ├── destroy.go ├── dev │ ├── start.go │ └── stop.go ├── dns.go ├── dns │ ├── add.go │ ├── list.go │ ├── remove.go │ └── remove_all.go ├── env.go ├── env │ └── server.go ├── evar.go ├── evar │ ├── add.go │ ├── evar_internal_test.go │ ├── list.go │ ├── load.go │ └── remove.go ├── implode.go ├── info.go ├── inspect.go ├── log.go ├── login.go ├── logout.go ├── registry │ └── registry.go ├── remote.go ├── run.go ├── server │ ├── registry.go │ ├── server.go │ ├── svc_unix.go │ └── svc_windows.go ├── sim │ ├── start.go │ └── stop.go ├── start.go ├── status.go ├── steps │ ├── build.go │ ├── run.go │ └── steps.go ├── stop.go ├── tunnel.go ├── update.go └── version.go ├── generators ├── containers │ ├── bridge.go │ ├── build.go │ ├── compile.go │ ├── component.go │ ├── containers_internal_test.go │ ├── containers_test.go │ ├── dev.go │ ├── environment.go │ └── publish.go ├── hooks │ ├── build │ │ ├── boxfile.go │ │ ├── build.go │ │ ├── clean.go │ │ ├── compile.go │ │ ├── configure.go │ │ ├── dev.go │ │ ├── empty.go │ │ ├── fetch.go │ │ ├── mount.go │ │ ├── pack_app.go │ │ ├── pack_build.go │ │ ├── pack_deploy.go │ │ ├── publish.go │ │ ├── setup.go │ │ └── user.go │ ├── code │ │ ├── configure.go │ │ ├── deploy.go │ │ └── fetch.go │ └── component │ │ ├── component.go │ │ ├── configure.go │ │ ├── plan.go │ │ ├── start.go │ │ └── update.go └── router │ ├── certs.go │ ├── router.go │ ├── routes.go │ └── services.go ├── helpers ├── endpoint.go └── odin.go ├── main.go ├── manifest.xml ├── models ├── app.go ├── app_test.go ├── auth.go ├── auth_test.go ├── component.go ├── component_plan.go ├── component_plan_test.go ├── component_test.go ├── config.go ├── console.go ├── db.go ├── db_test.go ├── env.go ├── env_test.go ├── ips.go ├── ips_test.go ├── log.go ├── models.go ├── provider.go ├── provider_test.go ├── tunnel.go ├── update.go └── version.go ├── processors ├── app │ ├── deploy.go │ ├── destroy.go │ ├── dns │ │ ├── add.go │ │ ├── list.go │ │ ├── remove.go │ │ └── remove_all.go │ ├── evar │ │ ├── add.go │ │ ├── list.go │ │ └── remove.go │ ├── info.go │ ├── setup.go │ ├── start.go │ └── stop.go ├── build.go ├── clean.go ├── code │ ├── build.go │ ├── code.go │ ├── compile.go │ ├── destroy.go │ ├── publish.go │ ├── setup.go │ ├── sync.go │ └── types.go ├── compile.go ├── component │ ├── clean.go │ ├── component.go │ ├── destroy.go │ ├── setup.go │ ├── start.go │ ├── start_all.go │ ├── stop.go │ ├── stop_all.go │ └── sync.go ├── configure.go ├── configure_set.go ├── console.go ├── deploy.go ├── env │ ├── console.go │ ├── destroy.go │ ├── mount.go │ ├── setup.go │ ├── share │ │ ├── add.go │ │ └── remove.go │ └── unmount.go ├── evar │ ├── add.go │ ├── list.go │ └── remove.go ├── implode.go ├── log │ └── log.go ├── login.go ├── logout.go ├── platform │ ├── mist.go │ ├── platform.go │ ├── portal.go │ ├── provision.go │ ├── setup.go │ └── stop.go ├── provider │ ├── bridge │ │ ├── bridge.go │ │ ├── setup.go │ │ ├── start.go │ │ ├── stop.go │ │ └── teardown.go │ ├── destroy.go │ ├── init.go │ ├── setup.go │ └── stop.go ├── remote │ ├── add.go │ ├── list.go │ └── remove.go ├── run.go ├── server │ ├── setup.go │ ├── start.go │ ├── stop.go │ └── teardown.go ├── start.go ├── status.go ├── stop.go ├── submit_log.go ├── tunnel.go ├── types.go └── update.go ├── scripts ├── build.sh └── upload.sh ├── updater └── main.go ├── util ├── config │ ├── config_internal_test.go │ ├── dirs.go │ ├── files.go │ └── vars.go ├── console │ └── console.go ├── dhcp │ ├── dhcp.go │ └── dhcp_test.go ├── display │ ├── characters.go │ ├── characters_windows.go │ ├── command_error.go │ ├── display.go │ ├── docker_percent.go │ ├── download_percent.go │ ├── logs.go │ ├── messages.go │ ├── prefix.go │ ├── privilege_unix.go │ ├── privilege_windows.go │ ├── prompt.go │ ├── prompt_unix.go │ ├── prompt_windows.go │ ├── stream.go │ └── summary.go ├── dns │ ├── dns.go │ ├── dns_unix.go │ └── dns_windows.go ├── error.go ├── exec.go ├── fileutil │ ├── download.go │ └── file.go ├── hookit │ └── hookit.go ├── locker │ ├── global.go │ ├── local.go │ └── locker_internal_test.go ├── mixpanel │ └── mixpanel.go ├── nanoagent │ ├── connect.go │ ├── console.go │ ├── ssh.go │ └── tunnel.go ├── odin │ └── odin.go ├── os.go ├── provider │ ├── bridge │ │ ├── bridge.go │ │ ├── start.go │ │ └── stop.go │ ├── dockermachine.go │ ├── dockermachine_mount.go │ ├── dockermachine_mount_unix.go │ ├── dockermachine_mount_windows.go │ ├── native.go │ ├── provider.go │ └── share │ │ ├── rpc.go │ │ ├── share.go │ │ ├── share_darwin.go │ │ ├── share_linux.go │ │ ├── share_test.go │ │ ├── share_unix.go │ │ └── share_windows.go ├── retry.go ├── service │ ├── create_darwin.go │ ├── create_linux.go │ ├── create_windows.go │ ├── remove_unix.go │ ├── remove_windows.go │ ├── service_darwin.go │ ├── service_linux.go │ ├── service_windows.go │ ├── start_unix.go │ ├── start_windows.go │ ├── stop_unix.go │ └── stop_windows.go ├── terminal_size.go ├── terminal_size_windows.go ├── unique.go ├── update │ ├── check.go │ ├── name.go │ ├── name_windows.go │ ├── run.go │ └── update.go ├── util.go ├── util_test.go ├── util_unix.go ├── util_windows.go ├── vbox │ ├── vbox.go │ ├── vbox_unix.go │ └── vbox_windows.go └── watch │ ├── crawl.go │ ├── crawl_internal_test.go │ ├── notify.go │ ├── notify_internal_test.go │ └── watch.go └── vendor └── vendor.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | # Project specific files 27 | .DS_Store 28 | TODO 29 | nanobox 30 | nanobox-* 31 | updater/updater 32 | updater/updater-* 33 | .build 34 | rsrc.syso 35 | Icon 36 | .sync 37 | nb.setup 38 | vendor/*/ 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | language: go 4 | 5 | # ~1 hour is too long to wait for an osx container, plus docker/dependencies 6 | dist: trusty 7 | 8 | go: 9 | - 1.9 10 | 11 | before_install: 12 | - docker version 13 | - ifconfig 14 | - sudo -H pip install awscli 15 | 16 | install: 17 | - go get github.com/kardianos/govendor 18 | - go get github.com/mitchellh/gox 19 | - govendor sync 20 | 21 | script: 22 | - govendor test +local -v 23 | 24 | after_success: 25 | - export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi) 26 | - 'if [ "$BRANCH" == "master" ]; then 27 | ./scripts/build.sh; 28 | ./scripts/upload.sh; 29 | curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${TRAVIS_TOKEN}" -d "{\"request\":{\"branch\":\"master\"}}" https://api.travis-ci.org/repo/nanobox-io%2Fnanobox-installers/requests; 30 | fi' -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Nanobox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TAG=`git describe --always --tags --abbrev=0 | tr -d "[v\r\n]"` 2 | COMMIT=`git rev-parse --short HEAD | tr -d "[ \r\n\']"` 3 | BUILD_DATE=`date -u +%y%m%dT%H%M` 4 | GITSTATUS='$(shell git status 2> /dev/null | tail -n1)' 5 | DIRTY="$(shell [ $(GITSTATUS) = 'no changes added to commit (use "git add" and/or "git commit -a")' ] && echo -n "*")" 6 | GO_LDFLAGS="-s -X github.com/nanobox-io/nanobox/util/odin.apiKey=$(API_KEY) -X github.com/nanobox-io/nanobox/models.nanoVersion=$(TAG) -X github.com/nanobox-io/nanobox/models.nanoCommit=$(COMMIT)$(DIRTY) -X github.com/nanobox-io/nanobox/models.nanoBuild=$(BUILD_DATE)" 7 | 8 | default: build 9 | 10 | local: linux windows darwin 11 | 12 | clean: 13 | @echo "Cleaning old builds" 14 | @rm -rf "./.build" 15 | 16 | # go get github.com/mitchellh/gox 17 | build: clean 18 | @echo "Building nanobox" 19 | @gox -ldflags=$(GO_LDFLAGS) -osarch "darwin/amd64 linux/amd64 windows/amd64" -output="./.build/v2/{{.OS}}/{{.Arch}}/nanobox" 20 | @echo -en "Nanobox Version $(TAG)-$(BUILD_DATE) ($(COMMIT))" > ./.build/v2/version 21 | @echo "Building nanobox-update" 22 | @cd ./updater && gox -osarch "darwin/amd64 linux/amd64 windows/amd64" -ldflags="-s" -output="../.build/v2/{{.OS}}/{{.Arch}}/nanobox-update" 23 | 24 | linux: 25 | @echo "Building nanobox-linux" 26 | @GOOS=linux go build -ldflags=$(GO_LDFLAGS) -o nanobox-linux 27 | 28 | windows: 29 | @echo "Building nanobox-windows" 30 | @GOOS=windows go build -ldflags=$(GO_LDFLAGS) -o nanobox-windows 31 | 32 | darwin: 33 | @echo "Building nanobox-darwin" 34 | @GOOS=darwin go build -ldflags=$(GO_LDFLAGS) -o nanobox-darwin 35 | 36 | # go get github.com/kardianos/govendor 37 | test: 38 | @govendor test +local -v 39 | 40 | 41 | .PHONY: fmt test clean build linux windows darwin 42 | -------------------------------------------------------------------------------- /boxfile.yml: -------------------------------------------------------------------------------- 1 | run.config: 2 | engine: golang 3 | engine.config: 4 | package: github.com/nanobox-io/nanobox 5 | dev_packages: 6 | - py27-awscli 7 | - coreutils 8 | extra_steps: 9 | - "go get github.com/kardianos/govendor" 10 | - "go get github.com/mitchellh/gox" -------------------------------------------------------------------------------- /commands/build.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/nanobox-io/nanobox-boxfile" 5 | "github.com/spf13/cobra" 6 | 7 | "github.com/nanobox-io/nanobox/commands/steps" 8 | "github.com/nanobox-io/nanobox/generators/hooks/build" 9 | "github.com/nanobox-io/nanobox/models" 10 | "github.com/nanobox-io/nanobox/processors" 11 | "github.com/nanobox-io/nanobox/util" 12 | "github.com/nanobox-io/nanobox/util/config" 13 | "github.com/nanobox-io/nanobox/util/display" 14 | ) 15 | 16 | var ( 17 | // BuildCmd builds the app's runtime. 18 | BuildCmd = &cobra.Command{ 19 | Use: "build-runtime", 20 | Short: "Build your app's runtime.", 21 | Long: ` 22 | Builds your app's runtime, which is used both 23 | locally and in live environments. 24 | `, 25 | PreRun: steps.Run("start"), 26 | Run: buildFn, 27 | Aliases: []string{"build"}, 28 | } 29 | 30 | cacheClear bool 31 | ) 32 | 33 | func init() { 34 | steps.Build("build-runtime", buildComplete, buildFn) 35 | 36 | BuildCmd.Flags().BoolVar(&cacheClear, "clear-cache", false, "Clear package cache for this build.") 37 | } 38 | 39 | func buildFn(ccmd *cobra.Command, args []string) { 40 | if cacheClear { 41 | build.ClearPkgCache = true 42 | } 43 | 44 | env, _ := models.FindEnvByID(config.EnvID()) 45 | display.CommandErr(processors.Build(env)) 46 | } 47 | 48 | // update: this runs on deploy 49 | func buildComplete() bool { 50 | // check the boxfile to be sure it hasnt changed 51 | env, _ := models.FindEnvByID(config.EnvID()) 52 | box := boxfile.NewFromPath(config.Boxfile()) 53 | 54 | // we need to rebuild if this isnt true without going to check triggers 55 | if env.UserBoxfile == "" || env.UserBoxfile != box.String() { 56 | return false 57 | } 58 | 59 | // now check to see if any of the build triggers have changed 60 | lastBuildsBoxfile := boxfile.New([]byte(env.BuiltBoxfile)) 61 | for _, trigger := range lastBuildsBoxfile.Node("run.config").StringSliceValue("build_triggers") { 62 | if env.BuildTriggers[trigger] != util.FileMD5(trigger) { 63 | return false 64 | } 65 | } 66 | return true 67 | } 68 | -------------------------------------------------------------------------------- /commands/clean.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/nanobox-io/nanobox/commands/steps" 9 | "github.com/nanobox-io/nanobox/models" 10 | "github.com/nanobox-io/nanobox/processors" 11 | "github.com/nanobox-io/nanobox/util/display" 12 | ) 13 | 14 | var ( 15 | 16 | // CleanCmd ... 17 | CleanCmd = &cobra.Command{ 18 | Use: "clean", 19 | Short: "Clean out any apps that no longer exist.", 20 | Long: ` 21 | Clean out any apps whose working directory no longer exists. This 22 | will remove all associated app information from your Nanobox database. 23 | `, 24 | PreRun: steps.Run("start"), 25 | Run: cleanFn, 26 | } 27 | ) 28 | 29 | // cleanFn ... 30 | func cleanFn(ccmd *cobra.Command, args []string) { 31 | // get the environments 32 | envs, err := models.AllEnvs() 33 | if err != nil { 34 | fmt.Printf("TODO: write message for command clean: %s\n", err.Error()) 35 | return 36 | } 37 | 38 | display.CommandErr(processors.Clean(envs)) 39 | } 40 | -------------------------------------------------------------------------------- /commands/compile.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/nanobox-io/nanobox/commands/registry" 9 | "github.com/nanobox-io/nanobox/commands/steps" 10 | "github.com/nanobox-io/nanobox/models" 11 | "github.com/nanobox-io/nanobox/processors" 12 | "github.com/nanobox-io/nanobox/util/config" 13 | "github.com/nanobox-io/nanobox/util/display" 14 | ) 15 | 16 | var ( 17 | 18 | // CompileCmd ... 19 | CompileCmd = &cobra.Command{ 20 | Use: "compile-app", 21 | Short: "Compile your application.", 22 | Long: ` 23 | Compiles your application source code into a deployable package. 24 | `, 25 | PreRun: steps.Run("start", "build-runtime"), 26 | Run: compileFn, 27 | Aliases: []string{"compile"}, 28 | } 29 | ) 30 | 31 | func init() { 32 | steps.Build("compile-app", compileComplete, compileFn) 33 | } 34 | 35 | // compileFn ... 36 | func compileFn(ccmd *cobra.Command, args []string) { 37 | env, _ := models.FindEnvByID(config.EnvID()) 38 | display.CommandErr(processors.Compile(env)) 39 | } 40 | 41 | func compileComplete() bool { 42 | env, _ := models.FindEnvByID(config.EnvID()) 43 | // if the last compile has been set and they want to skip 44 | return !env.LastCompile.Equal(time.Time{}) && registry.GetBool("skip-compile") 45 | } 46 | -------------------------------------------------------------------------------- /commands/destroy.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/cobra" 6 | 7 | "github.com/nanobox-io/nanobox/commands/steps" 8 | "github.com/nanobox-io/nanobox/helpers" 9 | "github.com/nanobox-io/nanobox/models" 10 | "github.com/nanobox-io/nanobox/processors/app" 11 | "github.com/nanobox-io/nanobox/processors/env" 12 | "github.com/nanobox-io/nanobox/util/config" 13 | "github.com/nanobox-io/nanobox/util/display" 14 | ) 15 | 16 | var ( 17 | 18 | // DestroyCmd ... 19 | DestroyCmd = &cobra.Command{ 20 | Use: "destroy", 21 | Short: "Destroy the current project and remove it from Nanobox.", 22 | Long: ` 23 | Destroys the current project and removes it from Nanobox – destroying 24 | the filesystem mount, associated dns aliases, and local app data. 25 | `, 26 | PreRun: steps.Run("start"), 27 | Run: destroyFunc, 28 | } 29 | ) 30 | 31 | // destroyFunc ... 32 | func destroyFunc(ccmd *cobra.Command, args []string) { 33 | envModel, err := models.FindEnvByID(config.EnvID()) 34 | if err != nil { 35 | fmt.Println("This project doesn't exist on nanobox.") 36 | return 37 | } 38 | 39 | if len(args) == 0 { 40 | display.CommandErr(env.Destroy(envModel)) 41 | return 42 | } 43 | 44 | _, _, name := helpers.Endpoint(envModel, args, 2) 45 | appModel, err := models.FindAppBySlug(envModel.ID, name) 46 | if err != nil { 47 | fmt.Println("Could not find the application") 48 | } 49 | 50 | display.CommandErr(app.Destroy(appModel)) 51 | 52 | } 53 | -------------------------------------------------------------------------------- /commands/dev/start.go: -------------------------------------------------------------------------------- 1 | package dev 2 | 3 | import ( 4 | "fmt" 5 | "github.com/nanobox-io/golang-docker-client" 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/nanobox-io/nanobox/commands/steps" 9 | "github.com/nanobox-io/nanobox/models" 10 | "github.com/nanobox-io/nanobox/processors/app" 11 | "github.com/nanobox-io/nanobox/processors/env" 12 | "github.com/nanobox-io/nanobox/processors/provider" 13 | "github.com/nanobox-io/nanobox/util/config" 14 | "github.com/nanobox-io/nanobox/util/display" 15 | util_provider "github.com/nanobox-io/nanobox/util/provider" 16 | ) 17 | 18 | func init() { 19 | steps.Build("dev start", startCheck, devStart) 20 | } 21 | 22 | // devStart ... 23 | func devStart(ccmd *cobra.Command, args []string) { 24 | envModel, _ := models.FindEnvByID(config.EnvID()) 25 | appModel, _ := models.FindAppBySlug(config.EnvID(), "dev") 26 | 27 | display.CommandErr(env.Setup(envModel)) 28 | display.CommandErr(app.Start(envModel, appModel, "dev")) 29 | } 30 | 31 | func startCheck() bool { 32 | // check to see if the app is up 33 | 34 | app, _ := models.FindAppBySlug(config.EnvID(), "dev") 35 | if app.Status != "up" { 36 | return false 37 | } 38 | 39 | // make sure im mounted and ready to go 40 | envModel, _ := models.FindEnvByID(config.EnvID()) 41 | if !util_provider.HasMount(fmt.Sprintf("%s%s/code", util_provider.HostShareDir(), envModel.ID)) { 42 | return false 43 | } 44 | 45 | // connect to docker and find out if the components exist and are running 46 | provider.Init() 47 | components, _ := app.Components() 48 | for _, component := range components { 49 | info, err := docker.ContainerInspect(component.ID) 50 | if err != nil || !info.State.Running { 51 | return false 52 | } 53 | } 54 | 55 | // if we found nothing wrong we are already started :) 56 | return true 57 | } 58 | -------------------------------------------------------------------------------- /commands/dev/stop.go: -------------------------------------------------------------------------------- 1 | package dev 2 | 3 | import ( 4 | "github.com/nanobox-io/golang-docker-client" 5 | "github.com/spf13/cobra" 6 | 7 | "github.com/nanobox-io/nanobox/commands/steps" 8 | container_generator "github.com/nanobox-io/nanobox/generators/containers" 9 | "github.com/nanobox-io/nanobox/models" 10 | "github.com/nanobox-io/nanobox/processors/app" 11 | "github.com/nanobox-io/nanobox/util/config" 12 | "github.com/nanobox-io/nanobox/util/display" 13 | ) 14 | 15 | func init() { 16 | steps.Build("dev stop", stopCheck, stopFn) 17 | } 18 | 19 | // 20 | // stopFn ... 21 | func stopFn(ccmd *cobra.Command, args []string) { 22 | // TODO: check the app and return some message 23 | appModel, _ := models.FindAppBySlug(config.EnvID(), "dev") 24 | 25 | display.CommandErr(app.Stop(appModel)) 26 | } 27 | 28 | func stopCheck() bool { 29 | container, err := docker.GetContainer(container_generator.DevName()) 30 | 31 | // if the container doesn't exist then just return false 32 | return err == nil && container.State.Status == "running" 33 | } 34 | -------------------------------------------------------------------------------- /commands/dns.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/nanobox-io/nanobox/commands/dns" 7 | ) 8 | 9 | var ( 10 | 11 | // DnsCmd ... 12 | DnsCmd = &cobra.Command{ 13 | Use: "dns", 14 | Short: "Manage dns aliases for local applications.", 15 | Long: ` 16 | Manages dns aliases for local applications. This modifies 17 | your local hosts file, requiring administrative privileges. 18 | `, 19 | } 20 | ) 21 | 22 | // 23 | func init() { 24 | DnsCmd.AddCommand(dns.AddCmd) 25 | DnsCmd.AddCommand(dns.RemoveCmd) 26 | DnsCmd.AddCommand(dns.RemoveAllCmd) 27 | DnsCmd.AddCommand(dns.ListCmd) 28 | } 29 | -------------------------------------------------------------------------------- /commands/dns/add.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/nanobox-io/nanobox/helpers" 9 | "github.com/nanobox-io/nanobox/models" 10 | app_dns "github.com/nanobox-io/nanobox/processors/app/dns" 11 | "github.com/nanobox-io/nanobox/util/config" 12 | "github.com/nanobox-io/nanobox/util/display" 13 | ) 14 | 15 | // AddCmd ... 16 | var AddCmd = &cobra.Command{ 17 | Use: "add [local|dry-run] ", 18 | Short: "Adds dns entries", 19 | Long: ``, 20 | // PreRun: steps.Run("login"), 21 | Run: addFn, 22 | } 23 | 24 | // addFn ... 25 | func addFn(ccmd *cobra.Command, args []string) { 26 | 27 | // parse the dnss excluding the context 28 | env, _ := models.FindEnvByID(config.EnvID()) 29 | args, location, name := helpers.Endpoint(env, args, 2) 30 | 31 | if len(args) != 1 { 32 | fmt.Println("i need a dns") 33 | } 34 | 35 | switch location { 36 | case "local": 37 | app, _ := models.FindAppBySlug(config.EnvID(), name) 38 | app.Generate(env, name) 39 | display.CommandErr(app_dns.Add(env, app, args[0])) 40 | case "production": 41 | fmt.Printf(` 42 | -------------------------------------------------------- 43 | Production dns aliasing is not yet implemented. 44 | -------------------------------------------------------- 45 | 46 | `) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /commands/dns/list.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | // "github.com/nanobox-io/nanobox/commands/steps" 9 | "github.com/nanobox-io/nanobox/helpers" 10 | "github.com/nanobox-io/nanobox/models" 11 | app_dns "github.com/nanobox-io/nanobox/processors/app/dns" 12 | "github.com/nanobox-io/nanobox/util/config" 13 | "github.com/nanobox-io/nanobox/util/display" 14 | ) 15 | 16 | // ListCmd ... 17 | var ListCmd = &cobra.Command{ 18 | Use: "ls [local|dry-run]", 19 | Short: "list dns entries", 20 | Long: ``, 21 | // PreRun: steps.Run("login"), 22 | Run: listFn, 23 | } 24 | 25 | // listFn ... 26 | func listFn(ccmd *cobra.Command, args []string) { 27 | 28 | env, _ := models.FindEnvByID(config.EnvID()) 29 | args, location, name := helpers.Endpoint(env, args, 0) 30 | 31 | switch location { 32 | case "local": 33 | app, _ := models.FindAppBySlug(config.EnvID(), name) 34 | display.CommandErr(app_dns.List(app)) 35 | case "production": 36 | fmt.Printf(` 37 | -------------------------------------------------------- 38 | Production dns aliasing is not yet implemented. 39 | -------------------------------------------------------- 40 | 41 | `) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /commands/dns/remove.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/nanobox-io/nanobox/helpers" 9 | "github.com/nanobox-io/nanobox/models" 10 | app_dns "github.com/nanobox-io/nanobox/processors/app/dns" 11 | "github.com/nanobox-io/nanobox/util/config" 12 | "github.com/nanobox-io/nanobox/util/display" 13 | ) 14 | 15 | // RemoveCmd ... 16 | var RemoveCmd = &cobra.Command{ 17 | Use: "rm [local|dry-run] ", 18 | Short: "Remove dns entries", 19 | Long: ``, 20 | // PreRun: steps.Run("login"), 21 | Run: removeFn, 22 | } 23 | 24 | // removeFn ... 25 | func removeFn(ccmd *cobra.Command, args []string) { 26 | // parse the dnss excluding the context 27 | env, _ := models.FindEnvByID(config.EnvID()) 28 | args, location, name := helpers.Endpoint(env, args, 0) 29 | 30 | if len(args) != 1 { 31 | fmt.Println("i need a dns") 32 | } 33 | 34 | switch location { 35 | case "local": 36 | app, _ := models.FindAppBySlug(config.EnvID(), name) 37 | display.CommandErr(app_dns.Remove(app, args[0])) 38 | case "production": 39 | fmt.Printf(` 40 | -------------------------------------------------------- 41 | Production dns aliasing is not yet implemented. 42 | -------------------------------------------------------- 43 | 44 | `) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /commands/dns/remove_all.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/nanobox-io/nanobox/helpers" 9 | "github.com/nanobox-io/nanobox/models" 10 | app_dns "github.com/nanobox-io/nanobox/processors/app/dns" 11 | "github.com/nanobox-io/nanobox/util/config" 12 | "github.com/nanobox-io/nanobox/util/display" 13 | ) 14 | 15 | // RemoveAllCmd ... 16 | var RemoveAllCmd = &cobra.Command{ 17 | Use: "rm-all [local|dry-run]", 18 | Short: "remove all dns entries", 19 | Long: ``, 20 | Run: removeAllFn, 21 | Hidden: true, 22 | } 23 | 24 | // removeAllFn ... 25 | func removeAllFn(ccmd *cobra.Command, args []string) { 26 | // parse the dnss excluding the context 27 | env, _ := models.FindEnvByID(config.EnvID()) 28 | _, location, name := helpers.Endpoint(env, args, 0) 29 | 30 | switch location { 31 | case "local": 32 | app, _ := models.FindAppBySlug(config.EnvID(), name) 33 | display.CommandErr(app_dns.RemoveAll(app)) 34 | case "production": 35 | fmt.Printf(` 36 | -------------------------------------------------------- 37 | Production dns aliasing is not yet implemented. 38 | -------------------------------------------------------- 39 | 40 | `) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /commands/env.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/nanobox-io/nanobox/commands/env" 7 | ) 8 | 9 | var ( 10 | 11 | // EnvCmd ...; currently a hidden command because its only used for one thing 12 | EnvCmd = &cobra.Command{ 13 | Use: "env", 14 | Short: "Shared environment provisioning", 15 | Long: ` 16 | A namespaced collection of hidded subcommands used primarily as share provisioning processes. 17 | `, 18 | Hidden: true, 19 | } 20 | ) 21 | 22 | // 23 | func init() { 24 | // hidden subcommands 25 | EnvCmd.AddCommand(env.ServerCmd) 26 | } 27 | -------------------------------------------------------------------------------- /commands/env/server.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/nanobox-io/nanobox/processors/server" 7 | "github.com/nanobox-io/nanobox/util/display" 8 | ) 9 | 10 | var ( 11 | 12 | // ServerCmd ... 13 | ServerCmd = &cobra.Command{ 14 | Hidden: true, 15 | Use: "server", 16 | Short: "Server control", 17 | Long: ``, 18 | } 19 | 20 | // ServerStartCmd ... 21 | ServerStartCmd = &cobra.Command{ 22 | Hidden: true, 23 | Use: "start", 24 | Short: "Start the server", 25 | Long: ``, 26 | Run: serverStartFn, 27 | } 28 | 29 | // ServerStopCmd ... 30 | ServerStopCmd = &cobra.Command{ 31 | Hidden: true, 32 | Use: "stop", 33 | Short: "Stop the server", 34 | Long: ``, 35 | Run: serverStopFn, 36 | } 37 | 38 | // ServerTeadownCmd ... 39 | ServerTeadownCmd = &cobra.Command{ 40 | Hidden: true, 41 | Use: "teardown", 42 | Short: "Teardown the server", 43 | Long: ``, 44 | Run: serverTeardownFn, 45 | } 46 | ) 47 | 48 | // 49 | func init() { 50 | ServerCmd.AddCommand(ServerStartCmd) 51 | ServerCmd.AddCommand(ServerStopCmd) 52 | ServerCmd.AddCommand(ServerTeadownCmd) 53 | } 54 | 55 | func serverStartFn(ccmd *cobra.Command, args []string) { 56 | display.CommandErr(server.Setup()) 57 | } 58 | 59 | func serverStopFn(ccmd *cobra.Command, args []string) { 60 | 61 | display.CommandErr(server.Stop()) 62 | } 63 | 64 | func serverTeardownFn(ccmd *cobra.Command, args []string) { 65 | 66 | display.CommandErr(server.Teardown()) 67 | } 68 | -------------------------------------------------------------------------------- /commands/evar.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/nanobox-io/nanobox/commands/evar" 7 | ) 8 | 9 | var ( 10 | 11 | // EvarCmd ... 12 | EvarCmd = &cobra.Command{ 13 | Use: "evar", 14 | Short: "Manage environment variables.", 15 | Long: ` 16 | Manages environment variables in your different environments. 17 | `, 18 | } 19 | ) 20 | 21 | // 22 | func init() { 23 | EvarCmd.AddCommand(evar.AddCmd) 24 | EvarCmd.AddCommand(evar.LoadCmd) 25 | EvarCmd.AddCommand(evar.RemoveCmd) 26 | EvarCmd.AddCommand(evar.ListCmd) 27 | } 28 | -------------------------------------------------------------------------------- /commands/evar/list.go: -------------------------------------------------------------------------------- 1 | package evar 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/jcelliott/lumber" 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/nanobox-io/nanobox/commands/steps" 10 | "github.com/nanobox-io/nanobox/helpers" 11 | "github.com/nanobox-io/nanobox/models" 12 | app_evar "github.com/nanobox-io/nanobox/processors/app/evar" 13 | production_evar "github.com/nanobox-io/nanobox/processors/evar" 14 | "github.com/nanobox-io/nanobox/util/config" 15 | "github.com/nanobox-io/nanobox/util/display" 16 | ) 17 | 18 | // ListCmd ... 19 | var ListCmd = &cobra.Command{ 20 | Use: "ls [local|dry-run|remote-alias]", 21 | Short: "list environment variable(s)", 22 | Long: ``, 23 | Run: listFn, 24 | } 25 | 26 | // listFn ... 27 | func listFn(ccmd *cobra.Command, args []string) { 28 | 29 | env, _ := models.FindEnvByID(config.EnvID()) 30 | args, location, name := helpers.Endpoint(env, args, 0) 31 | 32 | // if the first argument is not a keyvalue pair, (at this point the 33 | // remote-alias would be stripped from helpers.Endpoint) it is likely 34 | // the app name. try setting vars on that app 35 | if len(args) > 0 && !strings.Contains(args[0], "=") { 36 | lumber.Info("Remote alias not found for '%s', attempting to set vars on app named '%s'\n", args[0], args[0]) 37 | name = args[0] 38 | args = args[1:] 39 | } 40 | 41 | switch location { 42 | case "local": 43 | app, _ := models.FindAppBySlug(config.EnvID(), name) 44 | display.CommandErr(app_evar.List(app)) 45 | case "production": 46 | steps.Run("login")(ccmd, args) 47 | 48 | env, _ := models.FindEnvByID(config.EnvID()) 49 | production_evar.List(env, name) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /commands/implode.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/nanobox-io/nanobox/commands/registry" 7 | "github.com/nanobox-io/nanobox/processors" 8 | "github.com/nanobox-io/nanobox/util/display" 9 | ) 10 | 11 | var ( 12 | 13 | // ImplodeCmd ... 14 | ImplodeCmd = &cobra.Command{ 15 | Use: "implode", 16 | Short: "Remove all Nanobox-created containers, files, & data.", 17 | Long: ` 18 | Removes the Nanobox container, all projects, filesystem mounts, 19 | & local data. All that will remain is nanobox binaries. 20 | `, 21 | Run: implodeFn, 22 | } 23 | ) 24 | 25 | // implodeFn ... 26 | func implodeFn(ccmd *cobra.Command, args []string) { 27 | registry.Set("full-implode", true) 28 | display.CommandErr(processors.Implode()) 29 | } 30 | -------------------------------------------------------------------------------- /commands/info.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/nanobox-io/nanobox/helpers" 9 | "github.com/nanobox-io/nanobox/models" 10 | "github.com/nanobox-io/nanobox/processors/app" 11 | "github.com/nanobox-io/nanobox/util/config" 12 | "github.com/nanobox-io/nanobox/util/display" 13 | ) 14 | 15 | var ( 16 | 17 | // InfoCmd ... 18 | InfoCmd = &cobra.Command{ 19 | Use: "info [local | dry-run]", 20 | Short: "Show information about the specified environment.", 21 | Long: ` 22 | Shows information about the specified environment. You must 23 | specify which environment you would like information about. 24 | `, 25 | Run: infoFn, 26 | } 27 | ) 28 | 29 | // infoFn ... 30 | func infoFn(ccmd *cobra.Command, args []string) { 31 | 32 | env, _ := models.FindEnvByID(config.EnvID()) 33 | args, location, name := helpers.Endpoint(env, args, 0) 34 | 35 | switch location { 36 | case "local": 37 | appModel, _ := models.FindAppBySlug(config.EnvID(), name) 38 | display.CommandErr(app.Info(env, appModel)) 39 | case "production": 40 | fmt.Printf(` 41 | ---------------------------------------------------------- 42 | Showing production app information is not yet implemented. 43 | ---------------------------------------------------------- 44 | 45 | `) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /commands/inspect.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/nanobox-io/nanobox/models" 9 | ) 10 | 11 | type ( 12 | anything interface { 13 | } 14 | ) 15 | 16 | var ( 17 | // InspectCmd ... 18 | InspectCmd = &cobra.Command{ 19 | Use: "inspect", 20 | Short: "Show element from the nanobox database.", 21 | Long: ``, 22 | Run: inspectFunc, 23 | Hidden: true, 24 | } 25 | ) 26 | 27 | // inspectFunc ... 28 | func inspectFunc(ccmd *cobra.Command, args []string) { 29 | switch { 30 | case len(args) == 1 && args[0] == "ip-tree": 31 | showIPTree() 32 | default: 33 | fmt.Println("I need to know some data starting point") 34 | 35 | case len(args) == 1: 36 | showData(models.Inspect(args[0], "")) 37 | case len(args) == 2: 38 | showData(models.Inspect(args[0], args[1])) 39 | } 40 | } 41 | 42 | func showData(v interface{}) { 43 | fmt.Printf("%+v\n", v) 44 | } 45 | 46 | func showIPTree() { 47 | fmt.Print("reservedIPS: ") 48 | showData(models.Inspect("registry", "ips")) 49 | envs, _ := models.AllEnvs() 50 | for _, env := range envs { 51 | fmt.Printf("%s\n", env.Name) 52 | apps, _ := env.Apps() 53 | for _, app := range apps { 54 | fmt.Printf(" %s local: %v\n", app.Name, app.LocalIPs) 55 | components, _ := app.Components() 56 | for _, component := range components { 57 | fmt.Printf(" %-15s IP: %s\n", component.Name, component.IPAddr()) 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /commands/login.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/nanobox-io/nanobox/commands/steps" 7 | "github.com/nanobox-io/nanobox/models" 8 | "github.com/nanobox-io/nanobox/processors" 9 | "github.com/nanobox-io/nanobox/util/display" 10 | ) 11 | 12 | var ( 13 | 14 | // LoginCmd ... 15 | LoginCmd = &cobra.Command{ 16 | Use: "login", 17 | Short: "Authenticate your nanobox client with your nanobox.io account.", 18 | Long: ` 19 | Authenticate with your nanobox account by passing the username and password in or using the following environment variables: 20 | NANOBOX_USERNAME 21 | NANOBOX_PASSWORD 22 | `, 23 | Run: loginFn, 24 | } 25 | 26 | // loginCmdFlags ... 27 | loginCmdFlags = struct { 28 | username string 29 | password string 30 | endpoint string 31 | }{} 32 | ) 33 | 34 | // 35 | func init() { 36 | LoginCmd.Flags().StringVarP(&loginCmdFlags.username, "username", "u", "", "username") 37 | LoginCmd.Flags().StringVarP(&loginCmdFlags.password, "password", "p", "", "password") 38 | LoginCmd.Flags().StringVarP(&loginCmdFlags.endpoint, "endpoint", "e", "", "endpoint") 39 | 40 | steps.Build("login", loginCheck, loginFn) 41 | } 42 | 43 | // loginFn ... 44 | func loginFn(ccmd *cobra.Command, args []string) { 45 | err := processors.Login(loginCmdFlags.username, loginCmdFlags.password, loginCmdFlags.endpoint) 46 | 47 | display.CommandErr(err) 48 | } 49 | 50 | func loginCheck() bool { 51 | auth, _ := models.LoadAuth() 52 | return auth.Key != "" 53 | } 54 | -------------------------------------------------------------------------------- /commands/logout.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/nanobox-io/nanobox/processors" 7 | "github.com/nanobox-io/nanobox/util/display" 8 | ) 9 | 10 | var ( 11 | 12 | // LogoutCmd ... 13 | LogoutCmd = &cobra.Command{ 14 | Use: "logout", 15 | Short: "Remove your nanobox.io api token from your local nanobox client.", 16 | Long: ``, 17 | Run: logoutFn, 18 | } 19 | 20 | // loginCmdFlags ... 21 | logoutCmdFlags = struct { 22 | endpoint string 23 | }{} 24 | ) 25 | 26 | func init() { 27 | LogoutCmd.Flags().StringVarP(&logoutCmdFlags.endpoint, "endpoint", "e", "", "endpoint") 28 | } 29 | 30 | // logoutFn ... 31 | func logoutFn(ccmd *cobra.Command, args []string) { 32 | display.CommandErr(processors.Logout(logoutCmdFlags.endpoint)) 33 | } 34 | -------------------------------------------------------------------------------- /commands/registry/registry.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/spf13/viper" 5 | "time" 6 | ) 7 | 8 | var v = viper.New() 9 | 10 | func Set(key string, value interface{}) { 11 | v.Set(key, value) 12 | } 13 | 14 | func Get(key string) interface{} { 15 | return v.Get(key) 16 | } 17 | 18 | func GetBool(key string) bool { 19 | return v.GetBool(key) 20 | } 21 | 22 | func GetDuration(key string) time.Duration { 23 | return v.GetDuration(key) 24 | } 25 | 26 | func GetFloat64(key string) float64 { 27 | return v.GetFloat64(key) 28 | } 29 | 30 | func GetInt(key string) int { 31 | return v.GetInt(key) 32 | } 33 | 34 | func GetString(key string) string { 35 | return v.GetString(key) 36 | } 37 | 38 | func GetStringMap(key string) map[string]interface{} { 39 | return v.GetStringMap(key) 40 | } 41 | 42 | func GetStringMapString(key string) map[string]string { 43 | return v.GetStringMapString(key) 44 | } 45 | 46 | func GetStringMapStringSlice(key string) map[string][]string { 47 | return v.GetStringMapStringSlice(key) 48 | } 49 | 50 | func GetStringSlice(key string) []string { 51 | return v.GetStringSlice(key) 52 | } 53 | 54 | func GetTime(key string) time.Time { 55 | return v.GetTime(key) 56 | } 57 | -------------------------------------------------------------------------------- /commands/run.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/nanobox-io/nanobox/commands/steps" 9 | "github.com/nanobox-io/nanobox/models" 10 | "github.com/nanobox-io/nanobox/processors" 11 | "github.com/nanobox-io/nanobox/processors/app" 12 | "github.com/nanobox-io/nanobox/util/config" 13 | "github.com/nanobox-io/nanobox/util/console" 14 | "github.com/nanobox-io/nanobox/util/display" 15 | 16 | // imported because we need its steps added 17 | _ "github.com/nanobox-io/nanobox/commands/dev" 18 | ) 19 | 20 | // RunCmd ... 21 | var RunCmd = &cobra.Command{ 22 | Use: "run", 23 | Short: "Start your local development environment.", 24 | Long: ` 25 | Starts your local development enviroment and opens an 26 | interactive console inside the environment. 27 | 28 | You can also pass a command into 'run'. Nanobox will 29 | run the command without dropping you into a console 30 | in your local environment. 31 | `, 32 | PreRun: steps.Run("start", "build-runtime", "dev start", "dev deploy"), 33 | Run: runFn, 34 | PostRun: steps.Run("dev stop"), 35 | } 36 | 37 | // runFn ... 38 | func runFn(ccmd *cobra.Command, args []string) { 39 | 40 | envModel, _ := models.FindEnvByID(config.EnvID()) 41 | appModel, _ := models.FindAppBySlug(config.EnvID(), "dev") 42 | 43 | consoleConfig := console.ConsoleConfig{} 44 | 45 | if len(args) > 0 { 46 | consoleConfig.Command = strings.Join(args, " ") 47 | } 48 | 49 | // set the meta arguments to be used in the processor and run the processor 50 | display.CommandErr(processors.Run(envModel, appModel, consoleConfig)) 51 | } 52 | 53 | func init() { 54 | steps.Build("dev deploy", devDeployComplete, devDeploy) 55 | } 56 | 57 | // devDeploy ... 58 | func devDeploy(ccmd *cobra.Command, args []string) { 59 | envModel, _ := models.FindEnvByID(config.EnvID()) 60 | appModel, _ := models.FindAppBySlug(envModel.ID, "dev") 61 | display.CommandErr(app.Deploy(envModel, appModel)) 62 | } 63 | 64 | func devDeployComplete() bool { 65 | app, _ := models.FindAppBySlug(config.EnvID(), "dev") 66 | env, _ := app.Env() 67 | return app.DeployedBoxfile != "" && env.BuiltBoxfile == app.DeployedBoxfile && buildComplete() 68 | } 69 | -------------------------------------------------------------------------------- /commands/server/registry.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | var registeredRPCs = []interface{}{} 4 | 5 | func Register(i interface{}) { 6 | registeredRPCs = append(registeredRPCs, i) 7 | } 8 | -------------------------------------------------------------------------------- /commands/server/svc_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package server 4 | 5 | func svcStart() { 6 | return 7 | } 8 | -------------------------------------------------------------------------------- /commands/server/svc_windows.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "golang.org/x/sys/windows/svc" 9 | "golang.org/x/sys/windows/svc/debug" 10 | "golang.org/x/sys/windows/svc/eventlog" 11 | ) 12 | 13 | var elog debug.Log 14 | 15 | type nanoboxServer struct{} 16 | 17 | func (ns *nanoboxServer) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) { 18 | elog.Info(1, fmt.Sprintf("execute called")) 19 | const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown 20 | changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} 21 | elog.Info(1, fmt.Sprintf("running")) 22 | 23 | loop: 24 | for { 25 | select { 26 | case c := <-r: 27 | switch c.Cmd { 28 | case svc.Interrogate: 29 | changes <- c.CurrentStatus 30 | // Testing deadlock from https://code.google.com/p/winsvc/issues/detail?id=4 31 | time.Sleep(100 * time.Millisecond) 32 | changes <- c.CurrentStatus 33 | case svc.Stop, svc.Shutdown: 34 | break loop 35 | default: 36 | elog.Error(1, fmt.Sprintf("unexpected control request #%#v", c)) 37 | } 38 | } 39 | } 40 | changes <- svc.Status{State: svc.StopPending} 41 | return 42 | } 43 | 44 | func svcStart() { 45 | var err error 46 | elog, err = eventlog.Open(name) 47 | if err != nil { 48 | return 49 | } 50 | 51 | defer elog.Close() 52 | 53 | elog.Info(1, fmt.Sprintf("starting %s service", name)) 54 | err = svc.Run(name, &nanoboxServer{}) 55 | if err != nil { 56 | elog.Error(1, fmt.Sprintf("%s service failed: %v", name, err)) 57 | return 58 | } 59 | elog.Info(1, fmt.Sprintf("%s service stopped", name)) 60 | 61 | // on windows when i get here the service has been stopped so we exit 62 | os.Exit(0) 63 | } 64 | -------------------------------------------------------------------------------- /commands/sim/start.go: -------------------------------------------------------------------------------- 1 | package sim 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/golang-docker-client" 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/nanobox-io/nanobox/commands/steps" 10 | "github.com/nanobox-io/nanobox/models" 11 | "github.com/nanobox-io/nanobox/processors/app" 12 | "github.com/nanobox-io/nanobox/processors/env" 13 | "github.com/nanobox-io/nanobox/processors/provider" 14 | "github.com/nanobox-io/nanobox/util/config" 15 | "github.com/nanobox-io/nanobox/util/display" 16 | util_provider "github.com/nanobox-io/nanobox/util/provider" 17 | ) 18 | 19 | func init() { 20 | steps.Build("sim start", startCheck, simStart) 21 | } 22 | 23 | // simStart ... 24 | func simStart(ccmd *cobra.Command, args []string) { 25 | envModel, _ := models.FindEnvByID(config.EnvID()) 26 | appModel, _ := models.FindAppBySlug(config.EnvID(), "sim") 27 | 28 | display.CommandErr(env.Setup(envModel)) 29 | display.CommandErr(app.Start(envModel, appModel, "sim")) 30 | } 31 | 32 | func startCheck() bool { 33 | app, _ := models.FindAppBySlug(config.EnvID(), "sim") 34 | if app.Status != "up" { 35 | return false 36 | } 37 | 38 | // make sure im mounted and ready to go 39 | envModel, _ := models.FindEnvByID(config.EnvID()) 40 | if !util_provider.HasMount(fmt.Sprintf("%s%s/code", util_provider.HostShareDir(), envModel.ID)) { 41 | return false 42 | } 43 | 44 | provider.Init() 45 | components, _ := app.Components() 46 | for _, component := range components { 47 | info, err := docker.ContainerInspect(component.ID) 48 | if err != nil || !info.State.Running { 49 | return false 50 | } 51 | } 52 | return true 53 | } 54 | -------------------------------------------------------------------------------- /commands/sim/stop.go: -------------------------------------------------------------------------------- 1 | package sim 2 | 3 | import ( 4 | // "fmt" 5 | "github.com/spf13/cobra" 6 | 7 | "github.com/nanobox-io/nanobox/commands/steps" 8 | "github.com/nanobox-io/nanobox/models" 9 | "github.com/nanobox-io/nanobox/processors/app" 10 | "github.com/nanobox-io/nanobox/util/config" 11 | "github.com/nanobox-io/nanobox/util/display" 12 | ) 13 | 14 | func init() { 15 | steps.Build("sim stop", stopCheck, stopFn) 16 | } 17 | 18 | // stopFn ... 19 | func stopFn(ccmd *cobra.Command, args []string) { 20 | appModel, _ := models.FindAppBySlug(config.EnvID(), "sim") 21 | display.CommandErr(app.Stop(appModel)) 22 | } 23 | 24 | func stopCheck() bool { 25 | // currently we always stop if we are asking weather to stop 26 | return false 27 | } 28 | -------------------------------------------------------------------------------- /commands/start.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/nanobox-io/nanobox/commands/steps" 7 | "github.com/nanobox-io/nanobox/processors" 8 | "github.com/nanobox-io/nanobox/processors/provider/bridge" 9 | "github.com/nanobox-io/nanobox/util/display" 10 | "github.com/nanobox-io/nanobox/util/provider" 11 | "github.com/nanobox-io/nanobox/util/service" 12 | ) 13 | 14 | var ( 15 | 16 | // StartCmd ... 17 | StartCmd = &cobra.Command{ 18 | Use: "start", 19 | Short: "Start the Nanobox virtual machine.", 20 | Long: ``, 21 | Run: startFn, 22 | } 23 | ) 24 | 25 | func init() { 26 | steps.Build("start", startCheck, startFn) 27 | } 28 | 29 | // startFn ... 30 | func startFn(ccmd *cobra.Command, args []string) { 31 | display.CommandErr(processors.Start()) 32 | } 33 | 34 | func startCheck() bool { 35 | bridgeReady := true 36 | if provider.BridgeRequired() { 37 | bridgeReady = bridge.Connected() 38 | } 39 | return provider.IsReady() && service.Running("nanobox-server") && bridgeReady 40 | } 41 | -------------------------------------------------------------------------------- /commands/status.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/nanobox-io/nanobox/processors" 7 | "github.com/nanobox-io/nanobox/util/display" 8 | ) 9 | 10 | var ( 11 | 12 | // StatusCmd ... 13 | StatusCmd = &cobra.Command{ 14 | Use: "status", 15 | Short: "Display the status of your Nanobox VM & apps.", 16 | Long: ``, 17 | Run: statusFn, 18 | } 19 | ) 20 | 21 | func statusFn(ccmd *cobra.Command, args []string) { 22 | display.CommandErr(processors.Status()) 23 | } 24 | -------------------------------------------------------------------------------- /commands/steps/build.go: -------------------------------------------------------------------------------- 1 | package steps 2 | 3 | func Build(name string, complete CompleteCheckFunc, cmd CmdFunc) { 4 | stepList[name] = step{ 5 | complete: complete, 6 | cmd: cmd, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /commands/steps/run.go: -------------------------------------------------------------------------------- 1 | package steps 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/nanobox-io/nanobox/commands/registry" 7 | ) 8 | 9 | func Run(stepNames ...string) func(ccmd *cobra.Command, args []string) { 10 | return func(ccmd *cobra.Command, args []string) { 11 | 12 | if registry.GetBool("internal") { 13 | return 14 | } 15 | 16 | for _, stepName := range stepNames { 17 | step, ok := stepList[stepName] 18 | if ok && !step.complete() { 19 | step.cmd(ccmd, args) 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /commands/steps/steps.go: -------------------------------------------------------------------------------- 1 | package steps 2 | 3 | import "github.com/spf13/cobra" 4 | 5 | var ( 6 | stepList = map[string]step{} 7 | ) 8 | 9 | type ( 10 | CompleteCheckFunc func() bool 11 | CmdFunc func(ccmd *cobra.Command, args []string) 12 | 13 | step struct { 14 | complete CompleteCheckFunc 15 | cmd CmdFunc 16 | } 17 | ) 18 | -------------------------------------------------------------------------------- /commands/stop.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/nanobox-io/nanobox/commands/registry" 7 | "github.com/nanobox-io/nanobox/processors" 8 | "github.com/nanobox-io/nanobox/util/display" 9 | ) 10 | 11 | var ( 12 | 13 | // StopCmd ... 14 | StopCmd = &cobra.Command{ 15 | Use: "stop", 16 | Short: "Stop the Nanobox virtual machine.", 17 | Long: ` 18 | Stops the Nanobox virtual machine as well as 19 | any running local or dry-run environments. 20 | `, 21 | Run: stopFn, 22 | } 23 | ) 24 | 25 | // stopFn ... 26 | func stopFn(ccmd *cobra.Command, args []string) { 27 | registry.Set("keep-share", true) 28 | display.CommandErr(processors.Stop()) 29 | } 30 | -------------------------------------------------------------------------------- /commands/update.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/nanobox-io/nanobox/commands/steps" 7 | "github.com/nanobox-io/nanobox/processors" 8 | "github.com/nanobox-io/nanobox/util/display" 9 | ) 10 | 11 | var ( 12 | // UpdateCmd updates the images 13 | UpdateCmd = &cobra.Command{ 14 | Use: "update-images", 15 | Short: "Updates docker images.", 16 | // Short: "Updates docker images and checks to see if the nanobox binary needs an update.", 17 | Long: ``, 18 | PreRun: steps.Run("start"), 19 | Run: updateFn, 20 | } 21 | ) 22 | 23 | // updateFn ... 24 | func updateFn(ccmd *cobra.Command, args []string) { 25 | display.CommandErr(processors.Update()) 26 | } 27 | -------------------------------------------------------------------------------- /commands/version.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/nanobox-io/nanobox/models" 9 | ) 10 | 11 | var ( 12 | 13 | // VersionCmd prints the nanobox version. 14 | VersionCmd = &cobra.Command{ 15 | Use: "version", 16 | Short: "Show the current Nanobox version.", 17 | Long: ``, 18 | PersistentPreRun: func(ccmd *cobra.Command, args []string) {}, 19 | Run: versionFn, 20 | } 21 | ) 22 | 23 | // versionFn does the actual printing 24 | func versionFn(ccmd *cobra.Command, args []string) { 25 | fmt.Println(models.VersionString()) 26 | } 27 | -------------------------------------------------------------------------------- /generators/containers/bridge.go: -------------------------------------------------------------------------------- 1 | package containers 2 | 3 | import ( 4 | "github.com/nanobox-io/golang-docker-client" 5 | 6 | "github.com/nanobox-io/nanobox/util/dhcp" 7 | ) 8 | 9 | // BridgeConfig generates the container configuration for a component container 10 | func BridgeConfig() docker.ContainerConfig { 11 | return docker.ContainerConfig{ 12 | Name: BridgeName(), 13 | Image: "nanobox/bridge", 14 | Network: "virt", 15 | IP: reserveIP(), 16 | RestartPolicy: "always", 17 | Ports: []string{"1194:1194/udp"}, 18 | } 19 | } 20 | 21 | // BridgeName returns the name of the component container 22 | func BridgeName() string { 23 | return "nanobox_bridge" 24 | } 25 | 26 | // reserveIP reserves a local IP for the build container 27 | func reserveIP() string { 28 | ip, _ := dhcp.ReserveLocal() 29 | return ip.String() 30 | } 31 | -------------------------------------------------------------------------------- /generators/containers/compile.go: -------------------------------------------------------------------------------- 1 | package containers 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/golang-docker-client" 7 | 8 | "github.com/nanobox-io/nanobox/models" 9 | "github.com/nanobox-io/nanobox/util/config" 10 | "github.com/nanobox-io/nanobox/util/provider" 11 | ) 12 | 13 | // CompileConfig generate the container configuration for the build container 14 | func CompileConfig(image string) docker.ContainerConfig { 15 | env := config.EnvID() 16 | code := fmt.Sprintf("%s%s/code:/share/code", provider.HostShareDir(), env) 17 | engine := fmt.Sprintf("%s%s/engine:/share/engine", provider.HostShareDir(), env) 18 | 19 | if !provider.RequiresMount() { 20 | code = fmt.Sprintf("%s:/share/code", config.LocalDir()) 21 | engineDir, _ := config.EngineDir() 22 | if engineDir != "" { 23 | engine = fmt.Sprintf("%s:/share/engine", engineDir) 24 | } 25 | } 26 | 27 | conf := docker.ContainerConfig{ 28 | Name: CompileName(), 29 | Image: image, 30 | Network: "host", 31 | Binds: []string{ 32 | code, 33 | engine, 34 | // fmt.Sprintf("%s%s/build:/data", provider.HostMntDir(), env), 35 | // fmt.Sprintf("%s%s/app:/mnt/app", provider.HostMntDir(), env), 36 | // fmt.Sprintf("%s%s/cache:/mnt/cache", provider.HostMntDir(), env), 37 | fmt.Sprintf("nanobox_%s_build:/data", env), 38 | fmt.Sprintf("nanobox_%s_app:/mnt/app", env), 39 | fmt.Sprintf("nanobox_%s_cache:/mnt/cache", env), 40 | }, 41 | RestartPolicy: "no", 42 | } 43 | 44 | // Some CI's have an old kernel and require us to use the virtual network 45 | // this is only in effect for CI's because it automatically reserves an ip on our nanobox 46 | // virtual network and we could have IP conflicts 47 | configModel, _ := models.LoadConfig() 48 | if configModel.CIMode { 49 | conf.Network = "virt" 50 | } 51 | 52 | // set http[s]_proxy and no_proxy vars 53 | setProxyVars(&conf) 54 | 55 | return conf 56 | } 57 | 58 | // CompileName returns the name of the build container 59 | func CompileName() string { 60 | return fmt.Sprintf("nanobox_%s_compile", config.EnvID()) 61 | } 62 | -------------------------------------------------------------------------------- /generators/containers/component.go: -------------------------------------------------------------------------------- 1 | package containers 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/golang-docker-client" 7 | 8 | "github.com/nanobox-io/nanobox/models" 9 | ) 10 | 11 | // ComponentConfig generates the container configuration for a component container 12 | func ComponentConfig(componentModel *models.Component) docker.ContainerConfig { 13 | config := docker.ContainerConfig{ 14 | Name: ComponentName(componentModel), 15 | Image: componentModel.Image, 16 | Network: "virt", 17 | IP: componentModel.IPAddr(), 18 | RestartPolicy: "no", 19 | } 20 | 21 | // set http[s]_proxy and no_proxy vars 22 | setProxyVars(&config) 23 | 24 | return config 25 | } 26 | 27 | // ComponentName returns the name of the component container 28 | func ComponentName(componentModel *models.Component) string { 29 | return fmt.Sprintf("nanobox_%s_%s", componentModel.AppID, componentModel.Name) 30 | } 31 | -------------------------------------------------------------------------------- /generators/containers/containers_internal_test.go: -------------------------------------------------------------------------------- 1 | package containers 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/nanobox-io/golang-docker-client" 7 | ) 8 | 9 | func TestSetProxyVars(t *testing.T) { 10 | config := docker.ContainerConfig{ 11 | Name: "test-container", 12 | } 13 | 14 | config.Env = []string{"thing=thang"} 15 | 16 | setProxyVars(&config) 17 | 18 | if config.Env[0] != "thing=thang" { 19 | t.Errorf("Failed to preserve prior envs!") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /generators/containers/containers_test.go: -------------------------------------------------------------------------------- 1 | package containers_test 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | "github.com/nanobox-io/nanobox/generators/containers" 8 | "github.com/nanobox-io/nanobox/models" 9 | "github.com/nanobox-io/nanobox/util/dhcp" 10 | ) 11 | 12 | func TestBuildConfig(t *testing.T) { 13 | result := containers.BuildConfig("imagename") 14 | if result.Image != "imagename" || 15 | result.Name != containers.BuildName() { 16 | // TODO: add checks for the binds 17 | t.Errorf("bad credentials") 18 | } 19 | } 20 | 21 | func TestCompileConfig(t *testing.T) { 22 | result := containers.CompileConfig("imagename") 23 | if result.Image != "imagename" || 24 | result.Name != containers.CompileName() { 25 | // TODO: add checks for the binds 26 | t.Errorf("bad results") 27 | } 28 | } 29 | 30 | func TestComponentConfig(t *testing.T) { 31 | componentModel := &models.Component{ 32 | Image: "imagename", 33 | InternalIP: "1.2.3.4", 34 | AppID: "2", 35 | Name: "name", 36 | } 37 | 38 | result := containers.ComponentConfig(componentModel) 39 | if result.Image != "imagename" || 40 | result.IP != "1.2.3.4" || 41 | result.Name != "nanobox_2_name" { 42 | t.Errorf("bad results") 43 | } 44 | } 45 | 46 | func TestPublishConfig(t *testing.T) { 47 | result := containers.PublishConfig("imagename") 48 | if result.Image != "imagename" || 49 | result.Name != containers.PublishName() { 50 | // TODO: add checks for the binds 51 | t.Errorf("bad results") 52 | } 53 | } 54 | 55 | func TestDevConfig(t *testing.T) { 56 | appModel := &models.App{EnvID: "1", ID: "2"} 57 | result := containers.DevConfig(appModel) 58 | if result.Image != "nanobox/build" || 59 | result.Name != "nanobox_2" { 60 | // TODO: add checks for the binds 61 | // TODO: add lib dir check 62 | t.Errorf("bad results") 63 | } 64 | dhcp.ReturnIP(net.ParseIP(result.IP)) 65 | } 66 | -------------------------------------------------------------------------------- /generators/containers/environment.go: -------------------------------------------------------------------------------- 1 | package containers 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/nanobox-io/golang-docker-client" 8 | ) 9 | 10 | func setProxyVars(config *docker.ContainerConfig) { 11 | // set the proxy variables 12 | httpProxyEvar := os.Getenv("HTTP_PROXY") 13 | if httpProxyEvar != "" { 14 | config.Env = append(config.Env, fmt.Sprintf("HTTP_PROXY=%s", httpProxyEvar)) 15 | } 16 | httpsProxyEvar := os.Getenv("HTTPS_PROXY") 17 | if httpsProxyEvar != "" { 18 | config.Env = append(config.Env, fmt.Sprintf("HTTPS_PROXY=%s", httpsProxyEvar)) 19 | } 20 | noProxyEvar := os.Getenv("NO_PROXY") 21 | if noProxyEvar != "" { 22 | config.Env = append(config.Env, fmt.Sprintf("NO_PROXY=%s", noProxyEvar)) 23 | } 24 | httpProxyEvar2 := os.Getenv("http_proxy") 25 | if httpProxyEvar2 != "" { 26 | config.Env = append(config.Env, fmt.Sprintf("http_proxy=%s", httpProxyEvar2)) 27 | } 28 | httpsProxyEvar2 := os.Getenv("https_proxy") 29 | if httpsProxyEvar2 != "" { 30 | config.Env = append(config.Env, fmt.Sprintf("https_proxy=%s", httpsProxyEvar2)) 31 | } 32 | noProxyEvar2 := os.Getenv("no_proxy") 33 | if noProxyEvar2 != "" { 34 | config.Env = append(config.Env, fmt.Sprintf("no_proxy=%s", noProxyEvar2)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /generators/containers/publish.go: -------------------------------------------------------------------------------- 1 | package containers 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/golang-docker-client" 7 | 8 | "github.com/nanobox-io/nanobox/models" 9 | "github.com/nanobox-io/nanobox/util/config" 10 | // "github.com/nanobox-io/nanobox/util/provider" 11 | ) 12 | 13 | // PublishConfig generate the container configuration for the build container 14 | func PublishConfig(image string) docker.ContainerConfig { 15 | env := config.EnvID() 16 | config := docker.ContainerConfig{ 17 | Name: PublishName(), 18 | Image: image, 19 | Network: "host", 20 | Binds: []string{ 21 | // fmt.Sprintf("%s%s/app:/mnt/app", provider.HostMntDir(), env), 22 | // fmt.Sprintf("%s%s/cache:/mnt/cache", provider.HostMntDir(), env), 23 | // fmt.Sprintf("%s%s/deploy:/mnt/deploy", provider.HostMntDir(), env), 24 | fmt.Sprintf("nanobox_%s_app:/mnt/app", env), 25 | fmt.Sprintf("nanobox_%s_cache:/mnt/cache", env), 26 | fmt.Sprintf("nanobox_%s_deploy:/mnt/deploy", env), 27 | }, 28 | RestartPolicy: "no", 29 | } 30 | 31 | // Some CI's have an old kernel and require us to use the virtual network 32 | // this is only in effect for CI's because it automatically reserves an ip on our nanobox 33 | // virtual network and we could have IP conflicts 34 | configModel, _ := models.LoadConfig() 35 | if configModel.CIMode { 36 | config.Network = "virt" 37 | } 38 | 39 | // set http[s]_proxy and no_proxy vars 40 | setProxyVars(&config) 41 | 42 | return config 43 | } 44 | 45 | // PublishName returns the name of the build container 46 | func PublishName() string { 47 | return fmt.Sprintf("nanobox_%s_publish", config.EnvID()) 48 | } 49 | -------------------------------------------------------------------------------- /generators/hooks/build/boxfile.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | // BoxfilePayload returns a string for the user hook payload 4 | func BoxfilePayload() string { 5 | // currently, this payload is empty. This may change at some point 6 | return emptyPayload() 7 | } 8 | -------------------------------------------------------------------------------- /generators/hooks/build/build.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | // BuildPayload returns a string for the build hook payload 4 | func BuildPayload() string { 5 | // currently, this payload is empty. This may change at some point 6 | return emptyPayload() 7 | } 8 | -------------------------------------------------------------------------------- /generators/hooks/build/clean.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | // CleanPayload returns a string for the user hook payload 4 | func CleanPayload() string { 5 | // currently, this payload is empty. This may change at some point 6 | return emptyPayload() 7 | } 8 | -------------------------------------------------------------------------------- /generators/hooks/build/compile.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | // CompilePayload returns a string for the user hook payload 4 | func CompilePayload() string { 5 | // currently, this payload is empty. This may change at some point 6 | return emptyPayload() 7 | } 8 | -------------------------------------------------------------------------------- /generators/hooks/build/configure.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | // ConfigurePayload returns a string for the user hook payload 4 | func ConfigurePayload() string { 5 | // currently, this payload is empty. This may change at some point 6 | return emptyPayload() 7 | } 8 | -------------------------------------------------------------------------------- /generators/hooks/build/dev.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | "github.com/nanobox-io/nanobox/util/dns" 8 | ) 9 | 10 | func DevPayload(appModel *models.App) string { 11 | // create an APP_IP evar 12 | evars := appModel.Evars 13 | evars["APP_IP"] = appModel.LocalIPs["env"] 14 | 15 | rtn := map[string]interface{}{} 16 | rtn["env"] = evars 17 | rtn["boxfile"] = appModel.DeployedBoxfile 18 | rtn["dns_entries"] = dns.List(" by nanobox") 19 | bytes, _ := json.Marshal(rtn) 20 | return string(bytes) 21 | } 22 | -------------------------------------------------------------------------------- /generators/hooks/build/empty.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | // an empty payload function 4 | // this is here so if we redefine 'empty' 5 | // then we can modify it in one place 6 | func emptyPayload() string { 7 | return "{}" 8 | } 9 | -------------------------------------------------------------------------------- /generators/hooks/build/fetch.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | // FetchPayload returns a string for the user hook payload 4 | func FetchPayload() string { 5 | // currently, this payload is empty. This may change at some point 6 | return emptyPayload() 7 | } 8 | -------------------------------------------------------------------------------- /generators/hooks/build/mount.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | func MountPayload() string { 4 | // currently, this payload is empty. This may change at some point 5 | return emptyPayload() 6 | } 7 | -------------------------------------------------------------------------------- /generators/hooks/build/pack_app.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | // PackAppPayload returns a string for the user hook payload 4 | func PackAppPayload() string { 5 | // currently, this payload is empty. This may change at some point 6 | return emptyPayload() 7 | } 8 | -------------------------------------------------------------------------------- /generators/hooks/build/pack_build.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // PackBuildPayload returns a string for the user hook payload 8 | func PackBuildPayload() string { 9 | if ClearPkgCache { 10 | rtn := map[string]string{} 11 | rtn["clear_cache"] = "true" 12 | bytes, _ := json.Marshal(rtn) 13 | return string(bytes) 14 | } 15 | 16 | // currently, this payload is empty. This may change at some point 17 | return emptyPayload() 18 | } 19 | -------------------------------------------------------------------------------- /generators/hooks/build/pack_deploy.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | // PackDeployPayload returns a string for the user hook payload 4 | func PackDeployPayload() string { 5 | // currently, this payload is empty. This may change at some point 6 | return emptyPayload() 7 | } 8 | -------------------------------------------------------------------------------- /generators/hooks/build/publish.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | ) 8 | 9 | type WarehouseConfig struct { 10 | BuildID string 11 | WarehouseURL string 12 | WarehouseToken string 13 | PreviousBuild string 14 | } 15 | 16 | func PublishPayload(envModel *models.Env, warehouseConfig WarehouseConfig) string { 17 | config, _ := models.LoadConfig() 18 | 19 | pload := map[string]interface{}{ 20 | "build": warehouseConfig.BuildID, 21 | "warehouse": warehouseConfig.WarehouseURL, 22 | "warehouse_token": warehouseConfig.WarehouseToken, 23 | "boxfile": envModel.BuiltBoxfile, 24 | "sync_verbose": !(config.CIMode && !config.CISyncVerbose), 25 | } 26 | 27 | if warehouseConfig.PreviousBuild != "" { 28 | pload["previous_build"] = warehouseConfig.PreviousBuild 29 | } 30 | 31 | b, _ := json.Marshal(pload) 32 | 33 | return string(b) 34 | } 35 | -------------------------------------------------------------------------------- /generators/hooks/build/setup.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | var ClearPkgCache bool 8 | 9 | // SetupPayload returns a string for the user hook payload 10 | func SetupPayload() string { 11 | if ClearPkgCache { 12 | rtn := map[string]string{} 13 | rtn["clear_cache"] = "true" 14 | bytes, _ := json.Marshal(rtn) 15 | return string(bytes) 16 | } 17 | // currently, this payload is empty. This may change at some point 18 | return emptyPayload() 19 | } 20 | -------------------------------------------------------------------------------- /generators/hooks/code/deploy.go: -------------------------------------------------------------------------------- 1 | package code 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/nanobox-io/nanobox-boxfile" 7 | "github.com/nanobox-io/nanobox/models" 8 | ) 9 | 10 | type ( 11 | deploy struct { 12 | LogvacHost string `json:"logvac_host"` 13 | Platform string `json:"platform"` 14 | Member map[string]int `json:"member"` 15 | Component component `json:"component"` 16 | BeforeLive interface{} `json:"before_live,omitempty"` 17 | BeforeLiveAll interface{} `json:"before_live_all,omitempty"` 18 | AfterLive interface{} `json:"after_live,omitempty"` 19 | AfterLiveAll interface{} `json:"after_live_all,omitempty"` 20 | DeployHookTimeout interface{} `json:"deploy_hook_timeout,omitempty"` 21 | } 22 | ) 23 | 24 | // hookPayload ... 25 | func DeployPayload(appModel *models.App, componentModel *models.Component) string { 26 | boxfile := boxfile.New([]byte(appModel.DeployedBoxfile)) 27 | // build the payload 28 | pload := deploy{ 29 | LogvacHost: appModel.LocalIPs["logvac"], 30 | Platform: "local", 31 | Member: map[string]int{"uid": 1}, 32 | Component: component{ 33 | Name: componentModel.Name, 34 | UID: componentModel.Name, 35 | ID: componentModel.ID, 36 | }, 37 | BeforeLive: boxfile.Node("deploy.config").Node("before_live").Value(componentModel.Name), 38 | BeforeLiveAll: boxfile.Node("deploy.config").Node("before_live_all").Value(componentModel.Name), 39 | AfterLive: boxfile.Node("deploy.config").Node("after_live").Value(componentModel.Name), 40 | AfterLiveAll: boxfile.Node("deploy.config").Node("after_live_all").Value(componentModel.Name), 41 | DeployHookTimeout: boxfile.Node("deploy.config").Value("deploy_hook_timeout"), 42 | } 43 | 44 | // turn it into json 45 | j, err := json.Marshal(pload) 46 | if err != nil { 47 | return "{}" 48 | } 49 | 50 | return string(j) 51 | } 52 | -------------------------------------------------------------------------------- /generators/hooks/code/fetch.go: -------------------------------------------------------------------------------- 1 | package code 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | ) 8 | 9 | type ( 10 | fetch struct { 11 | Component component `json:"component"` 12 | LogvacHost string `json:"logvac_host"` 13 | Member map[string]int `json:"member"` 14 | Build string `json:"build"` 15 | Warehouse string `json:"warehouse"` 16 | WarehouseToken string `json:"warehouse_token"` 17 | } 18 | ) 19 | 20 | // Fetch payload 21 | func FetchPayload(componentModel *models.Component, warehouse string) string { 22 | 23 | logvac, _ := models.FindComponentBySlug(componentModel.AppID, "logvac") 24 | 25 | pload := fetch{ 26 | LogvacHost: logvac.IPAddr(), 27 | Component: component{ 28 | Name: componentModel.Name, 29 | UID: componentModel.Name, 30 | ID: componentModel.ID, 31 | }, 32 | Member: map[string]int{"uid": 1}, 33 | Build: "1234", 34 | Warehouse: warehouse, 35 | WarehouseToken: "123", 36 | } 37 | 38 | bytes, err := json.Marshal(pload) 39 | if err != nil { 40 | return "{}" 41 | } 42 | 43 | return string(bytes) 44 | } 45 | -------------------------------------------------------------------------------- /generators/hooks/component/component.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/nanobox-boxfile" 7 | 8 | "github.com/nanobox-io/nanobox/models" 9 | ) 10 | 11 | // componentConfig returns the config data from the component boxfile 12 | func componentConfig(component *models.Component) (config map[string]interface{}, err error) { 13 | 14 | // fetch the env 15 | env, err := models.FindEnvByID(component.EnvID) 16 | if err != nil { 17 | err = fmt.Errorf("failed to load env model: %s", err.Error()) 18 | return 19 | } 20 | 21 | box := boxfile.New([]byte(env.BuiltBoxfile)) 22 | config = box.Node(component.Name).Node("config").Parsed 23 | 24 | switch component.Name { 25 | case "portal", "logvac", "hoarder", "mist": 26 | config["token"] = "123" 27 | } 28 | return 29 | } 30 | -------------------------------------------------------------------------------- /generators/hooks/component/configure.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | ) 8 | 9 | // member ... 10 | type member struct { 11 | LocalIP string `json:"local_ip"` 12 | UID int `json:"uid"` 13 | Role string `json:"role"` 14 | } 15 | 16 | // component ... 17 | type component struct { 18 | Name string `json:"name"` 19 | UID string `json:"uid"` 20 | ID string `json:"id"` 21 | } 22 | 23 | // configPayload ... 24 | type configPayload struct { 25 | LogvacHost string `json:"logvac_host"` 26 | MistHost string `json:"mist_host"` 27 | MistToken string `json:"mist_token"` 28 | Platform string `json:"platform"` 29 | Config map[string]interface{} `json:"config"` 30 | Member member `json:"member"` 31 | Component component `json:"component"` 32 | Users []models.ComponentPlanUser `json:"users"` 33 | } 34 | 35 | // ConfigurePayload returns a string for the configure hook payload 36 | func ConfigurePayload(appModel *models.App, componentModel *models.Component) string { 37 | config, err := componentConfig(componentModel) 38 | if err != nil { 39 | // lumber.Error("unable to fetch component config: %s", err.Error()) 40 | return "{}" 41 | } 42 | 43 | payload := configPayload{ 44 | LogvacHost: appModel.LocalIPs["logvac"], 45 | MistHost: appModel.LocalIPs["mist"], 46 | MistToken: "123", 47 | Platform: "local", 48 | Config: config, 49 | Member: member{ 50 | LocalIP: componentModel.IPAddr(), 51 | UID: 1, 52 | Role: "primary", 53 | }, 54 | Component: component{ 55 | Name: componentModel.Name, 56 | UID: componentModel.Name, 57 | ID: componentModel.ID, 58 | }, 59 | Users: componentModel.Plan.Users, 60 | } 61 | 62 | j, err := json.Marshal(payload) 63 | if err != nil { 64 | return "{}" 65 | } 66 | 67 | return string(j) 68 | } 69 | -------------------------------------------------------------------------------- /generators/hooks/component/plan.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | ) 8 | 9 | // PlanPayload returns a string for the user hook payload 10 | func PlanPayload(component *models.Component) string { 11 | config, err := componentConfig(component) 12 | if err != nil { 13 | return "{}" 14 | } 15 | 16 | payload := map[string]interface{}{ 17 | "config": config, 18 | } 19 | 20 | // marshal the payload into json 21 | b, err := json.Marshal(payload) 22 | if err != nil { 23 | return "{}" 24 | } 25 | 26 | return string(b) 27 | } 28 | -------------------------------------------------------------------------------- /generators/hooks/component/start.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | ) 8 | 9 | type startPayload struct { 10 | Config map[string]interface{} `json:"config"` 11 | } 12 | 13 | // StartPayload returns a string for the start hook payload 14 | func StartPayload(c *models.Component) string { 15 | config, err := componentConfig(c) 16 | if err != nil { 17 | return "{}" 18 | } 19 | 20 | payload := startPayload{ 21 | Config: config, 22 | } 23 | 24 | switch c.Name { 25 | case "portal", "logvac", "hoarder", "mist": 26 | payload.Config["token"] = "123" 27 | } 28 | 29 | j, err := json.Marshal(payload) 30 | if err != nil { 31 | return "{}" 32 | } 33 | 34 | return string(j) 35 | } 36 | -------------------------------------------------------------------------------- /generators/hooks/component/update.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | ) 8 | 9 | type updatePayload struct { 10 | Config map[string]interface{} `json:"config"` 11 | } 12 | 13 | // UpdatePayload returns a string for the update hook payload 14 | func UpdatePayload(c *models.Component) string { 15 | config, err := componentConfig(c) 16 | if err != nil { 17 | // log the failure 18 | return "{}" 19 | } 20 | 21 | payload := updatePayload{ 22 | Config: config, 23 | } 24 | 25 | switch c.Name { 26 | case "portal", "logvac", "hoarder", "mist": 27 | payload.Config["token"] = "123" 28 | } 29 | 30 | j, err := json.Marshal(payload) 31 | if err != nil { 32 | // log the failure 33 | return "{}" 34 | } 35 | 36 | return string(j) 37 | } 38 | -------------------------------------------------------------------------------- /generators/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/nanobox-io/nanobox-boxfile" 5 | "github.com/nanobox-io/nanobox/models" 6 | ) 7 | 8 | // 9 | func loadBoxfile(appModel *models.App) boxfile.Boxfile { 10 | return boxfile.New([]byte(appModel.DeployedBoxfile)) 11 | } 12 | -------------------------------------------------------------------------------- /helpers/endpoint.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import "github.com/nanobox-io/nanobox/models" 4 | 5 | func Endpoint(envModel *models.Env, args []string, maxArgs int) ([]string, string, string) { 6 | if len(args) == 0 { 7 | return args, "production", "default" 8 | } 9 | 10 | switch args[0] { 11 | case "local": 12 | return args[1:], "local", "dev" 13 | case "dry-run": 14 | return args[1:], "local", "sim" 15 | default: 16 | _, ok := envModel.Remotes[args[0]] 17 | if ok { 18 | return args[1:], "production", args[0] 19 | } 20 | } 21 | 22 | // if we were given the maximum number of arguments then the first artument must be a production 23 | // application name that was not in our remotes 24 | if maxArgs == len(args) { 25 | return args[1:], "production", args[0] 26 | } 27 | 28 | // todo: MAYBE check the remote here (`fetch the remote` in other locations in code) 29 | // // fetch the remote 30 | // remote, ok := envModel.Remotes[args[0]] 31 | // if ok { 32 | // // set the app id 33 | // return args[1:], "production", remote.ID 34 | // } 35 | 36 | return args, "production", "default" 37 | } 38 | -------------------------------------------------------------------------------- /helpers/odin.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/jcelliott/lumber" 8 | 9 | "github.com/nanobox-io/nanobox/util" 10 | "github.com/nanobox-io/nanobox/util/odin" 11 | ) 12 | 13 | // Validates an app exists and is accessible on odin 14 | func ValidateOdinApp(slug string) error { 15 | // fetch the app 16 | _, err := odin.App(slug) 17 | 18 | // handle errors 19 | if err != nil { 20 | 21 | lumber.Error("helpers: ValidateOdinApp(%s): %s", slug, err) 22 | 23 | if strings.Contains(err.Error(), "Unauthorized") { 24 | fmt.Printf("\n! Sorry, but you don't have access to %s\n\n", slug) 25 | return util.ErrorAppend(err, "Unauthorized access to app '%s'", slug) 26 | } 27 | 28 | if strings.Contains(err.Error(), "Not Found") { 29 | fmt.Printf("\n! Sorry, the app '%s' doesn't exist\n\n", slug) 30 | return util.ErrorAppend(err, "Unknown app '%s'", slug) 31 | } 32 | 33 | // All other scenarios 34 | fmt.Printf("\n%s\n\n", err.Error()) 35 | return util.ErrorAppend(err, "Failed to communicate with nanobox") 36 | } 37 | 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | Nanobox 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /models/auth.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Auth ... 8 | type Auth struct { 9 | Endpoint string // nanobox, bonesalt, dev, sim 10 | Key string // api_token from dashboard 11 | } 12 | 13 | // determines if the auth record is new 14 | func (a *Auth) IsNew() bool { 15 | return a.Key == "" 16 | } 17 | 18 | // Save persists the Auth to the database 19 | func (a *Auth) Save() error { 20 | 21 | // Since there is only ever a single auth value, we'll use the registry 22 | if err := put("auths", a.Endpoint, a); err != nil { 23 | return fmt.Errorf("failed to save auth: %s", err.Error()) 24 | } 25 | 26 | return nil 27 | } 28 | 29 | // Delete deletes the auth record from the database 30 | func (a *Auth) Delete() error { 31 | 32 | return DeleteAuth(a.Endpoint) 33 | } 34 | 35 | // LoadAuth loads the default (nanobox) auth entry 36 | func LoadAuth() (*Auth, error) { 37 | auth := &Auth{ 38 | Endpoint: "nanobox", 39 | } 40 | 41 | if err := get("auths", auth.Endpoint, &auth); err != nil { 42 | return auth, fmt.Errorf("failed to load auth: %s", err.Error()) 43 | } 44 | 45 | return auth, nil 46 | } 47 | 48 | // loads an auth by a specific endpoint 49 | func LoadAuthByEndpoint(endpoint string) (*Auth, error) { 50 | auth := &Auth{ 51 | Endpoint: endpoint, 52 | } 53 | 54 | if err := get("auths", endpoint, &auth); err != nil { 55 | return auth, fmt.Errorf("failed to load auth: %s", err.Error()) 56 | } 57 | 58 | return auth, nil 59 | } 60 | 61 | // DeleteAuth ... 62 | func DeleteAuth(endpoint string) error { 63 | 64 | if err := destroy("auths", endpoint); err != nil { 65 | return fmt.Errorf("failed to delete auth: %s", err.Error()) 66 | } 67 | 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /models/auth_test.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // import ( 4 | // "testing" 5 | // ) 6 | 7 | // func TestAuthSave(t *testing.T) { 8 | // // clear the registry table when we're finished 9 | // defer truncate("registry") 10 | 11 | // auth := Auth{ 12 | // Key: "123", 13 | // } 14 | 15 | // err := auth.Save() 16 | // if err != nil { 17 | // t.Error(err) 18 | // } 19 | 20 | // // fetch the auth 21 | // auth2 := Auth{} 22 | 23 | // if err = get("registry", "auth", &auth2); err != nil { 24 | // t.Errorf("failed to fetch auth: %s", err.Error()) 25 | // } 26 | 27 | // if auth2.Key != "123" { 28 | // t.Errorf("auth doesn't match") 29 | // } 30 | // } 31 | 32 | // func TestAuthDelete(t *testing.T) { 33 | // // clear the registry table when we're finished 34 | // defer truncate("registry") 35 | 36 | // auth := Auth{ 37 | // Key: "123", 38 | // } 39 | 40 | // if err := auth.Save(); err != nil { 41 | // t.Error(err) 42 | // } 43 | 44 | // if err := auth.Delete(); err != nil { 45 | // t.Error(err) 46 | // } 47 | 48 | // // make sure the auth is gone 49 | // keys, err := keys("registry") 50 | // if err != nil { 51 | // t.Error(err) 52 | // } 53 | 54 | // if len(keys) > 0 { 55 | // t.Errorf("auth was not deleted") 56 | // } 57 | // } 58 | 59 | // func TestLoadAuth(t *testing.T) { 60 | // // clear the registry table when we're finished 61 | // defer truncate("registry") 62 | 63 | // auth := Auth{ 64 | // Key: "123", 65 | // } 66 | 67 | // if err := auth.Save(); err != nil { 68 | // t.Error(err) 69 | // } 70 | 71 | // auth2, err := LoadAuth() 72 | // if err != nil { 73 | // t.Error(err) 74 | // } 75 | 76 | // if auth2.Key != "123" { 77 | // t.Errorf("did not load the correct auth") 78 | // } 79 | // } 80 | -------------------------------------------------------------------------------- /models/component_plan.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // ComponentPlan ... 4 | type ComponentPlan struct { 5 | IPs []string `json:"ips"` 6 | Users []ComponentPlanUser `json:"users"` 7 | MountProtocol string `json:"mount_protocol"` 8 | Behaviors []string `json:"behaviors"` 9 | DefaultUser string `json:"user"` 10 | } 11 | 12 | // ComponentPlanUser ... 13 | type ComponentPlanUser struct { 14 | Username string `json:"username"` 15 | Password string `json:"password"` 16 | Meta map[string]interface{} `json:"meta"` 17 | } 18 | 19 | // BehaviorPresent ... 20 | func (p ComponentPlan) BehaviorPresent(b string) bool { 21 | for _, behavior := range p.Behaviors { 22 | if behavior == b { 23 | return true 24 | } 25 | } 26 | 27 | return false 28 | } 29 | -------------------------------------------------------------------------------- /models/component_plan_test.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestComponentPlanBehaviors(t *testing.T) { 8 | p1 := ComponentPlan{ 9 | Behaviors: []string{"backupable", "migratable"}, 10 | } 11 | 12 | p2 := ComponentPlan{} 13 | 14 | if !p1.BehaviorPresent("backupable") { 15 | t.Errorf("behavior doesn't exist and should") 16 | } 17 | 18 | if p2.BehaviorPresent("backupable") { 19 | t.Errorf("behavior exists and should not") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /models/ips.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | // IPs ... 9 | type IPs []net.IP 10 | 11 | // Save persists the IPs to the database 12 | func (ips *IPs) Save() error { 13 | 14 | // Since there is only ever ips single auth value, we'll use the registry 15 | if err := put("registry", "ips", ips); err != nil { 16 | return fmt.Errorf("failed to save auth: %s", err.Error()) 17 | } 18 | 19 | return nil 20 | } 21 | 22 | // Delete deletes the auth record from the database 23 | func (ips *IPs) Delete() error { 24 | 25 | // Since there is only ever a single auth value, we'll use the registry 26 | if err := destroy("registry", "ips"); err != nil { 27 | return fmt.Errorf("failed to delete auth: %s", err.Error()) 28 | } 29 | 30 | return nil 31 | } 32 | 33 | // LoadIPs loads the auth entry 34 | func LoadIPs() (IPs, error) { 35 | ips := IPs{} 36 | 37 | if err := get("registry", "ips", &ips); err != nil { 38 | return ips, fmt.Errorf("failed to load ips: %s", err.Error()) 39 | } 40 | 41 | return ips, nil 42 | } 43 | -------------------------------------------------------------------------------- /models/ips_test.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | ) 7 | 8 | func TestIPsSave(t *testing.T) { 9 | // clear the registry table when we're finished 10 | defer truncate("registry") 11 | 12 | ips := IPs{net.ParseIP("1.2.3.4")} 13 | 14 | err := ips.Save() 15 | if err != nil { 16 | t.Error(err) 17 | } 18 | 19 | // fetch the ips 20 | ips2 := IPs{} 21 | 22 | if err = get("registry", "ips", &ips2); err != nil { 23 | t.Errorf("failed to fetch ips: %s", err.Error()) 24 | } 25 | 26 | if len(ips) != 1 { 27 | t.Errorf("ips doesn't match") 28 | } 29 | } 30 | 31 | func TestIPsDelete(t *testing.T) { 32 | // clear the registry table when we're finished 33 | defer truncate("registry") 34 | 35 | ips := IPs{net.ParseIP("1.2.3.4")} 36 | 37 | if err := ips.Save(); err != nil { 38 | t.Error(err) 39 | } 40 | 41 | if err := ips.Delete(); err != nil { 42 | t.Error(err) 43 | } 44 | 45 | // make sure the auth is gone 46 | keys, err := keys("registry") 47 | if err != nil { 48 | t.Error(err) 49 | } 50 | 51 | if len(keys) > 0 { 52 | t.Errorf("auth was not deleted") 53 | } 54 | } 55 | 56 | func TestLoadIPs(t *testing.T) { 57 | // clear the registry table when we're finished 58 | defer truncate("registry") 59 | 60 | ips := IPs{net.ParseIP("1.2.3.4")} 61 | 62 | if err := ips.Save(); err != nil { 63 | t.Error(err) 64 | } 65 | 66 | ips2, err := LoadIPs() 67 | if err != nil { 68 | t.Error(err) 69 | } 70 | 71 | if len(ips2) != 1 { 72 | t.Errorf("did not load the correct ips") 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /models/log.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // LogOpts are options for logging 4 | type LogOpts struct { 5 | Follow bool // Follow is whether or not to follow the log stream. 6 | Number int // Number of logs to print. 7 | Raw bool // Raw will not strip out the timestamp from the log stream. 8 | Start string // Start is where to start the logs from. 9 | End string // End is where to end the logs. 10 | Limit string // Limit is how many logs to show. 11 | } 12 | -------------------------------------------------------------------------------- /models/models.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Inspect ... 4 | func Inspect(bucket, key string) interface{} { 5 | if key == "" { 6 | v := []interface{}{} 7 | getAll(bucket, &v) 8 | return v 9 | } 10 | var v interface{} 11 | get(bucket, key, &v) 12 | return v 13 | } 14 | -------------------------------------------------------------------------------- /models/provider.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Provider ... 8 | type Provider struct { 9 | Name string // name of the provider 10 | HostIP string // the ip the host knows about 11 | MountIP string // the ip we reserved for mounting 12 | } 13 | 14 | // Save persists the Provider to the database 15 | func (a *Provider) Save() error { 16 | 17 | // Since there is only ever a single provider value, we'll use the registry 18 | if err := put("registry", "provider", a); err != nil { 19 | return fmt.Errorf("failed to save provider: %s", err.Error()) 20 | } 21 | 22 | return nil 23 | } 24 | 25 | // Delete deletes the provider record from the database 26 | func (a *Provider) Delete() error { 27 | 28 | // Since there is only ever a single provider value, we'll use the registry 29 | if err := destroy("registry", "provider"); err != nil { 30 | return fmt.Errorf("failed to delete provider: %s", err.Error()) 31 | } 32 | 33 | // clear the current entry 34 | a = nil 35 | 36 | return nil 37 | } 38 | 39 | // LoadProvider loads the provider entry 40 | func LoadProvider() (*Provider, error) { 41 | provider := &Provider{} 42 | 43 | if err := get("registry", "provider", &provider); err != nil { 44 | return provider, fmt.Errorf("failed to load provider: %s", err.Error()) 45 | } 46 | 47 | return provider, nil 48 | } 49 | -------------------------------------------------------------------------------- /models/provider_test.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestProviderSave(t *testing.T) { 8 | // clear the registry table when we're finished 9 | defer truncate("registry") 10 | 11 | provider := Provider{ 12 | HostIP: "192.168.1.2", 13 | } 14 | 15 | err := provider.Save() 16 | if err != nil { 17 | t.Error(err) 18 | } 19 | 20 | // fetch the provider 21 | provider2 := Provider{} 22 | 23 | if err = get("registry", "provider", &provider2); err != nil { 24 | t.Errorf("failed to fetch provider: %s", err.Error()) 25 | } 26 | 27 | if provider2.HostIP != "192.168.1.2" { 28 | t.Errorf("provider doesn't match") 29 | } 30 | } 31 | 32 | func TestProviderDelete(t *testing.T) { 33 | // clear the registry table when we're finished 34 | defer truncate("registry") 35 | 36 | provider := Provider{ 37 | HostIP: "192.168.1.2", 38 | } 39 | 40 | if err := provider.Save(); err != nil { 41 | t.Error(err) 42 | } 43 | 44 | if err := provider.Delete(); err != nil { 45 | t.Error(err) 46 | } 47 | 48 | // make sure the provider is gone 49 | keys, err := keys("registry") 50 | if err != nil { 51 | t.Error(err) 52 | } 53 | 54 | if len(keys) > 0 { 55 | t.Errorf("provider was not deleted") 56 | } 57 | } 58 | 59 | func TestLoadProvider(t *testing.T) { 60 | // clear the registry table when we're finished 61 | defer truncate("registry") 62 | 63 | provider := Provider{ 64 | HostIP: "192.168.1.2", 65 | } 66 | 67 | if err := provider.Save(); err != nil { 68 | t.Error(err) 69 | } 70 | 71 | provider2, err := LoadProvider() 72 | if err != nil { 73 | t.Error(err) 74 | } 75 | 76 | if provider2.HostIP != "192.168.1.2" { 77 | t.Errorf("did not load the correct provider") 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /models/tunnel.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type ( 4 | // TunnelConfig contains the endpoint information. 5 | TunnelConfig struct { 6 | AppName string // name of app to tunnel to 7 | ListenPort int // local port to listen on 8 | DestPort int // port to tunnel to 9 | Component string // component to tunnel to 10 | } 11 | 12 | TunnelInfo struct { 13 | Name string `json:"name,omitempty"` // component name being tunneled to 14 | Token string `json:"token,omitempty"` // token to complete the tunnel 15 | URL string `json:"url,omitempty"` // url/ip of nanoagent 16 | Port int `json:"port,omitempty"` // port to tunnel to. *must* omitempty. odin will use whatever port is passed (if 0, don't send) 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /models/update.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // Update ... 9 | type Update struct { 10 | LastCheckAt time.Time 11 | LastUpdatedAt time.Time 12 | } 13 | 14 | // LoadUpdate loads the update entry 15 | func LoadUpdate() (*Update, error) { 16 | update := &Update{} 17 | 18 | if err := get("registry", "update", &update); err != nil { 19 | return update, fmt.Errorf("failed to load update: %s", err.Error()) 20 | } 21 | 22 | return update, nil 23 | } 24 | 25 | // Save persists the Update to the database 26 | func (u *Update) Save() error { 27 | 28 | // Since there is only ever a single update value, we'll use the registry 29 | if err := put("registry", "update", u); err != nil { 30 | return fmt.Errorf("failed to save update: %s", err.Error()) 31 | } 32 | 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /models/version.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "fmt" 4 | 5 | var ( 6 | // will be set with build flags, defaults for one-off `go build` 7 | nanoVersion string = "0.0.0" // git tag 8 | nanoCommit string = "custom" // commit id of build 9 | nanoBuild string = "now" // date of build 10 | ) 11 | 12 | func VersionString() string { 13 | return fmt.Sprintf("Nanobox Version %s-%s (%s)", nanoVersion, nanoBuild, nanoCommit) 14 | } 15 | -------------------------------------------------------------------------------- /processors/app/dns/add.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | // "fmt" 5 | 6 | "github.com/jcelliott/lumber" 7 | 8 | "github.com/nanobox-io/nanobox/models" 9 | "github.com/nanobox-io/nanobox/processors/server" 10 | "github.com/nanobox-io/nanobox/util" 11 | "github.com/nanobox-io/nanobox/util/display" 12 | "github.com/nanobox-io/nanobox/util/dns" 13 | ) 14 | 15 | var AppSetup func(envModel *models.Env, appModel *models.App, name string) error 16 | 17 | // Add adds a dns entry to the local hosts file 18 | func Add(envModel *models.Env, appModel *models.App, name string) error { 19 | 20 | if err := AppSetup(envModel, appModel, appModel.Name); err != nil { 21 | return util.ErrorAppend(err, "failed to setup app") 22 | } 23 | 24 | // fetch the IP 25 | // env in dev is used in the dev container 26 | // env in sim is used for portal 27 | envIP := appModel.LocalIPs["env"] 28 | 29 | // generate the dns entry 30 | entry := dns.Entry(envIP, name, appModel.ID) 31 | 32 | // short-circuit if this entry already exists 33 | if dns.Exists(entry) { 34 | return nil 35 | } 36 | 37 | // make sure the server is running since it will do the dns addition 38 | if err := server.Setup(); err != nil { 39 | return util.ErrorAppend(err, "failed to setup server") 40 | } 41 | 42 | // issue a warning about `.dev` being unusable with Chrome 43 | if name[len(name)-4:] == ".dev" { 44 | tld := appModel.DisplayName() 45 | if tld == "dry-run" { 46 | tld = "test" 47 | } 48 | 49 | return util.Errorf("Google has been locking down use of the .dev TLD, and your app may not be accessible with this domain.\nTry using %s.%s instead.", name[0:len(name)-4], tld) 50 | } 51 | 52 | // add the entry 53 | if err := dns.Add(entry); err != nil { 54 | lumber.Error("dns:Add:dns.Add(%s): %s", entry, err.Error()) 55 | return util.ErrorAppend(err, "unable to add dns entry") 56 | } 57 | 58 | display.Info("\n%s %s added\n", display.TaskComplete, name) 59 | 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /processors/app/dns/list.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | "github.com/nanobox-io/nanobox/util/dns" 8 | ) 9 | 10 | // List lists all dns entries for an app 11 | func List(a *models.App) error { 12 | if a.ID == "" { 13 | fmt.Println("No DNS aliases registered") 14 | return nil 15 | } 16 | 17 | // print the header 18 | fmt.Printf("\nDNS Aliases\n") 19 | 20 | // iterate 21 | for _, domain := range dns.List(a.ID) { 22 | fmt.Printf(" %s\n", domain.Domain) 23 | } 24 | 25 | fmt.Println() 26 | 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /processors/app/dns/remove.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | // "fmt" 5 | 6 | "github.com/jcelliott/lumber" 7 | 8 | "github.com/nanobox-io/nanobox/models" 9 | "github.com/nanobox-io/nanobox/processors/server" 10 | "github.com/nanobox-io/nanobox/util" 11 | "github.com/nanobox-io/nanobox/util/display" 12 | "github.com/nanobox-io/nanobox/util/dns" 13 | ) 14 | 15 | // Remove removes a dns entry from the local hosts file 16 | func Remove(a *models.App, name string) error { 17 | // fetch the IP 18 | // env in dev is used in the dev container 19 | // env in sim is used for portal 20 | envIP := a.LocalIPs["env"] 21 | 22 | // generate the dns entry 23 | entry := dns.Entry(envIP, name, a.ID) 24 | 25 | // short-circuit if this entry doesn't exist 26 | if !dns.Exists(entry) { 27 | return nil 28 | } 29 | 30 | // make sure the server is running since it will do the dns work 31 | if err := server.Setup(); err != nil { 32 | return util.ErrorAppend(err, "failed to setup server") 33 | } 34 | 35 | // remove the entry 36 | if err := dns.Remove(entry); err != nil { 37 | lumber.Error("dns:Remove:dns.Remove(%s): %s", entry, err.Error()) 38 | return util.ErrorAppend(err, "unable to add dns entry: %s") 39 | } 40 | 41 | display.Info("\n%s %s removed\n", display.TaskComplete, name) 42 | 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /processors/app/dns/remove_all.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | // "fmt" 5 | 6 | "github.com/jcelliott/lumber" 7 | 8 | "github.com/nanobox-io/nanobox/models" 9 | "github.com/nanobox-io/nanobox/processors/server" 10 | "github.com/nanobox-io/nanobox/util" 11 | "github.com/nanobox-io/nanobox/util/display" 12 | "github.com/nanobox-io/nanobox/util/dns" 13 | ) 14 | 15 | // RemoveAll removes all dns entries for an app 16 | func RemoveAll(a *models.App) error { 17 | 18 | // shortcut if we dont have any entries for this app 19 | if len(dns.List(a.ID)) == 0 { 20 | return nil 21 | } 22 | 23 | // make sure the server is running since it will do the dns work 24 | if err := server.Setup(); err != nil { 25 | return util.ErrorAppend(err, "failed to setup server") 26 | } 27 | 28 | if err := dns.Remove(a.ID); err != nil { 29 | lumber.Error("dns:RemoveAll:dns.Remove(%s): %s", a.ID, err.Error()) 30 | return util.ErrorAppend(err, "failed to remove all dns entries") 31 | } 32 | 33 | display.Info("\n%s removed all\n", display.TaskComplete) 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /processors/app/evar/add.go: -------------------------------------------------------------------------------- 1 | package evar 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | "github.com/nanobox-io/nanobox/processors/app" 8 | "github.com/nanobox-io/nanobox/util" 9 | "github.com/nanobox-io/nanobox/util/display" 10 | ) 11 | 12 | func Add(envModel *models.Env, appModel *models.App, evars map[string]string) error { 13 | 14 | if err := app.Setup(envModel, appModel, appModel.Name); err != nil { 15 | return util.ErrorAppend(err, "failed to setup app") 16 | } 17 | 18 | // iterate through the evars and add them to the app 19 | for key, val := range evars { 20 | appModel.Evars[key] = val 21 | } 22 | 23 | // save the app 24 | if err := appModel.Save(); err != nil { 25 | return util.ErrorAppend(err, "failed to persist evars") 26 | } 27 | 28 | // iterate one more time for display 29 | fmt.Println() 30 | for key := range evars { 31 | fmt.Printf("%s %s added\n", display.TaskComplete, key) 32 | } 33 | fmt.Println() 34 | 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /processors/app/evar/list.go: -------------------------------------------------------------------------------- 1 | package evar 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | ) 8 | 9 | func List(appModel *models.App) error { 10 | 11 | // print the header 12 | fmt.Printf("\nEnvironment Variables\n") 13 | 14 | // iterate 15 | for key, val := range appModel.Evars { 16 | fmt.Printf(" %s = %s\n", key, val) 17 | } 18 | 19 | fmt.Println() 20 | 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /processors/app/evar/remove.go: -------------------------------------------------------------------------------- 1 | package evar 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | "github.com/nanobox-io/nanobox/util" 8 | "github.com/nanobox-io/nanobox/util/display" 9 | ) 10 | 11 | func Remove(appModel *models.App, keys []string) error { 12 | 13 | // delete the evars 14 | for _, key := range keys { 15 | delete(appModel.Evars, key) 16 | } 17 | 18 | // persist the app model 19 | if err := appModel.Save(); err != nil { 20 | return util.ErrorAppend(err, "failed to delete evars") 21 | } 22 | 23 | // print the deleted keys 24 | fmt.Println() 25 | for _, key := range keys { 26 | fmt.Printf("%s %s removed\n", display.TaskComplete, key) 27 | } 28 | fmt.Println() 29 | 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /processors/app/info.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/nanobox-io/nanobox/models" 8 | "github.com/nanobox-io/nanobox/util/config" 9 | "github.com/nanobox-io/nanobox/util/dns" 10 | ) 11 | 12 | // Info ... 13 | func Info(env *models.Env, app *models.App) error { 14 | 15 | if app.State != "active" { 16 | fmt.Printf("\n------------------------------------------------------\n") 17 | fmt.Printf("Whoops, it doesn't look like a dev environment has\n") 18 | fmt.Printf("been setup for the current working directory:\n") 19 | fmt.Printf("\n%s\n", config.LocalDir()) 20 | fmt.Printf("------------------------------------------------------\n\n") 21 | 22 | return nil 23 | } 24 | 25 | // print header 26 | line := strings.Repeat("-", len(env.Name)+32) 27 | fmt.Printf("\n%s\n", line) 28 | fmt.Printf("%s (%s) Status: %s \n", env.Name, app.Name, app.Status) 29 | fmt.Printf("%s\n", line) 30 | 31 | fmt.Printf("\nMount Path: %s\n", env.Directory) 32 | fmt.Printf("Env IP: %s\n", app.LocalIPs["env"]) 33 | 34 | components, _ := app.Components() 35 | 36 | for _, component := range components { 37 | 38 | // print the component header 39 | if component.Name != component.Label { 40 | fmt.Printf("\n%s (%s)\n", component.Name, component.Label) 41 | } else { 42 | fmt.Printf("\n%s\n", component.Name) 43 | } 44 | 45 | // print the IP 46 | fmt.Printf(" IP : %s\n", component.IPAddr()) 47 | 48 | // print users 49 | if len(component.Plan.Users) > 0 { 50 | fmt.Printf(" User(s) :\n") 51 | for _, user := range component.Plan.Users { 52 | fmt.Printf(" %s - %s\n", user.Username, user.Password) 53 | } 54 | } 55 | } 56 | 57 | // print environment variables 58 | fmt.Printf("\nEnvironment Variables\n") 59 | for key, val := range app.Evars { 60 | fmt.Printf(" %s = %s\n", key, val) 61 | } 62 | 63 | // print aliases 64 | fmt.Printf("\nDNS Aliases\n") 65 | entries := dns.List(app.ID) 66 | 67 | if len(entries) == 0 { 68 | fmt.Printf(" none\n") 69 | } else { 70 | for _, entry := range entries { 71 | fmt.Printf(" %s\n", entry.Domain) 72 | } 73 | } 74 | 75 | // end on an empty line 76 | fmt.Println() 77 | 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /processors/app/start.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/jcelliott/lumber" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | "github.com/nanobox-io/nanobox/processors/component" 8 | "github.com/nanobox-io/nanobox/util" 9 | "github.com/nanobox-io/nanobox/util/display" 10 | "github.com/nanobox-io/nanobox/util/locker" 11 | ) 12 | 13 | // Start will start all services associated with an app 14 | func Start(envModel *models.Env, appModel *models.App, name string) error { 15 | 16 | display.OpenContext("%s (%s)", envModel.Name, appModel.DisplayName()) 17 | defer display.CloseContext() 18 | 19 | // if the app been initialized run the setup 20 | if appModel.State != "active" { 21 | if err := Setup(envModel, appModel, name); err != nil { 22 | return util.ErrorAppend(err, "failed to setup the app") 23 | } 24 | } else { 25 | // restoring app 26 | display.StartTask("Restoring App") 27 | display.StopTask() 28 | } 29 | 30 | // we reserver here only while people are transitioning 31 | // this can go away once everyone is on the new natless method 32 | reserveIPs(appModel) 33 | 34 | locker.LocalLock() 35 | defer locker.LocalUnlock() 36 | 37 | // clean crufty components 38 | if err := component.Clean(appModel); err != nil { 39 | return util.ErrorAppend(err, "failed to clean crufty components") 40 | } 41 | 42 | // start all the app components 43 | if err := component.StartAll(appModel); err != nil { 44 | return util.ErrorAppend(err, "failed to start app components") 45 | } 46 | 47 | // set the status to up 48 | appModel.Status = "up" 49 | if err := appModel.Save(); err != nil { 50 | lumber.Error("app:Start:models.App.Save()") 51 | return util.ErrorAppend(err, "failed to persist app status") 52 | } 53 | 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /processors/build.go: -------------------------------------------------------------------------------- 1 | package processors 2 | 3 | import ( 4 | "github.com/nanobox-io/nanobox/models" 5 | "github.com/nanobox-io/nanobox/processors/code" 6 | "github.com/nanobox-io/nanobox/processors/env" 7 | "github.com/nanobox-io/nanobox/util" 8 | "github.com/nanobox-io/nanobox/util/display" 9 | "github.com/nanobox-io/nanobox/util/locker" 10 | ) 11 | 12 | // Build sets up the environment and runs a code build 13 | func Build(envModel *models.Env) error { 14 | // by aquiring a local lock we are only allowing 15 | // one build to happen at a time 16 | locker.LocalLock() 17 | defer locker.LocalUnlock() 18 | 19 | // init docker client and env mounts 20 | if err := env.Setup(envModel); err != nil { 21 | return util.ErrorAppend(err, "failed to prepare environment") 22 | } 23 | 24 | // print a warning if this is the first build 25 | if envModel.BuiltBoxfile == "" { 26 | display.FirstBuild() 27 | } 28 | 29 | // build code 30 | if err := code.Build(envModel); err != nil { 31 | return util.ErrorAppend(err, "failed to build the code") 32 | } 33 | 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /processors/clean.go: -------------------------------------------------------------------------------- 1 | package processors 2 | 3 | import ( 4 | "github.com/nanobox-io/nanobox/models" 5 | "github.com/nanobox-io/nanobox/processors/env" 6 | "github.com/nanobox-io/nanobox/util" 7 | "github.com/nanobox-io/nanobox/util/display" 8 | "github.com/nanobox-io/nanobox/util/locker" 9 | ) 10 | 11 | // 12 | func Clean(envModels []*models.Env) error { 13 | locker.GlobalLock() 14 | defer locker.GlobalUnlock() 15 | 16 | display.OpenContext("Cleaning stale environments") 17 | defer display.CloseContext() 18 | 19 | // if any of the apps are stale, we'll mark this to true 20 | stale := false 21 | 22 | for _, envModel := range envModels { 23 | // check to see if the app folder still exists 24 | if !util.FolderExists(envModel.Directory) { 25 | 26 | if err := env.Destroy(envModel); err != nil { 27 | return util.ErrorAppend(err, "unable to destroy environment(%s)", envModel.Name) 28 | } 29 | } 30 | } 31 | 32 | if !stale { 33 | display.StartTask("Skipping (none detected)") 34 | display.StopTask() 35 | } 36 | 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /processors/code/code.go: -------------------------------------------------------------------------------- 1 | // Package code ... 2 | package code 3 | 4 | import ( 5 | "time" 6 | 7 | "github.com/jcelliott/lumber" 8 | "github.com/nanobox-io/golang-docker-client" 9 | "github.com/nanobox-io/nanobox-boxfile" 10 | 11 | "github.com/nanobox-io/nanobox/util" 12 | "github.com/nanobox-io/nanobox/util/config" 13 | "github.com/nanobox-io/nanobox/util/display" 14 | ) 15 | 16 | // these constants represent different potential names a service can have 17 | const ( 18 | BUILD = "build" 19 | ) 20 | 21 | // these constants represent different potential states an app can end up in 22 | const ( 23 | ACTIVE = "active" 24 | ) 25 | 26 | func pullBuildImage() (string, error) { 27 | // extract the build image from the boxfile 28 | buildImage := buildImage() 29 | 30 | if docker.ImageExists(buildImage) { 31 | return buildImage, nil 32 | } 33 | 34 | display.StartTask("Pulling %s image", buildImage) 35 | defer display.StopTask() 36 | 37 | // generate a docker percent display 38 | dockerPercent := &display.DockerPercentDisplay{ 39 | Output: display.NewStreamer("info"), 40 | // Prefix: buildImage, 41 | } 42 | 43 | // pull the build image 44 | imagePull := func() error { 45 | _, err := docker.ImagePull(buildImage, dockerPercent) 46 | return err 47 | } 48 | if err := util.Retry(imagePull, 5, time.Second); err != nil { 49 | lumber.Error("code:pullBuildImage:docker.ImagePull(%s, nil): %s", buildImage, err.Error()) 50 | display.ErrorTask() 51 | return "", util.ErrorAppend(err, "failed to pull docker image (%s)", buildImage) 52 | } 53 | 54 | return buildImage, nil 55 | } 56 | 57 | // BuildImage fetches the build image from the boxfile 58 | func buildImage() string { 59 | // first let's see if the user has a custom build image they want to use 60 | box := boxfile.NewFromPath(config.Boxfile()) 61 | image := box.Node("run.config").StringValue("image") 62 | 63 | // then let's set the default if the user hasn't specified 64 | if image == "" { 65 | image = "nanobox/build" 66 | } 67 | 68 | return image 69 | } 70 | -------------------------------------------------------------------------------- /processors/code/destroy.go: -------------------------------------------------------------------------------- 1 | package code 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/jcelliott/lumber" 7 | 8 | "github.com/nanobox-io/golang-docker-client" 9 | "github.com/nanobox-io/nanobox/models" 10 | "github.com/nanobox-io/nanobox/util" 11 | "github.com/nanobox-io/nanobox/util/dhcp" 12 | "github.com/nanobox-io/nanobox/util/display" 13 | ) 14 | 15 | // Destroy destroys a code component from the app 16 | func Destroy(componentModel *models.Component) error { 17 | display.OpenContext(componentModel.Label) 18 | defer display.CloseContext() 19 | 20 | // remove the docker container 21 | if err := destroyContainer(componentModel.ID); err != nil { 22 | return err 23 | } 24 | 25 | // detach from the host network 26 | if err := detachNetwork(componentModel); err != nil { 27 | return util.ErrorAppend(err, "failed to detach container from the host network") 28 | } 29 | 30 | // remove the componentModel from the database 31 | if err := componentModel.Delete(); err != nil { 32 | lumber.Error("code:Destroy:Component.Delete()") 33 | display.ErrorTask() 34 | return util.ErrorAppend(err, "unable to delete database model") 35 | } 36 | 37 | return nil 38 | } 39 | 40 | // destroys a docker container associated with this app 41 | func destroyContainer(id string) error { 42 | display.StartTask("Destroying docker container") 43 | defer display.StopTask() 44 | 45 | if id == "" { 46 | return nil 47 | } 48 | 49 | if err := docker.ContainerRemove(id); err != nil { 50 | lumber.Error("component:Destroy:docker.ContainerRemove(%s): %s", id, err.Error()) 51 | display.ErrorTask() 52 | return util.ErrorAppend(err, "failed to remove docker container") 53 | } 54 | 55 | return nil 56 | } 57 | 58 | // detachNetwork detaches the network from the host 59 | func detachNetwork(componentModel *models.Component) error { 60 | display.StartTask("Releasing IPs") 61 | defer display.StopTask() 62 | 63 | // 64 | if err := dhcp.ReturnIP(net.ParseIP(componentModel.IPAddr())); err != nil { 65 | lumber.Error("code:Destroy:dhcp.ReturnIP(%s): %s", componentModel.IPAddr(), err.Error()) 66 | display.ErrorTask() 67 | return util.ErrorAppend(err, "") 68 | } 69 | 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /processors/code/types.go: -------------------------------------------------------------------------------- 1 | package code 2 | 3 | type WarehouseConfig struct { 4 | BuildID string 5 | WarehouseURL string 6 | WarehouseToken string 7 | PreviousBuild string 8 | } 9 | -------------------------------------------------------------------------------- /processors/compile.go: -------------------------------------------------------------------------------- 1 | package processors 2 | 3 | import ( 4 | "github.com/nanobox-io/nanobox/models" 5 | "github.com/nanobox-io/nanobox/processors/code" 6 | "github.com/nanobox-io/nanobox/processors/env" 7 | "github.com/nanobox-io/nanobox/util" 8 | "github.com/nanobox-io/nanobox/util/locker" 9 | ) 10 | 11 | // Compile sets up the environment and runs a code build 12 | func Compile(envModel *models.Env) error { 13 | // by aquiring a local lock we are only allowing 14 | // one build to happen at a time 15 | locker.LocalLock() 16 | defer locker.LocalUnlock() 17 | 18 | // init docker client and env mounts 19 | if err := env.Setup(envModel); err != nil { 20 | return util.ErrorAppend(err, "failed to init docker client") 21 | } 22 | 23 | // build code 24 | if err := code.Compile(envModel); err != nil { 25 | return util.ErrorAppend(err, "failed to compile the code") 26 | } 27 | 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /processors/component/component.go: -------------------------------------------------------------------------------- 1 | // Package service ... 2 | package component 3 | 4 | import ( 5 | "fmt" 6 | 7 | "github.com/nanobox-io/golang-docker-client" 8 | "github.com/nanobox-io/nanobox-boxfile" 9 | 10 | "github.com/nanobox-io/nanobox/models" 11 | ) 12 | 13 | // isComponentRunning returns true if a service is already running 14 | func isComponentRunning(containerID string) bool { 15 | container, err := docker.GetContainer(containerID) 16 | 17 | // if the container doesn't exist then just return false 18 | return err == nil && container.State.Status == "running" 19 | } 20 | 21 | // componentImage returns the image for the component 22 | func componentImage(component *models.Component) (string, error) { 23 | // fetch the env 24 | env, err := models.FindEnvByID(component.EnvID) 25 | if err != nil { 26 | return "", fmt.Errorf("failed to load env model: %s", err.Error()) 27 | } 28 | 29 | box := boxfile.New([]byte(env.BuiltBoxfile)) 30 | image := box.Node(component.Name).StringValue("image") 31 | 32 | // the only way image can be empty is if it's a platform service 33 | if image == "" { 34 | image = fmt.Sprintf("nanobox/%s", component.Name) 35 | } 36 | 37 | return image, nil 38 | } 39 | -------------------------------------------------------------------------------- /processors/component/start.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "github.com/jcelliott/lumber" 5 | "github.com/nanobox-io/golang-docker-client" 6 | 7 | "github.com/nanobox-io/nanobox/models" 8 | "github.com/nanobox-io/nanobox/util" 9 | "github.com/nanobox-io/nanobox/util/display" 10 | ) 11 | 12 | // Start starts the component services 13 | func Start(componentModel *models.Component) error { 14 | 15 | // short-circuit if the container is already running 16 | if isComponentRunning(componentModel.ID) { 17 | return nil 18 | } 19 | 20 | display.OpenContext(componentModel.Label) 21 | defer display.CloseContext() 22 | 23 | // make sure the component is active 24 | if componentModel.State != "active" { 25 | return util.Errorf("tried to start an inactive component") 26 | } 27 | 28 | // start the container 29 | if err := startContainer(componentModel.ID); err != nil { 30 | return err 31 | } 32 | 33 | return nil 34 | } 35 | 36 | // startContainer starts the container for this component 37 | func startContainer(id string) error { 38 | display.StartTask("Start docker container") 39 | defer display.StopTask() 40 | 41 | if err := docker.ContainerStart(id); err != nil { 42 | lumber.Error("component:Start:docker.ContainerStart(%s): %s", id, err.Error()) 43 | return util.ErrorAppend(err, "failed to start docker container") 44 | } 45 | 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /processors/component/start_all.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "github.com/jcelliott/lumber" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | "github.com/nanobox-io/nanobox/util" 8 | "github.com/nanobox-io/nanobox/util/display" 9 | ) 10 | 11 | // StartAll starts all app components 12 | func StartAll(a *models.App) error { 13 | 14 | // get all the components that belong to this app 15 | components, err := models.AllComponentsByApp(a.ID) 16 | if err != nil { 17 | lumber.Error("component:StartAll:models.AllComponentsByApp(%s): %s", a.ID, err.Error()) 18 | return util.ErrorAppend(err, "unable to retrieve app components") 19 | } 20 | 21 | if len(components) == 0 { 22 | return nil 23 | } 24 | 25 | display.OpenContext("Starting components") 26 | defer display.CloseContext() 27 | 28 | // start each component 29 | for _, component := range components { 30 | if err := Start(component); err != nil { 31 | return util.ErrorAppend(err, "unable to start component(%s)", component.Name) 32 | } 33 | } 34 | 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /processors/component/stop.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "github.com/jcelliott/lumber" 5 | "github.com/nanobox-io/golang-docker-client" 6 | 7 | "github.com/nanobox-io/nanobox/models" 8 | "github.com/nanobox-io/nanobox/util" 9 | "github.com/nanobox-io/nanobox/util/display" 10 | ) 11 | 12 | // Stop stops the component's docker container 13 | func Stop(componentModel *models.Component) error { 14 | // short-circuit if the process is already stopped 15 | if !isComponentRunning(componentModel.ID) { 16 | return nil 17 | } 18 | 19 | display.OpenContext(componentModel.Label) 20 | defer display.CloseContext() 21 | 22 | // stop the docker container 23 | if err := stopContainer(componentModel.ID); err != nil { 24 | return err 25 | } 26 | 27 | return nil 28 | } 29 | 30 | // stopContainer stops the docker container for this component 31 | func stopContainer(id string) error { 32 | display.StartTask("Stopping docker container") 33 | defer display.StopTask() 34 | 35 | if err := docker.ContainerStop(id); err != nil { 36 | display.ErrorTask() 37 | lumber.Error("component:Stop:docker.ContainerStop(%s): %s", id, err.Error()) 38 | return util.ErrorAppend(err, "failed to stop docker container") 39 | } 40 | 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /processors/component/stop_all.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "github.com/jcelliott/lumber" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | "github.com/nanobox-io/nanobox/util" 8 | "github.com/nanobox-io/nanobox/util/display" 9 | ) 10 | 11 | // StopAll stops all app components 12 | func StopAll(appModel *models.App) error { 13 | 14 | // get all the components that belong to this app 15 | componentModels, err := appModel.Components() 16 | if err != nil { 17 | lumber.Error("component:StopAll:models.App{ID:%s}.Components() %s", appModel.ID, err.Error()) 18 | return util.ErrorAppend(err, "unable to retrieve components") 19 | } 20 | 21 | if len(componentModels) == 0 { 22 | return nil 23 | } 24 | 25 | display.OpenContext("Stopping components") 26 | defer display.CloseContext() 27 | 28 | // stop each component 29 | for _, componentModel := range componentModels { 30 | if err := Stop(componentModel); err != nil { 31 | return util.ErrorAppend(err, "unable to stop component(%s)", componentModel.Name) 32 | } 33 | } 34 | 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /processors/configure_set.go: -------------------------------------------------------------------------------- 1 | package processors 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/nanobox-io/nanobox/models" 8 | ) 9 | 10 | func ConfigureSet(key, val string) error { 11 | config, _ := models.LoadConfig() 12 | 13 | switch key { 14 | case "provider": 15 | config.Provider = val 16 | case "mount-type", "mount_type": 17 | config.MountType = val 18 | case "netfs_mount_opts", "netfs-mount-opts", "mount_options", "mount-options": 19 | config.NetfsMountOpts = val 20 | case "cpus", "CPUs": 21 | config.CPUs, _ = strconv.Atoi(val) 22 | case "ram", "RAM": 23 | config.RAM, _ = strconv.Atoi(val) 24 | case "disk": 25 | config.Disk, _ = strconv.Atoi(val) 26 | case "external_network_space", "external-network-space": 27 | config.ExternalNetworkSpace = val 28 | case "docker_machine_network_space", "docker-machine-network-space": 29 | config.DockerMachineNetworkSpace = val 30 | case "native_network_space", "native-network-space": 31 | config.NativeNetworkSpace = val 32 | case "ssh_key", "ssh-key": 33 | config.SshKey = val 34 | case "ssh_encrypted_keys", "ssh-encrypted-keys", "use_encrypted_keys", "use-encrypted-keys": 35 | config.SshEncryptedKeys = val == "true" || val == "t" || val == "1" 36 | case "lock_port", "lock-port": 37 | config.LockPort, _ = strconv.Atoi(val) 38 | case "ci-mode", "ci_mode": 39 | config.CIMode = val == "true" || val == "t" || val == "1" 40 | case "ci-sync-verbose", "ci_sync_verbose": 41 | config.CISyncVerbose = val == "true" || val == "t" || val == "1" 42 | case "anonymous": 43 | config.Anonymous = val == "true" || val == "t" || val == "1" 44 | default: 45 | fmt.Printf("'%s' is not a valid key.\n", key) 46 | return nil 47 | } 48 | 49 | err := config.Save() 50 | if err == nil { 51 | fmt.Printf("Successfully set '%s'\n", key) 52 | } else { 53 | fmt.Printf("Failed to set '%s'\n", key) 54 | } 55 | 56 | return err 57 | } 58 | -------------------------------------------------------------------------------- /processors/env/console.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | "github.com/nanobox-io/nanobox/processors/provider" 8 | "github.com/nanobox-io/nanobox/util" 9 | "github.com/nanobox-io/nanobox/util/console" 10 | "github.com/nanobox-io/nanobox/util/display" 11 | ) 12 | 13 | // Console ... 14 | func Console(componentModel *models.Component, consoleConfig console.ConsoleConfig) error { 15 | if componentModel.ID == "" { 16 | display.ConsoleNodeNotFound() 17 | return util.Err{ 18 | Message: "Node not found", 19 | Code: "USER", 20 | Stack: []string{"failed to console"}, 21 | Suggest: "It appears the node specified does not exist. Please double check the node name in your boxfile.yml.", 22 | } 23 | } 24 | // setup docker client 25 | if err := provider.Init(); err != nil { 26 | return err 27 | } 28 | 29 | switch { 30 | case consoleConfig.Command != "": 31 | display.InfoDevRunContainer(consoleConfig.Command, consoleConfig.DevIP) 32 | default: 33 | display.MOTD() 34 | display.InfoDevContainer(consoleConfig.DevIP) 35 | } 36 | <-time.After(100 * time.Millisecond) 37 | 38 | return console.Run(componentModel.ID, consoleConfig) 39 | } 40 | -------------------------------------------------------------------------------- /processors/env/destroy.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/jcelliott/lumber" 7 | "github.com/nanobox-io/golang-docker-client" 8 | 9 | "github.com/nanobox-io/nanobox/models" 10 | "github.com/nanobox-io/nanobox/processors/app" 11 | "github.com/nanobox-io/nanobox/processors/provider" 12 | "github.com/nanobox-io/nanobox/util" 13 | "github.com/nanobox-io/nanobox/util/locker" 14 | util_provider "github.com/nanobox-io/nanobox/util/provider" 15 | ) 16 | 17 | // Destroy brings down the environment setup 18 | func Destroy(env *models.Env) error { 19 | locker.LocalLock() 20 | defer locker.LocalUnlock() 21 | 22 | // init docker client 23 | if err := provider.Init(); err != nil { 24 | return util.ErrorAppend(err, "failed to init docker client") 25 | } 26 | 27 | // find apps 28 | apps, err := env.Apps() 29 | if err != nil { 30 | lumber.Error("env:Destroy:models.Env{ID:%s}.Apps(): %s", env.ID, err) 31 | return util.ErrorAppend(err, "failed to load app collection") 32 | } 33 | 34 | // destroy apps 35 | for _, a := range apps { 36 | 37 | err := app.Destroy(a) 38 | if err != nil { 39 | return util.ErrorAppend(err, "failed to remove app") 40 | } 41 | } 42 | 43 | // unmount the environment 44 | if err := Unmount(env); err != nil { 45 | return util.ErrorAppend(err, "failed to unmount env") 46 | } 47 | 48 | // TODO: remove folder from host /mnt/sda1/env_id 49 | if err := util_provider.RemoveEnvDir(env.ID); err != nil { 50 | // it is ok if the cleanup fails its not worth erroring here 51 | // return util.ErrorAppend(err, "failed to remove the environment from host") 52 | } 53 | 54 | // remove volumes 55 | docker.VolumeRemove(fmt.Sprintf("nanobox_%s_app", env.ID)) 56 | docker.VolumeRemove(fmt.Sprintf("nanobox_%s_cache", env.ID)) 57 | docker.VolumeRemove(fmt.Sprintf("nanobox_%s_mount", env.ID)) 58 | docker.VolumeRemove(fmt.Sprintf("nanobox_%s_deploy", env.ID)) 59 | docker.VolumeRemove(fmt.Sprintf("nanobox_%s_build", env.ID)) 60 | 61 | // remove the environment 62 | if err := env.Delete(); err != nil { 63 | return util.ErrorAppend(err, "failed to remove env") 64 | } 65 | 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /processors/env/mount.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | 7 | "github.com/nanobox-io/nanobox/models" 8 | "github.com/nanobox-io/nanobox/util" 9 | "github.com/nanobox-io/nanobox/util/config" 10 | "github.com/nanobox-io/nanobox/util/display" 11 | "github.com/nanobox-io/nanobox/util/provider" 12 | ) 13 | 14 | // Mount sets up the env mounts 15 | func Mount(env *models.Env) error { 16 | if !provider.RequiresMount() { 17 | return nil 18 | } 19 | 20 | display.StartTask("Mounting codebase") 21 | defer display.StopTask() 22 | 23 | // BUG(glinton) if the `build` isn't successful and the engine changes in the boxfile, 24 | // this mount sticks around and must be cleaned up manually. 25 | // 26 | // mount the engine if it's a local directory 27 | engineDir, _ := config.EngineDir() 28 | if engineDir != "" { 29 | src := engineDir // local directory 30 | dst := filepath.Join(provider.HostShareDir(), env.ID, "engine") // b2d "global zone" 31 | 32 | // first, export the env on the workstation 33 | if err := provider.AddMount(src, dst); err != nil { 34 | display.ErrorTask() 35 | return util.ErrorAppend(err, "failed to mount the engine share on the provider") 36 | } 37 | } 38 | 39 | // mount the app src 40 | src := env.Directory 41 | dst := fmt.Sprintf("%s%s/code", provider.HostShareDir(), env.ID) 42 | 43 | // first export the env on the workstation 44 | if err := provider.AddMount(src, dst); err != nil { 45 | display.ErrorTask() 46 | return util.ErrorAppend(err, "failed to mount the code share on the provider") 47 | } 48 | 49 | // // setup mount directories 50 | // provider.Run([]string{"mkdir", "-p", fmt.Sprintf("%s%s/build", provider.HostMntDir(), env.ID)}) 51 | // provider.Run([]string{"mkdir", "-p", fmt.Sprintf("%s%s/deploy", provider.HostMntDir(), env.ID)}) 52 | // provider.Run([]string{"mkdir", "-p", fmt.Sprintf("%s%s/cache", provider.HostMntDir(), env.ID)}) 53 | 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /processors/env/share/add.go: -------------------------------------------------------------------------------- 1 | package share 2 | 3 | import ( 4 | "github.com/jcelliott/lumber" 5 | 6 | "github.com/nanobox-io/nanobox/util" 7 | "github.com/nanobox-io/nanobox/util/provider/share" 8 | ) 9 | 10 | // Add adds a share share to the workstation 11 | func Add(path string) error { 12 | 13 | // since we dont 14 | // // short-circuit if the entry already exist 15 | // if share.Exists(path) { 16 | // return nil 17 | // } 18 | 19 | // add the share entry 20 | if err := share.Add(path); err != nil { 21 | lumber.Error("share:Add:share.Add(%s): %s", path, err.Error()) 22 | return util.ErrorAppend(err, "failed to add share") 23 | } 24 | 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /processors/env/share/remove.go: -------------------------------------------------------------------------------- 1 | package share 2 | 3 | import ( 4 | "github.com/jcelliott/lumber" 5 | 6 | "github.com/nanobox-io/nanobox/util" 7 | "github.com/nanobox-io/nanobox/util/provider/share" 8 | ) 9 | 10 | // Remove removes a share share from the workstation 11 | func Remove(path string) error { 12 | 13 | // short-circuit if the entry doesn't exist 14 | if !share.Exists(path) { 15 | return nil 16 | } 17 | 18 | // rm the share entry 19 | if err := share.Remove(path); err != nil { 20 | lumber.Error("share:Add:share.Remove(%s): %s", path, err.Error()) 21 | return util.ErrorAppend(err, "failed to remove share share") 22 | } 23 | 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /processors/evar/add.go: -------------------------------------------------------------------------------- 1 | package evar 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/nanobox/commands/registry" 7 | "github.com/nanobox-io/nanobox/models" 8 | "github.com/nanobox-io/nanobox/util/display" 9 | "github.com/nanobox-io/nanobox/util/odin" 10 | ) 11 | 12 | func Add(envModel *models.Env, appID string, evars map[string]string) error { 13 | 14 | // fetch the remote 15 | remote, ok := envModel.Remotes[appID] 16 | if ok { 17 | // set the odin endpoint 18 | odin.SetEndpoint(remote.Endpoint) 19 | // set the app id 20 | appID = remote.ID 21 | } 22 | 23 | // set odins endpoint if the arguement is passed 24 | if endpoint := registry.GetString("endpoint"); endpoint != "" { 25 | odin.SetEndpoint(endpoint) 26 | } 27 | 28 | // iterate through the evars and add them to the app 29 | for key, val := range evars { 30 | err := odin.AddEvar(appID, key, val) 31 | if err != nil { 32 | return err 33 | } 34 | fmt.Printf("%s %s added\n", display.TaskComplete, key) 35 | } 36 | 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /processors/evar/list.go: -------------------------------------------------------------------------------- 1 | package evar 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/nanobox/commands/registry" 7 | "github.com/nanobox-io/nanobox/models" 8 | "github.com/nanobox-io/nanobox/util/odin" 9 | ) 10 | 11 | func List(envModel *models.Env, appID string) error { 12 | // fetch the remote 13 | remote, ok := envModel.Remotes[appID] 14 | if ok { 15 | // set the odin endpoint 16 | odin.SetEndpoint(remote.Endpoint) 17 | // set the app id 18 | appID = remote.ID 19 | } 20 | 21 | // set odins endpoint if the arguement is passed 22 | if endpoint := registry.GetString("endpoint"); endpoint != "" { 23 | odin.SetEndpoint(endpoint) 24 | } 25 | 26 | evars, err := odin.ListEvars(appID) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | // print the header 32 | fmt.Printf("\nEnvironment Variables\n") 33 | 34 | // iterate 35 | for _, evar := range evars { 36 | fmt.Printf(" %s = %s\n", evar.Key, evar.Value) 37 | } 38 | 39 | fmt.Println() 40 | 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /processors/evar/remove.go: -------------------------------------------------------------------------------- 1 | package evar 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/nanobox/commands/registry" 7 | "github.com/nanobox-io/nanobox/models" 8 | "github.com/nanobox-io/nanobox/util/display" 9 | "github.com/nanobox-io/nanobox/util/odin" 10 | ) 11 | 12 | func Remove(envModel *models.Env, appID string, keys []string) error { 13 | 14 | // fetch the remote 15 | remote, ok := envModel.Remotes[appID] 16 | if ok { 17 | // set the odin endpoint 18 | odin.SetEndpoint(remote.Endpoint) 19 | // set the app id 20 | appID = remote.ID 21 | } 22 | 23 | // set odins endpoint if the arguement is passed 24 | if endpoint := registry.GetString("endpoint"); endpoint != "" { 25 | odin.SetEndpoint(endpoint) 26 | } 27 | 28 | evars, err := odin.ListEvars(appID) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | // delete the evars 34 | for _, key := range keys { 35 | removed := false 36 | for _, evar := range evars { 37 | if evar.Key == key { 38 | if err := odin.RemoveEvar(appID, evar.ID); err != nil { 39 | return err 40 | } 41 | removed = true 42 | fmt.Printf("%s %s removed\n", display.TaskComplete, key) 43 | } 44 | } 45 | if !removed { 46 | fmt.Printf("%s %s not found\n", display.TaskPause, key) 47 | } 48 | } 49 | fmt.Println() 50 | 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /processors/implode.go: -------------------------------------------------------------------------------- 1 | package processors 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/nanobox-io/nanobox/commands/registry" 8 | "github.com/nanobox-io/nanobox/models" 9 | "github.com/nanobox-io/nanobox/processors/env" 10 | "github.com/nanobox-io/nanobox/processors/provider" 11 | "github.com/nanobox-io/nanobox/processors/server" 12 | "github.com/nanobox-io/nanobox/util" 13 | "github.com/nanobox-io/nanobox/util/config" 14 | "github.com/nanobox-io/nanobox/util/display" 15 | util_provider "github.com/nanobox-io/nanobox/util/provider" 16 | ) 17 | 18 | // Implode destroys the provider and cleans nanobox off of the system 19 | func Implode() error { 20 | 21 | display.OpenContext("Imploding Nanobox") 22 | defer display.CloseContext() 23 | 24 | // remove all environments 25 | envModels, _ := models.AllEnvs() 26 | for _, envModel := range envModels { 27 | // remove all environments 28 | if err := env.Destroy(envModel); err != nil { 29 | fmt.Printf("unable to remove mounts: %s", err) 30 | } 31 | } 32 | 33 | // destroy the provider 34 | if err := provider.Destroy(); err != nil { 35 | return util.ErrorAppend(err, "failed to destroy the provider") 36 | } 37 | 38 | // destroy the provider (VM), remove images, remove containers 39 | if err := util_provider.Implode(); err != nil { 40 | return util.ErrorAppend(err, "failed to implode the provider") 41 | } 42 | 43 | // check to see if we need to uninstall nanobox 44 | // or just remove apps 45 | if registry.GetBool("full-implode") { 46 | 47 | // teardown the server 48 | if err := server.Teardown(); err != nil { 49 | // if we cant tear down the server dont worry about it 50 | // return util.ErrorAppend(err, "failed to remove server") 51 | } 52 | 53 | purgeConfiguration() 54 | } 55 | 56 | return nil 57 | } 58 | 59 | // purges the config data and dns entries 60 | func purgeConfiguration() error { 61 | 62 | display.StartTask("Purging configuration") 63 | defer display.StopTask() 64 | 65 | // implode the global dir 66 | if err := os.RemoveAll(config.GlobalDir()); err != nil { 67 | return util.ErrorAppend(util.ErrorQuiet(err), "failed to purge the data directory") 68 | } 69 | 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /processors/login.go: -------------------------------------------------------------------------------- 1 | package processors 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/nanobox-io/nanobox/models" 8 | "github.com/nanobox-io/nanobox/util" 9 | "github.com/nanobox-io/nanobox/util/display" 10 | "github.com/nanobox-io/nanobox/util/odin" 11 | ) 12 | 13 | // Process ... 14 | func Login(username, password, endpoint string) error { 15 | 16 | // request Username/Password if missing 17 | if username == "" && os.Getenv("NANOBOX_USERNAME") != "" { 18 | username = os.Getenv("NANOBOX_USERNAME") 19 | } 20 | 21 | if username == "" { 22 | user, err := display.ReadUsername() 23 | if err != nil { 24 | return util.ErrorAppend(err, "unable to retrieve username") 25 | } 26 | username = user 27 | } 28 | 29 | if password == "" && os.Getenv("NANOBOX_PASSWORD") != "" { 30 | password = os.Getenv("NANOBOX_PASSWORD") 31 | } 32 | 33 | if password == "" { 34 | // ReadPassword prints Password: already 35 | pass, err := display.ReadPassword("Nanobox") 36 | if err != nil { 37 | return util.ErrorAppend(err, "failed to read password") 38 | } 39 | password = pass 40 | } 41 | 42 | if endpoint == "" && os.Getenv("NANOBOX_ENDPOINT") != "" { 43 | endpoint = os.Getenv("NANOBOX_ENDPOINT") 44 | } 45 | 46 | if endpoint == "" { 47 | endpoint = "nanobox" 48 | } 49 | 50 | // set the odin endpoint 51 | odin.SetEndpoint(endpoint) 52 | 53 | // verify that the user exists 54 | token, err := odin.Auth(username, password) 55 | if err != nil { 56 | fmt.Println(`! The username/password was incorrect, but we're continuing on. 57 | To reattempt authentication, run 'nanobox login'. 58 | `) 59 | return nil 60 | } 61 | 62 | // store the user token 63 | auth := models.Auth{ 64 | Endpoint: endpoint, 65 | Key: token, 66 | } 67 | if auth.Save() != nil { 68 | return util.Errorf("unable to save user authentication") 69 | } 70 | 71 | display.LoginComplete() 72 | 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /processors/logout.go: -------------------------------------------------------------------------------- 1 | package processors 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | "github.com/nanobox-io/nanobox/util" 8 | "github.com/nanobox-io/nanobox/util/display" 9 | ) 10 | 11 | // Process ... 12 | func Logout(endpoint string) error { 13 | 14 | if endpoint == "" { 15 | endpoint = "nanobox" 16 | } 17 | 18 | // lookup the auth by the endpoint 19 | auth, _ := models.LoadAuthByEndpoint(endpoint) 20 | 21 | // short-circuit if the auth is already deleted 22 | if auth.IsNew() { 23 | fmt.Printf("%s Already logged out\n", display.TaskComplete) 24 | return nil 25 | } 26 | 27 | // remove token from database 28 | if err := auth.Delete(); err != nil { 29 | return util.ErrorAppend(err, "failed to delete user authentication") 30 | } 31 | 32 | fmt.Printf("%s You've logged out\n", display.TaskComplete) 33 | 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /processors/platform/mist.go: -------------------------------------------------------------------------------- 1 | package platform 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/signal" 7 | "time" 8 | 9 | "github.com/nanopack/mist/clients" 10 | 11 | "github.com/nanobox-io/nanobox/models" 12 | "github.com/nanobox-io/nanobox/util" 13 | "github.com/nanobox-io/nanobox/util/display" 14 | ) 15 | 16 | // MistListen ... 17 | func MistListen(appModel *models.App) error { 18 | mist, err := models.FindComponentBySlug(appModel.ID, "mist") 19 | if err != nil { 20 | return err 21 | } 22 | 23 | // connect to the mist server 24 | var client *clients.TCP 25 | clientConnect := func() (err error) { 26 | client, err = clients.New(mist.IPAddr()+":1445", "123") 27 | return err 28 | } 29 | if err := util.Retry(clientConnect, 3, time.Second); err != nil { 30 | return err 31 | } 32 | 33 | // subscribe to all logs 34 | if err := client.Subscribe([]string{"log"}); err != nil { 35 | return err 36 | } 37 | 38 | // catch kill signals 39 | sigChan := make(chan os.Signal, 1) 40 | signal.Notify(sigChan, os.Interrupt) 41 | signal.Notify(sigChan, os.Kill) 42 | 43 | fmt.Printf(` 44 | Connected to streaming logs: 45 | ctrl + c to quit 46 | ------------------------------------------------ 47 | waiting for output... 48 | 49 | `) 50 | 51 | // loop waiting for messages or signals if we recieve a kill signal quit 52 | // messages will be displayed 53 | for { 54 | select { 55 | case msg := <-client.Messages(): 56 | display.FormatLogMessage(msg, false) 57 | case <-sigChan: 58 | return nil 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /processors/platform/platform.go: -------------------------------------------------------------------------------- 1 | // Package platform ... 2 | package platform 3 | 4 | // these constants represent different potential states a platform can end up in 5 | const ( 6 | ACTIVE = "active" 7 | ) 8 | 9 | // these represent different protocols that a platform might use 10 | const ( 11 | HTTP = "http" 12 | HTTPS = "https" 13 | TCP = "tcp" 14 | UDP = "udp" 15 | ) 16 | 17 | // PlatformComponent ... 18 | type PlatformComponent struct { 19 | label string 20 | name string 21 | image string 22 | } 23 | 24 | // Components ... 25 | var setupComponents = []PlatformComponent{ 26 | { 27 | label: "Logger", 28 | name: "logvac", 29 | image: "nanobox/logvac", 30 | }, 31 | { 32 | label: "Message Bus", 33 | name: "mist", 34 | image: "nanobox/mist", 35 | }, 36 | { 37 | label: "Router", 38 | name: "portal", 39 | image: "nanobox/portal", 40 | }, 41 | { 42 | label: "Storage", 43 | name: "hoarder", 44 | image: "nanobox/hoarder", 45 | }, 46 | } 47 | -------------------------------------------------------------------------------- /processors/platform/portal.go: -------------------------------------------------------------------------------- 1 | package platform 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/jcelliott/lumber" 7 | 8 | "github.com/nanobox-io/golang-portal-client" 9 | generator "github.com/nanobox-io/nanobox/generators/router" 10 | "github.com/nanobox-io/nanobox/models" 11 | "github.com/nanobox-io/nanobox/util" 12 | ) 13 | 14 | // UpdatePortal ... 15 | func UpdatePortal(appModel *models.App) error { 16 | client := portalClient(appModel) 17 | 18 | // update routes 19 | routes := generator.BuildRoutes(appModel) 20 | updateRoute := func() error { 21 | return client.UpdateRoutes(routes) 22 | } 23 | 24 | // update cert 25 | certs, err := generator.BuildCert(appModel) 26 | if err != nil { 27 | return util.ErrorAppend(err, "failed to build cert") 28 | } 29 | 30 | updateCert := func() error { 31 | return client.UpdateCert(certs) 32 | } 33 | 34 | // use the retry method here because there is a chance the portal server isnt responding yet 35 | if err := util.Retry(updateRoute, 2, time.Second); err != nil { 36 | lumber.Error("platform:UpdatePortal:UpdateRoutes(%+v): %s", routes, err.Error()) 37 | return util.ErrorAppend(err, "failed to send routing updates to the router") 38 | } 39 | 40 | // use the retry method here because there is a chance the portal server isnt responding yet 41 | if err := util.Retry(updateCert, 2, time.Second); err != nil { 42 | lumber.Error("platform:UpdatePortal:UpdateCerts(%+v): %s", certs, err.Error()) 43 | return util.ErrorAppend(err, "failed to send cert updates to the router") 44 | } 45 | 46 | // update services 47 | services := generator.BuildServices(appModel) 48 | if err := client.UpdateServices(services); err != nil { 49 | lumber.Error("platform:UpdatePortal:UpdateServices(%+v): %s", services, err.Error()) 50 | return util.ErrorAppend(err, "failed to update port forwarding") 51 | } 52 | 53 | return nil 54 | } 55 | 56 | // 57 | func portalClient(appModel *models.App) portal.PortalClient { 58 | return portal.New(appModel.LocalIPs["env"]+":8443", "123") 59 | } 60 | -------------------------------------------------------------------------------- /processors/platform/provision.go: -------------------------------------------------------------------------------- 1 | package platform 2 | 3 | import ( 4 | "github.com/nanobox-io/nanobox/models" 5 | "github.com/nanobox-io/nanobox/processors/component" 6 | "github.com/nanobox-io/nanobox/util" 7 | ) 8 | 9 | // provisionComponent will provision an individual component 10 | func provisionComponent(appModel *models.App, platformComponent PlatformComponent) error { 11 | 12 | componentModel := &models.Component{ 13 | Name: platformComponent.name, 14 | Label: platformComponent.label, 15 | Image: platformComponent.image, 16 | } 17 | 18 | // if the component exists and is active just start it and return 19 | if isComponentActive(appModel, componentModel) { 20 | 21 | // start the component 22 | if err := component.Start(componentModel); err != nil { 23 | return util.ErrorAppend(err, "failed to start component") 24 | } 25 | 26 | return nil 27 | } 28 | 29 | // setup 30 | if err := component.Setup(appModel, componentModel); err != nil { 31 | return util.ErrorAppend(err, "failed to setup platform component (%s)", componentModel.Label) 32 | } 33 | 34 | return nil 35 | } 36 | 37 | // isComponentActive returns true if a component is already active 38 | func isComponentActive(appModel *models.App, componentModel *models.Component) bool { 39 | // component db entry 40 | component, _ := models.FindComponentBySlug(appModel.ID, componentModel.Name) 41 | if component.State == "active" { 42 | 43 | // set the componentModel pointer to the new component object 44 | *componentModel = *component 45 | return true 46 | } 47 | 48 | return false 49 | } 50 | -------------------------------------------------------------------------------- /processors/platform/setup.go: -------------------------------------------------------------------------------- 1 | package platform 2 | 3 | import ( 4 | "github.com/nanobox-io/nanobox/models" 5 | "github.com/nanobox-io/nanobox/util" 6 | "github.com/nanobox-io/nanobox/util/display" 7 | ) 8 | 9 | // Setup provisions platform components needed for an app setup 10 | func Setup(appModel *models.App) error { 11 | display.OpenContext("Starting components") 12 | defer display.CloseContext() 13 | 14 | for _, component := range setupComponents { 15 | if err := provisionComponent(appModel, component); err != nil { 16 | return util.ErrorAppend(err, "failed to provision platform component") 17 | } 18 | } 19 | 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /processors/platform/stop.go: -------------------------------------------------------------------------------- 1 | package platform 2 | 3 | import ( 4 | "github.com/jcelliott/lumber" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | "github.com/nanobox-io/nanobox/processors/component" 8 | "github.com/nanobox-io/nanobox/util" 9 | ) 10 | 11 | // Stop stops all platform components 12 | func Stop(a *models.App) error { 13 | for _, pc := range setupComponents { 14 | if err := stopComponent(a, pc); err != nil { 15 | return util.ErrorAppend(err, "failed to stop platform component") 16 | } 17 | } 18 | 19 | return nil 20 | } 21 | 22 | // stopComponent stops a platform component 23 | func stopComponent(a *models.App, pc PlatformComponent) error { 24 | // load the component 25 | c, err := models.FindComponentBySlug(a.ID, pc.name) 26 | if err != nil { 27 | lumber.Error("platform:stopComponent:models.FindComponentBySlug(%s, %s): %s", a.ID, pc.name, err.Error()) 28 | return util.ErrorAppend(err, "failed to load component") 29 | } 30 | 31 | // stop the component 32 | if err := component.Stop(c); err != nil { 33 | return util.ErrorAppend(err, "failed to stop component") 34 | } 35 | 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /processors/provider/bridge/bridge.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | // "runtime" 7 | "net" 8 | 9 | "github.com/nanobox-io/nanobox/util/config" 10 | "github.com/nanobox-io/nanobox/util/dhcp" 11 | "github.com/nanobox-io/nanobox/util/provider" 12 | ) 13 | 14 | func BridgeConfig() string { 15 | // node := "" 16 | // if runtime.GOOS == "windows" { 17 | // node = "dev-node MyTap" 18 | // } 19 | 20 | ip, _ := provider.HostIP() 21 | return fmt.Sprintf(`client 22 | 23 | dev tap 24 | proto udp 25 | remote %s 1194 26 | resolv-retry infinite 27 | nobind 28 | persist-key 29 | persist-tun 30 | 31 | ca "%s" 32 | cert "%s" 33 | key "%s" 34 | 35 | cipher none 36 | auth none 37 | verb 3 38 | `, ip, CaCrt(), ClientCrt(), ClientKey()) 39 | } 40 | 41 | func ConfigFile() string { 42 | return filepath.ToSlash(filepath.Join(config.EtcDir(), "openvpn", "openvpn.conf")) 43 | } 44 | 45 | func CaCrt() string { 46 | return filepath.ToSlash(filepath.Join(config.EtcDir(), "openvpn", "ca.crt")) 47 | } 48 | 49 | func ClientKey() string { 50 | return filepath.ToSlash(filepath.Join(config.EtcDir(), "openvpn", "client.key")) 51 | } 52 | 53 | func ClientCrt() string { 54 | return filepath.ToSlash(filepath.Join(config.EtcDir(), "openvpn", "client.crt")) 55 | } 56 | 57 | // check to see if the bridge is connected 58 | func Connected() bool { 59 | network, err := dhcp.LocalNet() 60 | if err != nil { 61 | return false 62 | } 63 | interfaces, err := net.Interfaces() 64 | if err != nil { 65 | return false 66 | } 67 | 68 | // look throught the interfaces on the system 69 | for _, i := range interfaces { 70 | addrs, err := i.Addrs() 71 | if err != nil { 72 | continue 73 | } 74 | 75 | // find all the addresses assigned to the interface 76 | for _, addr := range addrs { 77 | ip, _, err := net.ParseCIDR(addr.String()) 78 | if err != nil { 79 | continue 80 | } 81 | 82 | // check to see if the ip address is in our network 83 | if network.Contains(ip) { 84 | 85 | // now check to see if that interface is up 86 | if i.Flags&net.FlagUp != net.FlagUp { 87 | return false 88 | } 89 | 90 | return true 91 | } 92 | } 93 | } 94 | 95 | return false 96 | } 97 | -------------------------------------------------------------------------------- /processors/provider/bridge/start.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import ( 4 | "github.com/nanobox-io/nanobox/util/provider/bridge" 5 | ) 6 | 7 | // ask the server to start the bridge 8 | func Start() error { 9 | return bridge.Start(ConfigFile()) 10 | } 11 | -------------------------------------------------------------------------------- /processors/provider/bridge/stop.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import ( 4 | "github.com/nanobox-io/nanobox/util/provider/bridge" 5 | ) 6 | 7 | // ask the server to stop the bridge 8 | func Stop() error { 9 | return bridge.Stop() 10 | } 11 | -------------------------------------------------------------------------------- /processors/provider/bridge/teardown.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import ( 4 | "github.com/jcelliott/lumber" 5 | "github.com/nanobox-io/golang-docker-client" 6 | 7 | container_generator "github.com/nanobox-io/nanobox/generators/containers" 8 | "github.com/nanobox-io/nanobox/util" 9 | ) 10 | 11 | func Teardown() error { 12 | if !Connected() { 13 | return nil 14 | } 15 | 16 | // remove bridge client 17 | if err := Stop(); err != nil { 18 | return err 19 | } 20 | 21 | // remove component 22 | if err := removeComponent(); err != nil { 23 | return err 24 | } 25 | 26 | return nil 27 | } 28 | 29 | func removeComponent() error { 30 | // grab the container info 31 | container, err := docker.GetContainer(container_generator.BridgeName()) 32 | if err != nil { 33 | // if we cant get the container it may have been removed by someone else 34 | // just return here 35 | // if we cant talk to docker its ok too 36 | return nil 37 | } 38 | 39 | // remove the container 40 | if err := docker.ContainerRemove(container.ID); err != nil { 41 | lumber.Error("provider:bridge:teardown:docker.ContainerRemove(%s): %s", container.ID, err) 42 | return util.ErrorAppend(err, "failed to remove bridge container") 43 | } 44 | 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /processors/provider/destroy.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "github.com/jcelliott/lumber" 5 | 6 | "github.com/nanobox-io/nanobox/processors/provider/bridge" 7 | "github.com/nanobox-io/nanobox/util" 8 | "github.com/nanobox-io/nanobox/util/locker" 9 | "github.com/nanobox-io/nanobox/util/provider" 10 | ) 11 | 12 | // Destroy destroys the provider 13 | func Destroy() error { 14 | locker.GlobalLock() 15 | defer locker.GlobalUnlock() 16 | 17 | if provider.BridgeRequired() { 18 | 19 | // remove the network bridge 20 | if err := bridge.Teardown(); err != nil { 21 | return util.ErrorAppend(err, "failed to teardown network bridge") 22 | } 23 | } 24 | 25 | // destroy the provider 26 | if err := provider.Destroy(); err != nil { 27 | lumber.Error("provider:Destroy:provider.Destroy()") 28 | return util.ErrorAppend(err, "failed to destroy the provider") 29 | } 30 | 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /processors/provider/init.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/jcelliott/lumber" 7 | "github.com/nanobox-io/golang-docker-client" 8 | 9 | "github.com/nanobox-io/nanobox/models" 10 | "github.com/nanobox-io/nanobox/util" 11 | "github.com/nanobox-io/nanobox/util/provider" 12 | ) 13 | 14 | // Init initializes the docker client for the provider 15 | func Init() error { 16 | // load the docker environment 17 | if err := provider.DockerEnv(); err != nil { 18 | lumber.Error("provider:Init:provider.DockerEnv(): %s", err.Error()) 19 | return util.ErrorAppend(util.ErrorQuiet(err), "failed to load the docker environment") 20 | } 21 | 22 | // initialize the docker client 23 | if err := docker.Initialize("env"); err != nil { 24 | lumber.Error("provider:Init:docker.Initialize()") 25 | return util.ErrorAppend(util.ErrorQuiet(err), "failed to initialize the docker client") 26 | } 27 | 28 | // make sure we have the default ip 29 | providerModel, _ := models.LoadProvider() 30 | if err := setDefaultIP(providerModel); err != nil { 31 | return util.ErrorAppend(err, "failed to setup the provider network") 32 | } 33 | 34 | checkFunc := func() error { 35 | _, err := docker.ContainerList() 36 | return err 37 | } 38 | 39 | // confirm it is up and working 40 | if err := util.Retry(checkFunc, 20, time.Second); err != nil { 41 | return util.Errorf("unable to communicate with Docker") 42 | } 43 | 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /processors/provider/stop.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "github.com/jcelliott/lumber" 5 | 6 | "github.com/nanobox-io/nanobox/processors/provider/bridge" 7 | "github.com/nanobox-io/nanobox/util" 8 | "github.com/nanobox-io/nanobox/util/display" 9 | "github.com/nanobox-io/nanobox/util/locker" 10 | "github.com/nanobox-io/nanobox/util/provider" 11 | ) 12 | 13 | // Stop stops the provider (stops the VM) 14 | func Stop() error { 15 | locker.GlobalLock() 16 | defer locker.GlobalUnlock() 17 | 18 | display.OpenContext("Stopping Nanobox") 19 | defer display.CloseContext() 20 | 21 | // stop the vpn 22 | if err := bridge.Stop(); err != nil { 23 | // do nothing about the error since provider stop happens 24 | // then we shut down the server (killing the bridge) 25 | // return util.ErrorAppend(err, "failed to stop vpn") 26 | } 27 | 28 | // stop the provider (VM) 29 | if err := provider.Stop(); err != nil { 30 | lumber.Error("provider:Stop:provider.Stop(): %s", err.Error()) 31 | return util.ErrorAppend(err, "failed to stop the provider") 32 | } 33 | 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /processors/remote/add.go: -------------------------------------------------------------------------------- 1 | package remote 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/nanobox/commands/registry" 7 | "github.com/nanobox-io/nanobox/models" 8 | "github.com/nanobox-io/nanobox/util" 9 | "github.com/nanobox-io/nanobox/util/config" 10 | "github.com/nanobox-io/nanobox/util/display" 11 | "github.com/nanobox-io/nanobox/util/odin" 12 | ) 13 | 14 | func Add(envModel *models.Env, appName, alias string) error { 15 | 16 | // ensure the env model has been generated 17 | if err := envModel.Generate(); err != nil { 18 | return util.ErrorAppend(err, "unable to generate the environment") 19 | } 20 | 21 | // set the alias to be the default its missing 22 | if alias == "" { 23 | alias = "default" 24 | } 25 | 26 | // set the appName to the folder name if its missing 27 | if appName == "" { 28 | appName = config.LocalDirName() 29 | } 30 | 31 | endpoint := registry.GetString("endpoint") 32 | // set the endpoint to nanobox if it's missing 33 | if endpoint == "" { 34 | endpoint = "nanobox" 35 | } 36 | 37 | // set the odin endpoint 38 | odin.SetEndpoint(endpoint) 39 | 40 | // fetch the odin app 41 | app, err := odin.App(appName) 42 | if err != nil { 43 | fmt.Printf("! Sorry, but you don't have access to %s\n%s\n", appName, err) 44 | return nil 45 | } 46 | 47 | // ensure the links map is initialized 48 | if envModel.Remotes == nil { 49 | envModel.Remotes = map[string]models.Remote{} 50 | } 51 | 52 | envModel.Remotes[alias] = models.Remote{ 53 | ID: app.ID, 54 | Name: app.Name, 55 | Endpoint: endpoint, 56 | } 57 | 58 | if err := envModel.Save(); err != nil { 59 | return util.ErrorAppend(err, "failed to save remote") 60 | } 61 | 62 | fmt.Printf("\n%s Codebase linked to %s\n", display.TaskComplete, appName) 63 | 64 | if alias != "default" { 65 | fmt.Printf(" through the '%s' alias\n\n", alias) 66 | } else { 67 | fmt.Println() 68 | } 69 | 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /processors/remote/list.go: -------------------------------------------------------------------------------- 1 | package remote 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/nanobox-io/nanobox/models" 8 | ) 9 | 10 | // List ... 11 | func List(env *models.Env) error { 12 | 13 | if len(env.Remotes) == 0 { 14 | fmt.Printf("\n! This codebase is not connected to any apps\n\n") 15 | return nil 16 | } 17 | 18 | // set the left column width to the longest name 19 | leftColWidth := len(longestName(env)) + 2 20 | 21 | // unless the longest name is less than 10 characters :) 22 | if leftColWidth < 10 { 23 | leftColWidth = 10 24 | } 25 | 26 | // print the header 27 | margin := strings.Repeat(" ", leftColWidth-8) 28 | fmt.Printf("\nApp Name%s: Alias\n", margin) 29 | separator := strings.Repeat("-", leftColWidth+len(longestAlias(env))+1) 30 | fmt.Printf("%s\n", separator) 31 | 32 | // print the table 33 | for alias, remote := range env.Remotes { 34 | margin := strings.Repeat(" ", leftColWidth-len(remote.Name)) 35 | fmt.Printf("%s%s: %s\n", remote.Name, margin, alias) 36 | } 37 | 38 | // end with a newline 39 | fmt.Println() 40 | 41 | return nil 42 | } 43 | 44 | // returns the longest name 45 | func longestName(env *models.Env) string { 46 | longest := "" 47 | 48 | for _, remote := range env.Remotes { 49 | if len(remote.Name) > len(longest) { 50 | longest = remote.Name 51 | } 52 | } 53 | 54 | return longest 55 | } 56 | 57 | // returns the longest alias 58 | func longestAlias(env *models.Env) string { 59 | longest := "" 60 | 61 | for alias := range env.Remotes { 62 | if len(alias) > len(longest) { 63 | longest = alias 64 | } 65 | } 66 | 67 | return longest 68 | } 69 | -------------------------------------------------------------------------------- /processors/remote/remove.go: -------------------------------------------------------------------------------- 1 | package remote 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/nanobox/models" 7 | "github.com/nanobox-io/nanobox/util" 8 | "github.com/nanobox-io/nanobox/util/display" 9 | ) 10 | 11 | // Remove ... 12 | func Remove(envModel *models.Env, alias string) error { 13 | 14 | delete(envModel.Remotes, alias) 15 | 16 | // persist the model 17 | if err := envModel.Save(); err != nil { 18 | return util.ErrorAppend(err, "failed to remove remote") 19 | } 20 | 21 | fmt.Printf("\n%s %s remote removed\n\n", display.TaskComplete, alias) 22 | 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /processors/server/setup.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/jcelliott/lumber" 7 | 8 | "github.com/nanobox-io/nanobox/util" 9 | "github.com/nanobox-io/nanobox/util/config" 10 | "github.com/nanobox-io/nanobox/util/display" 11 | "github.com/nanobox-io/nanobox/util/service" 12 | ) 13 | 14 | func Setup() error { 15 | if service.Running("nanobox-server") { 16 | return nil 17 | } 18 | 19 | // run as admin 20 | if !util.IsPrivileged() { 21 | return reExecPrivilageStart() 22 | } 23 | 24 | // TEMP: we need to remove the old nanobox-vpn just incase it is left over 25 | // we will not catch errors here because if it doesnt exist or it breaks it 26 | // should not stop us from creating the new nanobox-server 27 | service.Stop("nanobox-vpn") 28 | service.Remove("nanobox-vpn") 29 | 30 | // create the service this call is idempotent so we shouldnt need to check 31 | if err := service.Create("nanobox-server", []string{config.NanoboxPath(), "server"}); err != nil { 32 | return err 33 | } 34 | 35 | // start the service 36 | return Start() 37 | 38 | } 39 | 40 | // reExecPrivilageStart re-execs the current process with a privileged user 41 | func reExecPrivilageStart() error { 42 | display.PauseTask() 43 | defer display.ResumeTask() 44 | 45 | display.PrintRequiresPrivilege("to start the server") 46 | 47 | cmd := fmt.Sprintf("\"%s\" env server start", config.NanoboxPath()) 48 | if err := util.PrivilegeExec(cmd); err != nil { 49 | lumber.Error("server:reExecPrivilageStart:util.PrivilegeExec(%s): %s", cmd, err) 50 | return err 51 | } 52 | 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /processors/server/start.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/nanobox-io/nanobox/util" 7 | "github.com/nanobox-io/nanobox/util/service" 8 | ) 9 | 10 | func Start() error { 11 | // run as admin 12 | // the reExecPrivilageStart function is defined in the setup 13 | // since the service create is idempotent it is fine to only have one 14 | // start command for the server 15 | if !util.IsPrivileged() { 16 | return reExecPrivilageStart() 17 | } 18 | 19 | fn := func() error { 20 | return service.Start("nanobox-server") 21 | } 22 | 23 | return util.Retry(fn, 3, time.Second) 24 | } 25 | -------------------------------------------------------------------------------- /processors/server/stop.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/jcelliott/lumber" 7 | 8 | "github.com/nanobox-io/nanobox/util" 9 | "github.com/nanobox-io/nanobox/util/config" 10 | "github.com/nanobox-io/nanobox/util/display" 11 | "github.com/nanobox-io/nanobox/util/service" 12 | ) 13 | 14 | func Stop() error { 15 | // run as admin 16 | if !util.IsPrivileged() { 17 | return reExecPrivilageStop() 18 | } 19 | 20 | return service.Stop("nanobox-server") 21 | } 22 | 23 | // reExecPrivilageStop re-execs the current process with a privileged user 24 | func reExecPrivilageStop() error { 25 | display.PauseTask() 26 | defer display.ResumeTask() 27 | 28 | display.PrintRequiresPrivilege("to stop the server") 29 | 30 | cmd := fmt.Sprintf("\"%s\" env server stop", config.NanoboxPath()) 31 | 32 | if err := util.PrivilegeExec(cmd); err != nil { 33 | lumber.Error("server:reExecPrivilageStop:util.PrivilegeExec(%s): %s", cmd, err) 34 | return err 35 | } 36 | 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /processors/server/teardown.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/jcelliott/lumber" 7 | 8 | "github.com/nanobox-io/nanobox/util" 9 | "github.com/nanobox-io/nanobox/util/config" 10 | "github.com/nanobox-io/nanobox/util/display" 11 | "github.com/nanobox-io/nanobox/util/service" 12 | ) 13 | 14 | func Teardown() error { 15 | // run as admin 16 | if !util.IsPrivileged() { 17 | return reExecPrivilageRemove() 18 | } 19 | 20 | // make sure its stopped first 21 | if err := Stop(); err != nil { 22 | return err 23 | } 24 | 25 | return service.Remove("nanobox-server") 26 | } 27 | 28 | // reExecPrivilageRemove re-execs the current process with a privileged user 29 | func reExecPrivilageRemove() error { 30 | display.PauseTask() 31 | defer display.ResumeTask() 32 | 33 | display.PrintRequiresPrivilege("to remove the server") 34 | 35 | cmd := fmt.Sprintf("\"%s\" env server teardown", config.NanoboxPath()) 36 | 37 | if err := util.PrivilegeExec(cmd); err != nil { 38 | lumber.Error("server:reExecPrivilageRemove:util.PrivilegeExec(%s): %s", cmd, err) 39 | return err 40 | } 41 | 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /processors/start.go: -------------------------------------------------------------------------------- 1 | package processors 2 | 3 | import ( 4 | "github.com/nanobox-io/nanobox/processors/provider" 5 | "github.com/nanobox-io/nanobox/processors/server" 6 | ) 7 | 8 | // Start starts the provider (VM) 9 | func Start() error { 10 | // start the nanobox server 11 | if err := server.Setup(); err != nil { 12 | return err 13 | } 14 | 15 | // run a provider setup 16 | return provider.Setup() 17 | } 18 | -------------------------------------------------------------------------------- /processors/status.go: -------------------------------------------------------------------------------- 1 | package processors 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/nanobox-io/nanobox/models" 8 | "github.com/nanobox-io/nanobox/util/provider" 9 | ) 10 | 11 | type status struct { 12 | envName string 13 | appName string 14 | status string 15 | directory string 16 | } 17 | 18 | // displays status about provider status and running apps 19 | func Status() error { 20 | fmt.Printf("Status: %s\n", provider.Status()) 21 | fmt.Println() 22 | 23 | statuses := []status{} 24 | 25 | envs, _ := models.AllEnvs() 26 | for _, env := range envs { 27 | 28 | apps, _ := env.Apps() 29 | for _, app := range apps { 30 | statuses = append(statuses, status{ 31 | envName: env.Name, 32 | appName: app.DisplayName(), 33 | status: app.Status, 34 | directory: env.Directory, 35 | }) 36 | } 37 | } 38 | 39 | if len(statuses) == 0 { 40 | return nil 41 | } 42 | 43 | // print the header 44 | nameLength := longestName(statuses) 45 | pathLength := longestPath(statuses) 46 | 47 | fmtString := fmt.Sprintf("%%-%ds : %%-7s : %%-%ds\n", nameLength, pathLength) 48 | 49 | fmt.Printf(fmtString, "App", "Status", "Path") 50 | fmt.Println(strings.Repeat("-", nameLength+pathLength+13)) 51 | 52 | for _, status := range statuses { 53 | fmt.Printf(fmtString, fmt.Sprintf("%s (%s)", status.envName, status.appName), status.status, status.directory) 54 | } 55 | 56 | // end with a newline 57 | fmt.Println() 58 | 59 | return nil 60 | } 61 | 62 | // returns the longest name 63 | func longestName(statuses []status) (rtn int) { 64 | 65 | for _, status := range statuses { 66 | name := fmt.Sprintf("%s (%s)", status.envName, status.appName) 67 | if len(name) > rtn { 68 | rtn = len(name) 69 | } 70 | } 71 | 72 | return 73 | } 74 | 75 | // returns the longest name 76 | func longestPath(statuses []status) (rtn int) { 77 | 78 | for _, status := range statuses { 79 | if len(status.directory) > rtn { 80 | rtn = len(status.directory) 81 | } 82 | } 83 | 84 | return 85 | } 86 | -------------------------------------------------------------------------------- /processors/submit_log.go: -------------------------------------------------------------------------------- 1 | package processors 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "strings" 7 | 8 | "github.com/nanobox-io/nanobox/models" 9 | "github.com/nanobox-io/nanobox/util" 10 | "github.com/nanobox-io/nanobox/util/config" 11 | "github.com/nanobox-io/nanobox/util/display" 12 | "github.com/nanobox-io/nanobox/util/odin" 13 | ) 14 | 15 | func SubmitLog(args string) error { 16 | // if we are running as privilage we dont submit 17 | if util.IsPrivileged() { 18 | return nil 19 | } 20 | 21 | auth, _ := models.LoadAuth() 22 | conf, _ := models.LoadConfig() 23 | 24 | // if we are in ci mode or we are setting a configuration 25 | // leave here 26 | if strings.Contains(args, "login") || strings.Contains(args, "config") || conf.CIMode { 27 | return nil 28 | } 29 | 30 | if auth.Key == "" && !conf.Anonymous { 31 | display.LoginRequired() 32 | err := Login("", "", "") 33 | if err != nil { 34 | return err 35 | } 36 | } 37 | 38 | app := "" 39 | 40 | env, err := models.FindEnvByID(config.EnvID()) 41 | if strings.Contains(args, "deploy") || strings.Contains(args, "tunnel") || strings.Contains(args, "console") { 42 | if err == nil { 43 | remote, ok := env.Remotes["default"] 44 | if ok { 45 | app = remote.ID 46 | } 47 | } 48 | } 49 | 50 | // tell nanobox 51 | go odin.SubmitEvent( 52 | fmt.Sprintf("desktop/%s", args), 53 | fmt.Sprintf("desktop command: nanobox %s", args), 54 | app, 55 | map[string]interface{}{ 56 | "os": runtime.GOOS, 57 | "provider": conf.Provider, 58 | "mount-type": conf.MountType, 59 | "boxfile": env.UserBoxfile, 60 | }, 61 | ) 62 | 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /processors/tunnel.go: -------------------------------------------------------------------------------- 1 | package processors 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nanobox-io/nanobox/commands/registry" 7 | "github.com/nanobox-io/nanobox/helpers" 8 | "github.com/nanobox-io/nanobox/models" 9 | "github.com/nanobox-io/nanobox/util" 10 | "github.com/nanobox-io/nanobox/util/config" 11 | "github.com/nanobox-io/nanobox/util/nanoagent" 12 | "github.com/nanobox-io/nanobox/util/odin" 13 | ) 14 | 15 | func Tunnel(envModel *models.Env, tunnelConfig models.TunnelConfig) error { 16 | // fetch the remote 17 | remote, ok := envModel.Remotes[tunnelConfig.AppName] 18 | if ok { 19 | // set the odin endpoint 20 | odin.SetEndpoint(remote.Endpoint) 21 | // set the app id 22 | tunnelConfig.AppName = remote.Name 23 | } 24 | 25 | // set the app id to the directory name if it's default 26 | if tunnelConfig.AppName == "default" { 27 | tunnelConfig.AppName = config.AppName() 28 | } 29 | 30 | // set odins endpoint if the arguement is passed 31 | if endpoint := registry.GetString("endpoint"); endpoint != "" { 32 | odin.SetEndpoint(endpoint) 33 | } 34 | 35 | // validate access to the app 36 | if err := helpers.ValidateOdinApp(tunnelConfig.AppName); err != nil { 37 | return util.ErrorAppend(err, "unable to validate app") 38 | } 39 | 40 | // initiate a tunnel session with odin 41 | tunInfo, err := odin.EstablishTunnel(tunnelConfig) 42 | if err != nil { 43 | return util.ErrorAppend(err, "failed to initiate a remote tunnel session") 44 | } 45 | 46 | // set a default port if the user didn't specify 47 | if tunnelConfig.ListenPort == 0 { 48 | tunnelConfig.ListenPort = tunInfo.Port 49 | } 50 | 51 | // connect up to the session 52 | if err := nanoagent.Tunnel(tunInfo.Token, tunInfo.URL, fmt.Sprint(tunnelConfig.ListenPort), tunnelConfig.Component); err != nil { 53 | return util.ErrorAppend(err, "failed to connect to remote tunnel session") 54 | } 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /processors/types.go: -------------------------------------------------------------------------------- 1 | package processors 2 | 3 | type DeployConfig struct { 4 | App string 5 | Message string 6 | Force bool 7 | } 8 | 9 | type ConsoleConfig struct { 10 | App string 11 | Host string 12 | } 13 | -------------------------------------------------------------------------------- /processors/update.go: -------------------------------------------------------------------------------- 1 | // This update processor updates the images on nanobox local 2 | 3 | package processors 4 | 5 | import ( 6 | "strings" 7 | "time" 8 | 9 | "github.com/jcelliott/lumber" 10 | "github.com/nanobox-io/golang-docker-client" 11 | 12 | process_provider "github.com/nanobox-io/nanobox/processors/provider" 13 | "github.com/nanobox-io/nanobox/util" 14 | "github.com/nanobox-io/nanobox/util/display" 15 | ) 16 | 17 | func Update() error { 18 | 19 | // init docker client 20 | if err := process_provider.Init(); err != nil { 21 | return util.ErrorAppend(err, "failed to init docker client") 22 | } 23 | 24 | // update all the nanobox images 25 | if err := pullImages(); err != nil { 26 | return util.ErrorAppend(err, "failed to pull images") 27 | } 28 | 29 | return nil 30 | } 31 | 32 | func pullImages() error { 33 | display.OpenContext("Updating Images") 34 | defer display.CloseContext() 35 | 36 | images, err := docker.ImageList() 37 | if err != nil { 38 | return err 39 | } 40 | 41 | for _, image := range images { 42 | if image.Slug == "" { 43 | continue 44 | } 45 | if !strings.Contains(image.Slug, "nanobox/") { 46 | continue 47 | } 48 | display.StartTask("Pulling %s image", image.Slug) 49 | 50 | // generate a docker percent display 51 | dockerPercent := &display.DockerPercentDisplay{ 52 | Output: display.NewStreamer("info"), 53 | } 54 | 55 | // pull the build image 56 | imagePullFunc := func() error { 57 | _, err := docker.ImagePull(image.Slug, dockerPercent) 58 | return err 59 | } 60 | 61 | if err := util.Retry(imagePullFunc, 5, time.Second); err != nil { 62 | lumber.Error("code:pullBuildImage:docker.ImagePull(%s, nil): %s", image.Slug, err.Error()) 63 | display.ErrorTask() 64 | return util.ErrorAppend(err, "failed to pull docker image (%s)", image.Slug) 65 | } 66 | 67 | display.StopTask() 68 | } 69 | 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # disable cgo for true static binaries (will work on alpine linux) 5 | export CGO_ENABLED=0; 6 | 7 | # vfor versioning 8 | getCurrCommit() { 9 | echo `git rev-parse --short HEAD | tr -d "[ \r\n\']"` 10 | } 11 | 12 | getCurrTag() { 13 | echo `git describe --always --tags --abbrev=0 | tr -d "[v\r\n]"` 14 | } 15 | 16 | BUILD_DATE=`date -u +%y%m%dT%H%M` 17 | # ^for versioning 18 | 19 | # remove any previous builds that may have failed 20 | [ -e "./.build" ] && \ 21 | echo "Cleaning up old builds..." && \ 22 | rm -rf "./.build" 23 | 24 | printf "\nBuilding nanobox...\n" 25 | 26 | # build nanobox 27 | gox -ldflags "-s -w -X github.com/nanobox-io/nanobox/util/odin.apiKey=$API_KEY \ 28 | -X github.com/nanobox-io/nanobox/models.nanoVersion=$(getCurrTag) \ 29 | -X github.com/nanobox-io/nanobox/models.nanoCommit=$(getCurrCommit) \ 30 | -X github.com/nanobox-io/nanobox/models.nanoBuild=$BUILD_DATE" \ 31 | -osarch "darwin/amd64 linux/amd64 windows/amd64" -output="./.build/v2/{{.OS}}/{{.Arch}}/nanobox" 32 | 33 | printf "\nWriting version file...\n" 34 | echo -en "Nanobox Version $(getCurrTag)-$BUILD_DATE ($(getCurrCommit))" > ./.build/v2/version 35 | 36 | printf "\nBuilding nanobox updater...\n" 37 | 38 | # change into updater directory and build nanobox updater 39 | cd ./updater && gox -osarch "darwin/amd64 linux/amd64 windows/amd64" -ldflags="-s" -output="../.build/v2/{{.OS}}/{{.Arch}}/nanobox-update" 40 | 41 | #cd .. 42 | 43 | #printf "\nCompacting binaries...\n" 44 | #upx ./.build/v2/*/amd64/nanobox* 45 | -------------------------------------------------------------------------------- /scripts/upload.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # try and use the correct MD5 lib (depending on user OS darwin/linux) 5 | MD5=$(which md5 || echo "$(which md5sum) --tag" ) 6 | 7 | echo "Generating md5s..." 8 | 9 | # look through each os/arch/file and generate an md5 for each 10 | for os in $(ls ./.build/v2); do 11 | for arch in $(ls ./.build/v2/${os}); do 12 | for file in $(ls ./.build/v2/${os}/${arch}); do 13 | cat "./.build/v2/${os}/${arch}/${file}" | ${MD5} >> "./.build/v2/${os}/${arch}/${file}.md5" 14 | done 15 | done 16 | done 17 | 18 | # upload to AWS S3 19 | echo "Uploading builds to S3..." 20 | aws s3 sync ./.build/v2/ s3://tools.nanobox.io/nanobox/v2 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --region us-east-1 21 | -------------------------------------------------------------------------------- /util/config/config_internal_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/mitchellh/go-homedir" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestGlobalDir(t *testing.T) { 10 | dir := GlobalDir() 11 | if !strings.HasSuffix(dir, ".nanobox") { 12 | t.Errorf("missing nanobox suffix") 13 | } 14 | } 15 | 16 | func TestLocalDir(t *testing.T) { 17 | dir := LocalDir() 18 | // this is 'nanobox', because the boxfile is at the root level. localdir returns 19 | // a parent boxfile if none is found in the current directory 20 | if !strings.HasSuffix(dir, "nanobox") { 21 | t.Errorf("local dir mismatch '%s'", dir) 22 | } 23 | } 24 | 25 | func TestLocalDirName(t *testing.T) { 26 | dir := LocalDirName() 27 | if dir != "nanobox" { 28 | t.Errorf("local dir name mismatch '%s'", dir) 29 | } 30 | } 31 | 32 | func TestSSHDir(t *testing.T) { 33 | homedir, _ := homedir.Dir() 34 | if SSHDir() != homedir+"/.ssh" { 35 | t.Errorf("incorrect ssh directory") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /util/config/files.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | // "fmt" 5 | // "io/ioutil" 6 | // "os" 7 | "path/filepath" 8 | // "github.com/jcelliott/lumber" 9 | // "github.com/nanobox-io/nanobox/util" 10 | ) 11 | 12 | // Boxfile ... 13 | func Boxfile() string { 14 | return filepath.ToSlash(filepath.Join(LocalDir(), "boxfile.yml")) 15 | } 16 | -------------------------------------------------------------------------------- /util/config/vars.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "runtime" 10 | 11 | "github.com/nanobox-io/nanobox-boxfile" 12 | 13 | "github.com/nanobox-io/nanobox/util/fileutil" 14 | ) 15 | 16 | // AppName ... 17 | func AppName() string { 18 | 19 | // if no name is given use localDirName 20 | app := LocalDirName() 21 | 22 | // read boxfile and look for dev:name 23 | box := boxfile.NewFromPath(Boxfile()) 24 | devName := box.Node("dev").StringValue("name") 25 | 26 | // set the app name 27 | if devName != "" { 28 | app = devName 29 | } 30 | 31 | return app 32 | } 33 | 34 | // EnvID ... 35 | func EnvID() string { 36 | return fmt.Sprintf("%x", md5.Sum([]byte(LocalDir()))) 37 | } 38 | 39 | // NanoboxPath ... 40 | func NanoboxPath() string { 41 | 42 | programName, err := os.Executable() 43 | if err == nil { 44 | return programName 45 | } 46 | 47 | // lookup the full path to nanobox 48 | path, err := exec.LookPath(os.Args[0]) 49 | if err == nil { 50 | return path 51 | } 52 | 53 | // if args[0] was a path to nanobox already 54 | if fileutil.Exists(programName) { 55 | return programName 56 | } 57 | 58 | // unable to find the full path, just return what was called 59 | return programName 60 | } 61 | 62 | // the path where the vpn is located 63 | func VpnPath() string { 64 | bridgeClient := "nanobox-vpn" 65 | 66 | // lookup the full path to nanobox 67 | path, err := exec.LookPath(bridgeClient) 68 | if err == nil { 69 | return path 70 | } 71 | 72 | cmd := filepath.Join(BinDir(), bridgeClient) 73 | 74 | if runtime.GOOS == "windows" { 75 | cmd = fmt.Sprintf(`%s\%s.exe`, BinDir(), bridgeClient) 76 | } 77 | 78 | return cmd 79 | } 80 | -------------------------------------------------------------------------------- /util/dhcp/dhcp_test.go: -------------------------------------------------------------------------------- 1 | package dhcp_test 2 | 3 | import ( 4 | "net" 5 | "os" 6 | "testing" 7 | 8 | "github.com/nanobox-io/nanobox/util/dhcp" 9 | ) 10 | 11 | // TestMain ... 12 | func TestMain(m *testing.M) { 13 | dhcp.Flush() 14 | os.Exit(m.Run()) 15 | } 16 | 17 | // TestReservingIps ... 18 | func TestReservingIps(t *testing.T) { 19 | ipOne, err := dhcp.ReserveGlobal() 20 | if err != nil { 21 | t.Errorf("unable to reserve ip", err) 22 | } 23 | ipTwo, err := dhcp.ReserveGlobal() 24 | if err != nil { 25 | t.Errorf("unable to reserve ip", err) 26 | } 27 | ipThree, err := dhcp.ReserveLocal() 28 | if err != nil { 29 | t.Errorf("unable to reserve ip", err) 30 | } 31 | if ipOne.String() != "192.168.99.51" || ipTwo.String() != "192.168.99.52" || ipThree.String() != "172.21.0.2" { 32 | t.Errorf("incorrect ip addresses", ipOne, ipTwo, ipThree) 33 | } 34 | } 35 | 36 | // TestReturnIP ... 37 | func TestReturnIP(t *testing.T) { 38 | err := dhcp.ReturnIP(net.ParseIP("192.168.99.50")) 39 | if err != nil { 40 | t.Errorf("unable to return ip", err) 41 | } 42 | err = dhcp.ReturnIP(net.ParseIP("192.168.99.51")) 43 | if err != nil { 44 | t.Errorf("unable to return ip", err) 45 | } 46 | err = dhcp.ReturnIP(net.ParseIP("192.168.0.50")) 47 | if err != nil { 48 | t.Errorf("unable to return ip", err) 49 | } 50 | } 51 | 52 | // TestReuseIP ... 53 | func TestReuseIP(t *testing.T) { 54 | one, err := dhcp.ReserveGlobal() 55 | if err != nil { 56 | t.Errorf("unable to reserve ip", err) 57 | } 58 | ipTwo, err := dhcp.ReserveGlobal() 59 | if err != nil { 60 | t.Errorf("unable to reserve ip", err) 61 | } 62 | three, err := dhcp.ReserveLocal() 63 | if err != nil { 64 | t.Errorf("unable to reserve ip", err) 65 | } 66 | err = dhcp.ReturnIP(ipTwo) 67 | if err != nil { 68 | t.Errorf("unable to return ip", err) 69 | } 70 | ipTwoAgain, err := dhcp.ReserveGlobal() 71 | if err != nil { 72 | t.Errorf("unable to reserve ip", err) 73 | } 74 | if !ipTwo.Equal(ipTwoAgain) { 75 | t.Errorf("i should ahve recieved a repeat of %s but i got %s", ipTwo.String(), ipTwoAgain.String()) 76 | } 77 | dhcp.ReturnIP(one) 78 | dhcp.ReturnIP(three) 79 | } 80 | -------------------------------------------------------------------------------- /util/display/characters.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package display 4 | 5 | // ... 6 | var ( 7 | TaskSpinner = []string{"⣷", "⣯", "⣟", "⡿", "⢿", "⣻", "⣽", "⣾"} 8 | TaskComplete = "✓" 9 | TaskPause = "*" 10 | ) 11 | -------------------------------------------------------------------------------- /util/display/characters_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package display 4 | 5 | var ( 6 | TaskSpinner = []string{"\\", "|", "/", "-"} 7 | TaskComplete = "√" 8 | TaskPause = "*" 9 | ) 10 | -------------------------------------------------------------------------------- /util/display/download_percent.go: -------------------------------------------------------------------------------- 1 | package display 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | const bytesPerMB = 1024 * 1024 11 | 12 | type DownloadPercent struct { 13 | current int64 14 | Total int64 15 | Output io.Writer 16 | } 17 | 18 | func (dp *DownloadPercent) Copy(writer io.Writer, reader io.Reader) (err error) { 19 | fmt.Println() 20 | // initialize variables 21 | buf := make([]byte, 32*1024) 22 | dp.current = 0 23 | 24 | if dp.Output == nil { 25 | dp.Output = os.Stdout 26 | } 27 | 28 | for { 29 | dp.UpdateDisplay() 30 | readBytes, readErr := reader.Read(buf) 31 | if readBytes > 0 { 32 | writtenBytes, writeErr := writer.Write(buf[0:readBytes]) 33 | if writeErr != nil { 34 | err = writeErr 35 | break 36 | } 37 | if readBytes != writtenBytes { 38 | err = io.ErrShortWrite 39 | break 40 | } 41 | 42 | } 43 | 44 | // success 45 | if readErr == io.EOF { 46 | break 47 | } 48 | 49 | // failure 50 | if readErr != nil { 51 | err = readErr 52 | break 53 | } 54 | 55 | dp.current += int64(readBytes) 56 | dp.UpdateDisplay() 57 | } 58 | 59 | if err == nil { 60 | dp.current = dp.Total 61 | dp.UpdateDisplay() 62 | } 63 | fmt.Println() 64 | return err 65 | } 66 | 67 | func (dp *DownloadPercent) UpdateDisplay() { 68 | // clear the link 69 | fmt.Fprintf(dp.Output, "\r\x1b[K") 70 | 71 | if dp.Total == 0 { 72 | dp.SimpleDisplay() 73 | return 74 | } 75 | 76 | // show download progress: 0.0/0.0MB [*** progress *** 0.0%] 77 | currentInMB := float64(dp.current) / bytesPerMB 78 | totalInMB := float64(dp.Total) / bytesPerMB 79 | percent := (float64(dp.current) / float64(dp.Total)) * 100 80 | 81 | fmt.Fprintf(dp.Output, "\r %.2f/%.2fMB [%-41s %.2f%%]", currentInMB, totalInMB, strings.Repeat("*", int(percent/2.5)), percent) 82 | 83 | } 84 | 85 | func (dp *DownloadPercent) SimpleDisplay() { 86 | fmt.Fprintf(dp.Output, " %.2fMB", float64(dp.current)/bytesPerMB) 87 | } 88 | -------------------------------------------------------------------------------- /util/display/privilege_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package display 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | // PrintRequiresPrivilege prints a message informing privilege escalation is required 10 | func PrintRequiresPrivilege(reason string) { 11 | fmt.Printf("Root privileges are required %s. Your system password may be requested...\n", reason) 12 | } 13 | -------------------------------------------------------------------------------- /util/display/privilege_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package display 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | // PrintRequiresPrivilege prints a message informing privilege escalation is required 10 | func PrintRequiresPrivilege(reason string) { 11 | fmt.Printf("Administrator privileges are required %s.\n", reason) 12 | fmt.Println("Another window will be opened as the Administrator and permission may be requested.") 13 | 14 | // block here until the user hits enter. It's not ideal, but we need to make 15 | // sure they see the new window open if it requests their password. 16 | fmt.Println("Enter to continue:") 17 | var input string 18 | fmt.Scanln(&input) 19 | } 20 | -------------------------------------------------------------------------------- /util/display/prompt.go: -------------------------------------------------------------------------------- 1 | package display 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | // get the username 11 | func ReadUsername() (string, error) { 12 | return Ask("Nanobox Username") 13 | } 14 | 15 | func Ask(question string) (string, error) { 16 | fmt.Printf("%s: ", question) 17 | 18 | reader := bufio.NewReader(os.Stdin) 19 | str, err := reader.ReadString('\n') 20 | 21 | return strings.TrimSpace(str), err 22 | 23 | } 24 | -------------------------------------------------------------------------------- /util/display/prompt_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package display 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | 9 | "golang.org/x/crypto/ssh/terminal" 10 | ) 11 | 12 | // ReadPassword reads a password from the terminal and masks the input 13 | func ReadPassword(label string) (string, error) { 14 | fmt.Printf("%s Password: ", label) 15 | 16 | pass, err := terminal.ReadPassword(int(os.Stdin.Fd())) 17 | fmt.Println("") 18 | 19 | return string(pass), err 20 | } 21 | -------------------------------------------------------------------------------- /util/display/prompt_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package display 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | 9 | "golang.org/x/crypto/ssh/terminal" 10 | ) 11 | 12 | // ReadPassword reads a password from the terminal and masks the input 13 | func ReadPassword(label string) (string, error) { 14 | 15 | // Fetch the current state of the terminal so it can be restored later 16 | oldState, err := terminal.GetState(int(os.Stdin.Fd())) 17 | if err != nil { 18 | return "", err 19 | } 20 | // Turn off echo and make stdin blank 21 | terminal.MakeRaw(int(os.Stdin.Fd())) 22 | // Restore echo after the function exits 23 | defer terminal.Restore(int(os.Stdin.Fd()), oldState) 24 | 25 | fmt.Printf("%s Password: ", label) 26 | 27 | // Read the password from stdin 28 | t := terminal.NewTerminal(os.Stdin, "") 29 | pass, err := t.ReadPassword("") 30 | 31 | // Add a newline so the next output isn't next to the Password: 32 | fmt.Println("") 33 | 34 | if err != nil { 35 | return "", err 36 | } 37 | 38 | return pass, nil 39 | } 40 | -------------------------------------------------------------------------------- /util/display/stream.go: -------------------------------------------------------------------------------- 1 | package display 2 | 3 | // Streamer ... 4 | type Streamer struct { 5 | logLevel string 6 | prefixer *Prefixer 7 | captureOutput bool 8 | message []byte 9 | } 10 | 11 | // NewStreamer returns a new Streamer 12 | func NewStreamer(logLevel string) *Streamer { 13 | return &Streamer{ 14 | logLevel: logLevel, 15 | } 16 | } 17 | 18 | // NewPrefixedStreamer returns a new Streamer with a Prefixer 19 | func NewPrefixedStreamer(logLevel string, prefix string) Streamer { 20 | return Streamer{ 21 | logLevel: logLevel, 22 | prefixer: NewPrefixer(prefix), 23 | } 24 | } 25 | 26 | // CaptureOutput will write messages to the message var. 27 | func (s *Streamer) CaptureOutput(output bool) { 28 | s.captureOutput = output 29 | } 30 | 31 | // Write implements the io.Writer interface to write bytes on a writer 32 | func (s *Streamer) Write(p []byte) (n int, err error) { 33 | msg := string(p) 34 | 35 | // todo: likely want to goroutine this in case there isn't a channel receiver 36 | if s.captureOutput { 37 | s.message = append(s.message, p...) 38 | } 39 | 40 | // if we have a prefixer run the message through it 41 | if s.prefixer != nil { 42 | msg = s.prefixer.Parse(msg) 43 | } 44 | 45 | switch s.logLevel { 46 | case "info": 47 | err = Info(msg) 48 | case "warn": 49 | err = Warn(msg) 50 | case "error": 51 | err = Error(msg) 52 | case "debug": 53 | err = Debug(msg) 54 | case "trace": 55 | err = Trace(msg) 56 | } 57 | 58 | if err != nil { 59 | return 0, err 60 | } 61 | 62 | return len(p), nil 63 | } 64 | 65 | func (s Streamer) Output() string { 66 | return string(s.message) 67 | } 68 | -------------------------------------------------------------------------------- /util/dns/dns_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package dns 4 | 5 | // DetectHostsFile returns the location of the hosts file on unix machines 6 | func detectHostsFile() string { 7 | return "/etc/hosts" 8 | } 9 | 10 | // detectNewlineChar returns a newline character on unix machines 11 | func detectNewlineChar() string { 12 | return "\n" 13 | } 14 | -------------------------------------------------------------------------------- /util/dns/dns_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package dns 4 | 5 | import ( 6 | "os" 7 | ) 8 | 9 | // detectHostsFile returns the location of the hosts file after expanding the 10 | // %SystemRoot% environment variable on windows 11 | func detectHostsFile() string { 12 | return os.Getenv("SystemRoot") + "\\system32\\drivers\\etc\\hosts" 13 | } 14 | 15 | // detectNewlineChar returns a carriage return and a newline on windows 16 | func detectNewlineChar() string { 17 | return "\r\n" 18 | } 19 | -------------------------------------------------------------------------------- /util/fileutil/download.go: -------------------------------------------------------------------------------- 1 | package fileutil 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "os" 8 | ) 9 | 10 | // Download a file from a url to the path specified 11 | func Download(url, path string) error { 12 | // create the file at the path specified 13 | fd, err := os.Create(path) 14 | if err != nil { 15 | return fmt.Errorf("failed to create file to download into: %s", err.Error()) 16 | } 17 | 18 | // ensure the file descriptor is closed 19 | defer fd.Close() 20 | 21 | // fetch the file from the url specified 22 | res, err := http.Get(url) 23 | if err != nil { 24 | return fmt.Errorf("failed to fetch file to download: %s", err.Error()) 25 | } 26 | 27 | // ensure the body is closed 28 | defer res.Body.Close() 29 | 30 | // create a buffer to read into 31 | b := make([]byte, 2048) 32 | 33 | for { 34 | // read the response body (streaming) 35 | n, err := res.Body.Read(b) 36 | 37 | // write the contents of our buffer to the file 38 | fd.Write(b[:n]) 39 | 40 | if err != nil { 41 | if err == io.EOF { 42 | break 43 | } 44 | 45 | return fmt.Errorf("failed to read body: %s", err.Error()) 46 | } 47 | } 48 | 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /util/fileutil/file.go: -------------------------------------------------------------------------------- 1 | package fileutil 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | // Determines if a file or directory exists 8 | func Exists(path string) bool { 9 | // stat the file and check for an error 10 | _, err := os.Stat(path) 11 | 12 | return !os.IsNotExist(err) 13 | } 14 | -------------------------------------------------------------------------------- /util/locker/global.go: -------------------------------------------------------------------------------- 1 | package locker 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "sync" 7 | "time" 8 | 9 | "github.com/jcelliott/lumber" 10 | 11 | "github.com/nanobox-io/nanobox/models" 12 | ) 13 | 14 | var ( 15 | gln net.Listener 16 | gCount int 17 | mutex = sync.Mutex{} 18 | ) 19 | 20 | // GlobalLock locks on port 21 | func GlobalLock() error { 22 | lumber.Trace("global locking") 23 | 24 | // 25 | for { 26 | if success, _ := GlobalTryLock(); success { 27 | break 28 | } 29 | lumber.Trace("global lock waiting...") 30 | <-time.After(time.Second) 31 | } 32 | 33 | mutex.Lock() 34 | gCount++ 35 | lumber.Trace("global lock aqquired (%d)", gCount) 36 | mutex.Unlock() 37 | 38 | return nil 39 | } 40 | 41 | // GlobalTryLock ... 42 | func GlobalTryLock() (bool, error) { 43 | 44 | var err error 45 | 46 | // 47 | if gln != nil { 48 | return true, nil 49 | } 50 | 51 | // 52 | config, _ := models.LoadConfig() 53 | port := config.LockPort 54 | if port == 0 { 55 | port = 12345 56 | } 57 | 58 | // 59 | if gln, err = net.Listen("tcp", fmt.Sprintf(":%d", port)); err == nil { 60 | return true, nil 61 | } 62 | 63 | return false, nil 64 | } 65 | 66 | // GlobalUnlock removes the lock if im the last global unlock to be called; this 67 | // needs to be called EXACTLY he same number of tiems as lock 68 | func GlobalUnlock() error { 69 | 70 | mutex.Lock() 71 | gCount-- 72 | lumber.Trace("global lock released (%d)", gCount) 73 | mutex.Unlock() 74 | 75 | // 76 | if gCount > 0 || gln == nil { 77 | return nil 78 | } 79 | 80 | err := gln.Close() 81 | gln = nil 82 | 83 | return err 84 | } 85 | -------------------------------------------------------------------------------- /util/locker/local.go: -------------------------------------------------------------------------------- 1 | package locker 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "time" 7 | 8 | "github.com/jcelliott/lumber" 9 | "github.com/nanobox-io/nanobox/models" 10 | "github.com/nanobox-io/nanobox/util/config" 11 | ) 12 | 13 | var ( 14 | lln net.Listener // local locking network 15 | lCount int 16 | ) 17 | 18 | // LocalLock locks on port 19 | func LocalLock() error { 20 | 21 | // 22 | for { 23 | if success, _ := LocalTryLock(); success { 24 | break 25 | } 26 | lumber.Trace("local lock waiting...") 27 | <-time.After(time.Second) 28 | } 29 | 30 | mutex.Lock() 31 | lCount++ 32 | lumber.Trace("local lock aquired (%d)", lCount) 33 | mutex.Unlock() 34 | 35 | return nil 36 | } 37 | 38 | // LocalTryLock ... 39 | func LocalTryLock() (bool, error) { 40 | 41 | var err error 42 | 43 | // 44 | if lln != nil { 45 | return true, nil 46 | } 47 | 48 | // 49 | config, _ := models.LoadConfig() 50 | port := config.LockPort 51 | if port == 0 { 52 | port = 12345 53 | } 54 | port = port + localPort() 55 | 56 | // 57 | if lln, err = net.Listen("tcp", fmt.Sprintf(":%d", port)); err == nil { 58 | return true, nil 59 | } 60 | 61 | return false, nil 62 | } 63 | 64 | // LocalUnlock ... 65 | func LocalUnlock() (err error) { 66 | 67 | mutex.Lock() 68 | lCount-- 69 | lumber.Trace("local lock released (%d)", lCount) 70 | mutex.Unlock() 71 | 72 | // if im not the last guy to release my lock quit immidiately instead of closing 73 | // the connection 74 | if lCount > 0 || lln == nil { 75 | return nil 76 | } 77 | 78 | err = lln.Close() 79 | lln = nil 80 | 81 | return 82 | } 83 | 84 | // localPort ... 85 | func localPort() (num int) { 86 | 87 | b := []byte(config.EnvID()) 88 | 89 | // 90 | for i := 0; i < len(b); i++ { 91 | num = num + int(b[i]) 92 | } 93 | 94 | return num 95 | } 96 | -------------------------------------------------------------------------------- /util/mixpanel/mixpanel.go: -------------------------------------------------------------------------------- 1 | package mixpanel 2 | 3 | import ( 4 | "runtime" 5 | 6 | "github.com/jcelliott/lumber" 7 | mp "github.com/timehop/go-mixpanel" 8 | 9 | "github.com/nanobox-io/nanobox/models" 10 | "github.com/nanobox-io/nanobox/util" 11 | ) 12 | 13 | var token string 14 | 15 | func Report(args string) { 16 | 17 | go func() { 18 | config, _ := models.LoadConfig() 19 | // do not include ci in the mixpanel results 20 | if config.CIMode { 21 | return 22 | } 23 | mx := mp.NewMixpanel(token) 24 | id := util.UniqueID() 25 | 26 | err := mx.Track(id, "command", mp.Properties{ 27 | "os": runtime.GOOS, 28 | "provider": config.Provider, 29 | "mount-type": config.MountType, 30 | "args": args, 31 | "cpus": runtime.NumCPU(), 32 | }) 33 | 34 | if err != nil { 35 | lumber.Error("mixpanel(%s).Report(%s): %s", token, args, err) 36 | } 37 | }() 38 | } 39 | -------------------------------------------------------------------------------- /util/nanoagent/connect.go: -------------------------------------------------------------------------------- 1 | package nanoagent 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "net" 7 | "net/http" 8 | ) 9 | 10 | func connect(req *http.Request, location string) (net.Conn, error) { 11 | 12 | // if we can't connect to the server, lets bail out early 13 | conn, err := tls.Dial("tcp4", location, &tls.Config{InsecureSkipVerify: true}) 14 | if err != nil { 15 | return conn, fmt.Errorf("failed to establish connection to nanoagent: %s", err.Error()) 16 | } 17 | 18 | // we dont defer a conn.Close() here because we're returning the conn and 19 | // want it to remain open 20 | 21 | // make an http request 22 | if err := req.Write(conn); err != nil { 23 | return conn, fmt.Errorf("failed to establish console session with nanoagent: %s", err.Error()) 24 | } 25 | 26 | return conn, nil 27 | } 28 | -------------------------------------------------------------------------------- /util/nanoagent/ssh.go: -------------------------------------------------------------------------------- 1 | package nanoagent 2 | 3 | import ( 4 | "fmt" 5 | "github.com/nanobox-io/golang-ssh" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/nanobox-io/nanobox/util/display" 10 | ) 11 | 12 | func SSH(key, location string) error { 13 | 14 | // create the ssh client 15 | nanPass := ssh.Auth{Passwords: []string{key}} 16 | locationParts := strings.Split(location, ":") 17 | if len(locationParts) != 2 { 18 | return fmt.Errorf("location is not formatted properly (%s)", location) 19 | } 20 | 21 | // parse port 22 | port, err := strconv.Atoi(locationParts[1]) 23 | if err != nil { 24 | return fmt.Errorf("unable to convert port (%s)", locationParts[1]) 25 | } 26 | 27 | // establish connection 28 | client, err := ssh.NewNativeClient(key, locationParts[0], "SSH-2.0-nanobox", port, &nanPass) 29 | if err != nil { 30 | return fmt.Errorf("Failed to create new client - %s", err) 31 | } 32 | 33 | // printMOTD and warning 34 | display.MOTD() 35 | display.InfoProductionHost() 36 | 37 | // establish the ssh client connection and shell 38 | err = client.Shell() 39 | if err != nil && err.Error() != "exit status 255" { 40 | return fmt.Errorf("Failed to request shell - %s", err) 41 | } 42 | 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /util/os.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "regexp" 7 | "runtime" 8 | ) 9 | 10 | func OsDetect() (string, error) { 11 | switch runtime.GOOS { 12 | case "darwin": 13 | return getDarwin() 14 | case "windows": 15 | return "windows", nil 16 | case "linux": 17 | return "linux", nil 18 | } 19 | 20 | return "", fmt.Errorf("Unsupported operating system. Please contact support.") 21 | } 22 | 23 | func getDarwin() (string, error) { 24 | out, err := exec.Command("/usr/bin/sw_vers", "-productVersion").Output() 25 | if err != nil { 26 | return "", fmt.Errorf("Failed to retrieve version - %s", err.Error()) 27 | } 28 | r, _ := regexp.Compile("10\\.([0-9]+).*") 29 | match := r.FindStringSubmatch(string(out)) 30 | if len(match) != 2 { 31 | return "", fmt.Errorf("Failed to parse version") 32 | } 33 | 34 | return toDarwin(match[1]) 35 | } 36 | 37 | func toDarwin(v string) (string, error) { 38 | switch v { 39 | case "12": 40 | return "sierra", nil 41 | case "13": 42 | return "high sierra", nil 43 | default: 44 | return "incompatible", fmt.Errorf("Incompatible OSX version. Please contact support.") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /util/provider/bridge/bridge.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import ( 4 | "github.com/nanobox-io/nanobox/commands/server" 5 | "os/exec" 6 | ) 7 | 8 | type Bridge struct{} 9 | 10 | // not being used yet. but could be 11 | type Response struct { 12 | Output string 13 | ExitCode int 14 | } 15 | 16 | var runningBridge *exec.Cmd 17 | 18 | func init() { 19 | server.Register(&Bridge{}) 20 | } 21 | -------------------------------------------------------------------------------- /util/provider/bridge/start.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | 7 | "github.com/jcelliott/lumber" 8 | 9 | "github.com/nanobox-io/nanobox/commands/server" 10 | "github.com/nanobox-io/nanobox/util/config" 11 | ) 12 | 13 | type stream struct { 14 | } 15 | 16 | func (s stream) Write(p []byte) (n int, err error) { 17 | lumber.Info("bridge: %s", p) 18 | return len(p), nil 19 | } 20 | 21 | func Start(conf string) error { 22 | resp := &Response{} 23 | 24 | return server.ClientRun("Bridge.Start", conf, resp) 25 | } 26 | 27 | func (br *Bridge) Start(conf string, resp *Response) error { 28 | 29 | if runningBridge != nil { 30 | // if asked to start but we are already running 31 | // lets teardown the old and recreate with the new conf 32 | br.Stop(conf, resp) 33 | // return nil 34 | } 35 | 36 | runningBridge = exec.Command(config.VpnPath(), "--config", conf) 37 | runningBridge.Stdout = stream{} 38 | runningBridge.Stderr = stream{} 39 | 40 | err := runningBridge.Start() 41 | if err != nil { 42 | runningBridge = nil 43 | err = fmt.Errorf("failed to start cmd(%s --config %s): %s", config.VpnPath(), conf, err) 44 | } 45 | return err 46 | } 47 | -------------------------------------------------------------------------------- /util/provider/bridge/stop.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import ( 4 | "github.com/nanobox-io/nanobox/commands/server" 5 | ) 6 | 7 | func Stop() error { 8 | resp := &Response{} 9 | 10 | return server.ClientRun("Bridge.Stop", "", resp) 11 | } 12 | 13 | func (br *Bridge) Stop(config string, resp *Response) error { 14 | if runningBridge == nil { 15 | return nil 16 | } 17 | 18 | if err := runningBridge.Process.Kill(); err != nil { 19 | return err 20 | } 21 | 22 | if err := runningBridge.Wait(); err != nil { 23 | // it gets a signal but it shows up as an error 24 | // we dont want that 25 | return nil 26 | } 27 | 28 | // if we killed it and released the resources 29 | // remove running bridge 30 | runningBridge = nil 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /util/provider/dockermachine_mount_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package provider 4 | 5 | import ( 6 | "fmt" 7 | "path/filepath" 8 | 9 | "github.com/jcelliott/lumber" 10 | 11 | "github.com/nanobox-io/nanobox/models" 12 | ) 13 | 14 | // Mount mounts a share on a guest machine 15 | func (machine DockerMachine) addNetfsMount(local, host string) error { 16 | // make local the actual path instead of the link 17 | local, _ = filepath.EvalSymlinks(local) 18 | 19 | // ensure nfs-client is running 20 | cmd := []string{"sudo", "/usr/local/etc/init.d/nfs-client", "start"} 21 | if b, err := Run(cmd); err != nil { 22 | lumber.Debug("output: %s", b) 23 | return fmt.Errorf("nfs-client:%s", err.Error()) 24 | } 25 | 26 | // ensure the destination directory exists 27 | cmd = []string{"sudo", "/bin/mkdir", "-p", host} 28 | if b, err := Run(cmd); err != nil { 29 | lumber.Debug("output: %s", b) 30 | return fmt.Errorf("mkdir:%s", err.Error()) 31 | } 32 | 33 | // get the netfsmount options 34 | config, _ := models.LoadConfig() 35 | // additionalOptions := config.NetfsMountOpts 36 | 37 | // cmd = []string{"sudo", "/bin/mount", "-t", "nfs", "-o", "nolock"} 38 | cmd = []string{"sudo", "/bin/mount", "-t", "nfs"} 39 | if config.NetfsMountOpts != "" { 40 | cmd = append(cmd, "-o", config.NetfsMountOpts) 41 | } 42 | 43 | // the ip is hardcoded because the ip in docker is always set 44 | // no need to detect 45 | source := fmt.Sprintf("\"192.168.99.1:%s\"", local) 46 | cmd = append(cmd, source, host) 47 | if b, err := Run(cmd); err != nil { 48 | lumber.Debug("output: %s", b) 49 | return fmt.Errorf("mount: output: %s err:%s", b, err.Error()) 50 | } 51 | 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /util/provider/share/rpc.go: -------------------------------------------------------------------------------- 1 | package share 2 | 3 | import ( 4 | "github.com/nanobox-io/nanobox/commands/server" 5 | ) 6 | 7 | type ShareRPC struct{} 8 | 9 | type Response struct { 10 | Message string 11 | Success bool 12 | } 13 | 14 | func init() { 15 | server.Register(&ShareRPC{}) 16 | } 17 | -------------------------------------------------------------------------------- /util/provider/share/share.go: -------------------------------------------------------------------------------- 1 | package share 2 | -------------------------------------------------------------------------------- /util/provider/share/share_unix.go: -------------------------------------------------------------------------------- 1 | package share 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | ) 7 | 8 | // uid will grab the original uid that called sudo if set 9 | func uid() (uid int) { 10 | 11 | // 12 | uid = os.Geteuid() 13 | 14 | // if this process was started with sudo, sudo is nice enough to set 15 | // environment variables to inform us about the user that executed sudo 16 | // 17 | // let's see if this is the case 18 | if sudoUID := os.Getenv("SUDO_UID"); sudoUID != "" { 19 | 20 | // SUDO_UID was set, so we need to cast the string to an int 21 | if s, err := strconv.Atoi(sudoUID); err == nil { 22 | uid = s 23 | } 24 | } 25 | 26 | return 27 | } 28 | 29 | // gid will grab the original gid that called sudo if set 30 | func gid() (gid int) { 31 | 32 | // 33 | gid = os.Getgid() 34 | 35 | // if this process was started with sudo, sudo is nice enough to set 36 | // environment variables to inform us about the user that executed sudo 37 | // 38 | // let's see if this is the case 39 | if sudoGid := os.Getenv("SUDO_GID"); sudoGid != "" { 40 | 41 | // SUDO_UID was set, so we need to cast the string to an int 42 | if s, err := strconv.Atoi(sudoGid); err == nil { 43 | gid = s 44 | } 45 | } 46 | 47 | return 48 | } 49 | -------------------------------------------------------------------------------- /util/retry.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Retryable func() error 8 | 9 | func Retry(retryFunc Retryable, attempts int, delay time.Duration) (err error) { 10 | 11 | for i := 0; i < attempts; i++ { 12 | err = retryFunc() 13 | if err == nil { 14 | return 15 | } 16 | // delay 17 | <-time.After(delay) 18 | } 19 | return 20 | } 21 | -------------------------------------------------------------------------------- /util/service/create_darwin.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os/exec" 7 | "strings" 8 | ) 9 | 10 | func Create(name string, command []string) error { 11 | 12 | // setup config file 13 | if err := ioutil.WriteFile(serviceConfigFile(name), []byte(serviceConfig(name, command)), 0644); err != nil { 14 | return err 15 | } 16 | 17 | out, err := exec.Command("launchctl", "load", serviceConfigFile(name)).CombinedOutput() 18 | if err != nil { 19 | fmt.Errorf("out: %s, err: %s", out, err) 20 | } 21 | 22 | return nil 23 | } 24 | 25 | func serviceConfig(name string, command []string) string { 26 | return fmt.Sprintf(` 27 | 28 | 29 | 30 | Label 31 | io.%s 32 | 33 | ProgramArguments 34 | 35 | %s 36 | 37 | 38 | 39 | `, name, strings.Join(command, "\n ")) 40 | } 41 | -------------------------------------------------------------------------------- /util/service/create_linux.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "strings" 7 | ) 8 | 9 | func Create(name string, command []string) error { 10 | // setup config file 11 | return ioutil.WriteFile(serviceConfigFile(name), []byte(serviceConfig(name, command)), 0644) 12 | } 13 | 14 | func serviceConfig(name string, command []string) string { 15 | switch launchSystem() { 16 | case "systemd": 17 | 18 | return fmt.Sprintf(`[Unit] 19 | Description=%s 20 | After=network.target 21 | 22 | [Service] 23 | Type=simple 24 | ExecStart=%s 25 | 26 | [Install] 27 | WantedBy=multi-user.target 28 | `, name, strings.Join(command, " ")) 29 | 30 | case "upstart": 31 | 32 | return fmt.Sprintf(` 33 | script 34 | %s 35 | end script`, strings.Join(command, " ")) 36 | 37 | } 38 | 39 | return "" 40 | } 41 | -------------------------------------------------------------------------------- /util/service/create_windows.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | // "fmt" 5 | // // "io/ioutil" 6 | // "os/exec" 7 | // "strings" 8 | 9 | "golang.org/x/sys/windows/svc/eventlog" 10 | "golang.org/x/sys/windows/svc/mgr" 11 | ) 12 | 13 | func Create(name string, command []string) error { 14 | m, err := mgr.Connect() 15 | if err != nil { 16 | return err 17 | } 18 | defer m.Disconnect() 19 | 20 | // check to see if we need to create at all 21 | s, err := m.OpenService(name) 22 | if err == nil { 23 | s.Close() 24 | // jobs done 25 | return nil 26 | } 27 | 28 | // create the service 29 | args := []string{} 30 | if len(command) > 1 { 31 | args = command[1:] 32 | } 33 | s, err = m.CreateService(name, command[0], mgr.Config{DisplayName: name}, args...) 34 | if err != nil { 35 | return err 36 | } 37 | defer s.Close() 38 | 39 | err = eventlog.InstallAsEventCreate(name, eventlog.Error|eventlog.Warning|eventlog.Info) 40 | if err != nil { 41 | // s.Delete() 42 | // eventlog.Remove(name) 43 | // return fmt.Errorf("SetupEventLogSource() failed: %s", err) 44 | } 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /util/service/remove_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package service 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "os/exec" 9 | ) 10 | 11 | func Remove(name string) error { 12 | if len(removeCmd(name)) != 0 { 13 | cmd := removeCmd(name) 14 | out, err := exec.Command(cmd[0], cmd[1:]...).CombinedOutput() 15 | if err != nil { 16 | fmt.Errorf("out: %s, err: %s", out, err) 17 | } 18 | 19 | } 20 | 21 | os.Remove(serviceConfigFile(name)) 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /util/service/remove_windows.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.org/x/sys/windows/svc/eventlog" 7 | "golang.org/x/sys/windows/svc/mgr" 8 | ) 9 | 10 | func Remove(name string) error { 11 | // connect to the service manager 12 | m, err := mgr.Connect() 13 | if err != nil { 14 | return err 15 | } 16 | defer m.Disconnect() 17 | s, err := m.OpenService(name) 18 | if err != nil { 19 | // no service exists.. job done 20 | return nil 21 | } 22 | defer s.Close() 23 | 24 | err = s.Delete() 25 | if err != nil { 26 | return err 27 | } 28 | 29 | err = eventlog.Remove(name) 30 | if err != nil { 31 | return fmt.Errorf("RemoveEventLogSource() failed: %s", err) 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /util/service/service_darwin.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "time" 7 | ) 8 | 9 | func serviceConfigFile(name string) string { 10 | return fmt.Sprintf("/Library/LaunchDaemons/io.%s.plist", name) 11 | } 12 | 13 | func startCmd(name string) []string { 14 | return []string{"launchctl", "start", fmt.Sprintf("io.%s", name)} 15 | } 16 | 17 | func Running(name string) bool { 18 | <-time.After(500 * time.Millisecond) 19 | conn, err := net.DialTimeout("tcp", "127.0.0.1:23456", 100*time.Millisecond) 20 | if err != nil { 21 | return false 22 | } 23 | conn.Close() 24 | return true 25 | } 26 | 27 | func stopCmd(name string) []string { 28 | return []string{"launchctl", "stop", fmt.Sprintf("io.%s", name)} 29 | } 30 | 31 | func removeCmd(name string) []string { 32 | return []string{"launchctl", "remove", fmt.Sprintf("io.%s", name)} 33 | } 34 | -------------------------------------------------------------------------------- /util/service/service_linux.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os/exec" 7 | ) 8 | 9 | func serviceConfigFile(name string) string { 10 | fmtString := "" 11 | switch launchSystem() { 12 | case "systemd": 13 | fmtString = "/etc/systemd/system/%s.service" 14 | case "upstart": 15 | fmtString = "/etc/init/%s.conf" 16 | } 17 | return fmt.Sprintf(fmtString, name) 18 | } 19 | 20 | func launchSystem() string { 21 | _, err := exec.LookPath("systemctl") 22 | if err == nil { 23 | return "systemd" 24 | } 25 | 26 | _, err = exec.LookPath("initctl") 27 | if err == nil { 28 | return "upstart" 29 | } 30 | 31 | return "" 32 | } 33 | 34 | func startCmd(name string) []string { 35 | switch launchSystem() { 36 | case "systemd": 37 | // systemctl start nanobox-openvpn.service 38 | return []string{"systemctl", "start", fmt.Sprintf("%s.service", name)} 39 | case "upstart": 40 | // initctl start nanobox-openvpn 41 | return []string{"initctl", "start", name} 42 | } 43 | 44 | return nil 45 | } 46 | 47 | func Running(name string) bool { 48 | switch launchSystem() { 49 | case "systemd": 50 | out, err := exec.Command("systemctl", "--no-pager", "status", name).CombinedOutput() 51 | if err != nil { 52 | return false 53 | } 54 | 55 | if !bytes.Contains(out, []byte("running")) { 56 | return false 57 | } 58 | return true 59 | case "upstart": 60 | out, err := exec.Command("initctl", "status", name).CombinedOutput() 61 | if err != nil { 62 | return false 63 | } 64 | 65 | if !bytes.Contains(out, []byte("running")) { 66 | return false 67 | } 68 | return true 69 | } 70 | 71 | return false 72 | } 73 | 74 | func stopCmd(name string) []string { 75 | switch launchSystem() { 76 | case "systemd": 77 | // systemctl start nanobox-openvpn.service 78 | return []string{"systemctl", "stop", fmt.Sprintf("%s.service", name)} 79 | case "upstart": 80 | // initctl start nanobox-openvpn 81 | return []string{"initctl", "stop", name} 82 | } 83 | 84 | return nil 85 | } 86 | 87 | func removeCmd(name string) []string { 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /util/service/service_windows.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "bytes" 5 | "os/exec" 6 | // "golang.org/x/sys/windows/svc" 7 | // "golang.org/x/sys/windows/svc/mgr" 8 | ) 9 | 10 | func Running(name string) bool { 11 | out, err := exec.Command("sc.exe", "query", name).CombinedOutput() 12 | if err != nil { 13 | return false 14 | } 15 | 16 | if !bytes.Contains(out, []byte("RUNNING")) { 17 | return false 18 | } 19 | return true 20 | // m, err := mgr.Connect() 21 | // if err != nil { 22 | // return false 23 | // } 24 | // defer m.Disconnect() 25 | 26 | // // check to see if we need to create at all 27 | // s, err := m.OpenService(name) 28 | // if err != nil { 29 | // // jobs done 30 | // return false 31 | // } 32 | // defer s.Close() 33 | 34 | // status, err := s.Query() 35 | // if err != nil { 36 | // return false 37 | // } 38 | 39 | // return status.State == svc.Running 40 | } 41 | -------------------------------------------------------------------------------- /util/service/start_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package service 4 | 5 | import ( 6 | "fmt" 7 | "os/exec" 8 | ) 9 | 10 | func Start(name string) error { 11 | cmd := startCmd(name) 12 | out, err := exec.Command(cmd[0], cmd[1:]...).CombinedOutput() 13 | if err != nil { 14 | fmt.Errorf("out: %s, err: %s", out, err) 15 | } 16 | 17 | if !Running(name) { 18 | fmt.Printf("%s\n", out) 19 | return fmt.Errorf("%s service start was successful but the service is not running", name) 20 | } 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /util/service/start_windows.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "golang.org/x/sys/windows/svc/mgr" 8 | ) 9 | 10 | func Start(name string) error { 11 | m, err := mgr.Connect() 12 | if err != nil { 13 | return err 14 | } 15 | defer m.Disconnect() 16 | 17 | s, err := m.OpenService(name) 18 | if err != nil { 19 | return fmt.Errorf("could not access service: %v", err) 20 | } 21 | defer s.Close() 22 | 23 | err = s.Start() 24 | if err != nil { 25 | if strings.Contains(err.Error(), "already running") { 26 | return nil 27 | } 28 | return fmt.Errorf("could not start service: %v", err) 29 | } 30 | 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /util/service/stop_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package service 4 | 5 | import ( 6 | "fmt" 7 | "os/exec" 8 | ) 9 | 10 | func Stop(name string) error { 11 | cmd := stopCmd(name) 12 | out, err := exec.Command(cmd[0], cmd[1:]...).CombinedOutput() 13 | if err != nil { 14 | fmt.Errorf("out: %s, err: %s", out, err) 15 | } 16 | return nil 17 | } 18 | -------------------------------------------------------------------------------- /util/service/stop_windows.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "golang.org/x/sys/windows/svc" 8 | "golang.org/x/sys/windows/svc/mgr" 9 | ) 10 | 11 | func Stop(name string) error { 12 | m, err := mgr.Connect() 13 | if err != nil { 14 | return err 15 | } 16 | defer m.Disconnect() 17 | 18 | s, err := m.OpenService(name) 19 | if err != nil { 20 | return fmt.Errorf("could not access service: %v", err) 21 | } 22 | defer s.Close() 23 | 24 | status, err := s.Control(svc.Stop) 25 | if err != nil { 26 | return fmt.Errorf("could not send control=%d: %v", svc.Stop, err) 27 | } 28 | 29 | timeout := time.Now().Add(10 * time.Second) 30 | 31 | for status.State != svc.Stopped { 32 | if timeout.Before(time.Now()) { 33 | return fmt.Errorf("timeout waiting for service to go to state=%d", svc.Stopped) 34 | } 35 | time.Sleep(300 * time.Millisecond) 36 | status, err = s.Query() 37 | if err != nil { 38 | return fmt.Errorf("could not retrieve service status: %v", err) 39 | } 40 | } 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /util/terminal_size.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package util 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/jcelliott/lumber" 10 | ) 11 | 12 | type winsize struct { 13 | Row uint16 14 | Col uint16 15 | Xpixel uint16 16 | Ypixel uint16 17 | } 18 | 19 | func GetTerminalSize() (int, int) { 20 | ws := &winsize{} 21 | retCode, _, errno := syscall.Syscall(syscall.SYS_IOCTL, 22 | uintptr(syscall.Stdin), 23 | uintptr(syscall.TIOCGWINSZ), 24 | uintptr(unsafe.Pointer(ws))) 25 | 26 | if int(retCode) == -1 { 27 | lumber.Error("GetTerminalSize(): %s", errno.Error()) 28 | return 30, 80 29 | } 30 | 31 | return int(ws.Row), int(ws.Col) 32 | } 33 | -------------------------------------------------------------------------------- /util/terminal_size_windows.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | func GetTerminalSize() (int, int) { 4 | return 80, 80 5 | } 6 | -------------------------------------------------------------------------------- /util/unique.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | "net" 7 | ) 8 | 9 | func UniqueID() string { 10 | interfaces, _ := net.Interfaces() 11 | 12 | for _, i := range interfaces { 13 | if i.Flags&net.FlagLoopback != net.FlagLoopback { 14 | return fmt.Sprintf("%x", md5.Sum(i.HardwareAddr)) 15 | } 16 | } 17 | 18 | return "generic" 19 | } 20 | -------------------------------------------------------------------------------- /util/update/name.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package update 4 | 5 | var Name = "nanobox" 6 | var TmpName = "nanobox.tmp" 7 | -------------------------------------------------------------------------------- /util/update/name_windows.go: -------------------------------------------------------------------------------- 1 | package update 2 | 3 | var Name = "nanobox.exe" 4 | var TmpName = "nanobox-tmp.exe" 5 | -------------------------------------------------------------------------------- /util/update/run.go: -------------------------------------------------------------------------------- 1 | package update 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "os" 8 | "os/exec" 9 | "path/filepath" 10 | "strings" 11 | 12 | "github.com/nanobox-io/nanobox/util/display" 13 | ) 14 | 15 | var Server bool 16 | 17 | func Run(path string) error { 18 | if path == "" { 19 | return fmt.Errorf("invalid path") 20 | } 21 | 22 | // create a temporary file 23 | tmpFileName := filepath.Join(filepath.Dir(path), TmpName) 24 | tmpFile, err := os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | if !strings.Contains(path, "nanobox-update") { 30 | fmt.Printf("Current version: %s", getCurrentVersion(path)) 31 | } 32 | 33 | // download the file and display the progress bar 34 | resp, err := http.Get(remotePath()) 35 | if err != nil { 36 | return err 37 | } 38 | defer resp.Body.Close() 39 | 40 | dp := display.DownloadPercent{Total: resp.ContentLength} 41 | if Server { 42 | // on the Server we dont really care to see this 43 | dp.Output = ioutil.Discard 44 | } 45 | dp.Copy(tmpFile, resp.Body) 46 | 47 | // close the tmp file 48 | tmpFile.Close() 49 | 50 | // replace binary 51 | if err := os.Rename(tmpFileName, path); err != nil { 52 | return err 53 | } 54 | 55 | // update the model 56 | update := newUpdate() 57 | 58 | if !strings.Contains(path, "nanobox-update") { 59 | fmt.Printf("\nUpdated to version: %s\n\n", getCurrentVersion(path)) 60 | fmt.Println("Check out the release notes here:") 61 | fmt.Println("https://github.com/nanobox-io/nanobox/blob/master/CHANGELOG.md") 62 | } 63 | 64 | return update.Save() 65 | } 66 | 67 | func getCurrentVersion(path string) string { 68 | if path == "" { 69 | fmt.Println("invalid path") 70 | return "" 71 | } 72 | version, err := exec.Command(path, "version").Output() 73 | if err != nil { 74 | fmt.Println("Error while trying to get the nanobox version") 75 | return "" 76 | } 77 | return string(version) 78 | } 79 | -------------------------------------------------------------------------------- /util/update/update.go: -------------------------------------------------------------------------------- 1 | // Package update handles the updating of nanobox cli 2 | package update 3 | 4 | import ( 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "runtime" 9 | "time" 10 | 11 | "github.com/jcelliott/lumber" 12 | 13 | "github.com/nanobox-io/nanobox/models" 14 | ) 15 | 16 | func remotePath() string { 17 | return fmt.Sprintf("https://s3.amazonaws.com/tools.nanobox.io/nanobox/v2/%s/%s/%s", runtime.GOOS, runtime.GOARCH, Name) 18 | } 19 | 20 | func latestVersion() string { 21 | remotePath := "https://s3.amazonaws.com/tools.nanobox.io/nanobox/v2/version" 22 | res, err := http.Get(remotePath) 23 | if err != nil { 24 | lumber.Error("update:http.Get(%s): %s", remotePath, err) 25 | return "" 26 | } 27 | 28 | // read the remote version string 29 | vers, err := ioutil.ReadAll(res.Body) 30 | if err != nil { 31 | lumber.Error("update:ioutil.ReadAll(body): %s", err) 32 | return "" 33 | } 34 | defer res.Body.Close() 35 | 36 | return string(vers) 37 | } 38 | 39 | func newUpdate() models.Update { 40 | return models.Update{ 41 | LastCheckAt: time.Now(), 42 | LastUpdatedAt: time.Now(), 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /util/util.go: -------------------------------------------------------------------------------- 1 | // Package util ... 2 | package util 3 | 4 | import ( 5 | "crypto/md5" 6 | "fmt" 7 | "io/ioutil" 8 | "math/rand" 9 | "os" 10 | "path/filepath" 11 | "time" 12 | 13 | "github.com/nanobox-io/nanobox/util/config" 14 | ) 15 | 16 | const ( 17 | 18 | // VERSION is the global version for nanobox; mainly used in the update process 19 | // but placed here to allow access when needed (commands, processor, etc.) 20 | letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 21 | ) 22 | 23 | // RandomString ... 24 | func RandomString(size int) string { 25 | 26 | // create a new randomizer with a unique seed 27 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 28 | 29 | // 30 | b := make([]byte, size) 31 | for i := range b { 32 | b[i] = letterBytes[r.Intn(len(letterBytes))] 33 | } 34 | 35 | return string(b) 36 | } 37 | 38 | func FolderExists(folderName string) bool { 39 | dir, err := os.Stat(folderName) 40 | if err != nil { 41 | return false 42 | } 43 | return dir.IsDir() 44 | } 45 | 46 | func FileMD5(name string) string { 47 | data, err := ioutil.ReadFile(name) 48 | if err != nil { 49 | // give the relative path a chance 50 | // but if it doesnt attach the filename given to the absolute path 51 | data, err = ioutil.ReadFile(filepath.ToSlash(filepath.Join(config.LocalDir(), name))) 52 | if err != nil { 53 | return "" 54 | } 55 | } 56 | return fmt.Sprintf("%x", md5.Sum(data)) 57 | } 58 | -------------------------------------------------------------------------------- /util/util_test.go: -------------------------------------------------------------------------------- 1 | package util_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/nanobox-io/nanobox/util" 9 | ) 10 | 11 | func TestRetry(t *testing.T) { 12 | failureCount := 0 13 | failingFunc := func() error { 14 | failureCount += 1 15 | if failureCount > 5 { 16 | return nil 17 | } 18 | return fmt.Errorf("error") 19 | } 20 | 21 | err := util.Retry(failingFunc, 3, time.Nanosecond) 22 | if err == nil { 23 | t.Errorf("func failed but didnt error") 24 | } 25 | 26 | err = util.Retry(failingFunc, 3, time.Nanosecond) 27 | if err != nil { 28 | t.Errorf("func succeeded but i recieved an error") 29 | } 30 | 31 | } 32 | 33 | func TestError(t *testing.T) { 34 | err := util.ErrorfQuiet("hi %s", "world") 35 | if err.Error() != "hi world" { 36 | t.Errorf("did not format correctly") 37 | } 38 | 39 | err = util.ErrorAppend(err, "james") 40 | if err.Error() != "james: hi world" { 41 | t.Errorf("append failed") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /util/util_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package util 4 | 5 | import ( 6 | "bufio" 7 | "fmt" 8 | "os" 9 | "os/exec" 10 | ) 11 | 12 | // IsPrivileged will return true if the current process is running under a 13 | // privileged user, like root 14 | func IsPrivileged() bool { 15 | 16 | // Execute a syscall to return the user id. If the user id is 0 then we're 17 | // running with root escalation. 18 | if os.Geteuid() == 0 { 19 | return true 20 | } 21 | 22 | return false 23 | } 24 | 25 | // PrivilegeExec runs a command as sudo 26 | func PrivilegeExec(command string) error { 27 | // 28 | if !sudoExists() { 29 | fmt.Println("We could not find 'sudo' in your path") 30 | fmt.Println("Please run the following command in a separate console, then press enter to continue once its complete:") 31 | fmt.Printf("sudo %v --internal", command) 32 | reader := bufio.NewReader(os.Stdin) 33 | reader.ReadString('\n') 34 | return nil 35 | } 36 | 37 | cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("sudo -E %v --internal", command)) 38 | 39 | cmd.Stdin = os.Stdin 40 | cmd.Stdout = os.Stdout 41 | cmd.Stderr = os.Stderr 42 | 43 | // run command 44 | return cmd.Run() 45 | } 46 | 47 | func sudoExists() bool { 48 | _, err := exec.LookPath("sudo") 49 | return err == nil 50 | } 51 | -------------------------------------------------------------------------------- /util/vbox/vbox.go: -------------------------------------------------------------------------------- 1 | package vbox 2 | 3 | import ( 4 | "os/exec" 5 | ) 6 | 7 | // Try to find VBoxManage on the path 8 | func detectVBoxManageCmdInPath() string { 9 | cmd := "VBoxManage" 10 | if path, err := exec.LookPath(cmd); err == nil { 11 | return path 12 | } 13 | return cmd 14 | } 15 | -------------------------------------------------------------------------------- /util/vbox/vbox_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package vbox 4 | 5 | // DetectVBoxManageCmd tries to find VBoxManage 6 | func DetectVBoxManageCmd() string { 7 | return detectVBoxManageCmdInPath() 8 | } 9 | -------------------------------------------------------------------------------- /util/vbox/vbox_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package vbox 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "os/exec" 9 | "path/filepath" 10 | 11 | "golang.org/x/sys/windows/registry" 12 | ) 13 | 14 | // DetectVBoxManageCmd tries to find VBoxManage 15 | func DetectVBoxManageCmd() string { 16 | cmd := "VBoxManage" 17 | 18 | if p := os.Getenv("VBOX_INSTALL_PATH"); p != "" { 19 | if path, err := exec.LookPath(filepath.Join(p, cmd)); err == nil { 20 | return path 21 | } 22 | } 23 | 24 | if p := os.Getenv("VBOX_MSI_INSTALL_PATH"); p != "" { 25 | if path, err := exec.LookPath(filepath.Join(p, cmd)); err == nil { 26 | return path 27 | } 28 | } 29 | 30 | // Look in default installation path for VirtualBox version > 5 31 | if path, err := exec.LookPath(filepath.Join("C:\\Program Files\\Oracle\\VirtualBox", cmd)); err == nil { 32 | return path 33 | } 34 | 35 | // Look in windows registry 36 | if p, err := findVBoxInstallDirInRegistry(); err == nil { 37 | if path, err := exec.LookPath(filepath.Join(p, cmd)); err == nil { 38 | return path 39 | } 40 | } 41 | 42 | return detectVBoxManageCmdInPath() //fallback to path 43 | } 44 | 45 | // findVBoxInstallDirInRegistry attempts to find VBoxManage from the registry 46 | func findVBoxInstallDirInRegistry() (string, error) { 47 | registryKey, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Oracle\VirtualBox`, registry.QUERY_VALUE) 48 | if err != nil { 49 | errorMessage := fmt.Sprintf("Can't find VirtualBox registry entries, is VirtualBox really installed properly? %s", err) 50 | return "", fmt.Errorf(errorMessage) 51 | } 52 | 53 | defer registryKey.Close() 54 | 55 | installDir, _, err := registryKey.GetStringValue("InstallDir") 56 | if err != nil { 57 | errorMessage := fmt.Sprintf("Can't find InstallDir registry key within VirtualBox registries entries, is VirtualBox really installed properly? %s", err) 58 | return "", fmt.Errorf(errorMessage) 59 | } 60 | 61 | return installDir, nil 62 | } 63 | -------------------------------------------------------------------------------- /util/watch/crawl_internal_test.go: -------------------------------------------------------------------------------- 1 | package watch 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "testing" 7 | ) 8 | 9 | func TestCrawlFiles(t *testing.T) { 10 | os.MkdirAll("/tmp/nanobox/", 0777) 11 | crawlWatcher := newCrawlWatcher("/tmp/nanobox/") 12 | err := crawlWatcher.watch() 13 | if err != nil { 14 | t.Fatalf("failed to watch: %s", err) 15 | } 16 | defer crawlWatcher.close() 17 | 18 | exec.Command("touch", "/tmp/nanobox/crawl.tmp").Run() 19 | 20 | // pull the first event off the channel 21 | ev := <-crawlWatcher.eventChan() 22 | 23 | if ev.file != "/tmp/nanobox/crawl.tmp" { 24 | t.Errorf("the wrong file path came out %s", ev.file) 25 | } 26 | if ev.error != nil { 27 | t.Errorf("an error occurred %s", ev.error) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /util/watch/notify_internal_test.go: -------------------------------------------------------------------------------- 1 | package watch 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestNotifyFiles(t *testing.T) { 11 | os.MkdirAll("/tmp/nanobox/", 0777) 12 | notifyWatcher, err := newRecursiveWatcher("/tmp/nanobox/") 13 | defer notifyWatcher.close() 14 | if err != nil { 15 | t.Fatalf("failed to watch: %s", err) 16 | } 17 | notifyWatcher.watch() 18 | 19 | <-time.After(time.Second) 20 | ioutil.WriteFile("/tmp/nanobox/notify.tmp", []byte("hi"), 0777) 21 | 22 | // pull the first event off the channel 23 | ev := <-notifyWatcher.eventChan() 24 | 25 | if ev.file != "/tmp/nanobox/notify.tmp" { 26 | t.Errorf("the wrong file path came out %s", ev.file) 27 | } 28 | if ev.error != nil { 29 | t.Errorf("an error occurred %s", ev.error) 30 | } 31 | } 32 | --------------------------------------------------------------------------------