Skip to content

Commit 527e949

Browse files
feat: support interactive mode for include and exclude target tags
1 parent 6e0259e commit 527e949

2 files changed

Lines changed: 147 additions & 2 deletions

File tree

pkg/cmd/release/deploy/deploy.go

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,8 +534,9 @@ func AskQuestions(octopus *octopusApiClient.Client, stdout io.Writer, asker ques
534534
isGuidedFailureModeSpecified := options.GuidedFailureMode != ""
535535
isForcePackageDownloadSpecified := options.ForcePackageDownloadWasSpecified
536536
isDeploymentTargetsSpecified := len(options.DeploymentTargets) > 0 || len(options.ExcludeTargets) > 0
537+
isDeploymentTargetTagsSpecified := len(options.SpecificTargetTagNames) > 0 || len(options.ExcludedTargetTagNames) > 0
537538

538-
allAdvancedOptionsSpecified := isDeployAtSpecified && isExcludedStepsSpecified && isGuidedFailureModeSpecified && isForcePackageDownloadSpecified && isDeploymentTargetsSpecified
539+
allAdvancedOptionsSpecified := isDeployAtSpecified && isExcludedStepsSpecified && isGuidedFailureModeSpecified && isForcePackageDownloadSpecified && isDeploymentTargetsSpecified && isDeploymentTargetTagsSpecified
539540

540541
shouldAskAdvancedQuestions := false
541542
if !allAdvancedOptionsSpecified {
@@ -635,7 +636,19 @@ func AskQuestions(octopus *octopusApiClient.Client, stdout io.Writer, asker ques
635636
}
636637
}
637638

638-
// TODO: Add support for isDeploymentTargetTagsSpecified
639+
if !isDeploymentTargetTagsSpecified {
640+
if len(deploymentEnvironmentIDs) == 0 { // if the Q&A process earlier hasn't loaded environments already, we need to load them now
641+
selectedEnvironments, err := executionscommon.FindEnvironments(octopus, options.Environments)
642+
if err != nil {
643+
return err
644+
}
645+
deploymentEnvironmentIDs = util.SliceTransform(selectedEnvironments, func(env *environments.Environment) string { return env.ID })
646+
}
647+
options.SpecificTargetTagNames, options.ExcludedTargetTagNames, err = askTargetTags(octopus, asker, space.ID, selectedRelease.ID, deploymentEnvironmentIDs)
648+
if err != nil {
649+
return err
650+
}
651+
}
639652
}
640653
// DONE
641654
return nil
@@ -829,6 +842,56 @@ func askDeploymentTargets(octopus *octopusApiClient.Client, asker question.Asker
829842
return nil, nil
830843
}
831844

845+
func askTargetTags(octopus *octopusApiClient.Client, asker question.Asker, spaceID string, releaseID string, deploymentEnvironmentIDs []string) ([]string, []string, error) {
846+
var results []string
847+
848+
// collect all available target tags from deployment previews across all environments
849+
for _, envID := range deploymentEnvironmentIDs {
850+
preview, err := deployments.GetReleaseDeploymentPreview(octopus, spaceID, releaseID, envID, true)
851+
if err != nil {
852+
return nil, nil, err
853+
}
854+
for _, step := range preview.StepsToExecute {
855+
for _, tagSet := range step.AvailableTagSets {
856+
for _, tag := range tagSet.AvailableTags {
857+
canonicalName := tagSet.TagSetName + "/" + tag.TagName
858+
if !util.SliceContains(results, canonicalName) {
859+
results = append(results, canonicalName)
860+
}
861+
}
862+
}
863+
}
864+
}
865+
866+
if len(results) == 0 {
867+
return nil, nil, nil
868+
}
869+
870+
sort.Strings(results)
871+
872+
var selectedSpecificTags []string
873+
err := asker(&survey.MultiSelect{
874+
Message: "Specific target tags to include (If none selected, include all)",
875+
Options: results,
876+
}, &selectedSpecificTags)
877+
if err != nil {
878+
return nil, nil, err
879+
}
880+
881+
var selectedExcludedTags []string
882+
if len(selectedSpecificTags) == 0 {
883+
err = asker(&survey.MultiSelect{
884+
Message: "Target tags to exclude (If none selected, exclude none)",
885+
Options: results,
886+
}, &selectedExcludedTags)
887+
if err != nil {
888+
return nil, nil, err
889+
}
890+
}
891+
892+
return selectedSpecificTags, selectedExcludedTags, nil
893+
}
894+
832895
func askDeploymentPreviewVariables(octopus *octopusApiClient.Client, variablesFromCmd map[string]string, asker question.Asker, spaceID string, releaseID string, deploymentPreviewsReqests []deployments.DeploymentPreviewRequest) (map[string]string, error) {
833896
previews, err := deployments.GetReleaseDeploymentPreviews(octopus, spaceID, releaseID, deploymentPreviewsReqests, true)
834897
if err != nil {

pkg/cmd/release/deploy/deploy_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,6 +1517,88 @@ func TestDeployCreate_AskQuestions(t *testing.T) {
15171517
ScheduledExpiryTime: "2022-09-08T13:31:03+08:00",
15181518
}, options)
15191519
}},
1520+
1521+
{"target tags with specific and excluded tags", func(t *testing.T, api *testutil.MockHttpServer, qa *testutil.AskMocker, stdout *bytes.Buffer) {
1522+
options := &executor.TaskOptionsDeployRelease{
1523+
ProjectName: "fire project",
1524+
ReleaseVersion: "1.9",
1525+
Environments: []string{"dev"},
1526+
ExcludedSteps: []string{"Cleanup"},
1527+
GuidedFailureMode: "false",
1528+
ForcePackageDownloadWasSpecified: true,
1529+
DeploymentTargets: []string{"vm-1"},
1530+
ScheduledStartTime: "now",
1531+
}
1532+
1533+
errReceiver := testutil.GoBegin(func() error {
1534+
defer testutil.Close(api, qa)
1535+
octopus, _ := octopusApiClient.NewClient(testutil.NewMockHttpClientWithTransport(api), serverUrl, placeholderApiKey, "")
1536+
return deploy.AskQuestions(octopus, stdout, qa.AsAsker(), space1, options, now)
1537+
})
1538+
1539+
doStandardApiResponses(options, api, release19, variableSnapshotNoVars)
1540+
stdout.Reset()
1541+
1542+
_ = qa.ExpectQuestion(t, &survey.Select{
1543+
Message: "Change additional options?",
1544+
Options: []string{"Proceed to deploy", "Change"},
1545+
}).AnswerWith("Change")
1546+
stdout.Reset()
1547+
1548+
api.ExpectRequest(t, "GET", fmt.Sprintf("/api/Spaces-1/releases/%s/deployments/preview/%s?includeDisabledSteps=true", release19.ID, devEnvironment.ID)).RespondWith(&deployments.DeploymentPreview{
1549+
StepsToExecute: []*deployments.DeploymentTemplateStep{
1550+
{
1551+
AvailableTagSets: []*deployments.TagSetPreview{
1552+
{
1553+
TagSetName: "Role",
1554+
AvailableTags: []*deployments.TargetTagPreview{
1555+
{TagName: "WebServer"},
1556+
{TagName: "Database"},
1557+
{TagName: "Legacy"},
1558+
},
1559+
},
1560+
{
1561+
TagSetName: "Environment",
1562+
AvailableTags: []*deployments.TargetTagPreview{
1563+
{TagName: "Production"},
1564+
{TagName: "Staging"},
1565+
},
1566+
},
1567+
},
1568+
},
1569+
},
1570+
})
1571+
1572+
_ = qa.ExpectQuestion(t, &survey.MultiSelect{
1573+
Message: "Specific target tags to include (If none selected, include all)",
1574+
Options: []string{"Environment/Production", "Environment/Staging", "Role/Database", "Role/Legacy", "Role/WebServer"},
1575+
}).AnswerWith([]string{"Role/WebServer", "Environment/Production"})
1576+
1577+
_ = qa.ExpectQuestion(t, &survey.MultiSelect{
1578+
Message: "Target tags to exclude (If none selected, exclude none)",
1579+
Options: []string{"Environment/Production", "Environment/Staging", "Role/Database", "Role/Legacy", "Role/WebServer"},
1580+
}).AnswerWith([]string{"Role/Legacy"})
1581+
1582+
err := <-errReceiver
1583+
assert.Nil(t, err)
1584+
1585+
// check that the question-asking process has filled out the things we told it to
1586+
assert.Equal(t, &executor.TaskOptionsDeployRelease{
1587+
ProjectName: "Fire Project",
1588+
ReleaseVersion: "1.9",
1589+
Environments: []string{"dev"},
1590+
GuidedFailureMode: "false",
1591+
ForcePackageDownload: false,
1592+
ForcePackageDownloadWasSpecified: true,
1593+
Variables: make(map[string]string, 0),
1594+
ExcludedSteps: []string{"Cleanup"},
1595+
DeploymentTargets: []string{"vm-1"},
1596+
SpecificTargetTagNames: []string{"Role/WebServer", "Environment/Production"},
1597+
ExcludedTargetTagNames: []string{"Role/Legacy"},
1598+
ReleaseID: release19.ID,
1599+
ScheduledStartTime: "now",
1600+
}, options)
1601+
}},
15201602
}
15211603

15221604
for _, test := range tests {

0 commit comments

Comments
 (0)