Plugin-framework provider covering projects, environments, applications, Compose stacks, managed databases, domains, mounts, ports, redirects, basic auth, registries, SSH keys, certificates and backup destinations, over Dokploy's tRPC-over-REST API. The shim package exposes the provider to other Go modules, which is how pulumi-dokploy bridges it.
294 lines
9.3 KiB
Go
294 lines
9.3 KiB
Go
package provider_test
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/terraform-plugin-framework/providerserver"
|
|
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
|
|
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
|
|
|
"github.com/maxvojtkov/terraform-provider-dokploy/internal/provider"
|
|
)
|
|
|
|
// Acceptance tests talk to a real Dokploy instance. They create resources with
|
|
// a `tfacc-` prefix and destroy them again, so point them at a scratch
|
|
// instance rather than production:
|
|
//
|
|
// TF_ACC=1 \
|
|
// DOKPLOY_HOST=https://dokploy.example.com \
|
|
// DOKPLOY_API_KEY=... \
|
|
// go test ./internal/provider/ -v -timeout 30m
|
|
|
|
var protoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
|
|
"dokploy": providerserver.NewProtocol6WithError(provider.New("test")()),
|
|
}
|
|
|
|
func testAccPreCheck(t *testing.T) {
|
|
t.Helper()
|
|
for _, key := range []string{"DOKPLOY_HOST", "DOKPLOY_API_KEY"} {
|
|
if os.Getenv(key) == "" {
|
|
t.Fatalf("%s must be set for acceptance tests", key)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAccProject(t *testing.T) {
|
|
resource.Test(t, resource.TestCase{
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
ProtoV6ProviderFactories: protoV6ProviderFactories,
|
|
Steps: []resource.TestStep{
|
|
{
|
|
Config: `
|
|
resource "dokploy_project" "test" {
|
|
name = "tfacc-project"
|
|
description = "created by acceptance tests"
|
|
}`,
|
|
Check: resource.ComposeAggregateTestCheckFunc(
|
|
resource.TestCheckResourceAttr("dokploy_project.test", "name", "tfacc-project"),
|
|
resource.TestCheckResourceAttr("dokploy_project.test", "description", "created by acceptance tests"),
|
|
resource.TestCheckResourceAttrSet("dokploy_project.test", "id"),
|
|
// Dokploy creates a default environment with every project.
|
|
resource.TestCheckResourceAttrSet("dokploy_project.test", "default_environment_id"),
|
|
resource.TestCheckResourceAttrSet("dokploy_project.test", "organization_id"),
|
|
),
|
|
},
|
|
{
|
|
ResourceName: "dokploy_project.test",
|
|
ImportState: true,
|
|
ImportStateVerify: true,
|
|
},
|
|
{
|
|
Config: `
|
|
resource "dokploy_project" "test" {
|
|
name = "tfacc-project-renamed"
|
|
description = "updated"
|
|
env = "SHARED=1"
|
|
}`,
|
|
Check: resource.ComposeAggregateTestCheckFunc(
|
|
resource.TestCheckResourceAttr("dokploy_project.test", "name", "tfacc-project-renamed"),
|
|
resource.TestCheckResourceAttr("dokploy_project.test", "description", "updated"),
|
|
resource.TestCheckResourceAttr("dokploy_project.test", "env", "SHARED=1"),
|
|
),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestAccEnvironment(t *testing.T) {
|
|
resource.Test(t, resource.TestCase{
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
ProtoV6ProviderFactories: protoV6ProviderFactories,
|
|
Steps: []resource.TestStep{
|
|
{
|
|
Config: `
|
|
resource "dokploy_project" "test" {
|
|
name = "tfacc-env-project"
|
|
}
|
|
|
|
resource "dokploy_environment" "test" {
|
|
name = "tfacc-staging"
|
|
project_id = dokploy_project.test.id
|
|
env = "STAGE=1"
|
|
}`,
|
|
Check: resource.ComposeAggregateTestCheckFunc(
|
|
resource.TestCheckResourceAttr("dokploy_environment.test", "name", "tfacc-staging"),
|
|
// env is only writable through a follow-up update; assert it stuck.
|
|
resource.TestCheckResourceAttr("dokploy_environment.test", "env", "STAGE=1"),
|
|
resource.TestCheckResourceAttr("dokploy_environment.test", "is_default", "false"),
|
|
),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// Applications are created with a minimal payload and then configured through
|
|
// application.update, so this asserts that update-only fields survive create.
|
|
func TestAccApplicationDockerSource(t *testing.T) {
|
|
resource.Test(t, resource.TestCase{
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
ProtoV6ProviderFactories: protoV6ProviderFactories,
|
|
Steps: []resource.TestStep{
|
|
{
|
|
Config: testAccApplicationConfig("512m", 1),
|
|
Check: resource.ComposeAggregateTestCheckFunc(
|
|
resource.TestCheckResourceAttr("dokploy_application.test", "source_type", "docker"),
|
|
resource.TestCheckResourceAttr("dokploy_application.test", "docker_image", "traefik/whoami:latest"),
|
|
resource.TestCheckResourceAttr("dokploy_application.test", "memory_limit", "512m"),
|
|
resource.TestCheckResourceAttr("dokploy_application.test", "replicas", "1"),
|
|
resource.TestCheckResourceAttr("dokploy_application.test", "env", "GREETING=hello"),
|
|
resource.TestCheckResourceAttrSet("dokploy_application.test", "app_name"),
|
|
),
|
|
},
|
|
{
|
|
ResourceName: "dokploy_application.test",
|
|
ImportState: true,
|
|
ImportStateVerify: true,
|
|
},
|
|
{
|
|
// An in-place update must not force replacement.
|
|
Config: testAccApplicationConfig("1g", 3),
|
|
Check: resource.ComposeAggregateTestCheckFunc(
|
|
resource.TestCheckResourceAttr("dokploy_application.test", "memory_limit", "1g"),
|
|
resource.TestCheckResourceAttr("dokploy_application.test", "replicas", "3"),
|
|
),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func testAccApplicationConfig(memoryLimit string, replicas int) string {
|
|
return fmt.Sprintf(`
|
|
resource "dokploy_project" "test" {
|
|
name = "tfacc-app-project"
|
|
}
|
|
|
|
resource "dokploy_application" "test" {
|
|
name = "tfacc-app"
|
|
environment_id = dokploy_project.test.default_environment_id
|
|
|
|
source_type = "docker"
|
|
docker_image = "traefik/whoami:latest"
|
|
|
|
env = "GREETING=hello"
|
|
memory_limit = %q
|
|
replicas = %d
|
|
}`, memoryLimit, replicas)
|
|
}
|
|
|
|
func TestAccComposeAndDomain(t *testing.T) {
|
|
resource.Test(t, resource.TestCase{
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
ProtoV6ProviderFactories: protoV6ProviderFactories,
|
|
Steps: []resource.TestStep{
|
|
{
|
|
Config: `
|
|
resource "dokploy_project" "test" {
|
|
name = "tfacc-compose-project"
|
|
}
|
|
|
|
resource "dokploy_compose" "test" {
|
|
name = "tfacc-stack"
|
|
environment_id = dokploy_project.test.default_environment_id
|
|
compose_type = "docker-compose"
|
|
source_type = "raw"
|
|
|
|
compose_file = <<-YAML
|
|
services:
|
|
whoami:
|
|
image: traefik/whoami:latest
|
|
YAML
|
|
}
|
|
|
|
resource "dokploy_domain" "test" {
|
|
compose_id = dokploy_compose.test.id
|
|
domain_type = "compose"
|
|
service_name = "whoami"
|
|
host = "tfacc.example.invalid"
|
|
port = 80
|
|
https = false
|
|
certificate_type = "none"
|
|
}`,
|
|
Check: resource.ComposeAggregateTestCheckFunc(
|
|
resource.TestCheckResourceAttr("dokploy_compose.test", "compose_type", "docker-compose"),
|
|
resource.TestCheckResourceAttrSet("dokploy_compose.test", "app_name"),
|
|
resource.TestCheckResourceAttr("dokploy_domain.test", "host", "tfacc.example.invalid"),
|
|
resource.TestCheckResourceAttr("dokploy_domain.test", "service_name", "whoami"),
|
|
),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// Databases follow the same create-then-update path as applications.
|
|
func TestAccPostgres(t *testing.T) {
|
|
resource.Test(t, resource.TestCase{
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
ProtoV6ProviderFactories: protoV6ProviderFactories,
|
|
Steps: []resource.TestStep{
|
|
{
|
|
Config: `
|
|
resource "dokploy_project" "test" {
|
|
name = "tfacc-pg-project"
|
|
}
|
|
|
|
resource "dokploy_postgres" "test" {
|
|
name = "tfacc-pg"
|
|
environment_id = dokploy_project.test.default_environment_id
|
|
docker_image = "postgres:16-alpine"
|
|
database_name = "acc"
|
|
database_user = "acc"
|
|
database_password = "acc-password"
|
|
memory_limit = "512m"
|
|
}`,
|
|
Check: resource.ComposeAggregateTestCheckFunc(
|
|
resource.TestCheckResourceAttr("dokploy_postgres.test", "database_name", "acc"),
|
|
// memory_limit is rejected by postgres.create and must be
|
|
// written by the follow-up update.
|
|
resource.TestCheckResourceAttr("dokploy_postgres.test", "memory_limit", "512m"),
|
|
),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
// redirects.create returns `true` rather than a row, so the provider finds the
|
|
// new ID by diffing the application's redirect list.
|
|
func TestAccRedirectIDDiscovery(t *testing.T) {
|
|
resource.Test(t, resource.TestCase{
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
ProtoV6ProviderFactories: protoV6ProviderFactories,
|
|
Steps: []resource.TestStep{
|
|
{
|
|
Config: `
|
|
resource "dokploy_project" "test" {
|
|
name = "tfacc-redirect-project"
|
|
}
|
|
|
|
resource "dokploy_application" "test" {
|
|
name = "tfacc-redirect-app"
|
|
environment_id = dokploy_project.test.default_environment_id
|
|
source_type = "docker"
|
|
docker_image = "traefik/whoami:latest"
|
|
}
|
|
|
|
resource "dokploy_redirect" "test" {
|
|
application_id = dokploy_application.test.id
|
|
regex = "^https://old\\.(.*)"
|
|
replacement = "https://new.$${1}"
|
|
permanent = true
|
|
}`,
|
|
Check: resource.ComposeAggregateTestCheckFunc(
|
|
resource.TestCheckResourceAttrSet("dokploy_redirect.test", "id"),
|
|
resource.TestCheckResourceAttr("dokploy_redirect.test", "permanent", "true"),
|
|
),
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestAccProjectsDataSource(t *testing.T) {
|
|
resource.Test(t, resource.TestCase{
|
|
PreCheck: func() { testAccPreCheck(t) },
|
|
ProtoV6ProviderFactories: protoV6ProviderFactories,
|
|
Steps: []resource.TestStep{
|
|
{
|
|
Config: `
|
|
resource "dokploy_project" "test" {
|
|
name = "tfacc-ds-project"
|
|
}
|
|
|
|
data "dokploy_project" "test" {
|
|
id = dokploy_project.test.id
|
|
}`,
|
|
Check: resource.ComposeAggregateTestCheckFunc(
|
|
resource.TestCheckResourceAttr("data.dokploy_project.test", "name", "tfacc-ds-project"),
|
|
resource.TestCheckResourceAttrSet("data.dokploy_project.test", "default_environment_id"),
|
|
resource.TestCheckResourceAttr("data.dokploy_project.test", "environments.#", "1"),
|
|
),
|
|
},
|
|
},
|
|
})
|
|
}
|