A Terraform provider for Dokploy
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.
This commit is contained in:
293
internal/provider/provider_test.go
Normal file
293
internal/provider/provider_test.go
Normal file
@@ -0,0 +1,293 @@
|
||||
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"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user