Garden centers around the Stack Graph, which allows you to describe your whole stack in a consistent, structured way.
Garden uses the graph to detect when modules need to be re-built or re-tested, services re-deployed etc. by comparing your current code with previously built, deployed and tested versions.
The Stack Graph is essentially an opinionated graph structure, with a handful of pre-defined entity types and verbs:
Project: The root of the graph. Contains one or more modules and configures providers.
Provider: Providers are configured at the project level. They take care of initializing deployment environments, and control what happens within each node of the graph, e.g. how modules are built, services are deployed etc. They also specify module types and how they are configured.
Module: A module is something you build. Each module specifies a type (e.g.
helm) which dictates how it is configured, built, deployed etc. It can contain zero or more services, tasks and tests. It can also have build dependencies.
Service: A service is something you deploy. It can depend on other services, as well as tasks.
Task: A task is something you run and wait for to finish. It can depend on other services and tasks.
Test: A test is also something you run and wait for to finish, similar to tasks, but with slightly different semantics and separate commands for execution. It can depend on services and tasks (but notably services and tasks cannot depend on tests).
Each part of your stack describes itself using a simple configuration file. Garden collects all those declarations, validates, and compiles them into a DAG (a directed acyclic graph, meaning it should have no circular dependencies).
Importantly, what happens within each of the actions that the graph describes—building, deploying, running etc.—is completely pluggable via the providers. The Stack Graph is only opinionated in terms of flows and dependencies—what should happen when—but the how is pluggable.
As mentioned above, each part of your stack should describe itself. This avoids massive project configuration files or scripts, and makes each part of your stack easy to understand, and even re-usable across projects.
This is done through simple configuration files, which are version controlled in your project, next to each of your code modules (if applicable). For example:
kind: Moduletype: helmname: redisdescription: Redis service for message queueingchart: stable/redisversion: 6.4.3
kind: Moduletype: containername: my-servicedescription: My HTTP service containerservices:- name: my-serviceports:- name: httpcontainerPort: 80ingresses:- path: /helloport: httptests:- name: integcommand: [./test]dependencies: [my-other-service]
Note here the first four fields, which are common across all module types—
description. Other fields are specified by the corresponding provider.
Also notice that the
container module explicitly declares a service, whereas the
helm module does not. This is dictated by the provider. Containers often only need to be built (e.g. base images for other containers), or may contain multiple services. A Helm chart , however, is generally a single deployable so the provider makes the service implicit when configuring it.
For more details on how to configure your project, take a look at the configuration guide.