Add support for App Platform (#497)
* Bump godo version. * Initial support for app service spec. * Update godo * Add support for static sites. * Refactor to share appSpecComponentBase * Add support for workers. * Fix expandAppDomainSpec * Add database support. * Add first set of acceptance tests. * Add test excercising envs. * Add worker test. * Add import test. * Add sweeper. * Add App data source. * Add documentation for the resource. * Add data source docs. * Update health_check attributes. * Use basic plan in acceptance tests. * Test upgrading an app from basic to professional. * Update waitForAppDeployment method. * Fix env docs. * Update digitalocean/datasource_digitalocean_app_test.go Co-authored-by: Cesar Garza <scotch.neat@live.com> * Simplify expand methods. * Fix typo in sweeper log message. Co-authored-by: Cesar Garza <scotch.neat@live.com>
This commit is contained in:
parent
4c6b74bb0d
commit
99f86f4aaa
|
@ -0,0 +1,846 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
|
||||
)
|
||||
|
||||
func appSpecSchema() map[string]*schema.Schema {
|
||||
return map[string]*schema.Schema{
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ValidateFunc: validation.StringLenBetween(2, 32),
|
||||
Description: "The name of the app. Must be unique across all apps in the same account.",
|
||||
},
|
||||
"region": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The slug for the DigitalOcean data center region hosting the app",
|
||||
},
|
||||
"domains": {
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
"service": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
MinItems: 1,
|
||||
Elem: appSpecServicesSchema(),
|
||||
},
|
||||
"static_site": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: appSpecStaticSiteSchema(),
|
||||
},
|
||||
"worker": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: appSpecWorkerSchema(),
|
||||
},
|
||||
"database": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: appSpecDatabaseSchema(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func appSpecGitSourceSchema() map[string]*schema.Schema {
|
||||
return map[string]*schema.Schema{
|
||||
"repo_clone_url": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The clone URL of the repo.",
|
||||
},
|
||||
"branch": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The name of the branch to use.",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func appSpecGitHubSourceSchema() map[string]*schema.Schema {
|
||||
return map[string]*schema.Schema{
|
||||
"repo": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The name of the repo in the format `owner/repo`.",
|
||||
},
|
||||
"branch": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The name of the branch to use.",
|
||||
},
|
||||
"deploy_on_push": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Description: "Whether to automatically deploy new commits made to the repo",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func appSpecEnvSchema() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"key": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The name of the environment variable.",
|
||||
},
|
||||
"value": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The value of the environment variable.",
|
||||
},
|
||||
"scope": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "RUN_AND_BUILD_TIME",
|
||||
ValidateFunc: validation.StringInSlice([]string{
|
||||
"UNSET",
|
||||
"RUN_TIME",
|
||||
"BUILD_TIME",
|
||||
"RUN_AND_BUILD_TIME",
|
||||
}, false),
|
||||
Description: "The visibility scope of the environment variable.",
|
||||
},
|
||||
"type": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ValidateFunc: validation.StringInSlice([]string{
|
||||
"GENERAL",
|
||||
"SECRET",
|
||||
}, false),
|
||||
Description: "The type of the environment variable.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func appSpecRouteSchema() map[string]*schema.Schema {
|
||||
return map[string]*schema.Schema{
|
||||
"path": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "Path specifies an route by HTTP path prefix. Paths must start with / and must be unique within the app.",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func appSpecHealthCheckSchema() map[string]*schema.Schema {
|
||||
return map[string]*schema.Schema{
|
||||
"http_path": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The route path used for the HTTP health check ping.",
|
||||
},
|
||||
"initial_delay_seconds": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Description: "The number of seconds to wait before beginning health checks.",
|
||||
},
|
||||
"period_seconds": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Description: "The number of seconds to wait between health checks.",
|
||||
},
|
||||
"timeout_seconds": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Description: "The number of seconds after which the check times out.",
|
||||
},
|
||||
"success_threshold": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Description: "The number of successful health checks before considered healthy.",
|
||||
},
|
||||
"failure_threshold": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Description: "The number of failed health checks before considered unhealthy.",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func appSpecComponentBase() map[string]*schema.Schema {
|
||||
return map[string]*schema.Schema{
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "The name of the component",
|
||||
},
|
||||
"git": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: appSpecGitSourceSchema(),
|
||||
},
|
||||
},
|
||||
"github": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: appSpecGitHubSourceSchema(),
|
||||
},
|
||||
},
|
||||
"dockerfile_path": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The path to a Dockerfile relative to the root of the repo. If set, overrides usage of buildpacks.",
|
||||
},
|
||||
"build_command": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "An optional build command to run while building this component from source.",
|
||||
},
|
||||
"env": {
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Elem: appSpecEnvSchema(),
|
||||
Set: schema.HashResource(appSpecEnvSchema()),
|
||||
},
|
||||
"routes": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: appSpecRouteSchema(),
|
||||
},
|
||||
},
|
||||
"source_dir": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "An optional path to the working directory to use for the build.",
|
||||
},
|
||||
"environment_slug": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "An environment slug describing the type of this app.",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func appSpecServicesSchema() *schema.Resource {
|
||||
serviceSchema := map[string]*schema.Schema{
|
||||
"run_command": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "An optional run command to override the component's default.",
|
||||
},
|
||||
"http_port": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The internal port on which this service's run command will listen.",
|
||||
},
|
||||
"instance_size_slug": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The instance size to use for this component.",
|
||||
},
|
||||
"instance_count": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Default: 1,
|
||||
Description: "The amount of instances that this component should be scaled to.",
|
||||
},
|
||||
"health_check": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: appSpecHealthCheckSchema(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range appSpecComponentBase() {
|
||||
serviceSchema[k] = v
|
||||
}
|
||||
|
||||
return &schema.Resource{
|
||||
Schema: serviceSchema,
|
||||
}
|
||||
}
|
||||
|
||||
func appSpecStaticSiteSchema() *schema.Resource {
|
||||
staticSiteSchema := map[string]*schema.Schema{
|
||||
"output_dir": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "An optional path to where the built assets will be located, relative to the build context. If not set, App Platform will automatically scan for these directory names: `_static`, `dist`, `public`.",
|
||||
},
|
||||
"index_document": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The name of the index document to use when serving this static site.",
|
||||
},
|
||||
"error_document": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The name of the error document to use when serving this static site.",
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range appSpecComponentBase() {
|
||||
staticSiteSchema[k] = v
|
||||
}
|
||||
|
||||
return &schema.Resource{
|
||||
Schema: staticSiteSchema,
|
||||
}
|
||||
}
|
||||
|
||||
func appSpecWorkerSchema() *schema.Resource {
|
||||
workerSchema := map[string]*schema.Schema{
|
||||
"run_command": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "An optional run command to override the component's default.",
|
||||
},
|
||||
"instance_size_slug": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The instance size to use for this component.",
|
||||
},
|
||||
"instance_count": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Default: 1,
|
||||
Description: "The amount of instances that this component should be scaled to.",
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range appSpecComponentBase() {
|
||||
workerSchema[k] = v
|
||||
}
|
||||
|
||||
return &schema.Resource{
|
||||
Schema: workerSchema,
|
||||
}
|
||||
}
|
||||
|
||||
func appSpecDatabaseSchema() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The name of the component",
|
||||
},
|
||||
"engine": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: validation.StringInSlice([]string{
|
||||
"UNSET",
|
||||
"MYSQL",
|
||||
"PG",
|
||||
"REDIS",
|
||||
}, false),
|
||||
Description: "The database engine to use.",
|
||||
},
|
||||
"version": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The version of the database engine.",
|
||||
},
|
||||
"production": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Description: "Whether this is a production or dev database.",
|
||||
},
|
||||
"cluster_name": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The name of the underlying DigitalOcean DBaaS cluster. This is required for production databases. For dev databases, if cluster_name is not set, a new cluster will be provisioned.",
|
||||
},
|
||||
"db_name": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The name of the MySQL or PostgreSQL database to configure.",
|
||||
},
|
||||
"db_user": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The name of the MySQL or PostgreSQL user to configure.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func expandAppSpec(config []interface{}) *godo.AppSpec {
|
||||
if len(config) == 0 || config[0] == nil {
|
||||
return &godo.AppSpec{}
|
||||
}
|
||||
appSpecConfig := config[0].(map[string]interface{})
|
||||
|
||||
appSpec := &godo.AppSpec{
|
||||
Name: appSpecConfig["name"].(string),
|
||||
Region: appSpecConfig["region"].(string),
|
||||
Domains: expandAppDomainSpec(appSpecConfig["domains"].(*schema.Set).List()),
|
||||
Services: expandAppSpecServices(appSpecConfig["service"].([]interface{})),
|
||||
StaticSites: expandAppSpecStaticSites(appSpecConfig["static_site"].([]interface{})),
|
||||
Workers: expandAppSpecWorkers(appSpecConfig["worker"].([]interface{})),
|
||||
Databases: expandAppSpecDatabases(appSpecConfig["database"].([]interface{})),
|
||||
}
|
||||
|
||||
return appSpec
|
||||
}
|
||||
|
||||
func flattenAppSpec(spec *godo.AppSpec) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, 1)
|
||||
|
||||
if spec != nil {
|
||||
|
||||
r := make(map[string]interface{})
|
||||
r["name"] = (*spec).Name
|
||||
r["region"] = (*spec).Region
|
||||
r["domains"] = flattenAppDomainSpec((*spec).Domains)
|
||||
|
||||
if len((*spec).Services) > 0 {
|
||||
r["service"] = flattenAppSpecServices((*spec).Services)
|
||||
}
|
||||
|
||||
if len((*spec).StaticSites) > 0 {
|
||||
r["static_site"] = flattenAppSpecStaticSites((*spec).StaticSites)
|
||||
}
|
||||
|
||||
if len((*spec).Workers) > 0 {
|
||||
r["worker"] = flattenAppSpecWorkers((*spec).Workers)
|
||||
}
|
||||
|
||||
if len((*spec).Databases) > 0 {
|
||||
r["database"] = flattenAppSpecDatabases((*spec).Databases)
|
||||
}
|
||||
|
||||
result = append(result, r)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func expandAppDomainSpec(config []interface{}) []*godo.AppDomainSpec {
|
||||
appDomains := make([]*godo.AppDomainSpec, 0, len(config))
|
||||
|
||||
for _, rawDomain := range config {
|
||||
domain := &godo.AppDomainSpec{
|
||||
Domain: rawDomain.(string),
|
||||
}
|
||||
|
||||
appDomains = append(appDomains, domain)
|
||||
}
|
||||
|
||||
return appDomains
|
||||
}
|
||||
|
||||
func flattenAppDomainSpec(spec []*godo.AppDomainSpec) *schema.Set {
|
||||
result := schema.NewSet(schema.HashString, []interface{}{})
|
||||
|
||||
for _, domain := range spec {
|
||||
result.Add(domain.Domain)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func expandAppGitHubSourceSpec(config []interface{}) *godo.GitHubSourceSpec {
|
||||
gitHubSourceConfig := config[0].(map[string]interface{})
|
||||
|
||||
gitHubSource := &godo.GitHubSourceSpec{
|
||||
Repo: gitHubSourceConfig["repo"].(string),
|
||||
Branch: gitHubSourceConfig["branch"].(string),
|
||||
DeployOnPush: gitHubSourceConfig["deploy_on_push"].(bool),
|
||||
}
|
||||
|
||||
return gitHubSource
|
||||
}
|
||||
|
||||
func flattenAppGitHubSourceSpec(spec *godo.GitHubSourceSpec) []interface{} {
|
||||
result := make([]interface{}, 0)
|
||||
|
||||
if spec != nil {
|
||||
|
||||
r := make(map[string]interface{})
|
||||
r["repo"] = (*spec).Repo
|
||||
r["branch"] = (*spec).Branch
|
||||
r["deploy_on_push"] = (*spec).DeployOnPush
|
||||
|
||||
result = append(result, r)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func expandAppGitSourceSpec(config []interface{}) *godo.GitSourceSpec {
|
||||
gitSourceConfig := config[0].(map[string]interface{})
|
||||
|
||||
gitSource := &godo.GitSourceSpec{
|
||||
Branch: gitSourceConfig["branch"].(string),
|
||||
RepoCloneURL: gitSourceConfig["repo_clone_url"].(string),
|
||||
}
|
||||
|
||||
return gitSource
|
||||
}
|
||||
|
||||
func flattenAppGitSourceSpec(spec *godo.GitSourceSpec) []interface{} {
|
||||
result := make([]interface{}, 0)
|
||||
|
||||
if spec != nil {
|
||||
|
||||
r := make(map[string]interface{})
|
||||
r["branch"] = (*spec).Branch
|
||||
r["repo_clone_url"] = (*spec).RepoCloneURL
|
||||
|
||||
result = append(result, r)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func expandAppEnvs(config []interface{}) []*godo.AppVariableDefinition {
|
||||
appEnvs := make([]*godo.AppVariableDefinition, 0, len(config))
|
||||
|
||||
for _, rawEnv := range config {
|
||||
env := rawEnv.(map[string]interface{})
|
||||
|
||||
e := &godo.AppVariableDefinition{
|
||||
Value: env["value"].(string),
|
||||
Scope: godo.AppVariableScope(env["scope"].(string)),
|
||||
Key: env["key"].(string),
|
||||
Type: godo.AppVariableType(env["type"].(string)),
|
||||
}
|
||||
|
||||
appEnvs = append(appEnvs, e)
|
||||
}
|
||||
|
||||
return appEnvs
|
||||
}
|
||||
|
||||
func flattenAppEnvs(appEnvs []*godo.AppVariableDefinition) *schema.Set {
|
||||
result := schema.NewSet(schema.HashResource(appSpecEnvSchema()), []interface{}{})
|
||||
|
||||
for _, env := range appEnvs {
|
||||
r := make(map[string]interface{})
|
||||
r["value"] = env.Value
|
||||
r["scope"] = string(env.Scope)
|
||||
r["key"] = env.Key
|
||||
r["type"] = string(env.Type)
|
||||
|
||||
result.Add(r)
|
||||
|
||||
setFunc := schema.HashResource(appSpecEnvSchema())
|
||||
log.Printf("[DEBUG] App env hash for %s: %d", r["key"], setFunc(r))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func expandAppHealthCheck(config []interface{}) *godo.AppServiceSpecHealthCheck {
|
||||
healthCheckConfig := config[0].(map[string]interface{})
|
||||
|
||||
healthCheck := &godo.AppServiceSpecHealthCheck{
|
||||
HTTPPath: healthCheckConfig["http_path"].(string),
|
||||
InitialDelaySeconds: int32(healthCheckConfig["initial_delay_seconds"].(int)),
|
||||
PeriodSeconds: int32(healthCheckConfig["period_seconds"].(int)),
|
||||
TimeoutSeconds: int32(healthCheckConfig["timeout_seconds"].(int)),
|
||||
SuccessThreshold: int32(healthCheckConfig["success_threshold"].(int)),
|
||||
FailureThreshold: int32(healthCheckConfig["failure_threshold"].(int)),
|
||||
}
|
||||
|
||||
return healthCheck
|
||||
}
|
||||
|
||||
func flattenAppHealthCheck(check *godo.AppServiceSpecHealthCheck) []interface{} {
|
||||
result := make([]interface{}, 0)
|
||||
|
||||
if check != nil {
|
||||
|
||||
r := make(map[string]interface{})
|
||||
r["http_path"] = check.HTTPPath
|
||||
r["initial_delay_seconds"] = check.InitialDelaySeconds
|
||||
r["period_seconds"] = check.PeriodSeconds
|
||||
r["timeout_seconds"] = check.TimeoutSeconds
|
||||
r["success_threshold"] = check.SuccessThreshold
|
||||
r["failure_threshold"] = check.FailureThreshold
|
||||
|
||||
result = append(result, r)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func expandAppRoutes(config []interface{}) []*godo.AppRouteSpec {
|
||||
appRoutes := make([]*godo.AppRouteSpec, 0, len(config))
|
||||
|
||||
for _, rawRoute := range config {
|
||||
route := rawRoute.(map[string]interface{})
|
||||
|
||||
r := &godo.AppRouteSpec{
|
||||
Path: route["path"].(string),
|
||||
}
|
||||
|
||||
appRoutes = append(appRoutes, r)
|
||||
}
|
||||
|
||||
return appRoutes
|
||||
}
|
||||
|
||||
func flattenAppRoutes(routes []*godo.AppRouteSpec) []interface{} {
|
||||
result := make([]interface{}, 0)
|
||||
|
||||
for _, route := range routes {
|
||||
r := make(map[string]interface{})
|
||||
r["path"] = route.Path
|
||||
|
||||
result = append(result, r)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func expandAppSpecServices(config []interface{}) []*godo.AppServiceSpec {
|
||||
appServices := make([]*godo.AppServiceSpec, 0, len(config))
|
||||
|
||||
for _, rawService := range config {
|
||||
service := rawService.(map[string]interface{})
|
||||
|
||||
s := &godo.AppServiceSpec{
|
||||
Name: service["name"].(string),
|
||||
RunCommand: service["run_command"].(string),
|
||||
BuildCommand: service["build_command"].(string),
|
||||
HTTPPort: int64(service["http_port"].(int)),
|
||||
DockerfilePath: service["dockerfile_path"].(string),
|
||||
Envs: expandAppEnvs(service["env"].(*schema.Set).List()),
|
||||
InstanceSizeSlug: service["instance_size_slug"].(string),
|
||||
InstanceCount: int64(service["instance_count"].(int)),
|
||||
SourceDir: service["source_dir"].(string),
|
||||
EnvironmentSlug: service["environment_slug"].(string),
|
||||
}
|
||||
|
||||
github := service["github"].([]interface{})
|
||||
if len(github) > 0 {
|
||||
s.GitHub = expandAppGitHubSourceSpec(github)
|
||||
}
|
||||
|
||||
git := service["git"].([]interface{})
|
||||
if len(git) > 0 {
|
||||
s.Git = expandAppGitSourceSpec(git)
|
||||
}
|
||||
|
||||
routes := service["routes"].([]interface{})
|
||||
if len(routes) > 0 {
|
||||
s.Routes = expandAppRoutes(routes)
|
||||
}
|
||||
|
||||
checks := service["health_check"].([]interface{})
|
||||
if len(checks) > 0 {
|
||||
s.HealthCheck = expandAppHealthCheck(checks)
|
||||
}
|
||||
|
||||
appServices = append(appServices, s)
|
||||
}
|
||||
|
||||
return appServices
|
||||
}
|
||||
|
||||
func flattenAppSpecServices(services []*godo.AppServiceSpec) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, len(services))
|
||||
|
||||
for i, s := range services {
|
||||
r := make(map[string]interface{})
|
||||
|
||||
r["name"] = s.Name
|
||||
r["run_command"] = s.RunCommand
|
||||
r["build_command"] = s.BuildCommand
|
||||
r["github"] = flattenAppGitHubSourceSpec(s.GitHub)
|
||||
r["git"] = flattenAppGitSourceSpec(s.Git)
|
||||
r["http_port"] = int(s.HTTPPort)
|
||||
r["routes"] = flattenAppRoutes(s.Routes)
|
||||
r["dockerfile_path"] = s.DockerfilePath
|
||||
r["env"] = flattenAppEnvs(s.Envs)
|
||||
r["health_check"] = flattenAppHealthCheck(s.HealthCheck)
|
||||
r["instance_size_slug"] = s.InstanceSizeSlug
|
||||
r["instance_count"] = int(s.InstanceCount)
|
||||
r["source_dir"] = s.SourceDir
|
||||
r["environment_slug"] = s.EnvironmentSlug
|
||||
|
||||
result[i] = r
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func expandAppSpecStaticSites(config []interface{}) []*godo.AppStaticSiteSpec {
|
||||
appSites := make([]*godo.AppStaticSiteSpec, 0, len(config))
|
||||
|
||||
for _, rawSite := range config {
|
||||
site := rawSite.(map[string]interface{})
|
||||
|
||||
s := &godo.AppStaticSiteSpec{
|
||||
Name: site["name"].(string),
|
||||
BuildCommand: site["build_command"].(string),
|
||||
DockerfilePath: site["dockerfile_path"].(string),
|
||||
Envs: expandAppEnvs(site["env"].(*schema.Set).List()),
|
||||
SourceDir: site["source_dir"].(string),
|
||||
OutputDir: site["output_dir"].(string),
|
||||
IndexDocument: site["index_document"].(string),
|
||||
ErrorDocument: site["error_document"].(string),
|
||||
EnvironmentSlug: site["environment_slug"].(string),
|
||||
}
|
||||
|
||||
github := site["github"].([]interface{})
|
||||
if len(github) > 0 {
|
||||
s.GitHub = expandAppGitHubSourceSpec(github)
|
||||
}
|
||||
|
||||
git := site["git"].([]interface{})
|
||||
if len(git) > 0 {
|
||||
s.Git = expandAppGitSourceSpec(git)
|
||||
}
|
||||
|
||||
routes := site["routes"].([]interface{})
|
||||
if len(routes) > 0 {
|
||||
s.Routes = expandAppRoutes(routes)
|
||||
}
|
||||
|
||||
appSites = append(appSites, s)
|
||||
}
|
||||
|
||||
return appSites
|
||||
}
|
||||
|
||||
func flattenAppSpecStaticSites(sites []*godo.AppStaticSiteSpec) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, len(sites))
|
||||
|
||||
for i, s := range sites {
|
||||
r := make(map[string]interface{})
|
||||
|
||||
r["name"] = s.Name
|
||||
r["build_command"] = s.BuildCommand
|
||||
r["github"] = flattenAppGitHubSourceSpec(s.GitHub)
|
||||
r["git"] = flattenAppGitSourceSpec(s.Git)
|
||||
r["routes"] = flattenAppRoutes(s.Routes)
|
||||
r["dockerfile_path"] = s.DockerfilePath
|
||||
r["env"] = flattenAppEnvs(s.Envs)
|
||||
r["source_dir"] = s.SourceDir
|
||||
r["output_dir"] = s.OutputDir
|
||||
r["index_document"] = s.IndexDocument
|
||||
r["error_document"] = s.ErrorDocument
|
||||
r["environment_slug"] = s.EnvironmentSlug
|
||||
|
||||
result[i] = r
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func expandAppSpecWorkers(config []interface{}) []*godo.AppWorkerSpec {
|
||||
appWorkers := make([]*godo.AppWorkerSpec, 0, len(config))
|
||||
|
||||
for _, rawWorker := range config {
|
||||
worker := rawWorker.(map[string]interface{})
|
||||
|
||||
s := &godo.AppWorkerSpec{
|
||||
Name: worker["name"].(string),
|
||||
RunCommand: worker["run_command"].(string),
|
||||
BuildCommand: worker["build_command"].(string),
|
||||
DockerfilePath: worker["dockerfile_path"].(string),
|
||||
Envs: expandAppEnvs(worker["env"].(*schema.Set).List()),
|
||||
InstanceSizeSlug: worker["instance_size_slug"].(string),
|
||||
InstanceCount: int64(worker["instance_count"].(int)),
|
||||
SourceDir: worker["source_dir"].(string),
|
||||
EnvironmentSlug: worker["environment_slug"].(string),
|
||||
}
|
||||
|
||||
github := worker["github"].([]interface{})
|
||||
if len(github) > 0 {
|
||||
s.GitHub = expandAppGitHubSourceSpec(github)
|
||||
}
|
||||
|
||||
git := worker["git"].([]interface{})
|
||||
if len(git) > 0 {
|
||||
s.Git = expandAppGitSourceSpec(git)
|
||||
}
|
||||
|
||||
appWorkers = append(appWorkers, s)
|
||||
}
|
||||
|
||||
return appWorkers
|
||||
}
|
||||
|
||||
func flattenAppSpecWorkers(workers []*godo.AppWorkerSpec) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, len(workers))
|
||||
|
||||
for i, w := range workers {
|
||||
r := make(map[string]interface{})
|
||||
|
||||
r["name"] = w.Name
|
||||
r["run_command"] = w.RunCommand
|
||||
r["build_command"] = w.BuildCommand
|
||||
r["github"] = flattenAppGitHubSourceSpec(w.GitHub)
|
||||
r["git"] = flattenAppGitSourceSpec(w.Git)
|
||||
r["dockerfile_path"] = w.DockerfilePath
|
||||
r["env"] = flattenAppEnvs(w.Envs)
|
||||
r["instance_size_slug"] = w.InstanceSizeSlug
|
||||
r["instance_count"] = int(w.InstanceCount)
|
||||
r["source_dir"] = w.SourceDir
|
||||
r["environment_slug"] = w.EnvironmentSlug
|
||||
|
||||
result[i] = r
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func expandAppSpecDatabases(config []interface{}) []*godo.AppDatabaseSpec {
|
||||
appDatabases := make([]*godo.AppDatabaseSpec, 0, len(config))
|
||||
|
||||
for _, rawDatabase := range config {
|
||||
db := rawDatabase.(map[string]interface{})
|
||||
|
||||
s := &godo.AppDatabaseSpec{
|
||||
Name: db["name"].(string),
|
||||
Engine: godo.AppDatabaseSpecEngine(db["engine"].(string)),
|
||||
Version: db["version"].(string),
|
||||
Production: db["production"].(bool),
|
||||
ClusterName: db["cluster_name"].(string),
|
||||
DBName: db["db_name"].(string),
|
||||
DBUser: db["db_user"].(string),
|
||||
}
|
||||
|
||||
appDatabases = append(appDatabases, s)
|
||||
}
|
||||
|
||||
return appDatabases
|
||||
}
|
||||
|
||||
func flattenAppSpecDatabases(databases []*godo.AppDatabaseSpec) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, len(databases))
|
||||
|
||||
for i, db := range databases {
|
||||
r := make(map[string]interface{})
|
||||
|
||||
r["name"] = db.Name
|
||||
r["engine"] = db.Engine
|
||||
r["version"] = db.Version
|
||||
r["production"] = db.Production
|
||||
r["cluster_name"] = db.ClusterName
|
||||
r["db_name"] = db.DBName
|
||||
r["db_user"] = db.DBUser
|
||||
|
||||
result[i] = r
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
|
||||
)
|
||||
|
||||
func dataSourceDigitalOceanApp() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourceDigitalOceanAppRead,
|
||||
Schema: map[string]*schema.Schema{
|
||||
"app_id": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"spec": {
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
Description: "A DigitalOcean App Platform Spec",
|
||||
Elem: &schema.Resource{
|
||||
Schema: appSpecSchema(),
|
||||
},
|
||||
},
|
||||
"default_ingress": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "The default URL to access the App",
|
||||
},
|
||||
"live_url": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"active_deployment_id": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "The ID the App's currently active deployment",
|
||||
},
|
||||
"updated_at": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "The date and time of when the App was last updated",
|
||||
},
|
||||
"created_at": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "The date and time of when the App was created",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceDigitalOceanAppRead(d *schema.ResourceData, meta interface{}) error {
|
||||
d.SetId(d.Get("app_id").(string))
|
||||
|
||||
return resourceDigitalOceanAppRead(d, meta)
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccDataSourceDigitalOceanApp_Basic(t *testing.T) {
|
||||
var app godo.App
|
||||
appName := randomTestName()
|
||||
appCreateConfig := fmt.Sprintf(testAccCheckDigitalOceanAppConfig_basic, appName)
|
||||
appDataConfig := fmt.Sprintf(testAccCheckDataSourceDigitalOceanAppConfig, appCreateConfig)
|
||||
|
||||
updatedAppCreateConfig := fmt.Sprintf(testAccCheckDigitalOceanAppConfig_addService, appName)
|
||||
updatedAppDataConfig := fmt.Sprintf(testAccCheckDataSourceDigitalOceanAppConfig, updatedAppCreateConfig)
|
||||
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: appCreateConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanAppExists("digitalocean_app.foobar", &app),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: appDataConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_app.foobar", "spec.0.name", appName),
|
||||
resource.TestCheckResourceAttrPair("digitalocean_app.foobar", "default_ingress",
|
||||
"data.digitalocean_app.foobar", "default_ingress"),
|
||||
resource.TestCheckResourceAttrPair("digitalocean_app.foobar", "live_url",
|
||||
"data.digitalocean_app.foobar", "live_url"),
|
||||
resource.TestCheckResourceAttrPair("digitalocean_app.foobar", "active_deployment_id",
|
||||
"data.digitalocean_app.foobar", "active_deployment_id"),
|
||||
resource.TestCheckResourceAttrPair("digitalocean_app.foobar", "updated_at",
|
||||
"data.digitalocean_app.foobar", "updated_at"),
|
||||
resource.TestCheckResourceAttrPair("digitalocean_app.foobar", "created_at",
|
||||
"data.digitalocean_app.foobar", "created_at"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_app.foobar", "spec.0.service.0.instance_count", "1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_app.foobar", "spec.0.service.0.instance_size_slug", "professional-xs"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_app.foobar", "spec.0.service.0.routes.0.path", "/"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_app.foobar", "spec.0.service.0.git.0.repo_clone_url",
|
||||
"https://github.com/digitalocean/sample-golang.git"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_app.foobar", "spec.0.service.0.git.0.branch", "main"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: updatedAppDataConfig,
|
||||
},
|
||||
{
|
||||
Config: updatedAppDataConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_app.foobar", "spec.0.service.0.name", "go-service"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_app.foobar", "spec.0.service.0.routes.0.path", "/go"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_app.foobar", "spec.0.service.1.name", "python-service"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"data.digitalocean_app.foobar", "spec.0.service.1.routes.0.path", "/python"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const testAccCheckDataSourceDigitalOceanAppConfig = `
|
||||
%s
|
||||
|
||||
data "digitalocean_app" "foobar" {
|
||||
app_id = digitalocean_app.foobar.id
|
||||
}`
|
|
@ -0,0 +1,29 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccDigitalOceanApp_importBasic(t *testing.T) {
|
||||
resourceName := "digitalocean_app.foobar"
|
||||
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDigitalOceanCertificateDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: fmt.Sprintf(testAccCheckDigitalOceanAppConfig_basic, randomTestName()),
|
||||
},
|
||||
|
||||
{
|
||||
ResourceName: resourceName,
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -45,6 +45,7 @@ func Provider() terraform.ResourceProvider {
|
|||
},
|
||||
DataSourcesMap: map[string]*schema.Resource{
|
||||
"digitalocean_account": dataSourceDigitalOceanAccount(),
|
||||
"digitalocean_app": dataSourceDigitalOceanApp(),
|
||||
"digitalocean_certificate": dataSourceDigitalOceanCertificate(),
|
||||
"digitalocean_container_registry": dataSourceDigitalOceanContainerRegistry(),
|
||||
"digitalocean_database_cluster": dataSourceDigitalOceanDatabaseCluster(),
|
||||
|
@ -78,6 +79,7 @@ func Provider() terraform.ResourceProvider {
|
|||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"digitalocean_app": resourceDigitalOceanApp(),
|
||||
"digitalocean_certificate": resourceDigitalOceanCertificate(),
|
||||
"digitalocean_container_registry": resourceDigitalOceanContainerRegistry(),
|
||||
"digitalocean_container_registry_docker_credentials": resourceDigitalOceanContainerRegistryDockerCredentials(),
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
|
||||
)
|
||||
|
||||
func resourceDigitalOceanApp() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceDigitalOceanAppCreate,
|
||||
Read: resourceDigitalOceanAppRead,
|
||||
Update: resourceDigitalOceanAppUpdate,
|
||||
Delete: resourceDigitalOceanAppDelete,
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: schema.ImportStatePassthrough,
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"spec": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
MaxItems: 1,
|
||||
Description: "A DigitalOcean App Platform Spec",
|
||||
Elem: &schema.Resource{
|
||||
Schema: appSpecSchema(),
|
||||
},
|
||||
},
|
||||
|
||||
// Computed attributes
|
||||
"default_ingress": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "The default URL to access the App",
|
||||
},
|
||||
|
||||
"live_url": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
// TODO: The full Deployment should be a data source, not a resource
|
||||
// specify the app id for the active deployment, include a deployment
|
||||
// id for a specific one
|
||||
"active_deployment_id": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "The ID the App's currently active deployment",
|
||||
},
|
||||
|
||||
"updated_at": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "The date and time of when the App was last updated",
|
||||
},
|
||||
"created_at": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Description: "The date and time of when the App was created",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceDigitalOceanAppCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*CombinedConfig).godoClient()
|
||||
appCreateRequest := &godo.AppCreateRequest{}
|
||||
appCreateRequest.Spec = expandAppSpec(d.Get("spec").([]interface{}))
|
||||
|
||||
log.Printf("[DEBUG] App create request: %#v", appCreateRequest)
|
||||
app, _, err := client.Apps.Create(context.Background(), appCreateRequest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating App: %s", err)
|
||||
}
|
||||
|
||||
d.SetId(app.ID)
|
||||
log.Printf("[DEBUG] Waiting for app (%s) deployment to become active", app.ID)
|
||||
|
||||
err = waitForAppDeployment(client, app.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[INFO] App created, ID: %s", d.Id())
|
||||
|
||||
return resourceDigitalOceanAppRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceDigitalOceanAppRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*CombinedConfig).godoClient()
|
||||
|
||||
app, resp, err := client.Apps.Get(context.Background(), d.Id())
|
||||
if err != nil {
|
||||
if resp != nil && resp.StatusCode == 404 {
|
||||
log.Printf("[DEBUG] App (%s) was not found - removing from state", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Error reading App: %s", err)
|
||||
}
|
||||
|
||||
d.SetId(app.ID)
|
||||
d.Set("default_ingress", app.DefaultIngress)
|
||||
d.Set("live_url", app.LiveURL)
|
||||
d.Set("active_deployment_id", app.ActiveDeployment.ID)
|
||||
d.Set("updated_at", app.UpdatedAt.UTC().String())
|
||||
d.Set("created_at", app.CreatedAt.UTC().String())
|
||||
|
||||
if err := d.Set("spec", flattenAppSpec(app.Spec)); err != nil {
|
||||
return fmt.Errorf("[DEBUG] Error setting app spec: %#v", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func resourceDigitalOceanAppUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*CombinedConfig).godoClient()
|
||||
|
||||
if d.HasChange("spec") {
|
||||
appUpdateRequest := &godo.AppUpdateRequest{}
|
||||
appUpdateRequest.Spec = expandAppSpec(d.Get("spec").([]interface{}))
|
||||
|
||||
app, _, err := client.Apps.Update(context.Background(), d.Id(), appUpdateRequest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating app (%s): %s", d.Id(), err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Waiting for app (%s) deployment to become active", app.ID)
|
||||
err = waitForAppDeployment(client, app.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Updated app (%s)", app.ID)
|
||||
}
|
||||
|
||||
return resourceDigitalOceanAppRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceDigitalOceanAppDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*CombinedConfig).godoClient()
|
||||
|
||||
log.Printf("[INFO] Deleting App: %s", d.Id())
|
||||
_, err := client.Apps.Delete(context.Background(), d.Id())
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deletingApp: %s", err)
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
func waitForAppDeployment(client *godo.Client, id string) error {
|
||||
tickerInterval := 10 //10s
|
||||
timeout := 1800 //1800s, 30min
|
||||
n := 0
|
||||
|
||||
var deploymentID string
|
||||
ticker := time.NewTicker(time.Duration(tickerInterval) * time.Second)
|
||||
for range ticker.C {
|
||||
if n*tickerInterval > timeout {
|
||||
ticker.Stop()
|
||||
break
|
||||
}
|
||||
|
||||
if deploymentID == "" {
|
||||
app, _, err := client.Apps.Get(context.Background(), id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error trying to read app deployment state: %s", err)
|
||||
}
|
||||
|
||||
if app.InProgressDeployment != nil {
|
||||
deploymentID = app.InProgressDeployment.ID
|
||||
}
|
||||
|
||||
} else {
|
||||
deployment, _, err := client.Apps.GetDeployment(context.Background(), id, deploymentID)
|
||||
if err != nil {
|
||||
ticker.Stop()
|
||||
return fmt.Errorf("Error trying to read app deployment state: %s", err)
|
||||
}
|
||||
|
||||
allSuccessful := deployment.Progress.SuccessSteps == deployment.Progress.TotalSteps
|
||||
if allSuccessful {
|
||||
ticker.Stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
if deployment.Progress.ErrorSteps > 0 {
|
||||
ticker.Stop()
|
||||
return fmt.Errorf("error deploying app (%s) (deployment ID: %s):\n%s", id, deployment.ID, godo.Stringify(deployment.Progress))
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Waiting for app (%s) deployment (%s) to become active. Phase: %s (%d/%d)",
|
||||
id, deployment.ID, deployment.Phase, deployment.Progress.SuccessSteps, deployment.Progress.TotalSteps)
|
||||
}
|
||||
|
||||
n++
|
||||
}
|
||||
|
||||
return fmt.Errorf("timeout waiting to app (%s) deployment", id)
|
||||
}
|
|
@ -0,0 +1,492 @@
|
|||
package digitalocean
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/terraform"
|
||||
)
|
||||
|
||||
func init() {
|
||||
resource.AddTestSweepers("digitalocean_app", &resource.Sweeper{
|
||||
Name: "digitalocean_app",
|
||||
F: testSweepApp,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func testSweepApp(region string) error {
|
||||
meta, err := sharedConfigForRegion(region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := meta.(*CombinedConfig).godoClient()
|
||||
|
||||
opt := &godo.ListOptions{PerPage: 200}
|
||||
apps, _, err := client.Apps.List(context.Background(), opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, app := range apps {
|
||||
if strings.HasPrefix(app.Spec.Name, testNamePrefix) {
|
||||
log.Printf("Destroying app %s", app.Spec.Name)
|
||||
|
||||
if _, err := client.Apps.Delete(context.Background(), app.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAccDigitalOceanApp_Basic(t *testing.T) {
|
||||
var app godo.App
|
||||
appName := randomTestName()
|
||||
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDigitalOceanAppDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: fmt.Sprintf(testAccCheckDigitalOceanAppConfig_basic, appName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanAppExists("digitalocean_app.foobar", &app),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.name", appName),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_app.foobar", "default_ingress"),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_app.foobar", "live_url"),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_app.foobar", "active_deployment_id"),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_app.foobar", "updated_at"),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_app.foobar", "created_at"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.instance_count", "1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.instance_size_slug", "basic-xxs"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.routes.0.path", "/"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.git.0.repo_clone_url",
|
||||
"https://github.com/digitalocean/sample-golang.git"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.git.0.branch", "main"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.health_check.0.http_path", "/"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.health_check.0.timeout_seconds", "10"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: fmt.Sprintf(testAccCheckDigitalOceanAppConfig_addService, appName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanAppExists("digitalocean_app.foobar", &app),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.name", "go-service"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.routes.0.path", "/go"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.1.name", "python-service"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.1.routes.0.path", "/python"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: fmt.Sprintf(testAccCheckDigitalOceanAppConfig_addDatabase, appName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanAppExists("digitalocean_app.foobar", &app),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.routes.0.path", "/"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.database.0.name", "test-db"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.database.0.engine", "PG"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDigitalOceanApp_StaticSite(t *testing.T) {
|
||||
var app godo.App
|
||||
appName := randomTestName()
|
||||
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDigitalOceanAppDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: fmt.Sprintf(testAccCheckDigitalOceanAppConfig_StaticSite, appName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanAppExists("digitalocean_app.foobar", &app),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.name", appName),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_app.foobar", "default_ingress"),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_app.foobar", "live_url"),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_app.foobar", "active_deployment_id"),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_app.foobar", "updated_at"),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_app.foobar", "created_at"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.static_site.0.routes.0.path", "/"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.static_site.0.build_command", "bundle exec jekyll build -d ./public"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.static_site.0.output_dir", "/public"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.static_site.0.git.0.repo_clone_url",
|
||||
"https://github.com/digitalocean/sample-jekyll.git"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.static_site.0.git.0.branch", "main"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDigitalOceanApp_Envs(t *testing.T) {
|
||||
var app godo.App
|
||||
appName := randomTestName()
|
||||
|
||||
oneEnv := `
|
||||
env {
|
||||
key = "FOO"
|
||||
value = "bar"
|
||||
}
|
||||
`
|
||||
|
||||
twoEnvs := `
|
||||
env {
|
||||
key = "FOO"
|
||||
value = "bar"
|
||||
}
|
||||
|
||||
env {
|
||||
key = "FIZZ"
|
||||
value = "pop"
|
||||
scope = "BUILD_TIME"
|
||||
}
|
||||
`
|
||||
|
||||
oneEnvUpdated := `
|
||||
env {
|
||||
key = "FOO"
|
||||
value = "baz"
|
||||
scope = "RUN_TIME"
|
||||
}
|
||||
`
|
||||
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDigitalOceanAppDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: fmt.Sprintf(testAccCheckDigitalOceanAppConfig_Envs, appName, oneEnv),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanAppExists("digitalocean_app.foobar", &app),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.name", appName),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.#", "1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.3118534296.key", "FOO"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.3118534296.value", "bar"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.3118534296.scope", "RUN_AND_BUILD_TIME"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: fmt.Sprintf(testAccCheckDigitalOceanAppConfig_Envs, appName, twoEnvs),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanAppExists("digitalocean_app.foobar", &app),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.name", appName),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.#", "2"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.3118534296.key", "FOO"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.3118534296.value", "bar"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.3118534296.scope", "RUN_AND_BUILD_TIME"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.1776096292.key", "FIZZ"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.1776096292.value", "pop"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.1776096292.scope", "BUILD_TIME"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: fmt.Sprintf(testAccCheckDigitalOceanAppConfig_Envs, appName, oneEnvUpdated),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanAppExists("digitalocean_app.foobar", &app),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.name", appName),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.#", "1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.1277866902.key", "FOO"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.1277866902.value", "baz"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.service.0.env.1277866902.scope", "RUN_TIME"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDigitalOceanApp_Worker(t *testing.T) {
|
||||
var app godo.App
|
||||
appName := randomTestName()
|
||||
workerConfig := fmt.Sprintf(testAccCheckDigitalOceanAppConfig_worker, appName, "basic-xxs")
|
||||
upgradedWorkerConfig := fmt.Sprintf(testAccCheckDigitalOceanAppConfig_worker, appName, "professional-xs")
|
||||
|
||||
resource.ParallelTest(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDigitalOceanAppDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: workerConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanAppExists("digitalocean_app.foobar", &app),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.name", appName),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_app.foobar", "active_deployment_id"),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_app.foobar", "updated_at"),
|
||||
resource.TestCheckResourceAttrSet("digitalocean_app.foobar", "created_at"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.worker.0.instance_count", "1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.worker.0.instance_size_slug", "basic-xxs"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.worker.0.git.0.repo_clone_url",
|
||||
"https://github.com/digitalocean/sample-sleeper.git"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.worker.0.git.0.branch", "main"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: upgradedWorkerConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_app.foobar", "spec.0.worker.0.instance_size_slug", "professional-xs"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckDigitalOceanAppDestroy(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*CombinedConfig).godoClient()
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "digitalocean_app" {
|
||||
continue
|
||||
}
|
||||
|
||||
_, _, err := client.Apps.Get(context.Background(), rs.Primary.ID)
|
||||
if err == nil {
|
||||
return fmt.Errorf("Container Registry still exists")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckDigitalOceanAppExists(n string, app *godo.App) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No Record ID is set")
|
||||
}
|
||||
|
||||
client := testAccProvider.Meta().(*CombinedConfig).godoClient()
|
||||
|
||||
foundApp, _, err := client.Apps.Get(context.Background(), rs.Primary.ID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*app = *foundApp
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var testAccCheckDigitalOceanAppConfig_basic = `
|
||||
resource "digitalocean_app" "foobar" {
|
||||
spec {
|
||||
name = "%s"
|
||||
region = "ams"
|
||||
|
||||
service {
|
||||
name = "go-service"
|
||||
environment_slug = "go"
|
||||
instance_count = 1
|
||||
instance_size_slug = "basic-xxs"
|
||||
|
||||
git {
|
||||
repo_clone_url = "https://github.com/digitalocean/sample-golang.git"
|
||||
branch = "main"
|
||||
}
|
||||
|
||||
health_check {
|
||||
http_path = "/"
|
||||
timeout_seconds = 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
var testAccCheckDigitalOceanAppConfig_addService = `
|
||||
resource "digitalocean_app" "foobar" {
|
||||
spec {
|
||||
name = "%s"
|
||||
region = "ams"
|
||||
|
||||
service {
|
||||
name = "go-service"
|
||||
environment_slug = "go"
|
||||
instance_count = 1
|
||||
instance_size_slug = "basic-xxs"
|
||||
|
||||
git {
|
||||
repo_clone_url = "https://github.com/digitalocean/sample-golang.git"
|
||||
branch = "main"
|
||||
}
|
||||
|
||||
routes {
|
||||
path = "/go"
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
||||
name = "python-service"
|
||||
environment_slug = "python"
|
||||
instance_count = 1
|
||||
instance_size_slug = "basic-xxs"
|
||||
|
||||
git {
|
||||
repo_clone_url = "https://github.com/digitalocean/sample-python.git"
|
||||
branch = "main"
|
||||
}
|
||||
|
||||
routes {
|
||||
path = "/python"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
var testAccCheckDigitalOceanAppConfig_addDatabase = `
|
||||
resource "digitalocean_app" "foobar" {
|
||||
spec {
|
||||
name = "%s"
|
||||
region = "ams"
|
||||
|
||||
service {
|
||||
name = "go-service"
|
||||
environment_slug = "go"
|
||||
instance_count = 1
|
||||
instance_size_slug = "basic-xxs"
|
||||
|
||||
git {
|
||||
repo_clone_url = "https://github.com/digitalocean/sample-golang.git"
|
||||
branch = "main"
|
||||
}
|
||||
|
||||
routes {
|
||||
path = "/"
|
||||
}
|
||||
}
|
||||
|
||||
database {
|
||||
name = "test-db"
|
||||
engine = "PG"
|
||||
production = false
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
var testAccCheckDigitalOceanAppConfig_StaticSite = `
|
||||
resource "digitalocean_app" "foobar" {
|
||||
spec {
|
||||
name = "%s"
|
||||
region = "ams"
|
||||
|
||||
static_site {
|
||||
name = "sample-jekyll"
|
||||
build_command = "bundle exec jekyll build -d ./public"
|
||||
output_dir = "/public"
|
||||
environment_slug = "jekyll"
|
||||
|
||||
git {
|
||||
repo_clone_url = "https://github.com/digitalocean/sample-jekyll.git"
|
||||
branch = "main"
|
||||
}
|
||||
|
||||
routes {
|
||||
path = "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
var testAccCheckDigitalOceanAppConfig_Envs = `
|
||||
resource "digitalocean_app" "foobar" {
|
||||
spec {
|
||||
name = "%s"
|
||||
region = "ams"
|
||||
|
||||
service {
|
||||
name = "go-service"
|
||||
environment_slug = "go"
|
||||
instance_count = 1
|
||||
instance_size_slug = "basic-xxs"
|
||||
|
||||
git {
|
||||
repo_clone_url = "https://github.com/digitalocean/sample-golang.git"
|
||||
branch = "main"
|
||||
}
|
||||
|
||||
%s
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
var testAccCheckDigitalOceanAppConfig_worker = `
|
||||
resource "digitalocean_app" "foobar" {
|
||||
spec {
|
||||
name = "%s"
|
||||
region = "ams"
|
||||
|
||||
worker {
|
||||
name = "go-worker"
|
||||
instance_count = 1
|
||||
instance_size_slug = "%s"
|
||||
|
||||
git {
|
||||
repo_clone_url = "https://github.com/digitalocean/sample-sleeper.git"
|
||||
branch = "main"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
3
go.mod
3
go.mod
|
@ -3,7 +3,7 @@ module github.com/digitalocean/terraform-provider-digitalocean
|
|||
require (
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.6.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.25.4
|
||||
github.com/digitalocean/godo v1.42.1
|
||||
github.com/digitalocean/godo v1.45.0
|
||||
github.com/hashicorp/go-version v1.2.0
|
||||
github.com/hashicorp/terraform-plugin-sdk v1.15.0
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
|
@ -12,6 +12,7 @@ require (
|
|||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
gopkg.in/yaml.v2 v2.2.4
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
)
|
||||
|
||||
replace git.apache.org/thrift.git => github.com/apache/thrift v0.0.0-20180902110319-2566ecd5d999
|
||||
|
|
5
go.sum
5
go.sum
|
@ -87,8 +87,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/digitalocean/godo v1.42.1 h1:SJ/XMVsp5CZmyQal8gLlOl9jSl1i3FaN20LlgtK5ZMs=
|
||||
github.com/digitalocean/godo v1.42.1/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU=
|
||||
github.com/digitalocean/godo v1.45.0 h1:Hg4Q216Xr0AJjnAxK4bkP/qacj4svGaapWRfC8z9URc=
|
||||
github.com/digitalocean/godo v1.45.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU=
|
||||
github.com/dustinkirkland/golang-petname v0.0.0-20170105215008-242afa0b4f8a/go.mod h1:V+Qd57rJe8gd4eiGzZyg4h54VLHmYVVw54iMnlAMrF8=
|
||||
github.com/dustinkirkland/golang-petname v0.0.0-20170921220637-d3c2ba80e75e h1:bRcq7ruHMqCVB/ugLbBylx+LrccNACFDEaqAD/aZ80Q=
|
||||
github.com/dustinkirkland/golang-petname v0.0.0-20170921220637-d3c2ba80e75e/go.mod h1:V+Qd57rJe8gd4eiGzZyg4h54VLHmYVVw54iMnlAMrF8=
|
||||
|
@ -274,6 +274,7 @@ github.com/hashicorp/terraform-plugin-sdk v1.1.1 h1:wQ2HtvOE8K4QYcm2JB8YFw9u7Opl
|
|||
github.com/hashicorp/terraform-plugin-sdk v1.1.1/go.mod h1:NuwtLpEpPsFaKJPJNGtMcn9vlhe6Ofe+Y6NqXhJgV2M=
|
||||
github.com/hashicorp/terraform-plugin-sdk v1.15.0 h1:bmYnTT7MqNXlUHDc7pT8E6uKT2g/upjlRLypJFK1OQU=
|
||||
github.com/hashicorp/terraform-plugin-sdk v1.15.0/go.mod h1:PuFTln8urDmRM6mV0II6apOTsyG/iHkxp+5W11eJE58=
|
||||
github.com/hashicorp/terraform-plugin-sdk v1.16.0 h1:NrkXMRjHErUPPTHQkZ6JIn6bByiJzGnlJzH1rVdNEuE=
|
||||
github.com/hashicorp/terraform-plugin-test v1.4.3 h1:HSOZZu2W7a9tx4QPYXhrT9oh7JptibaSkP7CK4C0OF0=
|
||||
github.com/hashicorp/terraform-plugin-test v1.4.3/go.mod h1:UA7z/02pgqsRLut4DJIPm0Hjnj27uOvhi19c8kTqIfM=
|
||||
github.com/hashicorp/terraform-svchost v0.0.0-20191011084731-65d371908596 h1:hjyO2JsNZUKT1ym+FAdlBEkGPevazYsmVgIMw7dVELg=
|
||||
|
|
|
@ -1,5 +1,21 @@
|
|||
# Change Log
|
||||
|
||||
## [v1.45.0] - 2020-09-25
|
||||
|
||||
**Note**: This release contains breaking changes to App Platform features currently in closed beta.
|
||||
|
||||
- #369 update apps types to latest - @kamaln7
|
||||
- #368 Kubernetes: add taints field to node pool create and update requests - @timoreimann
|
||||
- #367 update apps types, address marshaling bug - @kamaln7
|
||||
|
||||
## [v1.44.0] - 2020-09-08
|
||||
|
||||
- #364 apps: support aggregate deployment logs - @kamaln7
|
||||
|
||||
## [v1.43.0] - 2020-09-08
|
||||
|
||||
- #362 update apps types - @kamaln7
|
||||
|
||||
## [v1.42.1] - 2020-08-06
|
||||
|
||||
- #360 domains: Allow for SRV records with port 0. - @andrewsomething
|
||||
|
|
|
@ -3,15 +3,43 @@
|
|||
|
||||
package godo
|
||||
|
||||
import ()
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// App An application's configuration and status.
|
||||
type App struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
OwnerUUID string `json:"owner_uuid,omitempty"`
|
||||
Spec *AppSpec `json:"spec"`
|
||||
DefaultIngress string `json:"default_ingress,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
ActiveDeployment *Deployment `json:"active_deployment,omitempty"`
|
||||
InProgressDeployment *Deployment `json:"in_progress_deployment,omitempty"`
|
||||
LastDeploymentCreatedAt time.Time `json:"last_deployment_created_at,omitempty"`
|
||||
LiveURL string `json:"live_url,omitempty"`
|
||||
Region *AppRegion `json:"region,omitempty"`
|
||||
}
|
||||
|
||||
// AppDatabaseSpec struct for AppDatabaseSpec
|
||||
type AppDatabaseSpec struct {
|
||||
Name string `json:"name"`
|
||||
Engine AppDatabaseSpecEngine `json:"engine,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Size string `json:"size,omitempty"`
|
||||
NumNodes int64 `json:"num_nodes,omitempty"`
|
||||
// The name. Must be unique across all components within the same app.
|
||||
Name string `json:"name"`
|
||||
Engine AppDatabaseSpecEngine `json:"engine,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
// Deprecated.
|
||||
Size string `json:"size,omitempty"`
|
||||
// Deprecated.
|
||||
NumNodes int64 `json:"num_nodes,omitempty"`
|
||||
// Whether this is a production or dev database.
|
||||
Production bool `json:"production,omitempty"`
|
||||
// The name of the underlying DigitalOcean DBaaS cluster. This is required for production databases. For dev databases, if cluster_name is not set, a new cluster will be provisioned.
|
||||
ClusterName string `json:"cluster_name,omitempty"`
|
||||
// The name of the MySQL or PostgreSQL database to configure.
|
||||
DBName string `json:"db_name,omitempty"`
|
||||
// The name of the MySQL or PostgreSQL user to configure.
|
||||
DBUser string `json:"db_user,omitempty"`
|
||||
}
|
||||
|
||||
// AppDatabaseSpecEngine the model 'AppDatabaseSpecEngine'
|
||||
|
@ -19,118 +47,322 @@ type AppDatabaseSpecEngine string
|
|||
|
||||
// List of AppDatabaseSpecEngine
|
||||
const (
|
||||
APPDATABASESPECENGINE_UNSET AppDatabaseSpecEngine = "UNSET"
|
||||
APPDATABASESPECENGINE_MYSQL AppDatabaseSpecEngine = "MYSQL"
|
||||
APPDATABASESPECENGINE_PG AppDatabaseSpecEngine = "PG"
|
||||
APPDATABASESPECENGINE_REDIS AppDatabaseSpecEngine = "REDIS"
|
||||
AppDatabaseSpecEngine_Unset AppDatabaseSpecEngine = "UNSET"
|
||||
AppDatabaseSpecEngine_MySQL AppDatabaseSpecEngine = "MYSQL"
|
||||
AppDatabaseSpecEngine_PG AppDatabaseSpecEngine = "PG"
|
||||
AppDatabaseSpecEngine_Redis AppDatabaseSpecEngine = "REDIS"
|
||||
)
|
||||
|
||||
// AppDomainSpec struct for AppDomainSpec
|
||||
type AppDomainSpec struct {
|
||||
Domain string `json:"domain"`
|
||||
// The hostname.
|
||||
Domain string `json:"domain"`
|
||||
Type AppDomainSpecType `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// AppDomainSpecType - DEFAULT: The default .ondigitalocean.app domain assigned to this app. - PRIMARY: The primary domain for this app. This is the domain that is displayed as the default in the control panel, used in bindable environment variables, and any other places that reference an app's live URL. Only one domain may be set as primary. - ALIAS: A non-primary domain.
|
||||
type AppDomainSpecType string
|
||||
|
||||
// List of AppDomainSpecType
|
||||
const (
|
||||
AppDomainSpecType_Unspecified AppDomainSpecType = "UNSPECIFIED"
|
||||
AppDomainSpecType_Default AppDomainSpecType = "DEFAULT"
|
||||
AppDomainSpecType_Primary AppDomainSpecType = "PRIMARY"
|
||||
AppDomainSpecType_Alias AppDomainSpecType = "ALIAS"
|
||||
)
|
||||
|
||||
// AppJobSpec struct for AppJobSpec
|
||||
type AppJobSpec struct {
|
||||
// The name. Must be unique across all components within the same app.
|
||||
Name string `json:"name"`
|
||||
Git *GitSourceSpec `json:"git,omitempty"`
|
||||
GitHub *GitHubSourceSpec `json:"github,omitempty"`
|
||||
// The path to the Dockerfile relative to the root of the repo. If set, it will be used to build this component. Otherwise, App Platform will attempt to build it using buildpacks.
|
||||
DockerfilePath string `json:"dockerfile_path,omitempty"`
|
||||
// An optional build command to run while building this component from source.
|
||||
BuildCommand string `json:"build_command,omitempty"`
|
||||
// An optional run command to override the component's default.
|
||||
RunCommand string `json:"run_command,omitempty"`
|
||||
// An optional path to the working directory to use for the build. For Dockerfile builds, this will be used as the build context. Must be relative to the root of the repo.
|
||||
SourceDir string `json:"source_dir,omitempty"`
|
||||
// An environment slug describing the type of this app. For a full list, please refer to [the product documentation](https://www.digitalocean.com/docs/app-platform/).
|
||||
EnvironmentSlug string `json:"environment_slug,omitempty"`
|
||||
// A list of environment variables made available to the component.
|
||||
Envs []*AppVariableDefinition `json:"envs,omitempty"`
|
||||
// The instance size to use for this component.
|
||||
InstanceSizeSlug string `json:"instance_size_slug,omitempty"`
|
||||
InstanceCount int64 `json:"instance_count,omitempty"`
|
||||
}
|
||||
|
||||
// AppRouteSpec struct for AppRouteSpec
|
||||
type AppRouteSpec struct {
|
||||
// An HTTP path prefix. Paths must start with / and must be unique across all components within an app.
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
// AppServiceSpec struct for AppServiceSpec
|
||||
type AppServiceSpec struct {
|
||||
Name string `json:"name"`
|
||||
RunCommand string `json:"run_command,omitempty"`
|
||||
BuildCommand string `json:"build_command,omitempty"`
|
||||
HTTPPort int64 `json:"http_port,omitempty"`
|
||||
DockerfilePath string `json:"dockerfile_path,omitempty"`
|
||||
Git GitSourceSpec `json:"git,omitempty"`
|
||||
GitHub GitHubSourceSpec `json:"github,omitempty"`
|
||||
Envs []AppVariableDefinition `json:"envs,omitempty"`
|
||||
InstanceSizeSlug string `json:"instance_size_slug,omitempty"`
|
||||
InstanceCount int64 `json:"instance_count,omitempty"`
|
||||
Routes []AppRouteSpec `json:"routes,omitempty"`
|
||||
SourceDir string `json:"source_dir,omitempty"`
|
||||
EnvironmentSlug string `json:"environment_slug,omitempty"`
|
||||
// The name. Must be unique across all components within the same app.
|
||||
Name string `json:"name"`
|
||||
Git *GitSourceSpec `json:"git,omitempty"`
|
||||
GitHub *GitHubSourceSpec `json:"github,omitempty"`
|
||||
// The path to the Dockerfile relative to the root of the repo. If set, it will be used to build this component. Otherwise, App Platform will attempt to build it using buildpacks.
|
||||
DockerfilePath string `json:"dockerfile_path,omitempty"`
|
||||
// An optional build command to run while building this component from source.
|
||||
BuildCommand string `json:"build_command,omitempty"`
|
||||
// An optional run command to override the component's default.
|
||||
RunCommand string `json:"run_command,omitempty"`
|
||||
// An optional path to the working directory to use for the build. For Dockerfile builds, this will be used as the build context. Must be relative to the root of the repo.
|
||||
SourceDir string `json:"source_dir,omitempty"`
|
||||
// An environment slug describing the type of this app. For a full list, please refer to [the product documentation](https://www.digitalocean.com/docs/app-platform/).
|
||||
EnvironmentSlug string `json:"environment_slug,omitempty"`
|
||||
// A list of environment variables made available to the component.
|
||||
Envs []*AppVariableDefinition `json:"envs,omitempty"`
|
||||
// The instance size to use for this component.
|
||||
InstanceSizeSlug string `json:"instance_size_slug,omitempty"`
|
||||
InstanceCount int64 `json:"instance_count,omitempty"`
|
||||
// The internal port on which this service's run command will listen. Default: 8080 If there is not an environment variable with the name `PORT`, one will be automatically added with its value set to the value of this field.
|
||||
HTTPPort int64 `json:"http_port,omitempty"`
|
||||
// A list of HTTP routes that should be routed to this component.
|
||||
Routes []*AppRouteSpec `json:"routes,omitempty"`
|
||||
HealthCheck *AppServiceSpecHealthCheck `json:"health_check,omitempty"`
|
||||
}
|
||||
|
||||
// AppSpec struct for AppSpec
|
||||
// AppServiceSpecHealthCheck struct for AppServiceSpecHealthCheck
|
||||
type AppServiceSpecHealthCheck struct {
|
||||
// Deprecated. Use http_path instead.
|
||||
Path string `json:"path,omitempty"`
|
||||
// The number of seconds to wait before beginning health checks.
|
||||
InitialDelaySeconds int32 `json:"initial_delay_seconds,omitempty"`
|
||||
// The number of seconds to wait between health checks.
|
||||
PeriodSeconds int32 `json:"period_seconds,omitempty"`
|
||||
// The number of seconds after which the check times out.
|
||||
TimeoutSeconds int32 `json:"timeout_seconds,omitempty"`
|
||||
// The number of successful health checks before considered healthy.
|
||||
SuccessThreshold int32 `json:"success_threshold,omitempty"`
|
||||
// The number of failed health checks before considered unhealthy.
|
||||
FailureThreshold int32 `json:"failure_threshold,omitempty"`
|
||||
// The route path used for the HTTP health check ping. If not set, the HTTP health check will be disabled and a TCP health check used instead.
|
||||
HTTPPath string `json:"http_path,omitempty"`
|
||||
}
|
||||
|
||||
// AppSpec The desired configuration of an application.
|
||||
type AppSpec struct {
|
||||
Services []AppServiceSpec `json:"services,omitempty"`
|
||||
StaticSites []AppStaticSiteSpec `json:"static_sites,omitempty"`
|
||||
Databases []AppDatabaseSpec `json:"databases,omitempty"`
|
||||
Workers []AppWorkerSpec `json:"workers,omitempty"`
|
||||
Region string `json:"region,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Domains []AppDomainSpec `json:"domains,omitempty"`
|
||||
// The name of the app. Must be unique across all in the same account.
|
||||
Name string `json:"name"`
|
||||
// Workloads which expose publicy-accessible HTTP services.
|
||||
Services []*AppServiceSpec `json:"services,omitempty"`
|
||||
// Content which can be rendered to static web assets.
|
||||
StaticSites []*AppStaticSiteSpec `json:"static_sites,omitempty"`
|
||||
// Workloads which do not expose publicly-accessible HTTP services.
|
||||
Workers []*AppWorkerSpec `json:"workers,omitempty"`
|
||||
// One-time or recurring workloads which do not expose publicly-accessible HTTP routes.
|
||||
Jobs []*AppJobSpec `json:"jobs,omitempty"`
|
||||
// Database instances which can provide persistence to workloads within the application.
|
||||
Databases []*AppDatabaseSpec `json:"databases,omitempty"`
|
||||
// A set of hostnames where the application will be available.
|
||||
Domains []*AppDomainSpec `json:"domains,omitempty"`
|
||||
// The slug form of the geographical origin of the app.
|
||||
Region string `json:"region,omitempty"`
|
||||
}
|
||||
|
||||
// AppStaticSiteSpec struct for AppStaticSiteSpec
|
||||
type AppStaticSiteSpec struct {
|
||||
Name string `json:"name"`
|
||||
BuildCommand string `json:"build_command,omitempty"`
|
||||
Git GitSourceSpec `json:"git,omitempty"`
|
||||
GitHub GitHubSourceSpec `json:"github,omitempty"`
|
||||
Envs []AppVariableDefinition `json:"envs,omitempty"`
|
||||
Routes []AppRouteSpec `json:"routes,omitempty"`
|
||||
SourceDir string `json:"source_dir,omitempty"`
|
||||
EnvironmentSlug string `json:"environment_slug,omitempty"`
|
||||
// The name. Must be unique across all components within the same app.
|
||||
Name string `json:"name"`
|
||||
Git *GitSourceSpec `json:"git,omitempty"`
|
||||
GitHub *GitHubSourceSpec `json:"github,omitempty"`
|
||||
// The path to the Dockerfile relative to the root of the repo. If set, it will be used to build this component. Otherwise, App Platform will attempt to build it using buildpacks.
|
||||
DockerfilePath string `json:"dockerfile_path,omitempty"`
|
||||
// An optional build command to run while building this component from source.
|
||||
BuildCommand string `json:"build_command,omitempty"`
|
||||
// An optional path to the working directory to use for the build. For Dockerfile builds, this will be used as the build context. Must be relative to the root of the repo.
|
||||
SourceDir string `json:"source_dir,omitempty"`
|
||||
// An environment slug describing the type of this app. For a full list, please refer to [the product documentation](https://www.digitalocean.com/docs/app-platform/).
|
||||
EnvironmentSlug string `json:"environment_slug,omitempty"`
|
||||
// An optional path to where the built assets will be located, relative to the build context. If not set, App Platform will automatically scan for these directory names: `_static`, `dist`, `public`.
|
||||
OutputDir string `json:"output_dir,omitempty"`
|
||||
IndexDocument string `json:"index_document,omitempty"`
|
||||
// The name of the error document to use when serving this static site. Default: 404.html. If no such file exists within the built assets, App Platform will supply one.
|
||||
ErrorDocument string `json:"error_document,omitempty"`
|
||||
// A list of environment variables made available to the component.
|
||||
Envs []*AppVariableDefinition `json:"envs,omitempty"`
|
||||
// A list of HTTP routes that should be routed to this component.
|
||||
Routes []*AppRouteSpec `json:"routes,omitempty"`
|
||||
}
|
||||
|
||||
// AppVariableDefinition struct for AppVariableDefinition
|
||||
type AppVariableDefinition struct {
|
||||
// The name
|
||||
Key string `json:"key"`
|
||||
// The value. If the type is SECRET, the value will be encrypted on first submission. On following submissions, the encrypted value must be used.
|
||||
Value string `json:"value,omitempty"`
|
||||
Scope VariableScope `json:"scope,omitempty"`
|
||||
// POSIX allows a broader env var definition, but we restrict to what is allowed by bash. http://git.savannah.gnu.org/cgit/bash.git/tree/general.h?h=bash-5.0#n124 Based on the POSIX spec and some casting to unsigned char in bash code I think this is restricted to ASCII (not unicode).
|
||||
Key string `json:"key"`
|
||||
Type VariableType `json:"type,omitempty"`
|
||||
EncryptedValue string `json:"encrypted_value,omitempty"`
|
||||
Scope AppVariableScope `json:"scope,omitempty"`
|
||||
Type AppVariableType `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// AppWorkerSpec struct for AppWorkerSpec
|
||||
type AppWorkerSpec struct {
|
||||
Name string `json:"name"`
|
||||
RunCommand string `json:"run_command,omitempty"`
|
||||
BuildCommand string `json:"build_command,omitempty"`
|
||||
DockerfilePath string `json:"dockerfile_path,omitempty"`
|
||||
Git GitSourceSpec `json:"git,omitempty"`
|
||||
GitHub GitHubSourceSpec `json:"github,omitempty"`
|
||||
Envs []AppVariableDefinition `json:"envs,omitempty"`
|
||||
InstanceSizeSlug string `json:"instance_size_slug,omitempty"`
|
||||
InstanceCount int64 `json:"instance_count,omitempty"`
|
||||
SourceDir string `json:"source_dir,omitempty"`
|
||||
EnvironmentSlug string `json:"environment_slug,omitempty"`
|
||||
// The name. Must be unique across all components within the same app.
|
||||
Name string `json:"name"`
|
||||
Git *GitSourceSpec `json:"git,omitempty"`
|
||||
GitHub *GitHubSourceSpec `json:"github,omitempty"`
|
||||
// The path to the Dockerfile relative to the root of the repo. If set, it will be used to build this component. Otherwise, App Platform will attempt to build it using buildpacks.
|
||||
DockerfilePath string `json:"dockerfile_path,omitempty"`
|
||||
// An optional build command to run while building this component from source.
|
||||
BuildCommand string `json:"build_command,omitempty"`
|
||||
// An optional run command to override the component's default.
|
||||
RunCommand string `json:"run_command,omitempty"`
|
||||
// An optional path to the working directory to use for the build. For Dockerfile builds, this will be used as the build context. Must be relative to the root of the repo.
|
||||
SourceDir string `json:"source_dir,omitempty"`
|
||||
// An environment slug describing the type of this app. For a full list, please refer to [the product documentation](https://www.digitalocean.com/docs/app-platform/).
|
||||
EnvironmentSlug string `json:"environment_slug,omitempty"`
|
||||
// A list of environment variables made available to the component.
|
||||
Envs []*AppVariableDefinition `json:"envs,omitempty"`
|
||||
// The instance size to use for this component.
|
||||
InstanceSizeSlug string `json:"instance_size_slug,omitempty"`
|
||||
InstanceCount int64 `json:"instance_count,omitempty"`
|
||||
}
|
||||
|
||||
// Deployment struct for Deployment
|
||||
type Deployment struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Spec *AppSpec `json:"spec,omitempty"`
|
||||
Services []*DeploymentService `json:"services,omitempty"`
|
||||
StaticSites []*DeploymentStaticSite `json:"static_sites,omitempty"`
|
||||
Workers []*DeploymentWorker `json:"workers,omitempty"`
|
||||
Jobs []*DeploymentJob `json:"jobs,omitempty"`
|
||||
PhaseLastUpdatedAt time.Time `json:"phase_last_updated_at,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
Cause string `json:"cause,omitempty"`
|
||||
ClonedFrom string `json:"cloned_from,omitempty"`
|
||||
Progress *DeploymentProgress `json:"progress,omitempty"`
|
||||
Phase DeploymentPhase `json:"phase,omitempty"`
|
||||
}
|
||||
|
||||
// DeploymentJob struct for DeploymentJob
|
||||
type DeploymentJob struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
SourceCommitHash string `json:"source_commit_hash,omitempty"`
|
||||
}
|
||||
|
||||
// DeploymentPhase the model 'DeploymentPhase'
|
||||
type DeploymentPhase string
|
||||
|
||||
// List of DeploymentPhase
|
||||
const (
|
||||
DeploymentPhase_Unknown DeploymentPhase = "UNKNOWN"
|
||||
DeploymentPhase_PendingBuild DeploymentPhase = "PENDING_BUILD"
|
||||
DeploymentPhase_Building DeploymentPhase = "BUILDING"
|
||||
DeploymentPhase_PendingDeploy DeploymentPhase = "PENDING_DEPLOY"
|
||||
DeploymentPhase_Deploying DeploymentPhase = "DEPLOYING"
|
||||
DeploymentPhase_Active DeploymentPhase = "ACTIVE"
|
||||
DeploymentPhase_Superseded DeploymentPhase = "SUPERSEDED"
|
||||
DeploymentPhase_Error DeploymentPhase = "ERROR"
|
||||
DeploymentPhase_Canceled DeploymentPhase = "CANCELED"
|
||||
)
|
||||
|
||||
// DeploymentProgress struct for DeploymentProgress
|
||||
type DeploymentProgress struct {
|
||||
PendingSteps int32 `json:"pending_steps,omitempty"`
|
||||
RunningSteps int32 `json:"running_steps,omitempty"`
|
||||
SuccessSteps int32 `json:"success_steps,omitempty"`
|
||||
ErrorSteps int32 `json:"error_steps,omitempty"`
|
||||
TotalSteps int32 `json:"total_steps,omitempty"`
|
||||
Steps []*DeploymentProgressStep `json:"steps,omitempty"`
|
||||
SummarySteps []*DeploymentProgressStep `json:"summary_steps,omitempty"`
|
||||
}
|
||||
|
||||
// DeploymentService struct for DeploymentService
|
||||
type DeploymentService struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
SourceCommitHash string `json:"source_commit_hash,omitempty"`
|
||||
}
|
||||
|
||||
// DeploymentStaticSite struct for DeploymentStaticSite
|
||||
type DeploymentStaticSite struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
SourceCommitHash string `json:"source_commit_hash,omitempty"`
|
||||
}
|
||||
|
||||
// DeploymentWorker struct for DeploymentWorker
|
||||
type DeploymentWorker struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
SourceCommitHash string `json:"source_commit_hash,omitempty"`
|
||||
}
|
||||
|
||||
// GitHubSourceSpec struct for GitHubSourceSpec
|
||||
type GitHubSourceSpec struct {
|
||||
Repo string `json:"repo"`
|
||||
Branch string `json:"branch"`
|
||||
Repo string `json:"repo,omitempty"`
|
||||
Branch string `json:"branch,omitempty"`
|
||||
DeployOnPush bool `json:"deploy_on_push,omitempty"`
|
||||
}
|
||||
|
||||
// GitSourceSpec struct for GitSourceSpec
|
||||
type GitSourceSpec struct {
|
||||
Repo string `json:"repo,omitempty"`
|
||||
RequiresAuth bool `json:"requires_auth,omitempty"`
|
||||
Branch string `json:"branch,omitempty"`
|
||||
RepoCloneURL string `json:"repo_clone_url,omitempty"`
|
||||
Branch string `json:"branch,omitempty"`
|
||||
}
|
||||
|
||||
// VariableScope the model 'VariableScope'
|
||||
type VariableScope string
|
||||
// DeploymentProgressStep struct for DeploymentProgressStep
|
||||
type DeploymentProgressStep struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Status DeploymentProgressStepStatus `json:"status,omitempty"`
|
||||
Steps []*DeploymentProgressStep `json:"steps,omitempty"`
|
||||
StartedAt time.Time `json:"started_at,omitempty"`
|
||||
EndedAt time.Time `json:"ended_at,omitempty"`
|
||||
Reason *DeploymentProgressStepReason `json:"reason,omitempty"`
|
||||
ComponentName string `json:"component_name,omitempty"`
|
||||
// The base of a human-readable description of the step intended to be combined with the component name for presentation. For example: message_base = \"Building service\" component_name = \"api\"
|
||||
MessageBase string `json:"message_base,omitempty"`
|
||||
}
|
||||
|
||||
// List of VariableScope
|
||||
// DeploymentProgressStepStatus the model 'DeploymentProgressStepStatus'
|
||||
type DeploymentProgressStepStatus string
|
||||
|
||||
// List of DeploymentProgressStepStatus
|
||||
const (
|
||||
VARIABLESCOPE_UNSET VariableScope = "UNSET"
|
||||
VARIABLESCOPE_RUN_TIME VariableScope = "RUN_TIME"
|
||||
VARIABLESCOPE_BUILD_TIME VariableScope = "BUILD_TIME"
|
||||
VARIABLESCOPE_RUN_AND_BUILD_TIME VariableScope = "RUN_AND_BUILD_TIME"
|
||||
DeploymentProgressStepStatus_Unknown DeploymentProgressStepStatus = "UNKNOWN"
|
||||
DeploymentProgressStepStatus_Pending DeploymentProgressStepStatus = "PENDING"
|
||||
DeploymentProgressStepStatus_Running DeploymentProgressStepStatus = "RUNNING"
|
||||
DeploymentProgressStepStatus_Error DeploymentProgressStepStatus = "ERROR"
|
||||
DeploymentProgressStepStatus_Success DeploymentProgressStepStatus = "SUCCESS"
|
||||
)
|
||||
|
||||
// VariableType the model 'VariableType'
|
||||
type VariableType string
|
||||
// AppRegion struct for AppRegion
|
||||
type AppRegion struct {
|
||||
Slug string `json:"slug,omitempty"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Flag string `json:"flag,omitempty"`
|
||||
Continent string `json:"continent,omitempty"`
|
||||
Disabled bool `json:"disabled,omitempty"`
|
||||
DataCenters []string `json:"data_centers,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
// List of VariableType
|
||||
// DeploymentProgressStepReason struct for DeploymentProgressStepReason
|
||||
type DeploymentProgressStepReason struct {
|
||||
Code string `json:"code,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// AppVariableScope the model 'AppVariableScope'
|
||||
type AppVariableScope string
|
||||
|
||||
// List of AppVariableScope
|
||||
const (
|
||||
VARIABLETYPE_GENERAL VariableType = "GENERAL"
|
||||
VARIABLETYPE_SECRET VariableType = "SECRET"
|
||||
AppVariableScope_Unset AppVariableScope = "UNSET"
|
||||
AppVariableScope_RunTime AppVariableScope = "RUN_TIME"
|
||||
AppVariableScope_BuildTime AppVariableScope = "BUILD_TIME"
|
||||
AppVariableScope_RunAndBuildTime AppVariableScope = "RUN_AND_BUILD_TIME"
|
||||
)
|
||||
|
||||
// AppVariableType the model 'AppVariableType'
|
||||
type AppVariableType string
|
||||
|
||||
// List of AppVariableType
|
||||
const (
|
||||
AppVariableType_General AppVariableType = "GENERAL"
|
||||
AppVariableType_Secret AppVariableType = "SECRET"
|
||||
)
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -39,71 +38,6 @@ type AppsService interface {
|
|||
GetLogs(ctx context.Context, appID, deploymentID, component string, logType AppLogType, follow bool) (*AppLogs, *Response, error)
|
||||
}
|
||||
|
||||
// App represents an app.
|
||||
type App struct {
|
||||
ID string `json:"id"`
|
||||
Spec *AppSpec `json:"spec"`
|
||||
DefaultIngress string `json:"default_ingress"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
ActiveDeployment *Deployment `json:"active_deployment,omitempty"`
|
||||
InProgressDeployment *Deployment `json:"in_progress_deployment,omitempty"`
|
||||
}
|
||||
|
||||
// Deployment represents a deployment for an app.
|
||||
type Deployment struct {
|
||||
ID string `json:"id"`
|
||||
Spec *AppSpec `json:"spec"`
|
||||
Services []*DeploymentService `json:"services,omitempty"`
|
||||
Workers []*DeploymentWorker `json:"workers,omitempty"`
|
||||
StaticSites []*DeploymentStaticSite `json:"static_sites,omitempty"`
|
||||
|
||||
Cause string `json:"cause"`
|
||||
Progress *DeploymentProgress `json:"progress"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
||||
// DeploymentService represents a service component in a deployment.
|
||||
type DeploymentService struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
SourceCommitHash string `json:"source_commit_hash"`
|
||||
}
|
||||
|
||||
// DeploymentWorker represents a worker component in a deployment.
|
||||
type DeploymentWorker struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
SourceCommitHash string `json:"source_commit_hash"`
|
||||
}
|
||||
|
||||
// DeploymentStaticSite represents a static site component in a deployment.
|
||||
type DeploymentStaticSite struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
SourceCommitHash string `json:"source_commit_hash"`
|
||||
}
|
||||
|
||||
// DeploymentProgress represents the total progress of a deployment.
|
||||
type DeploymentProgress struct {
|
||||
PendingSteps int `json:"pending_steps"`
|
||||
RunningSteps int `json:"running_steps"`
|
||||
SuccessSteps int `json:"success_steps"`
|
||||
ErrorSteps int `json:"error_steps"`
|
||||
TotalSteps int `json:"total_steps"`
|
||||
|
||||
Steps []*DeploymentProgressStep `json:"steps"`
|
||||
}
|
||||
|
||||
// DeploymentProgressStep represents the progress of a deployment step.
|
||||
type DeploymentProgressStep struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Steps []*DeploymentProgressStep `json:"steps,omitempty"`
|
||||
Attempts uint32 `json:"attempts"`
|
||||
StartedAt time.Time `json:"started_at,omitempty"`
|
||||
EndedAt time.Time `json:"ended_at,omitempty"`
|
||||
}
|
||||
|
||||
// AppLogs represent app logs.
|
||||
type AppLogs struct {
|
||||
LiveURL string `json:"live_url"`
|
||||
|
@ -141,7 +75,7 @@ type AppsServiceOp struct {
|
|||
client *Client
|
||||
}
|
||||
|
||||
// Creates an app.
|
||||
// Create an app.
|
||||
func (s *AppsServiceOp) Create(ctx context.Context, create *AppCreateRequest) (*App, *Response, error) {
|
||||
path := appsBasePath
|
||||
req, err := s.client.NewRequest(ctx, http.MethodPost, path, create)
|
||||
|
@ -264,7 +198,11 @@ func (s *AppsServiceOp) CreateDeployment(ctx context.Context, appID string) (*De
|
|||
|
||||
// GetLogs retrieves app logs.
|
||||
func (s *AppsServiceOp) GetLogs(ctx context.Context, appID, deploymentID, component string, logType AppLogType, follow bool) (*AppLogs, *Response, error) {
|
||||
url := fmt.Sprintf("%s/%s/deployments/%s/components/%s/logs?type=%s&follow=%t", appsBasePath, appID, deploymentID, component, logType, follow)
|
||||
url := fmt.Sprintf("%s/%s/deployments/%s/logs?type=%s&follow=%t", appsBasePath, appID, deploymentID, logType, follow)
|
||||
if component != "" {
|
||||
url = fmt.Sprintf("%s&component_name=%s", url, component)
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequest(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
libraryVersion = "1.42.1"
|
||||
libraryVersion = "1.45.0"
|
||||
defaultBaseURL = "https://api.digitalocean.com/"
|
||||
userAgent = "godo/" + libraryVersion
|
||||
mediaType = "application/json"
|
||||
|
|
|
@ -83,6 +83,21 @@ type KubernetesClusterUpgradeRequest struct {
|
|||
VersionSlug string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// Taint represents a Kubernetes taint that can be associated with a node pool
|
||||
// (and, transitively, with all nodes of that pool).
|
||||
type Taint struct {
|
||||
Key string
|
||||
Value string
|
||||
Effect string
|
||||
}
|
||||
|
||||
func (t Taint) String() string {
|
||||
if t.Value == "" {
|
||||
return fmt.Sprintf("%s:%s", t.Key, t.Effect)
|
||||
}
|
||||
return fmt.Sprintf("%s=%s:%s", t.Key, t.Value, t.Effect)
|
||||
}
|
||||
|
||||
// KubernetesNodePoolCreateRequest represents a request to create a node pool for a
|
||||
// Kubernetes cluster.
|
||||
type KubernetesNodePoolCreateRequest struct {
|
||||
|
@ -91,6 +106,7 @@ type KubernetesNodePoolCreateRequest struct {
|
|||
Count int `json:"count,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
Taints []Taint `json:"taints,omitempty"`
|
||||
AutoScale bool `json:"auto_scale,omitempty"`
|
||||
MinNodes int `json:"min_nodes,omitempty"`
|
||||
MaxNodes int `json:"max_nodes,omitempty"`
|
||||
|
@ -103,6 +119,7 @@ type KubernetesNodePoolUpdateRequest struct {
|
|||
Count *int `json:"count,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
Taints *[]Taint `json:"taints,omitempty"`
|
||||
AutoScale *bool `json:"auto_scale,omitempty"`
|
||||
MinNodes *int `json:"min_nodes,omitempty"`
|
||||
MaxNodes *int `json:"max_nodes,omitempty"`
|
||||
|
@ -308,6 +325,7 @@ type KubernetesNodePool struct {
|
|||
Count int `json:"count,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
Taints []Taint `json:"taints,omitempty"`
|
||||
AutoScale bool `json:"auto_scale,omitempty"`
|
||||
MinNodes int `json:"min_nodes,omitempty"`
|
||||
MaxNodes int `json:"max_nodes,omitempty"`
|
||||
|
|
|
@ -80,7 +80,7 @@ github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1
|
|||
github.com/davecgh/go-spew/spew
|
||||
# github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/dgrijalva/jwt-go
|
||||
# github.com/digitalocean/godo v1.42.1
|
||||
# github.com/digitalocean/godo v1.45.0
|
||||
github.com/digitalocean/godo
|
||||
# github.com/fatih/color v1.7.0
|
||||
github.com/fatih/color
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
<li<%= sidebar_current("docs-do-datasource") %>>
|
||||
<a href="#">Data Sources</a>
|
||||
<ul class="nav nav-visible">
|
||||
<li<%= sidebar_current("docs-do-datasource-app") %>>
|
||||
<a href="/docs/providers/do/d/app.html">digitalocean_app</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-do-datasource-account") %>>
|
||||
<a href="/docs/providers/do/d/account.html">digitalocean_account</a>
|
||||
</li>
|
||||
|
@ -109,6 +112,9 @@
|
|||
<li<%= sidebar_current("docs-do-resource") %>>
|
||||
<a href="#">Resources</a>
|
||||
<ul class="nav nav-visible">
|
||||
<li<%= sidebar_current("docs-do-resource-app") %>>
|
||||
<a href="/docs/providers/do/r/app.html">digitalocean_app</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-do-resource-cdn") %>>
|
||||
<a href="/docs/providers/do/r/cdn.html">digitalocean_cdn</a>
|
||||
</li>
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
---
|
||||
layout: "digitalocean"
|
||||
page_title: "DigitalOcean: digitalocean_app"
|
||||
sidebar_current: "docs-do-datasource-app"
|
||||
description: |-
|
||||
Get information on a DigitalOcean App.
|
||||
---
|
||||
|
||||
# digitalocean_app
|
||||
|
||||
Get information on a DigitalOcean App.
|
||||
|
||||
## Example Usage
|
||||
|
||||
Get the account:
|
||||
|
||||
```hcl
|
||||
data "digitalocean_app" "example" {
|
||||
app_id = "e665d18d-7b56-44a9-92ce-31979174d544"
|
||||
}
|
||||
|
||||
output "default_ingress" {
|
||||
value = data.digitalocean_app.example.default_ingress
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
* `app_id` - The ID of the app to retrieve information about.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `default_ingress` - The default URL to access the app.
|
||||
* `live_url` - The live URL of the app.
|
||||
* `active_deployment_id` - The ID the app's currently active deployment.
|
||||
* `updated_at` - The date and time of when the app was last updated.
|
||||
* `created_at` - The date and time of when the app was created.
|
||||
* `spec` - A DigitalOcean App spec describing the app.
|
||||
|
||||
A spec can contain multiple components.
|
||||
|
||||
A `service` can contain:
|
||||
|
||||
* `name` - The name of the component
|
||||
* `build_command` - An optional build command to run while building this component from source.
|
||||
* `dockerfile_path` - The path to a Dockerfile relative to the root of the repo. If set, overrides usage of buildpacks.
|
||||
* `source_dir` - An optional path to the working directory to use for the build.
|
||||
* `run_command` - An optional run command to override the component's default.
|
||||
* `environment_slug` - An environment slug describing the type of this app.
|
||||
* `instance_size_slug` - The instance size to use for this component.
|
||||
* `instance_count` - The amount of instances that this component should be scaled to.
|
||||
* `http_port` - The internal port on which this service's run command will listen.
|
||||
* `git` - A Git repo to use as component's source. Only one of `git` and `github` may be set.
|
||||
- `repo_clone_url` - The clone URL of the repo.
|
||||
- `branch` - The name of the branch to use.
|
||||
* `github` - A GitHub repo to use as component's source. Only one of `git` and `github` may be set.
|
||||
- `repo` - The name of the repo in the format `owner/repo`.
|
||||
- `branch` - The name of the branch to use.
|
||||
- `deploy_on_push` - Whether to automatically deploy new commits made to the repo.
|
||||
* `env` - Describes an environment variable made available to an app competent.
|
||||
- `key` - The name of the environment variable.
|
||||
- `value` - The value of the environment variable.
|
||||
- `scope` - The visibility scope of the environment variable. One of `RUN_TIME`, `BUILD_TIME`, or `RUN_AND_BUILD_TIME` (default).
|
||||
- `type` - The type of the environment variable, `GENERAL` or `SECRET`.
|
||||
* `route` - An HTTP paths that should be routed to this component.
|
||||
- `path` - Paths must start with `/` and must be unique within the app.
|
||||
* `health_check` - A health check to determine the availability of this component.
|
||||
- `http_path` - The route path used for the HTTP health check ping.
|
||||
- `initial_delay_seconds` - The number of seconds to wait before beginning health checks.
|
||||
- `period_seconds` - The number of seconds to wait between health checks.
|
||||
- `timeout_seconds` - The number of seconds after which the check times out.
|
||||
- `success_threshold` - The number of successful health checks before considered healthy.
|
||||
- `failure_threshold` - The number of failed health checks before considered unhealthy.
|
||||
|
||||
A `worker` can contain:
|
||||
|
||||
* `name` - The name of the component
|
||||
* `build_command` - An optional build command to run while building this component from source.
|
||||
* `dockerfile_path` - The path to a Dockerfile relative to the root of the repo. If set, overrides usage of buildpacks.
|
||||
* `source_dir` - An optional path to the working directory to use for the build.
|
||||
* `run_command` - An optional run command to override the component's default.
|
||||
* `environment_slug` - An environment slug describing the type of this app.
|
||||
* `instance_size_slug` - The instance size to use for this component.
|
||||
* `instance_count` - The amount of instances that this component should be scaled to.
|
||||
* `git` - A Git repo to use as component's source. Only one of `git` and `github` may be set.
|
||||
- `repo_clone_url` - The clone URL of the repo.
|
||||
- `branch` - The name of the branch to use.
|
||||
* `github` - A GitHub repo to use as component's source. Only one of `git` and `github` may be set.
|
||||
- `repo` - The name of the repo in the format `owner/repo`.
|
||||
- `branch` - The name of the branch to use.
|
||||
- `deploy_on_push` - Whether to automatically deploy new commits made to the repo.
|
||||
* `env` - Describes an environment variable made available to an app competent.
|
||||
- `key` - The name of the environment variable.
|
||||
- `value` - The value of the environment variable.
|
||||
- `scope` - The visibility scope of the environment variable. One of `RUN_TIME`, `BUILD_TIME`, or `RUN_AND_BUILD_TIME` (default).
|
||||
- `type` - The type of the environment variable, `GENERAL` or `SECRET`.
|
||||
|
||||
A `static_site` can contain:
|
||||
|
||||
* `name` - The name of the component
|
||||
* `build_command` - An optional build command to run while building this component from source.
|
||||
* `dockerfile_path` - The path to a Dockerfile relative to the root of the repo. If set, overrides usage of buildpacks.
|
||||
* `source_dir` - An optional path to the working directory to use for the build.
|
||||
* `environment_slug` - An environment slug describing the type of this app.
|
||||
* `output_dir` - An optional path to where the built assets will be located, relative to the build context. If not set, App Platform will automatically scan for these directory names: `_static`, `dist`, `public`.
|
||||
* `index_document` - The name of the index document to use when serving this static site.
|
||||
* `error_document` - The name of the error document to use when serving this static site*
|
||||
* `git` - A Git repo to use as component's source. Only one of `git` and `github` may be set.
|
||||
- `repo_clone_url` - The clone URL of the repo.
|
||||
- `branch` - The name of the branch to use.
|
||||
* `github` - A GitHub repo to use as component's source. Only one of `git` and `github` may be set.
|
||||
- `repo` - The name of the repo in the format `owner/repo`.
|
||||
- `branch` - The name of the branch to use.
|
||||
- `deploy_on_push` - Whether to automatically deploy new commits made to the repo.
|
||||
* `env` - Describes an environment variable made available to an app competent.
|
||||
- `key` - The name of the environment variable.
|
||||
- `value` - The value of the environment variable.
|
||||
- `scope` - The visibility scope of the environment variable. One of `RUN_TIME`, `BUILD_TIME`, or `RUN_AND_BUILD_TIME` (default).
|
||||
- `type` - The type of the environment variable, `GENERAL` or `SECRET`.
|
||||
* `route` - An HTTP paths that should be routed to this component.
|
||||
- `path` - Paths must start with `/` and must be unique within the app.
|
|
@ -0,0 +1,223 @@
|
|||
---
|
||||
layout: "digitalocean"
|
||||
page_title: "DigitalOcean: digitalocean_app"
|
||||
sidebar_current: "docs-do-resource-app"
|
||||
description: |-
|
||||
Provides a DigitalOcean App resource.
|
||||
---
|
||||
|
||||
# digitalocean\_app
|
||||
|
||||
Provides a DigitalOcean App resource.
|
||||
|
||||
## Example Usage
|
||||
|
||||
To create an app, provide a [DigitalOcean app spec](https://www.digitalocean.com/docs/app-platform/resources/app-specification-reference/) specifying the app's components.
|
||||
|
||||
### Basic Example
|
||||
|
||||
```hcl
|
||||
resource "digitalocean_app" "golang-sample" {
|
||||
spec {
|
||||
name = "golang-sample"
|
||||
region = "ams"
|
||||
|
||||
service {
|
||||
name = "go-service"
|
||||
environment_slug = "go"
|
||||
instance_count = 1
|
||||
instance_size_slug = "professional-xs"
|
||||
|
||||
git {
|
||||
repo_clone_url = "https://github.com/digitalocean/sample-golang.git"
|
||||
branch = "main"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Static Site Example
|
||||
|
||||
```hcl
|
||||
resource "digitalocean_app" "static-ste-example" {
|
||||
spec {
|
||||
name = "static-ste-example"
|
||||
region = "ams"
|
||||
|
||||
static_site {
|
||||
name = "sample-jekyll"
|
||||
build_command = "bundle exec jekyll build -d ./public"
|
||||
output_dir = "/public"
|
||||
|
||||
git {
|
||||
repo_clone_url = "https://github.com/digitalocean/sample-jekyll.git"
|
||||
branch = "main"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Multiple Components Example
|
||||
|
||||
```hcl
|
||||
resource "digitalocean_app" "mono-repo-example" {
|
||||
spec {
|
||||
name = "mono-repo-example"
|
||||
region = "ams"
|
||||
domains = ["foo.example.com"]
|
||||
|
||||
# Build a Go project in the api/ directory that listens on port 3000
|
||||
# and serves it at https://foo.example.com/api
|
||||
service {
|
||||
name = "api"
|
||||
environment_slug = "go"
|
||||
instance_count = 2
|
||||
instance_size_slug = "professional-xs"
|
||||
|
||||
github {
|
||||
branch = "main"
|
||||
deploy_on_push = true
|
||||
repo = "username/repo"
|
||||
}
|
||||
|
||||
source_dir = "api/"
|
||||
http_port = 3000
|
||||
|
||||
routes {
|
||||
path = "/api"
|
||||
}
|
||||
|
||||
run_command = "bin/api"
|
||||
}
|
||||
|
||||
# Builds a static site in the project's root directory
|
||||
# and serves it at https://foo.example.com/
|
||||
static_site {
|
||||
name = "web"
|
||||
build_command = "npm run build"
|
||||
|
||||
github {
|
||||
branch = "main"
|
||||
deploy_on_push = true
|
||||
repo = "username/repo"
|
||||
}
|
||||
|
||||
routes {
|
||||
path = "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `spec` - (Required) A DigitalOcean App spec describing the app.
|
||||
- `name` - (Required) The name of the app. Must be unique across all apps in the same account.
|
||||
- `region` - The slug for the DigitalOcean data center region hosting the app.
|
||||
- `domains` - A list of hostnames where the application will be available.
|
||||
|
||||
A spec can contain multiple components.
|
||||
|
||||
A `service` can contain:
|
||||
|
||||
* `name` - The name of the component
|
||||
* `build_command` - An optional build command to run while building this component from source.
|
||||
* `dockerfile_path` - The path to a Dockerfile relative to the root of the repo. If set, overrides usage of buildpacks.
|
||||
* `source_dir` - An optional path to the working directory to use for the build.
|
||||
* `run_command` - An optional run command to override the component's default.
|
||||
* `environment_slug` - An environment slug describing the type of this app.
|
||||
* `instance_size_slug` - The instance size to use for this component.
|
||||
* `instance_count` - The amount of instances that this component should be scaled to.
|
||||
* `http_port` - The internal port on which this service's run command will listen.
|
||||
* `git` - A Git repo to use as component's source. Only one of `git` and `github` may be set.
|
||||
- `repo_clone_url` - The clone URL of the repo.
|
||||
- `branch` - The name of the branch to use.
|
||||
* `github` - A GitHub repo to use as component's source. Only one of `git` and `github` may be set.
|
||||
- `repo` - The name of the repo in the format `owner/repo`.
|
||||
- `branch` - The name of the branch to use.
|
||||
- `deploy_on_push` - Whether to automatically deploy new commits made to the repo.
|
||||
* `env` - Describes an environment variable made available to an app competent.
|
||||
- `key` - The name of the environment variable.
|
||||
- `value` - The value of the environment variable.
|
||||
- `scope` - The visibility scope of the environment variable. One of `RUN_TIME`, `BUILD_TIME`, or `RUN_AND_BUILD_TIME` (default).
|
||||
- `type` - The type of the environment variable, `GENERAL` or `SECRET`.
|
||||
* `route` - An HTTP paths that should be routed to this component.
|
||||
- `path` - Paths must start with `/` and must be unique within the app.
|
||||
* `health_check` - A health check to determine the availability of this component.
|
||||
- `http_path` - The route path used for the HTTP health check ping.
|
||||
- `initial_delay_seconds` - The number of seconds to wait before beginning health checks.
|
||||
- `period_seconds` - The number of seconds to wait between health checks.
|
||||
- `timeout_seconds` - The number of seconds after which the check times out.
|
||||
- `success_threshold` - The number of successful health checks before considered healthy.
|
||||
- `failure_threshold` - The number of failed health checks before considered unhealthy.
|
||||
|
||||
A `worker` can contain:
|
||||
|
||||
* `name` - The name of the component
|
||||
* `build_command` - An optional build command to run while building this component from source.
|
||||
* `dockerfile_path` - The path to a Dockerfile relative to the root of the repo. If set, overrides usage of buildpacks.
|
||||
* `source_dir` - An optional path to the working directory to use for the build.
|
||||
* `run_command` - An optional run command to override the component's default.
|
||||
* `environment_slug` - An environment slug describing the type of this app.
|
||||
* `instance_size_slug` - The instance size to use for this component.
|
||||
* `instance_count` - The amount of instances that this component should be scaled to.
|
||||
* `git` - A Git repo to use as component's source. Only one of `git` and `github` may be set.
|
||||
- `repo_clone_url` - The clone URL of the repo.
|
||||
- `branch` - The name of the branch to use.
|
||||
* `github` - A GitHub repo to use as component's source. Only one of `git` and `github` may be set.
|
||||
- `repo` - The name of the repo in the format `owner/repo`.
|
||||
- `branch` - The name of the branch to use.
|
||||
- `deploy_on_push` - Whether to automatically deploy new commits made to the repo.
|
||||
* `env` - Describes an environment variable made available to an app competent.
|
||||
- `key` - The name of the environment variable.
|
||||
- `value` - The value of the environment variable.
|
||||
- `scope` - The visibility scope of the environment variable. One of `RUN_TIME`, `BUILD_TIME`, or `RUN_AND_BUILD_TIME` (default).
|
||||
- `type` - The type of the environment variable, `GENERAL` or `SECRET`.
|
||||
|
||||
A `static_site` can contain:
|
||||
|
||||
* `name` - The name of the component
|
||||
* `build_command` - An optional build command to run while building this component from source.
|
||||
* `dockerfile_path` - The path to a Dockerfile relative to the root of the repo. If set, overrides usage of buildpacks.
|
||||
* `source_dir` - An optional path to the working directory to use for the build.
|
||||
* `environment_slug` - An environment slug describing the type of this app.
|
||||
* `output_dir` - An optional path to where the built assets will be located, relative to the build context. If not set, App Platform will automatically scan for these directory names: `_static`, `dist`, `public`.
|
||||
* `index_document` - The name of the index document to use when serving this static site.
|
||||
* `error_document` - The name of the error document to use when serving this static site*
|
||||
* `git` - A Git repo to use as component's source. Only one of `git` and `github` may be set.
|
||||
- `repo_clone_url` - The clone URL of the repo.
|
||||
- `branch` - The name of the branch to use.
|
||||
* `github` - A GitHub repo to use as component's source. Only one of `git` and `github` may be set.
|
||||
- `repo` - The name of the repo in the format `owner/repo`.
|
||||
- `branch` - The name of the branch to use.
|
||||
- `deploy_on_push` - Whether to automatically deploy new commits made to the repo.
|
||||
* `env` - Describes an environment variable made available to an app competent.
|
||||
- `key` - The name of the environment variable.
|
||||
- `value` - The value of the environment variable.
|
||||
- `scope` - The visibility scope of the environment variable. One of `RUN_TIME`, `BUILD_TIME`, or `RUN_AND_BUILD_TIME` (default).
|
||||
- `type` - The type of the environment variable, `GENERAL` or `SECRET`.
|
||||
* `route` - An HTTP paths that should be routed to this component.
|
||||
- `path` - Paths must start with `/` and must be unique within the app.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
In addition to the above attributes, the following are exported:
|
||||
|
||||
* `default_ingress` - The default URL to access the app.
|
||||
* `live_url` - The live URL of the app.
|
||||
* `active_deployment_id` - The ID the app's currently active deployment.
|
||||
* `updated_at` - The date and time of when the app was last updated.
|
||||
* `created_at` - The date and time of when the app was created.
|
||||
|
||||
## Import
|
||||
|
||||
An app can be imported using its `id`, e.g.
|
||||
|
||||
```
|
||||
terraform import digitalocean_app.myapp fb06ad00-351f-45c8-b5eb-13523c438661
|
||||
```
|
Loading…
Reference in New Issue