Skip to content

Commit 6972be8

Browse files
committed
Added the blue/green project sample
1 parent 0caf63f commit 6972be8

File tree

5 files changed

+153
-26
lines changed

5 files changed

+153
-26
lines changed

context/vmbluegreen.tf

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,28 @@ resource "octopusdeploy_environment" "environment_production" {
285285
}
286286
}
287287

288+
data "octopusdeploy_community_step_template" "communitysteptemplate_octopus___check_targets_available" {
289+
website = "https://library.octopus.com/step-templates/81444e7f-d77a-47db-b287-0f1ab5793880"
290+
}
291+
data "octopusdeploy_step_template" "steptemplate_octopus___check_targets_available" {
292+
name = "Octopus - Check Targets Available"
293+
}
294+
resource "octopusdeploy_community_step_template" "communitysteptemplate_octopus___check_targets_available" {
295+
community_action_template_id = "${data.octopusdeploy_community_step_template.communitysteptemplate_octopus___check_targets_available.steps[0].id}"
296+
count = "${data.octopusdeploy_step_template.steptemplate_octopus___check_targets_available.step_template != null ? 0 : 1}"
297+
}
298+
299+
data "octopusdeploy_community_step_template" "communitysteptemplate_octopus___check_smtp_server_configured" {
300+
website = "https://library.octopus.com/step-templates/ad8126be-37af-4297-b46e-fce02ba3987a"
301+
}
302+
data "octopusdeploy_step_template" "steptemplate_octopus___check_smtp_server_configured" {
303+
name = "Octopus - Check SMTP Server Configured"
304+
}
305+
resource "octopusdeploy_community_step_template" "communitysteptemplate_octopus___check_smtp_server_configured" {
306+
community_action_template_id = "${data.octopusdeploy_community_step_template.communitysteptemplate_octopus___check_smtp_server_configured.steps[0].id}"
307+
count = "${data.octopusdeploy_step_template.steptemplate_octopus___check_smtp_server_configured.step_template != null ? 0 : 1}"
308+
}
309+
288310
resource "octopusdeploy_process" "process_random_quotes__net_iis" {
289311
count = "${length(data.octopusdeploy_projects.project_random_quotes__net_iis.projects) != 0 ? 0 : 1}"
290312
project_id = "${length(data.octopusdeploy_projects.project_random_quotes__net_iis.projects) != 0 ? data.octopusdeploy_projects.project_random_quotes__net_iis.projects[0].id : octopusdeploy_project.project_random_quotes__net_iis[0].id}"
@@ -308,8 +330,8 @@ resource "octopusdeploy_process_step" "process_step_random_quotes__net_iis_appro
308330
properties = {
309331
}
310332
execution_properties = {
311-
"Octopus.Action.Manual.BlockConcurrentDeployments" = "False"
312333
"Octopus.Action.Manual.Instructions" = "Do you approve the production deployment?"
334+
"Octopus.Action.Manual.BlockConcurrentDeployments" = "False"
313335
"Octopus.Action.RunOnServer" = "true"
314336
}
315337
}
@@ -318,8 +340,8 @@ resource "octopusdeploy_process_templated_step" "process_step_random_quotes__net
318340
count = "${length(data.octopusdeploy_projects.project_random_quotes__net_iis.projects) != 0 ? 0 : 1}"
319341
name = "Octopus - Check Targets Available"
320342
process_id = "${length(data.octopusdeploy_projects.project_random_quotes__net_iis.projects) != 0 ? null : octopusdeploy_process.process_random_quotes__net_iis[0].id}"
321-
template_id = ""
322-
template_version = ""
343+
template_id = "${data.octopusdeploy_step_template.steptemplate_octopus___check_targets_available.step_template != null ? data.octopusdeploy_step_template.steptemplate_octopus___check_targets_available.step_template.id : octopusdeploy_community_step_template.communitysteptemplate_octopus___check_targets_available[0].id}"
344+
template_version = "${data.octopusdeploy_step_template.steptemplate_octopus___check_targets_available.step_template != null ? data.octopusdeploy_step_template.steptemplate_octopus___check_targets_available.step_template.version : octopusdeploy_community_step_template.communitysteptemplate_octopus___check_targets_available[0].version}"
323345
channels = null
324346
condition = "Success"
325347
environments = null
@@ -334,8 +356,8 @@ resource "octopusdeploy_process_templated_step" "process_step_random_quotes__net
334356
}
335357
execution_properties = {
336358
"OctopusUseBundledTooling" = "False"
337-
"Octopus.Action.Script.ScriptSource" = "Inline"
338359
"Octopus.Action.Script.Syntax" = "PowerShell"
360+
"Octopus.Action.Script.ScriptSource" = "Inline"
339361
"Octopus.Action.RunOnServer" = "true"
340362
"Octopus.Action.Script.ScriptBody" = "$errorCollection = @()\n$setupValid = $false\n\nWrite-Host \"Checking for deployment targets ...\"\n\ntry\n{\n # Check to make sure targets have been created\n if ([string]::IsNullOrWhitespace(\"#{Octopus.Web.ServerUri}\"))\n {\n $octopusUrl = \"#{Octopus.Web.BaseUrl}\"\n }\n else\n {\n $octopusUrl = \"#{Octopus.Web.ServerUri}\"\n }\n\n $apiKey = \"#{CheckTargets.Octopus.Api.Key}\"\n $role = \"#{CheckTargets.Octopus.Role}\"\n $message = \"#{CheckTargets.Message}\"\n\n if (![string]::IsNullOrWhitespace($apiKey) -and $apiKey.StartsWith(\"API-\"))\n {\n $spaceId = \"#{Octopus.Space.Id}\"\n $headers = @{ \"X-Octopus-ApiKey\" = \"$apiKey\" }\n\n try\n {\n $roleTargets = Invoke-RestMethod -Method Get -Uri \"$octopusUrl/api/$spaceId/machines?roles=$role\" -Headers $headers\n if ($roleTargets.Items.Count -lt 1)\n {\n $errorCollection += @(\"Expected at least 1 target for tag $role, but found $( $roleTargets.Items.Count ). $message\")\n }\n }\n catch\n {\n $errorCollection += @(\"Failed to retrieve role targets: $( $_.Exception.Message )\")\n }\n\n if ($errorCollection.Count -gt 0)\n {\n foreach ($item in $errorCollection)\n {\n Write-Highlight \"$item\"\n }\n }\n else\n {\n $setupValid = $true\n Write-Host \"Setup valid!\"\n }\n }\n else\n {\n Write-Highlight \"The project variable CheckTargets.Octopus.Api.Key has not been configured, unable to check deployment targets.\"\n }\n\n Set-OctopusVariable -Name SetupValid -Value $setupValid\n} catch {\n Write-Verbose \"Fatal error occurred:\"\n Write-Verbose \"$($_.Exception.Message)\"\n}"
341363
}
@@ -345,8 +367,8 @@ resource "octopusdeploy_process_templated_step" "process_step_random_quotes__net
345367
count = "${length(data.octopusdeploy_projects.project_random_quotes__net_iis.projects) != 0 ? 0 : 1}"
346368
name = "Octopus - Check SMTP Server Configured"
347369
process_id = "${length(data.octopusdeploy_projects.project_random_quotes__net_iis.projects) != 0 ? null : octopusdeploy_process.process_random_quotes__net_iis[0].id}"
348-
template_id = ""
349-
template_version = ""
370+
template_id = "${data.octopusdeploy_step_template.steptemplate_octopus___check_smtp_server_configured.step_template != null ? data.octopusdeploy_step_template.steptemplate_octopus___check_smtp_server_configured.step_template.id : octopusdeploy_community_step_template.communitysteptemplate_octopus___check_smtp_server_configured[0].id}"
371+
template_version = "${data.octopusdeploy_step_template.steptemplate_octopus___check_smtp_server_configured.step_template != null ? data.octopusdeploy_step_template.steptemplate_octopus___check_smtp_server_configured.step_template.version : octopusdeploy_community_step_template.communitysteptemplate_octopus___check_smtp_server_configured[0].version}"
350372
channels = null
351373
condition = "Success"
352374
environments = null
@@ -360,10 +382,10 @@ resource "octopusdeploy_process_templated_step" "process_step_random_quotes__net
360382
properties = {
361383
}
362384
execution_properties = {
363-
"Octopus.Action.Script.Syntax" = "PowerShell"
364-
"Octopus.Action.Script.ScriptBody" = "$apiKey = \"#{SmtpCheck.Octopus.Api.Key}\"\n$isSmtpConfigured = $false\n\nif (![string]::IsNullOrWhitespace($apiKey) -and $apiKey.StartsWith(\"API-\"))\n{\n if ([String]::IsNullOrWhitespace(\"#{Octopus.Web.ServerUri}\"))\n {\n $octopusUrl = \"#{Octopus.Web.BaseUrl}\"\n }\n else\n {\n $octopusUrl = \"#{Octopus.Web.ServerUri}\"\n }\n\n $uriBuilder = New-Object System.UriBuilder(\"$octopusUrl/api/smtpconfiguration/isconfigured\")\n $uri = $uriBuilder.ToString()\n\n try\n {\n $headers = @{ \"X-Octopus-ApiKey\" = $apiKey }\n $smtpConfigured = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers\n $isSmtpConfigured = $smtpConfigured.IsConfigured\n }\n catch\n {\n Write-Host \"Error checking SMTP configuration: $($_.Exception.Message)\"\n }\n}\nelse\n{\n Write-Highlight \"The project variable SmtpCheck.Octopus.Api.Key has not been configured, unable to check SMTP configuration.\"\n}\n\nif (-not $isSmtpConfigured)\n{\n Write-Highlight \"SMTP is not configured. Please [configure SMTP](https://octopus.com/docs/projects/built-in-step-templates/email-notifications#smtp-configuration) settings in Octopus Deploy.\"\n}\n\nSet-OctopusVariable -Name SmtpConfigured -Value $isSmtpConfigured"
365385
"Octopus.Action.RunOnServer" = "true"
366386
"OctopusUseBundledTooling" = "False"
387+
"Octopus.Action.Script.Syntax" = "PowerShell"
388+
"Octopus.Action.Script.ScriptBody" = "$apiKey = \"#{SmtpCheck.Octopus.Api.Key}\"\n$isSmtpConfigured = $false\n\nif (![string]::IsNullOrWhitespace($apiKey) -and $apiKey.StartsWith(\"API-\"))\n{\n if ([String]::IsNullOrWhitespace(\"#{Octopus.Web.ServerUri}\"))\n {\n $octopusUrl = \"#{Octopus.Web.BaseUrl}\"\n }\n else\n {\n $octopusUrl = \"#{Octopus.Web.ServerUri}\"\n }\n\n $uriBuilder = New-Object System.UriBuilder(\"$octopusUrl/api/smtpconfiguration/isconfigured\")\n $uri = $uriBuilder.ToString()\n\n try\n {\n $headers = @{ \"X-Octopus-ApiKey\" = $apiKey }\n $smtpConfigured = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers\n $isSmtpConfigured = $smtpConfigured.IsConfigured\n }\n catch\n {\n Write-Host \"Error checking SMTP configuration: $($_.Exception.Message)\"\n }\n}\nelse\n{\n Write-Highlight \"The project variable SmtpCheck.Octopus.Api.Key has not been configured, unable to check SMTP configuration.\"\n}\n\nif (-not $isSmtpConfigured)\n{\n Write-Highlight \"SMTP is not configured. Please [configure SMTP](https://octopus.com/docs/projects/built-in-step-templates/email-notifications#smtp-configuration) settings in Octopus Deploy.\"\n}\n\nSet-OctopusVariable -Name SmtpConfigured -Value $isSmtpConfigured"
367389
"Octopus.Action.Script.ScriptSource" = "Inline"
368390
}
369391
}

domain/requests/github/copilot_request_context.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@
190190
from domain.tools.wrapper.projects.create_terraform_project import (
191191
create_terraform_project_wrapper,
192192
)
193+
from domain.tools.wrapper.projects.create_vm_bluegreen_project import (
194+
create_vm_blue_green_project_wrapper,
195+
)
193196
from domain.tools.wrapper.projects.create_winservice_project import (
194197
create_winservice_project_wrapper,
195198
)
@@ -1373,6 +1376,33 @@ def build_form_tools(query, req: func.HttpRequest):
13731376
get_redirections_api_key(req),
13741377
),
13751378
),
1379+
FunctionDefinition(
1380+
create_vm_blue_green_project_wrapper(
1381+
query,
1382+
callback=create_template_project_callback(
1383+
lambda: get_api_key_and_url(req),
1384+
get_github_user_from_form(req),
1385+
get_functions_connection_string(),
1386+
log_query,
1387+
general_project_examples,
1388+
"vmbluegreen.tf",
1389+
"VM Blue/Green",
1390+
"generalinstructions.txt",
1391+
"vmbluegreensystemprompt.txt",
1392+
get_redirections(req),
1393+
get_redirections_api_key(req),
1394+
),
1395+
logging=log_query,
1396+
),
1397+
callback=create_template_project_confirm_callback_wrapper(
1398+
query,
1399+
get_github_user_from_form(req),
1400+
lambda: get_api_key_and_url(req),
1401+
log_query,
1402+
get_redirections(req),
1403+
get_redirections_api_key(req),
1404+
),
1405+
),
13761406
],
13771407
fallback=FunctionDefinitions(docs_functions),
13781408
invalid=FunctionDefinition(
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
def create_vm_blue_green_project_wrapper(query, callback, logging):
2+
def create_vm_blue_green_project(
3+
space_name=None,
4+
project_name=None,
5+
no_prompt=False,
6+
**kwargs,
7+
):
8+
"""
9+
Creates a Virtual Machine project with a blue-green deployment strategy in Octopus Deploy, in addition to any supporting resources.
10+
11+
Example prompts include:
12+
* Create a VM Blue/Green project in the space "My Space" called "My Project"
13+
* Create a VM project with the Blue/Green deployment strategy called "My Project" in the space "My Space"
14+
* Create a VM Blue Green project called "My Project"
15+
16+
Args:
17+
space_name: The name of the space
18+
project_name: The name of the project
19+
no_prompt: Weather to disable the prompt. Defaults to False.
20+
"""
21+
22+
if logging:
23+
logging("Enter:", create_vm_blue_green_project.__name__)
24+
25+
for key, value in kwargs.items():
26+
if logging:
27+
logging(f"Unexpected Key: {key}", "Value: {value}")
28+
29+
# This is just a passthrough to the original callback
30+
return callback(
31+
create_vm_blue_green_project.__name__,
32+
query,
33+
space_name,
34+
project_name,
35+
no_prompt,
36+
)
37+
38+
return create_vm_blue_green_project

infrastructure/octopus.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,23 @@ def get_space(space_id, api_key, octopus_url):
12221222
return resp.json()
12231223

12241224

1225+
@retry(HTTPError, tries=3, delay=2)
1226+
@logging_wrapper
1227+
def sync_community_step_templates(api_key, octopus_url):
1228+
base_url = f"api/tasks"
1229+
body = {
1230+
"Name": "SyncCommunityActionTemplates",
1231+
"Description": "Synchronize Community Step Templates",
1232+
"Arguments": {},
1233+
"SpaceId": None,
1234+
}
1235+
api, headers = build_url(octopus_url, api_key, base_url)
1236+
resp = handle_response(
1237+
lambda: http.request("POST", api, json=body, headers=headers)
1238+
)
1239+
return resp.json()
1240+
1241+
12251242
@retry(HTTPError, tries=3, delay=2)
12261243
@logging_wrapper
12271244
def get_project(space_id, project_name, api_key, octopus_url):

tests/application/test_copilot_chat_create_projects.py

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,32 @@
44
import re
55
import time
66
import unittest
7-
import uuid
8-
from datetime import datetime
97

108
import Levenshtein
119
import azure.functions as func
1210
from openai import RateLimitError
13-
from requests.exceptions import HTTPError
11+
1412
from retry import retry
1513
from testcontainers.core.container import DockerContainer
1614
from testcontainers.core.waiting_utils import wait_for_logs
1715

18-
from domain.lookup.octopus_lookups import (
19-
lookup_space,
20-
lookup_projects,
21-
lookup_environments,
22-
lookup_tenants,
23-
lookup_runbooks,
24-
)
16+
2517
from domain.transformers.sse_transformers import (
2618
convert_from_sse_response,
2719
get_confirmation_id,
2820
)
29-
from domain.url.session import create_session_blob
30-
from function_app import copilot_handler_internal, health_internal
21+
from function_app import copilot_handler_internal
3122
from infrastructure.octopus import (
32-
run_published_runbook_fuzzy,
3323
get_space_id_and_name_from_name,
3424
get_project,
3525
get_runbook_fuzzy,
3626
get_raw_deployment_process,
3727
get_tenants,
28+
sync_community_step_templates,
3829
)
3930
from infrastructure.terraform_context import save_terraform_context
4031
from infrastructure.users import save_users_octopus_url_from_login, save_default_values
41-
from tests.infrastructure.create_and_deploy_release import (
42-
create_and_deploy_release,
43-
wait_for_task,
44-
)
4532
from tests.infrastructure.octopus_config import Octopus_Api_Key, Octopus_Url
46-
from tests.infrastructure.publish_runbook import publish_runbook
4733
from tests.infrastructure.test_octopus_infrastructure import run_terraform
4834

4935

@@ -155,6 +141,8 @@ def setUpClass(cls):
155141
cls.octopus, "Web server is ready to process requests", timeout=300
156142
)
157143

144+
sync_community_step_templates(Octopus_Api_Key, Octopus_Url)
145+
158146
output = run_terraform(
159147
terraform_dir + "simple/space_creation", Octopus_Url, Octopus_Api_Key
160148
)
@@ -261,6 +249,38 @@ def test_create_k8s_project_no_prompt(self):
261249
f"The following resources were created:" in response_text,
262250
)
263251

252+
@retry((AssertionError, RateLimitError), tries=3, delay=2)
253+
def test_create_vm_blue_green_project(self):
254+
project_name = "My VM Blue Green project"
255+
prompt = f'Create a VM Blue/Green project called "{project_name}".'
256+
response = copilot_handler_internal(build_request(prompt))
257+
confirmation_id = get_confirmation_id(response.get_body().decode("utf8"))
258+
self.assertTrue(confirmation_id != "", "Confirmation ID was " + confirmation_id)
259+
260+
confirmation = {
261+
"messages": [
262+
{
263+
"role": "user",
264+
"content": "",
265+
"copilot_references": None,
266+
"copilot_confirmations": [
267+
{"state": "accepted", "confirmation": {"id": confirmation_id}}
268+
],
269+
}
270+
]
271+
}
272+
273+
run_response = copilot_handler_internal(
274+
build_confirmation_request(confirmation)
275+
)
276+
response_text = convert_from_sse_response(
277+
run_response.get_body().decode("utf8")
278+
)
279+
print(response_text)
280+
self.assertTrue(
281+
f"The following resources were created:" in response_text,
282+
)
283+
264284
@retry((AssertionError, RateLimitError), tries=2, delay=2)
265285
def test_create_azure_web_app_project(self):
266286
project_name = "My Azure WebApp Project"

0 commit comments

Comments
 (0)