diff --git a/.circleci/config.yml b/.circleci/config.yml index 8dfa141151..e4844b1454 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,12 +5,6 @@ version: 2.1 # These can be used to inject shared variables # see https://blog.daemonl.com/2016/02/yaml.html .references: - # Go configuration for all our jobs - go-config: &go-config - docker: - - image: circleci/golang:1.10 - working_directory: /go/src/github.com/garden-io/garden - # Configuration for our node jobs node-config: &node-config docker: @@ -71,28 +65,6 @@ commands: paths: [dashboard/node_modules] key: dashboard-{{ checksum "dashboard/package-lock.json" }} - go_install_deps: - description: | - Installs our go dependancies and deals with caching and loading the cache - steps: - - restore_cache: - keys: - - pkg-cache-{{ checksum "Gopkg.lock" }} - - run: go get -u github.com/jstemmer/go-junit-report - - run: - name: Install dep - command: | - if [ ! -d /go/src/github.com/garden-io/garden/vendor ]; then - # This needs to match the version installed locally - curl -L -s https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 -o /go/bin/dep - chmod +x /go/bin/dep - /go/bin/dep ensure - fi - - save_cache: - key: pkg-cache-{{ checksum "Gopkg.lock" }} - paths: - - "/go/src/github.com/garden-io/garden/vendor" - docker_build: description: Builds and Tags a Docker Image parameters: @@ -162,66 +134,6 @@ commands: # Jobs section # jobs: - test-cli: - <<: *go-config - steps: - - checkout - - go_install_deps - # NOTE: This always fails in CI with the error: - # - # # github.com/garden-io/garden/vendor/github.com/havoc-io/mutagen/pkg/session - # ../vendor/github.com/havoc-io/mutagen/pkg/session/controller.go:113:25: undefined: mutagen.VersionMajor - # ../vendor/github.com/havoc-io/mutagen/pkg/session/controller.go:114:25: undefined: mutagen.VersionMinor - # ../vendor/github.com/havoc-io/mutagen/pkg/session/controller.go:115:25: undefined: mutagen.VersionPatch - # - # However, it does work locally. Moreover, it does work locally inside a circleci/golang:1.10 Docker container. - # Not sure why it keeps failing in CI. - # - # Therefore we're removing this step for now so that we can get this merged. Additionally, the Go CLI - # is being deprecated and moved into a separate repo once this has been merged. - # - # - run: - # name: Unit Tests - # command: | - # cd garden-cli - # go test -v 2>&1 | go-junit-report > /tmp/report.xml - # - store_artifacts: - # path: /tmp/report.xml - # - store_test_results: - # path: /tmp/ - build-cli: - <<: *go-config - steps: - - checkout - - go_install_deps - - run: go get -u github.com/goreleaser/goreleaser - - run: - name: Build Go cli - command: | - cd garden-cli - # Set to snapshot builds unless we are on a git tag - FLAGS="--snapshot" - if [[ -n "$CIRCLE_TAG" ]]; then - FLAGS="" - fi - # Build them all - goreleaser $FLAGS --rm-dist - - store_artifacts: - path: garden-cli/dist/ - destination: /downloads - build-sync-docker: - <<: *node-config - steps: - - checkout - - docker_build: - context: garden-sync - release-sync: - <<: *node-config - steps: - - checkout - - docker_release: - context: garden-sync - image: $DOCKER_ORG/garden-sync build-service-docker: <<: *node-config steps: @@ -321,12 +233,8 @@ workflows: commit: jobs: - build-service - - test-cli: - requires: - - build-service - test-service - build-dashboard - - build-sync-docker - build-service-docker: requires: - build-service @@ -342,9 +250,6 @@ workflows: - build-service - build-dashboard: <<: *only-master - - release-sync: - <<: *only-master - context: docker - release-service-docker: <<: *only-master context: docker @@ -356,8 +261,6 @@ workflows: requires: - build-service - build-dashboard - - build-cli: - <<: *only-master tags: jobs: @@ -370,17 +273,12 @@ workflows: - build-service - build-dashboard: <<: *only-tags - - release-sync: - <<: *only-tags - context: docker - release-service-docker: <<: *only-tags context: docker requires: - build-service - build-dashboard - - build-cli: - <<: *only-tags - release-service-pkg: <<: *only-tags requires: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 295b282c78..f1aa8d3e42 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,10 +58,6 @@ Install Node modules for the root package, and the `dashboard` and `garden-servi npm run bootstrap -Install Go dependencies for the `garden-cli`: - - dep ensure - ## Running a development version of the CLI While developing the CLI, we recommend you run the dev command in your console: diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index 6b30bac744..0000000000 --- a/Gopkg.lock +++ /dev/null @@ -1,306 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - digest = "1:9f3b30d9f8e0d7040f729b82dcbc8f0dead820a133b3147ce355fc451f32d761" - name = "github.com/BurntSushi/toml" - packages = ["."] - pruneopts = "UT" - revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005" - version = "v0.3.1" - -[[projects]] - digest = "1:f9ae348e1f793dcf9ed930ed47136a67343dbd6809c5c91391322267f4476892" - name = "github.com/Microsoft/go-winio" - packages = ["."] - pruneopts = "UT" - revision = "97e4973ce50b2ff5f09635a57e2b88a037aae829" - version = "v0.4.11" - -[[projects]] - digest = "1:2aaf2cc045d0219bba79655e4df795b973168c310574669cb75786684f7287d3" - name = "github.com/bmatcuk/doublestar" - packages = ["."] - pruneopts = "UT" - revision = "85a78806aa1b4707d1dbace9be592cf1ece91ab3" - version = "v1.1.1" - -[[projects]] - digest = "1:4ddc17aeaa82cb18c5f0a25d7c253a10682f518f4b2558a82869506eec223d76" - name = "github.com/docker/distribution" - packages = [ - "digestset", - "reference", - ] - pruneopts = "UT" - revision = "2461543d988979529609e8cb6fca9ca190dc48da" - version = "v2.7.1" - -[[projects]] - digest = "1:c4c7064c2c67a0a00815918bae489dd62cd88d859d24c95115d69b00b3d33334" - name = "github.com/docker/docker" - packages = [ - "api/types", - "api/types/blkiodev", - "api/types/container", - "api/types/events", - "api/types/filters", - "api/types/mount", - "api/types/network", - "api/types/reference", - "api/types/registry", - "api/types/strslice", - "api/types/swarm", - "api/types/time", - "api/types/versions", - "api/types/volume", - "client", - "pkg/tlsconfig", - ] - pruneopts = "UT" - revision = "092cba3727bb9b4a2f0e922cd6c0f93ea270e363" - version = "v1.13.1" - -[[projects]] - digest = "1:811c86996b1ca46729bad2724d4499014c4b9effd05ef8c71b852aad90deb0ce" - name = "github.com/docker/go-connections" - packages = [ - "nat", - "sockets", - "tlsconfig", - ] - pruneopts = "UT" - revision = "7395e3f8aa162843a74ed6d48e79627d9792ac55" - version = "v0.4.0" - -[[projects]] - digest = "1:6f82cacd0af5921e99bf3f46748705239b36489464f4529a1589bc895764fb18" - name = "github.com/docker/go-units" - packages = ["."] - pruneopts = "UT" - revision = "47565b4f722fb6ceae66b95f853feed578a4a51c" - version = "v0.3.3" - -[[projects]] - digest = "1:4c0989ca0bcd10799064318923b9bc2db6b4d6338dd75f3f2d86c3511aaaf5cf" - name = "github.com/golang/protobuf" - packages = [ - "proto", - "ptypes", - "ptypes/any", - "ptypes/duration", - "ptypes/timestamp", - ] - pruneopts = "UT" - revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" - version = "v1.2.0" - -[[projects]] - digest = "1:236d7e1bdb50d8f68559af37dbcf9d142d56b431c9b2176d41e2a009b664cda8" - name = "github.com/google/uuid" - packages = ["."] - pruneopts = "UT" - revision = "9b3b1e0f5f99ae461456d768e7d301a7acdaa2d8" - version = "v1.1.0" - -[[projects]] - branch = "master" - digest = "1:e25fb8d2b000a8e3f40bf9813ede534e6a36f6bd663cebd012f179618bd61f8a" - name = "github.com/havoc-io/fsevents" - packages = ["."] - pruneopts = "UT" - revision = "10556809b434d8df14b69f16bfcd5602001feb76" - -[[projects]] - branch = "master" - digest = "1:158e0326806a3ab9987c63b8f8e5e7debc613755e27be4b9f814aab34e39fbc8" - name = "github.com/havoc-io/gopass" - packages = ["."] - pruneopts = "UT" - revision = "9a121bec1ae7ca68c434799fc572f69698be8750" - -[[projects]] - digest = "1:c32828b18521e578a90f2464df245d8be96b89e7eef1426ebb54ce2ad5037cd8" - name = "github.com/havoc-io/mutagen" - packages = [ - "pkg/configuration", - "pkg/daemon", - "pkg/encoding", - "pkg/filesystem", - "pkg/filesystem/notify", - "pkg/filesystem/winfsnotify", - "pkg/mutagen", - "pkg/prompt", - "pkg/rsync", - "pkg/service/session", - "pkg/session", - "pkg/state", - "pkg/sync", - "pkg/url", - ] - pruneopts = "UT" - revision = "f903b78c4b023f631964f8df59eb84e3cf92835b" - version = "v0.7.0" - -[[projects]] - digest = "1:5d231480e1c64a726869bc4142d270184c419749d34f167646baa21008eb0a79" - name = "github.com/mitchellh/go-homedir" - packages = ["."] - pruneopts = "UT" - revision = "af06845cf3004701891bf4fdb884bfe4920b3727" - version = "v1.1.0" - -[[projects]] - digest = "1:ee4d4af67d93cc7644157882329023ce9a7bcfce956a079069a9405521c7cc8d" - name = "github.com/opencontainers/go-digest" - packages = ["."] - pruneopts = "UT" - revision = "279bed98673dd5bef374d3b6e4b09e2af76183bf" - version = "v1.0.0-rc1" - -[[projects]] - digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b" - name = "github.com/pkg/errors" - packages = ["."] - pruneopts = "UT" - revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" - version = "v0.8.1" - -[[projects]] - branch = "master" - digest = "1:fde12c4da6237363bf36b81b59aa36a43d28061167ec4acb0d41fc49464e28b9" - name = "golang.org/x/crypto" - packages = ["ssh/terminal"] - pruneopts = "UT" - revision = "b8fe1690c61389d7d2a8074a507d1d40c5d30448" - -[[projects]] - branch = "master" - digest = "1:0971ddf481116e34b312a127ddb0dfff356a9c03ab75ec32f2fa2506b517ff08" - name = "golang.org/x/net" - packages = [ - "context", - "context/ctxhttp", - "http/httpguts", - "http2", - "http2/hpack", - "idna", - "internal/socks", - "internal/timeseries", - "proxy", - "trace", - ] - pruneopts = "UT" - revision = "d26f9f9a57f3fab6a695bec0d84433c2c50f8bbf" - -[[projects]] - branch = "master" - digest = "1:aa54abbf5dfa5f508ec6a975f34a84cad0b06423fa3a549b1dfe3ee2665da9d5" - name = "golang.org/x/sys" - packages = [ - "unix", - "windows", - "windows/registry", - ] - pruneopts = "UT" - revision = "41f3e6584952bb034a481797859f6ab34b6803bd" - -[[projects]] - digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" - name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "internal/colltab", - "internal/gen", - "internal/tag", - "internal/triegen", - "internal/ucd", - "language", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable", - ] - pruneopts = "UT" - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - branch = "master" - digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c" - name = "google.golang.org/genproto" - packages = ["googleapis/rpc/status"] - pruneopts = "UT" - revision = "4b09977fb92221987e99d190c8f88f2c92727a29" - -[[projects]] - digest = "1:9ab5a33d8cb5c120602a34d2e985ce17956a4e8c2edce7e6961568f95e40c09a" - name = "google.golang.org/grpc" - packages = [ - ".", - "balancer", - "balancer/base", - "balancer/roundrobin", - "binarylog/grpc_binarylog_v1", - "codes", - "connectivity", - "credentials", - "credentials/internal", - "encoding", - "encoding/proto", - "grpclog", - "internal", - "internal/backoff", - "internal/binarylog", - "internal/channelz", - "internal/envconfig", - "internal/grpcrand", - "internal/grpcsync", - "internal/syscall", - "internal/transport", - "keepalive", - "metadata", - "naming", - "peer", - "resolver", - "resolver/dns", - "resolver/passthrough", - "stats", - "status", - "tap", - ] - pruneopts = "UT" - revision = "a02b0774206b209466313a0b525d2c738fe407eb" - version = "v1.18.0" - -[[projects]] - digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "UT" - revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" - version = "v2.2.2" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/docker/docker/api/types", - "github.com/docker/docker/api/types/container", - "github.com/docker/docker/api/types/filters", - "github.com/docker/docker/api/types/mount", - "github.com/docker/docker/api/types/volume", - "github.com/docker/docker/client", - "github.com/havoc-io/mutagen/pkg/daemon", - "github.com/havoc-io/mutagen/pkg/service/session", - "github.com/havoc-io/mutagen/pkg/session", - "github.com/mitchellh/go-homedir", - "github.com/pkg/errors", - "google.golang.org/grpc", - "gopkg.in/yaml.v2", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index 7487dee948..0000000000 --- a/Gopkg.toml +++ /dev/null @@ -1,54 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true - - -[[constraint]] - name = "github.com/mitchellh/go-homedir" - version = "1.0.0" - -[[constraint]] - name = "gopkg.in/yaml.v2" - version = "2.2.1" - -[[constraint]] - name = "github.com/havoc-io/mutagen" - version = "v0.7.0-beta2" - -# [[constraint]] -# branch = "master" -# name = "k8s.io/api" - -# [[constraint]] -# name = "k8s.io/client-go" -# branch = "release-8.0" - -# [[constraint]] -# name = "k8s.io/apimachinery" -# revision = "488889b0007f63ffee90b66a34a2deca9ec58774" - -[prune] - go-tests = true - unused-packages = true diff --git a/garden-cli/.gitignore b/garden-cli/.gitignore deleted file mode 100644 index 836f156cad..0000000000 --- a/garden-cli/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -dist -# Default binaries generatted by go -garden-cli -garden diff --git a/garden-cli/.goreleaser.yaml b/garden-cli/.goreleaser.yaml deleted file mode 100644 index b86dad3d64..0000000000 --- a/garden-cli/.goreleaser.yaml +++ /dev/null @@ -1,41 +0,0 @@ -project_name: garden-cli - - -archive: - format_overrides: - - goos: windows - format: zip - -builds: - - binary: garden - env: - - CGO_ENABLED=0 - goos: - - windows - - darwin - - linux - goarch: - - amd64 - ldflags: - # -s Omit the symbol table and debug information. - # -w Omit the DWARF symbol table. - # These are the defaults specified by goreleaser: - # https://github.com/goreleaser/goreleaser/blob/682c811106f56ffe06c4212de546aec62161fb9d/internal/builders/golang/build.go#L46 - - -s -w -X main.Version={{.Version}} -X main.Commit={{.Commit}} -release: - # If set to true, will not auto-publish the release. - # Default is false. - draft: true - - # If set to true, will mark the release as not ready for production. - # Default is false. - prerelease: true - - # You can change the name of the GitHub release. - # Default is `` - name_template: "v{{.Version}}" - - # You can disable this pipe in order to not upload any artifacts to - # GitHub. - # Defaults to false. - disable: true diff --git a/garden-cli/config.go b/garden-cli/config.go deleted file mode 100644 index 7f94338eff..0000000000 --- a/garden-cli/config.go +++ /dev/null @@ -1,89 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "log" - "os" - "path" - "strings" - - "github.com/garden-io/garden/garden-cli/util" - "gopkg.in/yaml.v2" -) - -// Config type must be public for the yaml parser (for some reason). We only need the project key and the name. -type Config struct { - Project struct { - Name *string - } -} - -func findProject(cwd string) (string, string) { - projectDir := cwd - - for { - configPath := path.Join(projectDir, "garden.yml") - - if _, err := os.Stat(configPath); !os.IsNotExist(err) { - configYaml, err := ioutil.ReadFile(configPath) - util.Check(err) - - config := Config{} - - err = yaml.Unmarshal(configYaml, &config) - if err != nil { - log.Fatalf("Unable to parse %s as a valid garden configuration file", configPath) - } - - if config.Project.Name != nil { - // found project config - return projectDir, *config.Project.Name - } - } - - // move up one level - projectDir = path.Dir(projectDir) - - if projectDir == "/" { - log.Fatalf("Not a project directory (or any of the parent directories): %s", cwd) - } - } -} - -// Get or set the ID of this project (stored in PROJECT_ROOT/.garden/id). -// TODO: might wanna use a lockfile for concurrency here -func getProjectID(projectDir string) string { - gardenDir := path.Join(projectDir, ".garden") - util.EnsureDir(gardenDir) - - idPath := path.Join(gardenDir, "id") - - var projectID string - - if _, err := os.Stat(idPath); !os.IsNotExist(err) { - idData, err := ioutil.ReadFile(idPath) - util.Check(err) - projectID = strings.TrimSpace(string(idData)) - } else { - projectID = util.RandSeq(8) - err := ioutil.WriteFile(idPath, []byte(projectID), 0644) - util.Check(err) - } - - return projectID -} - -func makeResourceName(prefix string, name string, id string) string { - return fmt.Sprintf("%s--%s-%s", prefix, name, id) -} - -func getGardenHomeDir() string { - // TODO: allow override via env var - homeDir := util.GetHomeDir() - gardenHome := path.Join(homeDir, ".garden") - - util.EnsureDir(gardenHome) - - return gardenHome -} diff --git a/garden-cli/config_test.go b/garden-cli/config_test.go deleted file mode 100644 index d5297d0d7c..0000000000 --- a/garden-cli/config_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "log" - "os" - "path" - "testing" -) - -func TestWorkingDirectory(t *testing.T) { - testDir, err := os.Getwd() - if err != nil { - log.Fatal(err) - } - - path := path.Join(testDir, "../examples/hello-world/services/hello-container") - dir, name := findProject(path) - t.Log(dir) - t.Log(name) - if name != "hello-world" { - t.Errorf("Expected the projectname to be %v but instead got %v", "hello-world", name) - } -} diff --git a/garden-cli/constants.go b/garden-cli/constants.go deleted file mode 100644 index 478b6b63c8..0000000000 --- a/garden-cli/constants.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -// SyncImage is which docker image to use for syncing -const SyncImage = "gardenengine/garden-sync:latest" - -// ServiceImage is which docker image to use for garden service -const ServiceImage = "gardenengine/garden-service:latest" - -// ProjectPath is where to find the code inside ServiceImage -const ProjectPath = "/project" - -// Mutagen is the synchronization tool Garden uses for syncing files from -// the host into the sync container. Expects the following version. -const MutagenVersion = "0.7.0-beta2" diff --git a/garden-cli/dockerutil/docker.go b/garden-cli/dockerutil/docker.go deleted file mode 100644 index c2f016851f..0000000000 --- a/garden-cli/dockerutil/docker.go +++ /dev/null @@ -1,125 +0,0 @@ -// Package dockerutil containes utility funtions for interacting with the Docker SDK. -package dockerutil - -import ( - "context" - "os" - "os/exec" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/api/types/volume" - "github.com/docker/docker/client" - "github.com/garden-io/garden/garden-cli/util" - "github.com/pkg/errors" -) - -func RunContainer( - containerConfig container.Config, hostConfig container.HostConfig, containerName string, -) (container.ContainerCreateCreatedBody, error) { - cli, err := client.NewEnvClient() - util.Check(err) - - var resp container.ContainerCreateCreatedBody - ctx := context.Background() - - resp, err = cli.ContainerCreate(ctx, &containerConfig, &hostConfig, nil, containerName) - if err != nil { - return resp, errors.Wrap(err, "unable to run container "+containerName) - } - - if err := StartContainer(resp.ID); err != nil { - return resp, errors.Wrap(err, "unable to start container "+containerName) - } - - return resp, nil -} - -func StartContainer(containerID string) error { - cli, err := client.NewEnvClient() - util.Check(err) - - return cli.ContainerStart(context.Background(), containerID, types.ContainerStartOptions{}) -} - -func Exec(args []string, silent bool) error { - binary := util.GetBin("docker") - cmd := exec.Command(binary, args...) - - cmd.Env = os.Environ() - if !silent { - cmd.Stderr = os.Stderr - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - } - - return cmd.Run() -} - -func FindContainer(containerName string) (types.Container, bool, error) { - cli, err := client.NewEnvClient() - util.Check(err) - - var container types.Container - found := false - - containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{ - All: true, - }) - if err != nil { - return container, found, errors.Wrap(err, "unable to get container list") - } - - for _, con := range containers { - if con.Names[0] == "/"+containerName { - found = true - return con, found, nil - } - } - return container, found, nil -} - -func StopContainer(id string) error { - cli, err := client.NewEnvClient() - util.Check(err) - - return cli.ContainerStop(context.Background(), id, nil) -} - -func CreateVolume(volumeName string) (types.Volume, error) { - cli, err := client.NewEnvClient() - util.Check(err) - - return cli.VolumeCreate(context.Background(), volume.VolumesCreateBody{ - Name: volumeName, - }) -} - -func FindVolume(volumeName string) (*types.Volume, bool, error) { - cli, err := client.NewEnvClient() - util.Check(err) - - found := false - var volume *types.Volume - - volumeResponse, err := cli.VolumeList(context.Background(), filters.NewArgs()) - if err != nil { - return volume, found, errors.Wrap(err, "unable to get volume list") - } - - for _, vol := range volumeResponse.Volumes { - if vol.Name == volumeName { - found = true - return vol, found, nil - } - } - return volume, found, nil -} - -func Ping() (types.Ping, error) { - cli, err := client.NewEnvClient() - util.Check(err) - - return cli.Ping(context.Background()) -} diff --git a/garden-cli/gulpfile.ts b/garden-cli/gulpfile.ts deleted file mode 100644 index a5264078ce..0000000000 --- a/garden-cli/gulpfile.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2018 Garden Technologies, Inc. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { join } from "path" -import { spawn } from "../support/support-util" - -const sources = join(__dirname, "**", "*.go") - -module.exports = (gulp) => { - gulp.task("build", () => spawn("go", ["build", "-o", join("build", "garden")], __dirname)) - gulp.task("watch", () => gulp.watch([sources, join(__dirname, "Dockerfile")], gulp.parallel("build"))) -} - -if (process.cwd() === __dirname) { - module.exports(require("gulp")) -} diff --git a/garden-cli/main.go b/garden-cli/main.go deleted file mode 100644 index 74a8f8db65..0000000000 --- a/garden-cli/main.go +++ /dev/null @@ -1,130 +0,0 @@ -package main - -import ( - "log" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/garden-io/garden/garden-cli/dockerutil" - "github.com/garden-io/garden/garden-cli/util" - "github.com/pkg/errors" -) - -type Dependency struct { - bin string - errorMessage string -} - -// A CLI for running Garden commands in a garden-service container. -// -// For a new project the flow is as follows: -// -// 1. Create a named volume for the project. -// 2. Create and start a garden-sync container that mounts the named volume. -// 3. Start a sync session between the host and the garden-sync container that syncs the -// contents of the local project directory into the named project volume and watches for changes. -// 4. Start a garden-service container that mounts the named volume -// 5. Run the command inside the garden-service container -// -// The containers, volume and sync session are persistent, so for an existing project the CLI -// execs into to garden-service container and runs the command. -func main() { - - if err := checkDeps(); err != nil { - log.Panicln(err) - os.Exit(1) - } - - // find the project garden.yml - cwd, err := os.Getwd() - util.Check(err) - _, projectName := findProject(cwd) - - // get the git root and relative path to it (we mount the git root, so that git version checks work) - git := util.GetBin("git") - - cmd := exec.Command(git, "rev-parse", "--show-toplevel") - cmd.Env = os.Environ() - gitRootBytes, err := cmd.Output() - if err != nil { - log.Panicln( - "Current directory is not in a git repository (Garden projects currently need to be inside a git repository)", - ) - os.Exit(1) - } - gitRoot := strings.TrimSpace(string(gitRootBytes)) - - relPath, err := filepath.Rel(strings.TrimSpace(gitRoot), cwd) - util.Check(err) - - projectID := getProjectID(gitRoot) - volumeName := makeResourceName("garden-volume", projectName, projectID) - syncContainerName := makeResourceName("garden-sync", projectName, projectID) - serviceContainerName := makeResourceName("garden-service", projectName, projectID) - - // make sure the docker daemon is running - if _, err = dockerutil.Ping(); err != nil { - log.Panicln(err) - } - - if err := ensureVolume(volumeName, syncContainerName, serviceContainerName); err != nil { - log.Panicln(err) - } - - if err := runSyncContainer(syncContainerName, volumeName, gitRoot); err != nil { - log.Panicln(err) - } - - if err := initSync(gitRoot, syncContainerName); err != nil { - log.Panicln(err) - } - - if err := runServiceContainer(serviceContainerName, volumeName, relPath); err != nil { - log.Panicln(err) - } - - // run the command inside the garden-service container - err = dockerutil.Exec(append([]string{"exec", "-it", serviceContainerName, "garden"}, os.Args[1:]...), false) - // do not print error if garden-service errors or if SIGINT - if err != nil && err.Error() != "exit status 1" && err.Error() != "exit status 130" { - log.Panicln(err) - os.Exit(1) - } - -} - -func checkDeps() error { - deps := []Dependency{ - { - bin: "git", - errorMessage: "Could not find git - Garden requires git to be installed", - }, - { - bin: "docker", - errorMessage: "Could not find docker - Garden requires docker to be installed in order to run.", - }, - { - bin: "mutagen", - errorMessage: "Could not find mutagen - Garden requires mutagen to be installed in order to run.", - }, - } - - for _, dep := range deps { - if _, err := exec.LookPath(dep.bin); err != nil { - return errors.New(dep.errorMessage) - } - } - - // verify mutagen version - currentMutagenVersion, err := exec.Command("mutagen", "version").Output() - if err != nil { - return err - } - if strings.TrimSpace(string(currentMutagenVersion)) != MutagenVersion { - return errors.Errorf("expected Mutagen version %s, got %s", currentMutagenVersion, MutagenVersion) - } - - return nil -} diff --git a/garden-cli/run.go b/garden-cli/run.go deleted file mode 100644 index 81ba30f8e9..0000000000 --- a/garden-cli/run.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "fmt" - "path" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/mount" - "github.com/garden-io/garden/garden-cli/dockerutil" - "github.com/garden-io/garden/garden-cli/util" - "github.com/pkg/errors" -) - -// Runs the garden service container and executes the command inside the container -func runServiceContainer(containerName string, volumeName string, relPath string) error { - homeDir := util.GetHomeDir() - gardenHomeDir := getGardenHomeDir() - workingDir := path.Join(ProjectPath, relPath) - - serviceContainer, found, err := dockerutil.FindContainer(containerName) - if err != nil { - return err - } - - // Start the container if found but not running - if found && serviceContainer.State != "running" { - if err := dockerutil.StartContainer(serviceContainer.ID); err != nil { - return errors.Wrap(err, "unable to start garden service container") - } - } - - // Create and run the container if not found - if !found { - volumeMounts := []mount.Mount{ - { - Type: mount.TypeVolume, - Source: volumeName, - Target: ProjectPath, - }, - } - bindMounts := []string{ - "/var/run/docker.sock:/var/run/docker.sock", - fmt.Sprintf("%s/.docker:/root/.docker", homeDir), - fmt.Sprintf("%s/.kube:/root/.kube", homeDir), - // we mount ~/.ssh to allow the container to pull down private git repos - fmt.Sprintf("%s/.ssh:/root/.ssh", homeDir), - fmt.Sprintf("%s:/root/.garden", gardenHomeDir), - } - - containerConfig := container.Config{ - Image: ServiceImage, - Tty: true, - OpenStdin: true, - Cmd: []string{"/bin/sh"}, - WorkingDir: workingDir, - } - - hostConfig := container.HostConfig{ - Binds: bindMounts, - Mounts: volumeMounts, - AutoRemove: true, - NetworkMode: "host", // TODO Test if correct - } - - if _, err := dockerutil.RunContainer(containerConfig, hostConfig, containerName); err != nil { - return errors.Wrap(err, "unable to run garden service container") - } - } - - return nil -} diff --git a/garden-cli/sync.go b/garden-cli/sync.go deleted file mode 100644 index 77a8022e7d..0000000000 --- a/garden-cli/sync.go +++ /dev/null @@ -1,117 +0,0 @@ -package main - -import ( - "fmt" - "log" - - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/mount" - "github.com/garden-io/garden/garden-cli/dockerutil" - "github.com/garden-io/garden/garden-cli/syncutil" - "github.com/garden-io/garden/garden-cli/util" - "github.com/pkg/errors" -) - -// Runs the sync container and starts the sync session (if needed) -func runSyncContainer(containerName string, volumeName string, gitRoot string) error { - homeDir := util.GetHomeDir() - - syncContainer, found, err := dockerutil.FindContainer(containerName) - if err != nil { - return errors.Wrap(err, "find container error") - } - - // Stop the sync session if container not found or not running. We (re)start it once the container is running. - // TODO Enable resuming from a sync session instead of stopping and restarting. - if found && syncContainer.State != "running" || !found { - if err := stopSync(gitRoot); err != nil { - return err - } - } - - // Start the container if found but not running - if found && syncContainer.State != "running" { - if err := dockerutil.StartContainer(syncContainer.ID); err != nil { - return errors.Wrap(err, "unable to start garden sync container") - } - } - - // Create and run the container if not found - if !found { - volumeMounts := []mount.Mount{ - { - Type: mount.TypeVolume, - Source: volumeName, - Target: ProjectPath, - }, - } - binds := []string{ - "/var/run/docker.sock:/var/run/docker.sock", - fmt.Sprintf("%s/.docker:/root/.docker", homeDir), - } - - containerConfig := container.Config{ - Image: SyncImage, - Cmd: []string{"/bin/sh"}, - Tty: true, - } - - hostConfig := container.HostConfig{ - Binds: binds, - Mounts: volumeMounts, - AutoRemove: true, - } - - if _, err := dockerutil.RunContainer(containerConfig, hostConfig, containerName); err != nil { - return errors.Wrap(err, "unable to run garden sync container") - } - - } - - return nil -} - -func ensureVolume(volumeName string, syncContainerName string, serviceContainerName string) error { - _, found, err := dockerutil.FindVolume(volumeName) - if err != nil { - return err - } - - if !found { - if _, err := dockerutil.CreateVolume(volumeName); err != nil { - return errors.Wrap(err, "unable to create volume") - } - } - return nil -} - -// Initialises sync if no session with the given source found. If a session is found, removes any duplicates and returns. -func initSync(source string, targetContainer string) error { - if err := syncutil.StartSyncDaemon(); err != nil { - return err - } - - session, found, err := syncutil.FindSession(source) - if err != nil { - return err - } - - // Session found, nothing to do (except ensure that the session is unique) - if found { - // There could technically be several active sync sessions for the same source (shouldn't happen though) - if err := syncutil.RemoveDuplicateSessions(session); err != nil { - return err - } - - return nil - } - - // TODO Nicer log output - log.Println("Starting Garden for this project for the first time, it may take a while for the project to sync") - _, err = syncutil.CreateSession(source, targetContainer, ProjectPath) - return err -} - -func stopSync(source string) error { - return syncutil.TerminateSession(source) -} diff --git a/garden-cli/syncutil/sync.go b/garden-cli/syncutil/sync.go deleted file mode 100644 index e3d3732a78..0000000000 --- a/garden-cli/syncutil/sync.go +++ /dev/null @@ -1,207 +0,0 @@ -// Package syncutil is for managing synchronization sessions between the host and the Garden sync container. -// -// Internally it uses Mutagen as a synchronization tool. Calls the gRPC API exposed by Mutagen if -// possible, otherwise executes mutagen commands directly. -// Note: Mutagen can run several sync sessions from the same source so we include mechanism -// for cleaning up duplicate sessions (although they shouldn't get created from our side). -package syncutil - -import ( - "context" - "fmt" - "net" - "os/exec" - "time" - - "github.com/garden-io/garden/garden-cli/util" - "github.com/havoc-io/mutagen/pkg/daemon" - sessionsvcpkg "github.com/havoc-io/mutagen/pkg/service/session" - sessionpkg "github.com/havoc-io/mutagen/pkg/session" - "github.com/pkg/errors" - "google.golang.org/grpc" -) - -type SessionStatus int - -const ( - Ready SessionStatus = iota - NotReady -) - -func (s SessionStatus) String() string { - return [...]string{"Ready", "NotReady"}[s] -} - -type Session struct { - ID string - Source string - Target string - Status SessionStatus -} - -// Helper functions for connecting to the Daemon, borrowed from here: -// https://github.com/havoc-io/mutagen/blob/master/cmd/mutagen/common.go -func createDaemonClientConnection() (*grpc.ClientConn, error) { - // Create a context to timeout the dial. - dialContext, cancel := context.WithTimeout( - context.Background(), - daemon.RecommendedDialTimeout, - ) - defer cancel() - - // Perform dialing. - return grpc.DialContext( - dialContext, - "", - grpc.WithInsecure(), - grpc.WithDialer(daemonDialer), - grpc.WithBlock(), - ) -} - -func daemonDialer(_ string, timeout time.Duration) (net.Conn, error) { - return daemon.DialTimeout(timeout) -} - -// Helper function for executing mutagen commands -func mutagenExec(args []string) error { - binary := util.GetBin("mutagen") - return exec.Command(binary, args...).Run() -} - -// Helper function for getting all active Mutagen sessions -func getSessions(args []string) (*sessionsvcpkg.ListResponse, error) { - var listResponse *sessionsvcpkg.ListResponse - - // Connect to the daemon and defer closure of the connection. - daemonConnection, err := createDaemonClientConnection() - if err != nil { - return listResponse, errors.Wrap(err, "unable to connect to daemon") - } - defer daemonConnection.Close() - - // Create a session service client. - sessionService := sessionsvcpkg.NewSessionsClient(daemonConnection) - - // Invoke list. - request := &sessionsvcpkg.ListRequest{ - Specifications: args, - } - - return sessionService.List(context.Background(), request) -} - -// Starts sync daemon, no-op if already running -func StartSyncDaemon() error { - return mutagenExec([]string{"daemon", "start"}) -} - -// Creates a new sync session and wait until status is ready before returning -func CreateSession(source string, targetContainer string, containerPath string) (Session, error) { - var session Session - - target := fmt.Sprintf("docker://%s/%s", targetContainer, containerPath) - if err := mutagenExec([]string{"create", source, target}); err != nil { - return session, err - } - - // wait until sync is complete - timeout := time.After(120 * time.Second) - tick := time.Tick(500 * time.Millisecond) - // keep trying until the status is Ready, we get an error, or we time out - for { - select { - case <-timeout: - return session, errors.New("timed out waiting for sync to complete") - case <-tick: - session, _, err := FindSession(source) - if err != nil { - return session, err - } - - switch session.Status { - case Ready: - return session, nil - } - // try again - } - } -} - -// Terminate session (and remove duplicates) -func TerminateSession(source string) error { - session, found, err := FindSession(source) - if err != nil { - return err - } - - if !found { - return nil - } - - if err := RemoveDuplicateSessions(session); err != nil { - return err - } - - return mutagenExec([]string{"terminate", session.ID}) -} - -// Returns the first session found that matches the source -func FindSession(source string) (Session, bool, error) { - var session Session - found := false - - response, err := getSessions([]string{}) - if err != nil { - return session, found, err - } - - for _, s := range response.SessionStates { - // Validate the list response contents. - if err = s.EnsureValid(); err != nil { - return session, found, errors.Wrap(err, "invalid session state detected in response") - } - - var status SessionStatus - switch s.Status { - case sessionpkg.Status_Watching: - status = Ready - default: - status = NotReady - } - - if s.Session.Alpha.Path == source { - found = true - session = Session{ - ID: s.Session.Identifier, - Source: s.Session.Alpha.Path, - Target: s.Session.Beta.Path, - Status: status, - } - return session, found, nil - } - } - return session, found, nil -} - -// Given a session, removes all other sessions with the same source. -func RemoveDuplicateSessions(session Session) error { - response, err := getSessions([]string{}) - if err != nil { - return err - } - - for _, s := range response.SessionStates { - if err = s.EnsureValid(); err != nil { - return errors.Wrap(err, "invalid session state detected in response") - } - - // if there's another session with the same source we terminate it - if s.Session.Alpha.Path == session.Source && s.Session.Identifier != session.ID { - if err := mutagenExec([]string{"terminate", s.Session.Identifier}); err != nil { - return errors.Wrap(err, "unable to terminate session "+s.Session.Identifier) - } - } - } - return nil -} diff --git a/garden-cli/util/util.go b/garden-cli/util/util.go deleted file mode 100644 index 89cc3f53a4..0000000000 --- a/garden-cli/util/util.go +++ /dev/null @@ -1,57 +0,0 @@ -package util - -import ( - "fmt" - "log" - "math/rand" - "os" - "os/exec" - "time" - - "github.com/mitchellh/go-homedir" -) - -// Use this for unexpected errors, like system errors that we have no sensible way of dealing with. -func Check(err error) { - if err != nil { - panic(err) - } -} - -var letters = []rune("abcdefghijklmnopqrstuvwxyz1234567890") - -// Generate a random string of length n. -func RandSeq(n int) string { - rand.Seed(time.Now().UnixNano()) - b := make([]rune, n) - for i := range b { - b[i] = letters[rand.Intn(len(letters))] - } - return string(b) -} - -func int32Ptr(value int32) *int32 { - return &value -} - -// Returns the current user's home directory, as an absolute path. -func GetHomeDir() string { - homeDir, err := homedir.Dir() - Check(err) - homeDir, err = homedir.Expand(homeDir) - Check(err) - return homeDir -} - -// Makes sure the given directory path exists. -func EnsureDir(path string) { - os.MkdirAll(path, os.ModePerm) -} - -func GetBin(binary string) string { - binary, err := exec.LookPath(binary) - if err != nil { - log.Fatal(fmt.Sprintf("Could not find %s - Garden requires %s to be installed in order to run.", binary, binary)) - } - return binary -} diff --git a/garden-sync/Dockerfile b/garden-sync/Dockerfile deleted file mode 100644 index 1154c8492c..0000000000 --- a/garden-sync/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -FROM alpine:3.8 \ No newline at end of file diff --git a/garden-sync/gulpfile.ts b/garden-sync/gulpfile.ts deleted file mode 100644 index 502b45097d..0000000000 --- a/garden-sync/gulpfile.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2018 Garden Technologies, Inc. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -import { join } from "path" -import { spawn } from "../support/support-util" - -module.exports = (gulp) => { - gulp.task("build-container", () => spawn( - "docker", - ["build", "-t", "gardenengine/garden-sync:latest", join(__dirname)], - )) -} - -if (process.cwd() === __dirname) { - module.exports(require("gulp")) -} diff --git a/gulpfile.ts b/gulpfile.ts index b2a959c8c8..75111be640 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -20,7 +20,7 @@ const checkLicense = require("gulp-license-check") const tsSources = ["garden-service/src/**/*.ts", "dashboard/src/**/*.ts*"] const pegjsSources = "garden-service/src/*.pegjs" const licenseHeaderPath = "support/license-header.txt" -const modulePaths = ["garden-cli", "garden-service", "garden-sync", "dashboard"] +const modulePaths = ["garden-service", "dashboard"] const tmpDir = resolve(__dirname, "tmp") process.env.FORCE_COLOR = "true" @@ -45,15 +45,10 @@ modulePaths.forEach(m => { tasks(_gulp) }) -gulp.task("build", gulp.parallel( - "garden-cli:build", - "garden-service:build", - "garden-sync:build-container", - "dashboard:build", -)) +gulp.task("build", gulp.parallel("garden-service:build", "dashboard:build")) gulp.task("generate-docs", gulp.parallel("garden-service:generate-docs")) gulp.task("test", gulp.parallel("garden-service:test")) -gulp.task("watch", gulp.parallel("garden-cli:watch", "garden-service:watch")) +gulp.task("watch", gulp.parallel("garden-service:watch")) gulp.task("check-licenses", () => gulp.src([...tsSources, pegjsSources])