Merge pull request 'chore: merge main into alpha' (#89) from main into alpha

Reviewed-on: #89
This commit is contained in:
Marcel_Henselin 2026-03-25 09:25:58 +00:00
commit 233efb259d
Signed by: tf-provider.git.onstackit.cloud
GPG key ID: 6D7E8A1ED8955A9C
20 changed files with 298 additions and 57 deletions

View file

@ -94,6 +94,11 @@ runs:
fi
echo "::endgroup::"
# Install latest version of Terraform
- uses: hashicorp/setup-terraform@v4
with:
terraform_wrapper: false
- name: Setup JAVA
uses: actions/setup-java@v5
with:

1
.github/actions/clean_up/README.md vendored Normal file
View file

@ -0,0 +1 @@
# acceptance test action

168
.github/actions/clean_up/action.yaml vendored Normal file
View file

@ -0,0 +1,168 @@
name: CleanUp Project Resources
description: "Acceptance Testing CleanUp"
inputs:
project_id:
description: "STACKIT project ID for tests"
required: true
region:
description: "STACKIT region for tests"
default: 'eu01'
required: true
tf_resource_prefix:
description: "prefix in resource names"
default: 'tf-acc-'
required: true
service_account_json_content:
description: "STACKIT service account JSON file contents"
required: true
default: ''
service_account_json_content_b64:
description: "STACKIT service account JSON file contents"
required: true
default: ''
list_only:
description: "only list resources, DO NOT delete"
required: true
default: 'true'
log_level:
description: "Log Level"
required: true
default: 'warning'
outputs:
cli-version:
description: "stackit cli version"
value: ${{ steps.stackit_version.outputs.version }}
runs:
using: "composite"
steps:
- name: Install needed tools
shell: bash
run: |
echo "::group::apt install"
set -e
apt-get -y -qq update >apt_update.log 2>apt_update_err.log
if [ $? -ne 0 ]; then
cat apt_update.log apt_update_err.log
fi
apt-get -y -qq install curl gnupg jq >apt_get.log 2>apt_get_err.log
if [ $? -ne 0 ]; then
cat apt_get.log apt_get_err.log
fi
echo "::endgroup::"
echo "::group::apt add source"
curl https://packages.stackit.cloud/keys/key.gpg | gpg --dearmor -o /usr/share/keyrings/stackit.gpg
echo "deb [signed-by=/usr/share/keyrings/stackit.gpg] https://packages.stackit.cloud/apt/cli stackit main" | tee -a /etc/apt/sources.list.d/stackit.list
echo "::endgroup::"
echo "::group::apt install stackit cli"
apt-get -y -qq update >apt_update.log 2>apt_update_err.log
if [ $? -ne 0 ]; then
cat apt_update.log apt_update_err.log
fi
apt-get -y -qq install stackit >apt_get.log 2>apt_get_err.log
if [ $? -ne 0 ]; then
cat apt_get.log apt_get_err.log
fi
echo "::endgroup::"
- name: Check stackit cli version
id: stackit_version
run: |
set -e
VERSION=$(stackit --version | grep "Version:" | cut -d " " -f 2)
echo "stackit cli version: ${VERSION}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
shell: bash
- name: Creating service_account file from json input
if: inputs.service_account_json_content != ''
shell: bash
run: |
echo "::group::create service account file"
set -e
set -o pipefail
echo "${{ inputs.service_account_json_content }}" > .svc_acc.json
echo "::endgroup::"
- name: Creating service_account file from base64 json input
if: inputs.service_account_json_content_b64 != ''
shell: bash
run: |
echo "::group::create service account file"
set -e
set -o pipefail
echo "${{ inputs.service_account_json_content_b64 }}" | base64 -d > .svc_acc.json
echo "::endgroup::"
- name: Check service account file exists
shell: bash
run: |
set -e
if [[ ! -s .svc_acc.json ]]; then
echo "ERROR: service account file missing or empty"
exit 1
fi
- name: Retrieve resources
run: |
echo "::group::retrieve resources"
set -e
echo "authenticating api"
STACKIT_SERVICE_ACCOUNT_KEY_PATH="${PWD}/.svc_acc.json"
export STACKIT_SERVICE_ACCOUNT_KEY_PATH
stackit auth activate-service-account --service-account-key-path .svc_acc.json
echo "SQL Server Flex resources:"
stackit --verbosity ${{ inputs.log_level }} --project-id "${{ inputs.project_id }}" beta sqlserverflex instance list --output-format json | jq -r '.[] | select(.name | startswith("${{ inputs.tf_resource_prefix }}"))'
echo "PostgreSQL Flex resources:"
stackit --verbosity ${{ inputs.log_level }} --project-id "${{ inputs.project_id }}" postgresflex instance list --output-format json | jq -r '.[] | select(.name | startswith("${{ inputs.tf_resource_prefix }}"))'
echo "::endgroup::"
shell: bash
- name: Delete SQL Server Flex resources
if: ${{ inputs.list_only != 'true' }}
run: |
echo "::group::delete SQL Server Flex resources"
set -e
stackit --verbosity ${{ inputs.log_level }} auth activate-service-account --service-account-key-path .svc_acc.json
for s in $(stackit --verbosity ${{ inputs.log_level }} --project-id ${{ inputs.project_id }} beta sqlserverflex instance list --output-format json | jq -r '.[] | select(.name | startswith("${{ inputs.tf_resource_prefix }}")) | .id'); do stackit --verbosity ${{ inputs.log_level }} -y --project-id ${{ inputs.project_id }} beta sqlserverflex instance delete $s; done
echo "::endgroup::"
shell: bash
- name: Skip Delete SQL Server Flex resources
if: ${{ inputs.list_only == 'true' }}
run: |
set -e
echo "Skip deleting: list only mode"
shell: bash
- name: Delete PostgreSQL Flex resources
if: ${{ inputs.list_only != 'true' }}
run: |
echo "::group::delete PostgreSQL Flex resources"
set -e
stackit auth activate-service-account --service-account-key-path .svc_acc.json
for s in $(stackit --verbosity ${{ inputs.log_level }} --project-id ${{ inputs.project_id }} postgresflex instance list --output-format json | jq -r '.[] | select(.name | startswith("${{ inputs.tf_resource_prefix }}")) | .id'); do stackit --verbosity ${{ inputs.log_level }} -y --project-id ${{ inputs.project_id }} postgresflex instance delete $s; done
echo "::endgroup::"
shell: bash
- name: Skip Delete PostgreSQL Flex resources
if: ${{ inputs.list_only == 'true' }}
run: |
set -e
echo "Skip deleting: list only mode"
shell: bash

View file

@ -28,7 +28,7 @@ jobs:
config:
if: ${{ github.event_name != 'schedule' }}
name: Check GoReleaser config
runs-on: ubuntu-latest
runs-on: stackit-docker
steps:
- name: Checkout
uses: actions/checkout@v6
@ -40,7 +40,7 @@ jobs:
prepare:
name: Prepare GO cache
runs-on: ubuntu-latest
runs-on: stackit-docker
permissions:
actions: read # Required to identify workflow run.
checks: write # Required to add status summary.
@ -102,7 +102,7 @@ jobs:
needs:
- config
- prepare
runs-on: ubuntu-latest
runs-on: stackit-docker
permissions:
actions: read # Required to identify workflow run.
checks: write # Required to add status summary.
@ -185,7 +185,7 @@ jobs:
testing:
name: CI run tests
runs-on: ubuntu-latest
runs-on: stackit-docker
needs:
- config
- prepare
@ -278,7 +278,7 @@ jobs:
main:
if: ${{ github.event_name != 'schedule' }}
name: CI run build and linting
runs-on: ubuntu-latest
runs-on: stackit-docker
needs:
- config
- prepare
@ -329,7 +329,7 @@ jobs:
code_coverage:
name: "Code coverage report"
if: github.event_name == 'pull_request' # Do not run when workflow is triggered by push to main branch
runs-on: ubuntu-latest
runs-on: stackit-docker
needs:
- main
- prepare

45
.github/workflows/clean_up.yaml vendored Normal file
View file

@ -0,0 +1,45 @@
name: TF Acceptance Test CleanUp
on:
workflow_dispatch:
inputs:
list_only:
description: "only list resources"
type: boolean
default: true
required: true
res_prefix:
description: "resource name prefix"
type: string
default: 'tf-acc-'
required: true
log_level:
description: 'Log Level'
required: true
default: 'warning'
type: choice
options:
- info
- warning
- debug
- error
jobs:
clean:
name: Clean up
runs-on: stackit-docker
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Clean
uses: ./.github/actions/clean_up
with:
project_id: ${{ vars.TF_ACC_PROJECT_ID }}
region: 'eu01'
tf_resource_prefix: ${{ inputs.res_prefix }}
service_account_json_content_b64: "${{ secrets.TF_ACC_SERVICE_ACCOUNT_JSON_B64 }}"
list_only: ${{ inputs.list_only }}
log_level: ${{ inputs.log_level }}

View file

@ -4,9 +4,10 @@ run-name: Publish by @${{ github.actor }}
on:
workflow_dispatch:
push:
tags:
- 'v0.*'
- 'v*'
env:
GO_VERSION: "1.25"
@ -16,7 +17,6 @@ env:
jobs:
config:
name: Check GoReleaser config
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- name: Checkout
@ -29,13 +29,12 @@ jobs:
publish:
name: "Publish provider"
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
needs: config
runs-on: ubuntu-latest
permissions:
actions: read # Required to identify workflow run.
checks: write # Required to add status summary.
contents: read # Required to checkout repository.
contents: write # Required to checkout repository.
pull-requests: write # Required to add PR comment.
steps:
- name: Install needed tools
@ -45,6 +44,8 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-tags: true
- name: Setup Go
uses: actions/setup-go@v6
@ -82,7 +83,7 @@ jobs:
gpg --import ~/private.key.pem
rm ~/private.key.pem
- name: Run GoReleaser with SNAPSHOT
- name: Run GoReleaser
if: github.event_name == 'workflow_dispatch'
id: goreleaser
env:
@ -90,7 +91,8 @@ jobs:
GPG_FINGERPRINT: ${{ secrets.GPG_FINGERPRINT }}
uses: goreleaser/goreleaser-action@v7
with:
args: release --skip publish --clean --snapshot
# args: release --skip publish --clean --snapshot
args: release --skip publish --clean
- name: Run GoReleaser
if: github.event_name != 'workflow_dispatch'
@ -106,9 +108,15 @@ jobs:
run: |
echo "${{ secrets.PUBLIC_KEY_PEM }}" >public_key.pem
- name: Determine version
id: get_version
run: |
set -e
VERSION=$(jq -r .version < dist/metadata.json)
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- name: Prepare provider directory structure
run: |
VERSION=$(jq -r .version < dist/metadata.json)
go run generator/main.go \
publish \
--namespace=mhenselin \
@ -117,7 +125,7 @@ jobs:
--domain=tfregistry.sysops.stackit.rocks \
--gpgFingerprint="${{ secrets.GPG_FINGERPRINT }}" \
--gpgPubKeyFile=public_key.pem \
--version=${VERSION}
--version=${{ steps.get_version.outputs.version }}
- name: Prepare documentation nav file
run: |
@ -142,6 +150,7 @@ jobs:
run: |
set -e
ssh -o StrictHostKeyChecking=no ubuntu@${{ vars.DOCS_SERVER_IP }} 'rm -rf /srv/www/docs'
echo "${{ github.ref_name }}" >docs/_version.txt
echo "${{ steps.get_version.outputs.version }}" >docs/_version.txt
# echo "${{ github.ref_name }}" >docs/_version.txt
scp -o StrictHostKeyChecking=no -r docs ubuntu@${{ vars.DOCS_SERVER_IP }}:/srv/www/
scp -o StrictHostKeyChecking=no nav.md ubuntu@${{ vars.DOCS_SERVER_IP }}:/srv/www/

View file

@ -16,14 +16,14 @@ permissions:
jobs:
goreleaser:
runs-on: ubuntu-latest
runs-on: stackit-docker
steps:
- uses: actions/checkout@v6
with:
# Allow goreleaser to access older tag information.
fetch-depth: 0
- uses: https://code.forgejo.org/actions/setup-go@v6
- uses: actions/setup-go@v6
with:
go-version-file: "go.mod"
cache: true

View file

@ -8,13 +8,13 @@ on:
jobs:
renovate:
name: Renovate
runs-on: ubuntu-latest
runs-on: stackit-docker
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Self-hosted Renovate
uses: renovatebot/github-action@v46.1.4
uses: renovatebot/github-action@v46.1.5
with:
configurationFile: .github/renovate.json
# token: ${{ secrets.RENOVATE_TOKEN }}

View file

@ -20,7 +20,7 @@ permissions:
jobs:
stale:
name: "Stale"
runs-on: ubuntu-latest
runs-on: stackit-docker
timeout-minutes: 10
steps:
- name: "Mark old PRs as stale"

View file

@ -13,17 +13,19 @@ on:
inputs:
enable_debug:
description: "enable terraform debug logs"
default: 'false'
type: boolean
default: false
required: true
test_timeout_string:
description: "string that determines the timeout (default: 45m)"
type: string
default: '90m'
required: true
jobs:
acc_test:
name: Acceptance Tests
runs-on: ubuntu-latest
runs-on: stackit-docker
steps:
- name: Checkout
uses: actions/checkout@v6
@ -57,4 +59,3 @@ jobs:
tf_acc_kek_key_ring_id: ${{ vars.TF_ACC_KEK_KEY_RING_ID }}
tf_acc_kek_key_version: ${{ vars.TF_ACC_KEK_KEY_VERSION }}
tf_acc_kek_service_account: ${{ vars.TF_ACC_KEK_SERVICE_ACCOUNT }}
tf_debug: ${{ inputs.enable_debug }}

View file

@ -209,8 +209,8 @@ func (r *databaseResource) Create(
)
database, err := postgresflexalphaWait.GetDatabaseByIdWaitHandler(ctx, r.client.DefaultAPI, projectID, instanceID, region, databaseID).
SetTimeout(15 * time.Minute).
SetSleepBeforeWait(15 * time.Second).
SetTimeout(30 * time.Minute).
SetSleepBeforeWait(10 * time.Second).
WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(
@ -279,8 +279,8 @@ func (r *databaseResource) Read(
)
databaseResp, err := postgresflexalphaWait.GetDatabaseByIdWaitHandler(ctx, r.client.DefaultAPI, projectID, instanceID, region, databaseID).
SetTimeout(15 * time.Minute).
SetSleepBeforeWait(15 * time.Second).
SetTimeout(30 * time.Minute).
SetSleepBeforeWait(10 * time.Second).
WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(
@ -386,8 +386,8 @@ func (r *databaseResource) Update(
ctx = core.LogResponse(ctx)
databaseResp, err := postgresflexalphaWait.GetDatabaseByIdWaitHandler(ctx, r.client.DefaultAPI, projectId, instanceId, region, databaseId).
SetTimeout(15 * time.Minute).
SetSleepBeforeWait(15 * time.Second).
SetTimeout(30 * time.Minute).
SetSleepBeforeWait(10 * time.Second).
WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "error updating database", err.Error())

View file

@ -208,7 +208,7 @@ func (r *instanceResource) Create(
)
waitResp, err := wait.CreateInstanceWaitHandler(ctx, r.client.DefaultAPI, projectID, region, *instanceID).
SetTimeout(30 * time.Minute).
SetTimeout(90 * time.Minute).
SetSleepBeforeWait(10 * time.Second).
WaitWithContext(ctx)
if err != nil {
@ -446,7 +446,7 @@ func (r *instanceResource) Update(
region,
instanceID,
).
SetTimeout(30 * time.Minute).
SetTimeout(90 * time.Minute).
SetSleepBeforeWait(10 * time.Second).
WaitWithContext(ctx)
if err != nil {

View file

@ -799,7 +799,7 @@ func testAccCheckPostgresFlexDestroy(s *terraform.State) error {
testutils.ProjectId,
testutils.Region,
items[i].Id,
15*time.Minute,
30*time.Minute,
10*time.Second,
)
if err != nil {

View file

@ -241,7 +241,7 @@ func (r *userResource) Create(
).SetSleepBeforeWait(
10 * time.Second,
).SetTimeout(
15 * time.Minute,
30 * time.Minute,
).WaitWithContext(ctx)
if err != nil {
@ -322,7 +322,7 @@ func (r *userResource) Read(
).SetSleepBeforeWait(
10 * time.Second,
).SetTimeout(
15 * time.Minute,
30 * time.Minute,
).WaitWithContext(ctx)
if err != nil {
@ -445,7 +445,7 @@ func (r *userResource) Update(
).SetSleepBeforeWait(
10 * time.Second,
).SetTimeout(
15 * time.Minute,
30 * time.Minute,
).WaitWithContext(ctx)
if err != nil {

View file

@ -193,6 +193,7 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
data.Owner.ValueString(),
).
SetSleepBeforeWait(10 * time.Second).
SetTimeout(90 * time.Minute).
WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(
@ -253,9 +254,9 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
region,
databaseName,
).SetSleepBeforeWait(
30 * time.Second,
10 * time.Second,
).SetTimeout(
15 * time.Minute,
90 * time.Minute,
).WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(

View file

@ -354,8 +354,8 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
waitResp, err := wait.
UpdateInstanceWaitHandler(ctx, r.client.DefaultAPI, projectID, instanceID, region).
SetSleepBeforeWait(15 * time.Second).
SetTimeout(45 * time.Minute).
SetSleepBeforeWait(10 * time.Second).
SetTimeout(90 * time.Minute).
WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(
@ -416,7 +416,10 @@ func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteReques
ctx = core.LogResponse(ctx)
delResp, err := wait.DeleteInstanceWaitHandler(ctx, r.client.DefaultAPI, projectID, instanceID, region).WaitWithContext(ctx)
delResp, err := wait.DeleteInstanceWaitHandler(ctx, r.client.DefaultAPI, projectID, instanceID, region).
SetSleepBeforeWait(10 * time.Second).
SetTimeout(90 * time.Minute).
WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(
ctx,

View file

@ -158,7 +158,8 @@ func TestAccInstance(t *testing.T) {
PreConfig: func() {
t.Logf("testing: %s - %s", t.Name(), "create and verify")
},
ExpectNonEmptyPlan: true,
// empty refresh plan
ExpectNonEmptyPlan: false,
Config: testutils.StringFromTemplateMust(
"testdata/instance_template.gompl",
exData,

View file

@ -308,7 +308,7 @@ func (r *userResource) Create(
region,
userId,
).SetSleepBeforeWait(
90 * time.Second,
10 * time.Second,
).SetTimeout(
90 * time.Minute,
).WaitWithContext(ctx)
@ -459,23 +459,23 @@ func (r *userResource) Delete(
ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
userId := model.UserId.ValueInt64()
projectID := model.ProjectId.ValueString()
instanceID := model.InstanceId.ValueString()
userID := model.UserId.ValueInt64()
region := model.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "user_id", userId)
ctx = tflog.SetField(ctx, "project_id", projectID)
ctx = tflog.SetField(ctx, "instance_id", instanceID)
ctx = tflog.SetField(ctx, "user_id", userID)
ctx = tflog.SetField(ctx, "region", region)
// Delete existing record set
// err := r.client.DeleteUserRequest(ctx, projectId, region, instanceId, userId).Execute()
err := r.client.DefaultAPI.DeleteUserRequest(ctx, projectId, region, instanceId, userId).Execute()
err := r.client.DefaultAPI.DeleteUserRequest(ctx, projectID, region, instanceID, userID).Execute()
if err != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(err, &oapiErr)
if !ok {
// TODO err handling
core.LogAndAddError(ctx, &resp.Diagnostics, "User Delete Error", fmt.Sprintf("error is no oapi error: %v", err))
return
}
@ -487,12 +487,14 @@ func (r *userResource) Delete(
// tflog.Warn(ctx, "[delete user] Wait handler got error 500")
// return false, nil, nil
default:
// TODO err handling
core.LogAndAddError(ctx, &resp.Diagnostics, "User Delete Error", fmt.Sprintf("Unexpected API error: %v", err))
return
}
}
// Delete existing record set
_, err = sqlserverflexbetaWait.DeleteUserWaitHandler(ctx, r.client.DefaultAPI, projectId, region, instanceId, userId).
_, err = sqlserverflexbetaWait.DeleteUserWaitHandler(ctx, r.client.DefaultAPI, projectID, region, instanceID, userID).
SetTimeout(90 * time.Minute).
SetSleepBeforeWait(10 * time.Second).
WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "User Delete Error", fmt.Sprintf("Calling API: %v", err))

View file

@ -281,8 +281,8 @@ func GetDatabaseByIdWaitHandler(
if databaseID > math.MaxInt32 {
return false, nil, fmt.Errorf("databaseID too large for int32")
}
dbId32 := int32(databaseID) //nolint:gosec // is checked above
s, err := a.GetDatabaseRequest(ctx, projectID, region, instanceID, dbId32).Execute()
dbID32 := int32(databaseID) //nolint:gosec // is checked above
s, err := a.GetDatabaseRequest(ctx, projectID, region, instanceID, dbID32).Execute()
if err != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(err, &oapiErr)
@ -290,6 +290,7 @@ func GetDatabaseByIdWaitHandler(
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
}
switch oapiErr.StatusCode {
// TODO: work-around
case http.StatusBadGateway, http.StatusGatewayTimeout, http.StatusServiceUnavailable:
tflog.Warn(
ctx, "api responded with 50[2,3,4] status", map[string]interface{}{

View file

@ -89,9 +89,16 @@ func CreateInstanceWaitHandler(
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError: %w", err)
}
switch oapiErr.StatusCode {
case http.StatusOK:
return false, nil, nil
case http.StatusNotFound:
return false, nil, nil
default:
// TODO: work-around
if strings.Contains(err.Error(), "is not a valid InstanceEdition") {
tflog.Info(ctx, "API WORKAROUND", map[string]interface{}{"err": err})
return false, nil, nil
}
return false, nil, fmt.Errorf("api error: %w", err)
}
}
@ -257,7 +264,6 @@ func DeleteInstanceWaitHandler(
return true, nil, nil
},
)
handler.SetTimeout(30 * time.Minute)
return handler
}
@ -399,7 +405,5 @@ func DeleteUserWaitHandler(
}
},
)
handler.SetTimeout(15 * time.Minute)
handler.SetSleepBeforeWait(15 * time.Second)
return handler
}