Skip to content

Commit a8a47af

Browse files
Merge pull request #707 from NHSDigital/feature/made14-NRL-863-add-version-to-env-config
[NRL-863] Add version to env config
2 parents 41505dc + f099aba commit a8a47af

File tree

4 files changed

+120
-7
lines changed

4 files changed

+120
-7
lines changed

.github/workflows/persistent-environment.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,12 @@ jobs:
232232
- name: Terraform Apply
233233
run: terraform -chdir=terraform/infrastructure apply tfplan
234234

235+
- name: Update environment config version
236+
run: |
237+
short_commit_ref="$(echo ${{ github.sha }} | cut -c1-8)"
238+
deployed_version="${{ inputs.branch_name }}@${short_commit_ref}"
239+
poetry run python ./scripts/set_env_config.py inactive-version ${deployed_version} ${{ inputs.environment }}
240+
235241
- name: Smoke Test
236242
run: |
237243
account=$(echo '${{ inputs.environment }}' | cut -d '-' -f1)

scripts/activate_stack.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,45 @@
88

99
CONFIG_LOCK_STATE = "lock-state"
1010
CONFIG_INACTIVE_STACK = "inactive-stack"
11+
CONFIG_INACTIVE_VERSION = "inactive-version"
1112
CONFIG_ACTIVE_STACK = "active-stack"
13+
CONFIG_ACTIVE_VERSION = "active-version"
1214
CONFIG_DOMAIN_NAME = "domain-name"
1315

1416
STATE_LOCKED = "locked"
1517
STATE_OPEN = "open"
1618
VALID_LOCK_STATES = [STATE_LOCKED, STATE_OPEN]
1719

1820

21+
def _parse_env_config(raw_config: str) -> dict:
22+
env_config = json.loads(raw_config)
23+
if not all(
24+
key in env_config
25+
for key in [
26+
CONFIG_LOCK_STATE,
27+
CONFIG_INACTIVE_STACK,
28+
CONFIG_INACTIVE_VERSION,
29+
CONFIG_ACTIVE_STACK,
30+
CONFIG_ACTIVE_VERSION,
31+
CONFIG_DOMAIN_NAME,
32+
]
33+
):
34+
raise ValueError(
35+
f"Environment config must contain keys: {CONFIG_LOCK_STATE}, {CONFIG_INACTIVE_STACK}, {CONFIG_INACTIVE_VERSION}, {CONFIG_ACTIVE_STACK}, {CONFIG_ACTIVE_VERSION}, {CONFIG_DOMAIN_NAME}"
36+
)
37+
38+
if env_config[CONFIG_LOCK_STATE] not in VALID_LOCK_STATES:
39+
raise ValueError(
40+
f"Invalid lock state: {env_config[CONFIG_LOCK_STATE]}. Must be one of: {VALID_LOCK_STATES}"
41+
)
42+
43+
return env_config
44+
45+
46+
def _swap_config(config: dict[str, str], key1: str, key2: str):
47+
config[key1], config[key2] = config[key2], config[key1]
48+
49+
1950
def _set_lock_state(
2051
lock_state: str, environment_config: dict, parameters_key: str, sm: any
2152
):
@@ -90,7 +121,7 @@ def activate_stack(stack_name: str, env: str, session: any):
90121
parameters_key = f"nhsd-nrlf--{env}--env-config"
91122
response = sm.get_secret_value(SecretId=parameters_key)
92123

93-
environment_config = json.loads(response["SecretString"])
124+
environment_config = _parse_env_config(response["SecretString"])
94125
print(f"Got environment config for {env}: {environment_config}")
95126

96127
current_active_stack = environment_config[CONFIG_ACTIVE_STACK]
@@ -134,8 +165,8 @@ def activate_stack(stack_name: str, env: str, session: any):
134165
sys.exit(1)
135166

136167
print("Updating environment config and unlocking....")
137-
environment_config[CONFIG_INACTIVE_STACK] = current_active_stack
138-
environment_config[CONFIG_ACTIVE_STACK] = stack_name
168+
_swap_config(environment_config, CONFIG_INACTIVE_STACK, CONFIG_ACTIVE_STACK)
169+
_swap_config(environment_config, CONFIG_INACTIVE_VERSION, CONFIG_ACTIVE_VERSION)
139170
_update_and_unlock(environment_config, parameters_key=parameters_key, sm=sm)
140171

141172
print(f"Complete. Stack {stack_name} is now the active stack for {env}")

scripts/set_env_config.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env python
2+
import json
3+
import sys
4+
5+
import fire
6+
from aws_session_assume import get_boto_session
7+
8+
9+
def main(parameter_name: str, parameter_value: str, env: str):
10+
boto_session = get_boto_session(env)
11+
sm = boto_session.client("secretsmanager")
12+
13+
secret_key = f"nhsd-nrlf--{env}--env-config"
14+
response = sm.get_secret_value(SecretId=secret_key)
15+
parameters = json.loads(response["SecretString"])
16+
17+
if parameter_name in parameters:
18+
current_value = parameters[parameter_name]
19+
if current_value == parameter_value:
20+
print(
21+
f"'{parameter_name}' already set to '{parameter_value}'. No changes made."
22+
)
23+
sys.exit(0)
24+
25+
print(
26+
f"Updating '{parameter_name}' from '{current_value}' to '{parameter_value}'...."
27+
)
28+
parameters[parameter_name] = parameter_value
29+
else:
30+
print(f"Adding '{parameter_name}' with value '{parameter_value}'....")
31+
parameters[parameter_name] = parameter_value
32+
33+
response = sm.put_secret_value(
34+
SecretId=secret_key, SecretString=json.dumps(parameters)
35+
)
36+
37+
if response["ResponseMetadata"]["HTTPStatusCode"] == 200:
38+
print("Updated successfully")
39+
else:
40+
print(f"Update failed with: {response}", file=sys.stderr)
41+
sys.exit(1)
42+
43+
44+
if __name__ == "__main__":
45+
fire.Fire(main)

scripts/tests/test_activate_stack.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import pytest
55

6-
from scripts.activate_stack import activate_stack
6+
from scripts.activate_stack import _parse_env_config, activate_stack
77

88

99
@pytest.fixture
@@ -68,7 +68,9 @@ def _create_mock_env_config():
6868
return {
6969
"lock-state": "open",
7070
"active-stack": "test-stack-1",
71+
"active-version": "v1",
7172
"inactive-stack": "test-stack-2",
73+
"inactive-version": "v2",
7274
"domain-name": "test.domain.name",
7375
}
7476

@@ -88,16 +90,17 @@ def test_happy_path(mock_boto_session, mock_secretsmanager):
8890
expected_env_config = {
8991
**mock_env_config,
9092
"active-stack": "test-stack-2",
93+
"active-version": "v2",
9194
"inactive-stack": "test-stack-1",
95+
"inactive-version": "v1",
9296
}
9397
assert result["SecretString"] == json.dumps(expected_env_config)
9498

9599

96100
def test_lock_state_not_open(mock_boto_session, mock_secretsmanager):
97101
inital_env_config = {
102+
**_create_mock_env_config(),
98103
"lock-state": "locked",
99-
"inactive-stack": "test-stack-1",
100-
"active-stack": "test-stack-2",
101104
}
102105
mock_secretsmanager.create_secret(
103106
Name="nhsd-nrlf--locked--env-config", SecretString=json.dumps(inital_env_config)
@@ -115,7 +118,7 @@ def test_lock_state_not_open(mock_boto_session, mock_secretsmanager):
115118

116119
def test_stack_already_active(mock_boto_session, mock_secretsmanager):
117120
intial_env_config = {
118-
"lock-state": "open",
121+
**_create_mock_env_config(),
119122
"inactive-stack": "test-stack-1",
120123
"active-stack": "test-stack-2",
121124
}
@@ -132,3 +135,31 @@ def test_stack_already_active(mock_boto_session, mock_secretsmanager):
132135
SecretId="nhsd-nrlf--already-active--env-config" # pragma: allowlist secret
133136
)
134137
assert result["SecretString"] == json.dumps(intial_env_config)
138+
139+
140+
def test_parse_env_config_valid():
141+
valid_config = json.dumps(_create_mock_env_config())
142+
result = _parse_env_config(valid_config)
143+
assert result == _create_mock_env_config()
144+
145+
146+
def test_parse_env_config_empty():
147+
with pytest.raises(ValueError):
148+
_parse_env_config("")
149+
150+
151+
def test_parse_env_config_invalid_json():
152+
with pytest.raises(ValueError):
153+
_parse_env_config("this is not JSON!")
154+
155+
156+
def test_parse_env_config_missing_params():
157+
with pytest.raises(ValueError):
158+
_parse_env_config(json.dumps({"lock-state": "open"}))
159+
160+
161+
def test_parse_env_config_invalid_lock_state():
162+
with pytest.raises(ValueError):
163+
_parse_env_config(
164+
json.dumps({**_create_mock_env_config(), "lock-state": "invalid"})
165+
)

0 commit comments

Comments
 (0)