Skip to content

Commit db69a0c

Browse files
Don't build and test EOL versions (#287)
1 parent 47439a1 commit db69a0c

8 files changed

+97
-27
lines changed

.github/workflows/acceptance.yml

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ permissions:
3030

3131
env:
3232
CLOUDSTACK_API_URL: http://localhost:8080/client/api
33-
CLOUDSTACK_VERSIONS: "['4.19.0.1', '4.19.1.3', '4.19.2.0', '4.19.3.0', '4.20.1.0']"
33+
CLOUDSTACK_VERSIONS: "['4.20.2.0', '4.22.0.0']"
3434

3535
jobs:
3636
prepare-matrix:
@@ -48,17 +48,17 @@ jobs:
4848
needs: [prepare-matrix]
4949
runs-on: ubuntu-latest
5050
steps:
51-
- uses: actions/checkout@v4
51+
- uses: actions/checkout@v6
5252
- name: Set up Go
53-
uses: actions/setup-go@v5
53+
uses: actions/setup-go@v6
5454
with:
5555
go-version-file: 'go.mod'
5656
- name: Configure Cloudstack v${{ matrix.cloudstack-version }}
5757
uses: ./.github/actions/setup-cloudstack
5858
id: setup-cloudstack
5959
with:
6060
cloudstack-version: ${{ matrix.cloudstack-version }}
61-
- uses: hashicorp/setup-terraform@v3
61+
- uses: hashicorp/setup-terraform@v4
6262
with:
6363
terraform_version: ${{ matrix.terraform-version }}
6464
terraform_wrapper: false
@@ -78,26 +78,27 @@ jobs:
7878
fail-fast: false
7979
matrix:
8080
terraform-version:
81-
- '1.11.*'
8281
- '1.12.*'
82+
- '1.13.*'
83+
- '1.14.*'
8384
cloudstack-version: ${{ fromJson(needs.prepare-matrix.outputs.cloudstack-versions) }}
8485

8586
acceptance-opentofu:
8687
name: OpenTofu ${{ matrix.opentofu-version }} with Cloudstack ${{ matrix.cloudstack-version }}
8788
needs: [prepare-matrix]
8889
runs-on: ubuntu-latest
8990
steps:
90-
- uses: actions/checkout@v4
91+
- uses: actions/checkout@v6
9192
- name: Set up Go
92-
uses: actions/setup-go@v5
93+
uses: actions/setup-go@v6
9394
with:
9495
go-version-file: 'go.mod'
9596
- name: Configure Cloudstack v${{ matrix.cloudstack-version }}
9697
uses: ./.github/actions/setup-cloudstack
9798
id: setup-cloudstack
9899
with:
99100
cloudstack-version: ${{ matrix.cloudstack-version }}
100-
- uses: opentofu/setup-opentofu@000eeb8522f0572907c393e8151076c205fdba1b # v1.0.6
101+
- uses: opentofu/setup-opentofu@9d84900f3238fab8cd84ce47d658d25dd008be2f # v1.0.8
101102
with:
102103
tofu_version: ${{ matrix.opentofu-version }}
103104
- name: Run acceptance test
@@ -116,8 +117,9 @@ jobs:
116117
fail-fast: false
117118
matrix:
118119
opentofu-version:
119-
- '1.8.*'
120120
- '1.9.*'
121+
- '1.10.*'
122+
- '1.11.*'
121123
cloudstack-version: ${{ fromJson(needs.prepare-matrix.outputs.cloudstack-versions) }}
122124

123125
all-jobs-passed: # Will succeed if it is skipped

README.md

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -123,37 +123,51 @@ make test
123123

124124
In order to run the full suite of Acceptance tests you will need to run the CloudStack Simulator. Please follow these steps to prepare an environment for running the Acceptance tests:
125125

126+
### Step 1: Start the CloudStack Simulator
127+
126128
```sh
127-
docker pull apache/cloudstack-simulator
129+
# Pull the simulator image (recommended versions: 4.20.2.0 or 4.23.0.0-SNAPSHOT)
130+
docker pull apache/cloudstack-simulator:4.20.2.0
128131

129-
or pull it with a particular build tag
132+
# Start the simulator container
133+
docker run --name simulator -p 8080:5050 -d apache/cloudstack-simulator:4.20.2.0
134+
```
130135

131-
docker pull apache/cloudstack-simulator:4.20.1.0
136+
**Note:** Version 4.22.0.0 has a known bug with updating load balancer rules. CI currently tests against this version, but for local testing we recommend using 4.20.2.0 or 4.23.0.0-SNAPSHOT to avoid this issue.
132137

133-
docker run --name simulator -p 8080:5050 -d apache/cloudstack-simulator
138+
### Step 2: Wait for Simulator to be Ready
134139

135-
or
140+
When Docker starts the container, wait a few minutes for it to fully initialize. You can check if it's ready by visiting <http://localhost:8080/client> and logging in as user `admin` with password `password`. You may need to wait and refresh the page for a few minutes before the login page is shown.
136141

137-
docker run --name simulator -p 8080:5050 -d apache/cloudstack-simulator:4.20.1.0
138-
```
142+
### Step 3: Deploy the Data Center (REQUIRED)
139143

140-
When Docker started the container you can go to <http://localhost:8080/client> and login to the CloudStack UI as user `admin` with password `password`. It can take a few minutes for the container is fully ready, so you probably need to wait and refresh the page for a few minutes before the login page is shown.
141-
142-
Once the login page is shown and you can login, you need to provision a simulated data-center:
144+
**This step is critical!** Simply starting the simulator is not enough. You must run the data center deployment script to create the necessary CloudStack resources (zones, networks, service offerings, templates, etc.):
143145

144146
```sh
145147
docker exec -it simulator python /root/tools/marvin/marvin/deployDataCenter.py -i /root/setup/dev/advanced.cfg
146148
```
147149

148-
If you refresh the client or login again, you will now get passed the initial welcome screen and be able to go to your account details and retrieve the API key and secret. Export those together with the URL:
150+
This script creates the "Sandbox-simulator" zone and other resources that the acceptance tests expect. **Without this step, most tests will fail with "zone not found" errors.**
151+
152+
**Note:** This deployment script takes approximately **2-3 minutes** to complete. Wait for it to finish before proceeding to the next step. You should see output like "====Deploy DC Successful=====" when it's done.
153+
154+
### Step 4: Get API Credentials
155+
156+
After deploying the data center, refresh the CloudStack UI and log in again. You will now be able to access your account details and retrieve the API key and secret. Export those together with the URL:
149157

150158
```sh
151159
export CLOUDSTACK_API_URL=http://localhost:8080/client/api
152-
export CLOUDSTACK_API_KEY=r_gszj7e0ttr_C6CP5QU_1IV82EIOtK4o_K9i_AltVztfO68wpXihKs2Tms6tCMDY4HDmbqHc-DtTamG5x112w
153-
export CLOUDSTACK_SECRET_KEY=tsfMDShFe94f4JkJfEh6_tZZ--w5jqEW7vGL2tkZGQgcdbnxNoq9fRmwAtU5MEGGXOrDlNA6tfvGK14fk_MB6w
160+
export CLOUDSTACK_API_KEY=<your-api-key-from-ui>
161+
export CLOUDSTACK_SECRET_KEY=<your-secret-key-from-ui>
154162
```
155163

156-
In order for all the tests to pass, you will need to create a new (empty) project in the UI called `terraform`. When the project is created you can run the Acceptance tests against the CloudStack Simulator by simply running:
164+
### Step 5: Create Required Resources
165+
166+
In order for all the tests to pass, you will need to create a new (empty) project in the UI called `terraform`.
167+
168+
### Step 6: Run the Tests
169+
170+
When the project is created you can run the Acceptance tests against the CloudStack Simulator by simply running:
157171

158172
```sh
159173
make testacc

cloudstack/provider_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"regexp"
2626
"testing"
2727

28+
"github.com/apache/cloudstack-go/v2/cloudstack"
2829
"github.com/hashicorp/terraform-plugin-framework/providerserver"
2930
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
3031
"github.com/hashicorp/terraform-plugin-mux/tf5to6server"
@@ -145,3 +146,42 @@ func testAccPreCheck(t *testing.T) {
145146
t.Fatal("CLOUDSTACK_SECRET_KEY must be set for acceptance tests")
146147
}
147148
}
149+
150+
// newTestClient creates a CloudStack client from environment variables for use in test PreCheck functions.
151+
// This is needed because PreCheck functions run before the test framework configures the provider,
152+
// so testAccProvider.Meta() is nil at that point.
153+
func newTestClient(t *testing.T) *cloudstack.CloudStackClient {
154+
t.Helper()
155+
testAccPreCheck(t)
156+
157+
cfg := Config{
158+
APIURL: os.Getenv("CLOUDSTACK_API_URL"),
159+
APIKey: os.Getenv("CLOUDSTACK_API_KEY"),
160+
SecretKey: os.Getenv("CLOUDSTACK_SECRET_KEY"),
161+
HTTPGETOnly: true,
162+
Timeout: 60,
163+
}
164+
cs, err := cfg.NewClient()
165+
if err != nil {
166+
t.Fatalf("Failed to create CloudStack client: %v", err)
167+
}
168+
return cs
169+
}
170+
171+
// getCloudStackVersion returns the CloudStack version from the API
172+
func getCloudStackVersion(t *testing.T) string {
173+
t.Helper()
174+
cs := newTestClient(t)
175+
176+
p := cs.Configuration.NewListCapabilitiesParams()
177+
r, err := cs.Configuration.ListCapabilities(p)
178+
if err != nil {
179+
t.Fatalf("Failed to get CloudStack capabilities: %v", err)
180+
}
181+
182+
if r.Capabilities != nil {
183+
return r.Capabilities.Cloudstackversion
184+
}
185+
186+
return ""
187+
}

cloudstack/resource_cloudstack_cni_configuration.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,23 @@ func resourceCloudStackCniConfiguration() *schema.Resource {
5555
"account": {
5656
Type: schema.TypeString,
5757
Optional: true,
58+
Computed: true,
5859
ForceNew: true,
5960
Description: "An optional account for the CNI configuration. Must be used with domain_id.",
6061
},
6162

6263
"domain_id": {
6364
Type: schema.TypeString,
6465
Optional: true,
66+
Computed: true,
6567
ForceNew: true,
6668
Description: "An optional domain ID for the CNI configuration. If the account parameter is used, domain_id must also be used.",
6769
},
6870

6971
"project_id": {
7072
Type: schema.TypeString,
7173
Optional: true,
74+
Computed: true,
7275
ForceNew: true,
7376
Description: "An optional project for the CNI configuration",
7477
},

cloudstack/resource_cloudstack_cni_configuration_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ resource "cloudstack_cni_configuration" "foo" {
142142
`
143143

144144
func testAccPreCheckCniSupport(t *testing.T) {
145-
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
145+
cs := newTestClient(t)
146146

147147
// Try to list CNI configurations to check if the feature is available
148148
p := cs.Configuration.NewListCniConfigurationParams()

cloudstack/resource_cloudstack_loadbalancer_rule.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ func resourceCloudStackLoadBalancerRuleUpdate(d *schema.ResourceData, meta inter
369369
_, err := cs.LoadBalancer.UpdateLoadBalancerRule(p)
370370
if err != nil {
371371
return fmt.Errorf(
372-
"Error updating load balancer rule %s", name)
372+
"Error updating load balancer rule %s: %s", name, err)
373373
}
374374
}
375375

cloudstack/resource_cloudstack_loadbalancer_rule_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,15 @@ func TestAccCloudStackLoadBalancerRule_update(t *testing.T) {
5757
var id string
5858

5959
resource.Test(t, resource.TestCase{
60-
PreCheck: func() { testAccPreCheck(t) },
60+
PreCheck: func() {
61+
// Skip this test on CloudStack 4.22.0.0 due to a known simulator bug
62+
// that causes "530 Internal Server Error" when updating load balancer rules.
63+
// This bug does not exist in 4.20.1.0, 4.22.1.0+, or 4.23.0.0+.
64+
version := getCloudStackVersion(t)
65+
if version == "4.22.0.0" {
66+
t.Skip("Skipping TestAccCloudStackLoadBalancerRule_update on CloudStack 4.22.0.0 due to known simulator bug (Error 530: Internal Server Error)")
67+
}
68+
},
6169
Providers: testAccProviders,
6270
CheckDestroy: testAccCheckCloudStackLoadBalancerRuleDestroy,
6371
Steps: []resource.TestStep{

cloudstack/service_offering_util.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ func (state *serviceOfferingCommonResourceModel) commonRead(ctx context.Context,
7878
if cs.Deploymentplanner != "" {
7979
state.DeploymentPlanner = types.StringValue(cs.Deploymentplanner)
8080
}
81-
if cs.Diskofferingid != "" {
81+
// Only set DiskOfferingId if it was already set in the state (i.e., user explicitly provided it)
82+
// When using disk_offering block, CloudStack creates an internal disk offering and returns its ID,
83+
// but we should not populate disk_offering_id in that case to avoid drift
84+
if cs.Diskofferingid != "" && !state.DiskOfferingId.IsNull() && !state.DiskOfferingId.IsUnknown() {
8285
state.DiskOfferingId = types.StringValue(cs.Diskofferingid)
8386
}
8487
if cs.Displaytext != "" {

0 commit comments

Comments
 (0)