0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-07-04 21:14:05 +00:00

chore: refactor container acc test (#1408)

+ beautify test output on CI

---------

Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
Pavel Boldyrev 2024-06-24 23:08:59 -04:00 committed by GitHub
parent c926484249
commit a0d9300f0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 333 additions and 148 deletions

View File

@ -73,11 +73,21 @@ jobs:
if: ${{ steps.filter.outputs.go == 'true' }} if: ${{ steps.filter.outputs.go == 'true' }}
run: go mod download run: go mod download
- name: Set up gotestfmt
run: go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
- name: Unit tests - name: Unit tests
if: ${{ steps.filter.outputs.go == 'true' }} if: ${{ steps.filter.outputs.go == 'true' }}
timeout-minutes: 10 timeout-minutes: 10
run: go test -v -cover ./... run: go test -json -v ./... 2>&1 | tee /tmp/gotest.log | gotestfmt -hide empty-packages
- name: Upload test log
uses: actions/upload-artifact@v4
if: always()
with:
name: test-log
path: /tmp/gotest.log
if-no-files-found: error
- name: Check for uncommitted changes in generated docs - name: Check for uncommitted changes in generated docs
run: make docs && git diff --exit-code run: make docs && git diff --exit-code

View File

@ -69,4 +69,13 @@ jobs:
PROXMOX_VE_ACC_NODE_SSH_PORT: ${{ matrix.port }} PROXMOX_VE_ACC_NODE_SSH_PORT: ${{ matrix.port }}
PROXMOX_VE_ACC_CLOUD_IMAGES_SERVER: ${{ secrets.PROXMOX_VE_ACC_CLOUD_IMAGES_SERVER }} PROXMOX_VE_ACC_CLOUD_IMAGES_SERVER: ${{ secrets.PROXMOX_VE_ACC_CLOUD_IMAGES_SERVER }}
PROXMOX_VE_ACC_CONTAINER_IMAGES_SERVER: ${{ secrets.PROXMOX_VE_ACC_CONTAINER_IMAGES_SERVER }} PROXMOX_VE_ACC_CONTAINER_IMAGES_SERVER: ${{ secrets.PROXMOX_VE_ACC_CONTAINER_IMAGES_SERVER }}
run: make testacc run:
go test -json --timeout=30m --tags=acceptance -count=1 -v github.com/bpg/terraform-provider-proxmox/fwprovider/... 2>&1 | tee /tmp/gotest.log | gotestfmt -hide empty-packages
- name: Upload test log
uses: actions/upload-artifact@v4
if: always()
with:
name: test-log
path: /tmp/gotest.log
if-no-files-found: error

View File

@ -11,14 +11,14 @@ issues:
- EXC0012 - EXC0012
- EXC0014 - EXC0014
exclude-rules: exclude-rules:
# Exclude duplicate code and function length and complexity checking in test # Exclude some checks in tests
# files (due to common repeats and long functions in test code)
- path: _(test|gen)\.go - path: _(test|gen)\.go
linters: linters:
- cyclop - cyclop
- dupl - dupl
- gocognit
- funlen - funlen
- gocognit
- gosec
- lll - lll
- path: _types\.go - path: _types\.go
linters: linters:

View File

@ -95,7 +95,7 @@ test:
.PHONY: testacc .PHONY: testacc
testacc: testacc:
@# explicitly add TF_ACC=1 to trigger the acceptance tests, `testacc.env` might be missing or incomplete @# explicitly add TF_ACC=1 to trigger the acceptance tests, `testacc.env` might be missing or incomplete
@TF_ACC=1 env $$(cat testacc.env | xargs) go test --timeout=30m -count=1 -v github.com/bpg/terraform-provider-proxmox/fwprovider/... @TF_ACC=1 env $$(cat testacc.env | xargs) go test --timeout=30m --tags=acceptance -count=1 -v github.com/bpg/terraform-provider-proxmox/fwprovider/...
.PHONY: lint .PHONY: lint
lint: lint:

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
@ -13,146 +15,248 @@ import (
"testing" "testing"
"time" "time"
"github.com/brianvoe/gofakeit/v7"
"github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/bpg/terraform-provider-proxmox/proxmox/helpers/ptr"
"github.com/bpg/terraform-provider-proxmox/proxmox/nodes/storage"
) )
const ( const (
accTestContainerName = "proxmox_virtual_environment_container.test_container" accTestContainerName = "proxmox_virtual_environment_container.test_container"
) )
//nolint:gochecknoglobals func TestAccResourceContainer(t *testing.T) {
var ( t.Parallel()
accTestContainerID = 100000 + rand.Intn(99999) //nolint:gosec
accCloneContainerID = 200000 + rand.Intn(99999) //nolint:gosec
)
func TestAccResourceContainer(t *testing.T) { //nolint:wsl
// download fails with 404 or "exit code 8" if run in parallel
// t.Parallel()
te := InitEnvironment(t) te := InitEnvironment(t)
resource.Test(t, resource.TestCase{ imageFileName := gofakeit.Word() + "-ubuntu-23.04-standard_23.04-1_amd64.tar.zst"
ProtoV6ProviderFactories: te.AccProviders, accTestContainerID := 100000 + rand.Intn(99999)
Steps: []resource.TestStep{ accTestContainerIDClone := 100000 + rand.Intn(99999)
{
Config: te.RenderConfig(testAccResourceContainerCreateConfig(te, false)), te.AddTemplateVars(map[string]interface{}{
Check: testAccResourceContainerCreateCheck(te), "ImageFileName": imageFileName,
}, "TestContainerID": accTestContainerID,
{ "TestContainerIDClone": accTestContainerIDClone,
Config: te.RenderConfig(testAccResourceContainerCreateConfig(te, true) + testAccResourceContainerCreateCloneConfig(te)),
Check: testAccResourceContainerCreateCloneCheck(te),
},
},
}) })
}
err := te.NodeStorageClient().DownloadFileByURL(context.Background(), &storage.DownloadURLPostRequestBody{
func testAccResourceContainerCreateConfig(te *Environment, isTemplate bool) string { Content: ptr.Ptr("vztmpl"),
te.t.Helper() FileName: ptr.Ptr(imageFileName),
Node: ptr.Ptr(te.NodeName),
return fmt.Sprintf(` Storage: ptr.Ptr(te.DatastoreID),
resource "proxmox_virtual_environment_download_file" "ubuntu_container_template" { URL: ptr.Ptr(fmt.Sprintf("%s/images/system/ubuntu-23.04-standard_23.04-1_amd64.tar.zst", te.ContainerImagesServer)),
content_type = "vztmpl" })
datastore_id = "local" require.NoError(t, err)
node_name = "{{.NodeName}}"
url = "{{.ContainerImagesServer}}/images/system/ubuntu-23.04-standard_23.04-1_amd64.tar.zst" t.Cleanup(func() {
overwrite_unmanaged = true e := te.NodeStorageClient().DeleteDatastoreFile(context.Background(), fmt.Sprintf("vztmpl/%s", imageFileName))
} require.NoError(t, e)
resource "proxmox_virtual_environment_container" "test_container" { })
node_name = "{{.NodeName}}"
vm_id = %d tests := []struct {
template = %t name string
step []resource.TestStep
disk { }{
datastore_id = "local-lvm" {"crete and start container", []resource.TestStep{{
size = 4 Config: te.RenderConfig(`
} resource "proxmox_virtual_environment_container" "test_container" {
node_name = "{{.NodeName}}"
mount_point { vm_id = {{.TestContainerID}}
volume = "local-lvm" disk {
size = "4G" datastore_id = "local-lvm"
path = "mnt/local" size = 4
} }
mount_point {
description = <<-EOT volume = "local-lvm"
my size = "4G"
description path = "mnt/local"
value }
EOT description = <<-EOT
my
initialization { description
hostname = "test" value
EOT
ip_config { initialization {
ipv4 { hostname = "test"
address = "dhcp" ip_config {
} ipv4 {
} address = "dhcp"
} }
}
network_interface { }
name = "vmbr0" network_interface {
} name = "vmbr0"
}
operating_system { operating_system {
template_file_id = proxmox_virtual_environment_download_file.ubuntu_container_template.id template_file_id = "local:vztmpl/{{.ImageFileName}}"
type = "ubuntu" type = "ubuntu"
} }
} }`),
`, accTestContainerID, isTemplate) Check: resource.ComposeTestCheckFunc(
} resource.TestCheckResourceAttr(accTestContainerName, "description", "my\ndescription\nvalue\n"),
func(*terraform.State) error {
func testAccResourceContainerCreateCheck(te *Environment) resource.TestCheckFunc { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
te.t.Helper() defer cancel()
return resource.ComposeTestCheckFunc( err := te.NodeClient().Container(accTestContainerID).WaitForContainerStatus(ctx, "running")
resource.TestCheckResourceAttr(accTestContainerName, "description", "my\ndescription\nvalue\n"), require.NoError(te.t, err, "container did not start")
func(*terraform.State) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) return nil
defer cancel() },
),
err := te.NodeClient().Container(accTestContainerID).WaitForContainerStatus(ctx, "running") }}},
require.NoError(te.t, err, "container did not start") {"update mount points", []resource.TestStep{
{
return nil SkipFunc: func() (bool, error) {
}, // mount point deletion: https://github.com/bpg/terraform-provider-proxmox/issues/1392
) return true, nil
} },
Config: te.RenderConfig(`
func testAccResourceContainerCreateCloneConfig(te *Environment) string { resource "proxmox_virtual_environment_container" "test_container" {
te.t.Helper() node_name = "{{.NodeName}}"
vm_id = {{.RandomVMID}}
return fmt.Sprintf(` started = false
resource "proxmox_virtual_environment_container" "test_container_clone" { disk {
depends_on = [proxmox_virtual_environment_container.test_container] datastore_id = "local-lvm"
node_name = "{{.NodeName}}" size = 4
vm_id = %d }
mount_point {
clone { volume = "local-lvm"
vm_id = proxmox_virtual_environment_container.test_container.id size = "4G"
} path = "mnt/local1"
}
initialization { mount_point {
hostname = "test-clone" volume = "local"
} size = "8G"
} path = "mnt/local2"
`, accCloneContainerID) }
} initialization {
hostname = "test"
func testAccResourceContainerCreateCloneCheck(te *Environment) resource.TestCheckFunc { ip_config {
te.t.Helper() ipv4 {
address = "dhcp"
return resource.ComposeTestCheckFunc( }
func(*terraform.State) error { }
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) }
defer cancel() network_interface {
name = "vmbr0"
err := te.NodeClient().Container(accCloneContainerID).WaitForContainerStatus(ctx, "running") }
require.NoError(te.t, err, "container did not start") operating_system {
template_file_id = "local:vztmpl/{{.ImageFileName}}"
return nil type = "ubuntu"
}, }
) }`),
Check: ResourceAttributes("proxmox_virtual_environment_container.test_container", map[string]string{
"mount_point.#": "2",
}),
},
{
SkipFunc: func() (bool, error) {
// mount point deletion: https://github.com/bpg/terraform-provider-proxmox/issues/1392
return true, nil
},
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_container" "test_container" {
node_name = "{{.NodeName}}"
started = false
disk {
datastore_id = "local-lvm"
size = 4
}
mount_point {
volume = "local-lvm"
size = "4G"
path = "mnt/local1"
}
initialization {
hostname = "test"
ip_config {
ipv4 {
address = "dhcp"
}
}
}
network_interface {
name = "vmbr0"
}
operating_system {
template_file_id = "local:vztmpl/{{.ImageFileName}}"
type = "ubuntu"
}
}`),
Check: ResourceAttributes("proxmox_virtual_environment_container.test_container", map[string]string{
"mount_point.#": "2",
}),
},
}},
{"clone container", []resource.TestStep{{
Config: te.RenderConfig(`
resource "proxmox_virtual_environment_container" "test_container" {
node_name = "{{.NodeName}}"
vm_id = {{.RandomVMID}}
template = true
disk {
datastore_id = "local-lvm"
size = 4
}
mount_point {
volume = "local-lvm"
size = "4G"
path = "mnt/local"
}
initialization {
hostname = "test"
ip_config {
ipv4 {
address = "dhcp"
}
}
}
network_interface {
name = "vmbr0"
}
operating_system {
template_file_id = "local:vztmpl/{{.ImageFileName}}"
type = "ubuntu"
}
}
resource "proxmox_virtual_environment_container" "test_container_clone" {
depends_on = [proxmox_virtual_environment_container.test_container]
node_name = "{{.NodeName}}"
vm_id = {{.TestContainerIDClone}}
clone {
vm_id = proxmox_virtual_environment_container.test_container.id
}
initialization {
hostname = "test-clone"
}
}`),
Check: resource.ComposeTestCheckFunc(
func(*terraform.State) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err := te.NodeClient().Container(accTestContainerIDClone).WaitForContainerStatus(ctx, "running")
require.NoError(te.t, err, "container did not start")
return nil
},
),
}}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
ProtoV6ProviderFactories: te.AccProviders,
Steps: tt.step,
})
})
}
} }

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,9 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package test package test
import ( import (
@ -36,9 +42,11 @@ type Environment struct {
NodeName string NodeName string
DatastoreID string DatastoreID string
AccProviders map[string]func() (tfprotov6.ProviderServer, error) AccProviders map[string]func() (tfprotov6.ProviderServer, error)
once sync.Once once sync.Once
c api.Client c api.Client
CloudImagesServer string
ContainerImagesServer string
} }
// InitEnvironment initializes a new test environment for acceptance tests. // InitEnvironment initializes a new test environment for acceptance tests.
@ -97,10 +105,12 @@ provider "proxmox" {
"CloudImagesServer": cloudImagesServer, "CloudImagesServer": cloudImagesServer,
"ContainerImagesServer": containerImagesServer, "ContainerImagesServer": containerImagesServer,
}, },
providerConfig: pc, providerConfig: pc,
NodeName: nodeName, NodeName: nodeName,
DatastoreID: datastoreID, DatastoreID: datastoreID,
AccProviders: muxProviders(t), AccProviders: muxProviders(t),
CloudImagesServer: cloudImagesServer,
ContainerImagesServer: containerImagesServer,
} }
} }

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,11 @@
//go:build acceptance || all
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package cpu_test package cpu_test
import ( import (

View File

@ -1,3 +1,5 @@
//go:build acceptance || all
/* /*
* This Source Code Form is subject to the terms of the Mozilla Public * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this

View File

@ -1,3 +1,11 @@
//go:build acceptance || all
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package vga_test package vga_test
import ( import (

View File

@ -2831,13 +2831,15 @@ func containerUpdate(ctx context.Context, d *schema.ResourceData, m interface{})
// Prepare the new mount point configuration. // Prepare the new mount point configuration.
if d.HasChange(mkMountPoint) { if d.HasChange(mkMountPoint) {
mountPoint := d.Get(mkMountPoint).([]interface{}) _, newMountPoints := d.GetChange(mkMountPoint)
mountPoints := newMountPoints.([]interface{})
mountPointArray := make( mountPointArray := make(
containers.CustomMountPointArray, containers.CustomMountPointArray,
len(mountPoint), len(mountPoints),
) )
for i, mp := range mountPoint { for i, mp := range mountPoints {
mountPointMap := mp.(map[string]interface{}) mountPointMap := mp.(map[string]interface{})
mountPointObject := containers.CustomMountPoint{} mountPointObject := containers.CustomMountPoint{}
@ -2861,7 +2863,7 @@ func containerUpdate(ctx context.Context, d *schema.ResourceData, m interface{})
mountPointObject.Volume = volume mountPointObject.Volume = volume
if len(mountOptions) > 0 { if len(mountOptions) > 0 {
mountOptionsArray := make([]string, 0, len(mountPoint)) mountOptionsArray := make([]string, 0, len(mountPoints))
for _, option := range mountOptions { for _, option := range mountOptions {
mountOptionsArray = append(mountOptionsArray, option.(string)) mountOptionsArray = append(mountOptionsArray, option.(string))
@ -3040,7 +3042,7 @@ func containerUpdate(ctx context.Context, d *schema.ResourceData, m interface{})
} }
// As a final step in the update procedure, we might need to reboot the container. // As a final step in the update procedure, we might need to reboot the container.
if !bool(template) && rebootRequired { if !bool(template) && started && rebootRequired {
rebootTimeout := 300 rebootTimeout := 300
e = containerAPI.RebootContainer( e = containerAPI.RebootContainer(