Skip to content

Commit 55f3561

Browse files
committed
Add tofu config + initial tests
1 parent f837409 commit 55f3561

File tree

6 files changed

+269
-0
lines changed

6 files changed

+269
-0
lines changed

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ prometheus_client==0.20.0
1111
tzdata==2024.1
1212
psycopg2-binary==2.9.9
1313
whitenoise==6.9.0
14+
tofupy==1.1.1

tofu/.gitignore

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
tests/__pycache__/**
2+
3+
# Local .terraform directories
4+
.terraform/
5+
6+
# .tfstate files
7+
*.tfstate
8+
*.tfstate.*
9+
10+
# Crash log files
11+
crash.log
12+
crash.*.log
13+
14+
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
15+
# password, private keys, and other secrets. These should not be part of version
16+
# control as they are data points which are potentially sensitive and subject
17+
# to change depending on the environment.
18+
*.tfvars
19+
*.tfvars.json
20+
21+
# Ignore override files as they are usually used to override resources locally and so
22+
# are not checked in
23+
override.tf
24+
override.tf.json
25+
*_override.tf
26+
*_override.tf.json
27+
28+
# Ignore transient lock info files created by terraform apply
29+
.terraform.tfstate.lock.info
30+
31+
# Include override files you do wish to add to version control using negated pattern
32+
# !example_override.tf
33+
34+
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
35+
# example: *tfplan*
36+
37+
# Ignore CLI configuration files
38+
.terraformrc
39+
terraform.rc

tofu/example-config.tfvars

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# coral_uri = "http://credits.apps.<azimuth-domain>"
2+
# auth_token = Get bearer token with `curl -X POST -H "Content-Type: application/json" -d \
3+
# "{
4+
# \"username\": \"admin\",
5+
# \"password\": \"$TEST_PASSWORD\"
6+
# }" \ http://credits.apps.<azimuth-domain>/api_auth_token/
7+
8+
resource_provider_name = "Test Provider"
9+
resource_provider_email = "[email protected]"
10+
resource_provider_info_url = "https://www.google.com"
11+
12+
accounts = [
13+
{
14+
name = "TestAccount1"
15+
16+
openstack_project_id = "c2eced313b324cdb8e670e6e30bf387d"
17+
},
18+
{
19+
name = "TestAccount2"
20+
21+
openstack_project_id = "2fbf511968aa443e883a82283b0f0160"
22+
}
23+
]
24+
25+
allocations = {
26+
Q1 = {
27+
start_date = "2025-09-01-12:00:00"
28+
end_date = "2025-12-01-12:00:00"
29+
projects = [
30+
{
31+
account_email = "[email protected]"
32+
resources = {
33+
VCPU = 40000
34+
MEMORY_MB = 4423680
35+
DISK_GB = 108000
36+
}
37+
},
38+
{
39+
account_email = "[email protected]"
40+
resources = {
41+
VCPU = 20000
42+
MEMORY_MB = 2000000
43+
DISK_GB = 200000
44+
}
45+
}
46+
]
47+
}
48+
Q2 = {
49+
start_date = "2026-01-01-12:00:00"
50+
end_date = "2026-04-01-12:00:00"
51+
projects = [
52+
{
53+
account_email = "[email protected]"
54+
resources = {
55+
VCPU = 80000
56+
MEMORY_MB = 8000000
57+
DISK_GB = 300000
58+
}
59+
}
60+
]
61+
}
62+
}

tofu/main.tf

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
terraform {
2+
required_providers {
3+
restapi = {
4+
source = "Mastercard/restapi"
5+
version = "1.20.0"
6+
}
7+
}
8+
}
9+
10+
provider "restapi" {
11+
alias = "coral"
12+
uri = var.coral_uri
13+
debug = false
14+
write_returns_object = true
15+
create_returns_object = true
16+
id_attribute = "id"
17+
headers = {
18+
"Content-Type" = "application/json"
19+
Authorization = "Bearer ${var.auth_token}"
20+
}
21+
}
22+
23+
module "coral_tofu" {
24+
source = "git::https://github.com/stackhpc/coral-credits-tofu.git?ref=main" #TODO: move to versioned release
25+
26+
resource_provider_name = var.resource_provider_name
27+
resource_provider_email = var.resource_provider_email
28+
resource_provider_info_url = var.resource_provider_info_url
29+
allocations = var.allocations
30+
accounts = var.accounts
31+
resource_classes = var.resource_classes
32+
33+
providers = {
34+
restapi.coral = restapi.coral
35+
}
36+
}

tofu/tests/tofu_tests.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import pytest
2+
from tofupy import Tofu
3+
import requests
4+
import os
5+
import time
6+
7+
coral_uri = os.environ.get("TF_VAR_coral_uri")
8+
headers = {"Authorization": "Bearer "+os.environ.get("TF_VAR_auth_token")}
9+
10+
@pytest.fixture(scope="session")
11+
def terraform_rest_setup():
12+
working_dir = os.path.join(os.path.dirname(__file__), "..")
13+
var_file = os.path.join(working_dir, "example-config.tfvars")
14+
tf = Tofu(cwd=working_dir)
15+
tf.init()
16+
tf.apply(extra_args=["--var-file="+var_file])
17+
# add_consumers()
18+
yield
19+
tf.apply(extra_args=["--var-file="+var_file],destroy=True)
20+
21+
def api_get_request(resource):
22+
return requests.get(coral_uri+"/"+resource,headers=headers).json()
23+
24+
def contains_only(actual, expected):
25+
return len(actual) == len(expected) and set(expected).issubset(actual)
26+
27+
def test_resource_classes_created(terraform_rest_setup):
28+
resp = api_get_request("resource_class")
29+
created_resource_classes = [c["name"] for c in resp]
30+
31+
assert contains_only(created_resource_classes, ["DISK_GB", "MEMORY_MB", "VCPU"])
32+
33+
def test_resource_provider_created(terraform_rest_setup):
34+
providers = api_get_request("resource_provider")
35+
provider_names = [p["name"] for p in providers]
36+
37+
assert contains_only(provider_names, ["Test Provider"])
38+
39+
def test_accounts_created(terraform_rest_setup):
40+
accounts = api_get_request("account")
41+
account_names = [a["name"] for a in accounts]
42+
43+
assert contains_only(account_names, ["TestAccount1","TestAccount2"])
44+
45+
def test_rpas_created(terraform_rest_setup):
46+
rpas = api_get_request("resource_provider_account")
47+
accounts = api_get_request("account")
48+
rpa_account_urls = [a["account"] for a in rpas]
49+
account_urls = [a["url"] for a in accounts]
50+
assert contains_only(rpa_account_urls, account_urls)
51+
52+
def test_allocations_created(terraform_rest_setup):
53+
allocations = api_get_request("allocation")
54+
allocations_names = [a["name"] for a in allocations]
55+
56+
assert contains_only(allocations_names, ["Q1-0","Q1-1","Q2-0"])
57+
58+
def test_allocation_user_mappings(terraform_rest_setup):
59+
allocations = api_get_request("allocation")
60+
q1_allocations = [a for a in allocations if a["name"][:2] == "Q1"]
61+
q2_allocations = [a for a in allocations if a["name"][:2] == "Q2"]
62+
63+
accounts = api_get_request("account")
64+
account_urls = {a["name"]: a["url"] for a in accounts}
65+
66+
q1_allocated_accounts = [a["account"] for a in q1_allocations]
67+
q2_allocated_accounts = [a["account"] for a in q2_allocations]
68+
69+
assert contains_only(q1_allocated_accounts, [account_urls["TestAccount1"],account_urls["TestAccount2"]])
70+
assert contains_only(q2_allocated_accounts, [account_urls["TestAccount1"]])
71+
72+
def test_allocation_date_mappings(terraform_rest_setup):
73+
allocations = api_get_request("allocation")
74+
q1_allocations = [a for a in allocations if a["name"][:2] == "Q1"]
75+
q2_allocations = [a for a in allocations if a["name"][:2] == "Q2"]
76+
77+
q1_allocation_starts = [a["start"] for a in q1_allocations]
78+
q2_allocation_starts = [a["start"] for a in q2_allocations]
79+
80+
assert len(set(q1_allocation_starts)) == 1
81+
assert len(q2_allocation_starts) == 1
82+
assert q1_allocation_starts[0] != q2_allocation_starts[0]
83+
84+
def to_resource_map(allocation_resources):
85+
return {a["resource_class"]["name"] : a["resource_hours"] for a in allocation_resources}
86+
87+
def test_only_allocation_resources_returned(terraform_rest_setup):
88+
allocation_id = api_get_request("allocation")[0]["id"]
89+
assert len(api_get_request("allocation/"+str(allocation_id)+"/resources")) == 3
90+
91+
def test_resource_allocations_created(terraform_rest_setup):
92+
allocations = api_get_request("allocation")
93+
allocation_resources = {
94+
a["name"]: to_resource_map(api_get_request("allocation/"+str(a["id"])+"/resources"))
95+
for a in allocations
96+
}
97+
assert allocation_resources["Q1-0"] == {"VCPU": 40000, "MEMORY_MB": 4423680, "DISK_GB": 108000}
98+
assert allocation_resources["Q1-1"] == {"VCPU": 20000, "MEMORY_MB": 2000000, "DISK_GB": 200000}
99+
assert allocation_resources["Q2-0"] == {"VCPU": 80000, "MEMORY_MB": 8000000, "DISK_GB": 300000}

tofu/variables.tf

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
variable "coral_uri" {
2+
type = string
3+
}
4+
5+
variable "auth_token" {
6+
type = string
7+
}
8+
9+
variable "resource_provider_name" {
10+
type = string
11+
}
12+
13+
variable "resource_provider_email" {
14+
type = string
15+
}
16+
17+
variable "resource_provider_info_url" {
18+
type = string
19+
}
20+
21+
variable "allocations" {
22+
type = map(any)
23+
}
24+
25+
variable "accounts" {
26+
type = list(map(string))
27+
}
28+
29+
variable "resource_classes" {
30+
type = list(string)
31+
default = ["VCPU", "MEMORY_MB", "DISK_GB"]
32+
}

0 commit comments

Comments
 (0)