-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Matthew Casperson
committed
Jul 24, 2024
1 parent
dc6527e
commit 0c03362
Showing
4 changed files
with
422 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,294 @@ | ||
package infrastructure | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments" | ||
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds" | ||
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects" | ||
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/runbooks" | ||
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tasks" | ||
"github.com/mcasperson/OctoterraWizard/internal/octoclient" | ||
"github.com/mcasperson/OctoterraWizard/internal/state" | ||
"github.com/samber/lo" | ||
"io" | ||
"net/http" | ||
"strings" | ||
"time" | ||
) | ||
|
||
func WaitForTask(state state.State, taskId string) error { | ||
myclient, err := octoclient.CreateClient(state) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
// wait up to 10 minutes for the task to complete | ||
for i := 0; i < 600; i++ { | ||
mytasks, err := myclient.Tasks.Get(tasks.TasksQuery{ | ||
Environment: "", | ||
HasPendingInterruptions: false, | ||
HasWarningsOrErrors: false, | ||
IDs: []string{taskId}, | ||
IncludeSystem: false, | ||
IsActive: false, | ||
IsRunning: false, | ||
Name: "", | ||
Node: "", | ||
PartialName: "", | ||
Project: "", | ||
Runbook: "", | ||
Skip: 0, | ||
Spaces: nil, | ||
States: nil, | ||
Take: 1, | ||
Tenant: "", | ||
}) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
if mytasks.Items[0].IsCompleted != nil && *mytasks.Items[0].IsCompleted { | ||
if mytasks.Items[0].State != "Success" { | ||
return errors.New("task was not successful") | ||
} | ||
return nil | ||
} | ||
|
||
fmt.Println(mytasks.Items[0].State) | ||
|
||
time.Sleep(10 * time.Second) | ||
} | ||
|
||
return errors.New("task did not complete in 10 minutes") | ||
} | ||
|
||
func RunRunbook(state state.State, runbookName string, projectName string) (string, error) { | ||
myclient, err := octoclient.CreateClient(state) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
environment, err := environments.GetAll(myclient, myclient.GetSpaceID()) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
project, err := projects.GetByName(myclient, myclient.GetSpaceID(), projectName) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
runbook, err := runbooks.GetByName(myclient, myclient.GetSpaceID(), project.GetID(), runbookName) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
url := state.Server + runbook.GetLinks()["RunbookRunPreview"] | ||
url = strings.ReplaceAll(url, "{environment}", environment[0].GetID()) | ||
url = strings.ReplaceAll(url, "{?includeDisabledSteps}", "") | ||
|
||
runbookRunPreviewRequest, err := http.NewRequest("GET", url, nil) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
runbookRunPreviewResponse, err := myclient.HttpSession().DoRawRequest(runbookRunPreviewRequest) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
runbookRunPreviewRaw, err := io.ReadAll(runbookRunPreviewResponse.Body) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
runbookRunPreview := map[string]any{} | ||
err = json.Unmarshal(runbookRunPreviewRaw, &runbookRunPreview) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
runbookFormNames := lo.Map(runbookRunPreview["Form"].(map[string]any)["Elements"].([]any), func(value any, index int) any { | ||
return value.(map[string]any)["Name"] | ||
}) | ||
|
||
runbookFormValues := map[string]string{} | ||
|
||
for _, name := range runbookFormNames { | ||
runbookFormValues[name.(string)] = "dummy" | ||
} | ||
|
||
runbookBody := map[string]any{ | ||
"RunbookId": runbook.GetID(), | ||
"RunbookSnapShotId": runbook.PublishedRunbookSnapshotID, | ||
"FrozenRunbookProcessId": nil, | ||
"EnvironmentId": environment[0].GetID(), | ||
"TenantId": nil, | ||
"SkipActions": []string{}, | ||
"QueueTime": nil, | ||
"QueueTimeExpiry": nil, | ||
"FormValues": runbookFormValues, | ||
"ForcePackageDownload": false, | ||
"ForcePackageRedeployment": true, | ||
"UseGuidedFailure": false, | ||
"SpecificMachineIds": []string{}, | ||
"ExcludedMachineIds": []string{}, | ||
} | ||
|
||
runbookBodyJson, err := json.Marshal(runbookBody) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
url = state.Server + "/api/" + state.Space + "/runbookRuns" | ||
runbookRunRequest, err := http.NewRequest("POST", url, bytes.NewReader(runbookBodyJson)) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
runbookRunResponse, err := myclient.HttpSession().DoRawRequest(runbookRunRequest) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
runbookRunRaw, err := io.ReadAll(runbookRunResponse.Body) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
runbookRun := map[string]any{} | ||
err = json.Unmarshal(runbookRunRaw, &runbookRun) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return runbookRun["TaskId"].(string), nil | ||
|
||
} | ||
|
||
func PublishRunbook(state state.State, runbookName string, projectName string) error { | ||
myclient, err := octoclient.CreateClient(state) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
project, err := projects.GetByName(myclient, myclient.GetSpaceID(), projectName) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
runbook, err := runbooks.GetByName(myclient, myclient.GetSpaceID(), project.GetID(), runbookName) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
url := state.Server + runbook.GetLinks()["RunbookSnapshotTemplate"] | ||
runbookSnapshotTemplateRequest, err := http.NewRequest("GET", url, nil) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
runbookSnapshotTemplateResponse, err := myclient.HttpSession().DoRawRequest(runbookSnapshotTemplateRequest) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
runbookSnapshotTemplateRaw, err := io.ReadAll(runbookSnapshotTemplateResponse.Body) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
runbookSnapshotTemplate := map[string]any{} | ||
err = json.Unmarshal(runbookSnapshotTemplateRaw, &runbookSnapshotTemplate) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
snapshot := map[string]any{ | ||
"ProjectId": project.GetID(), | ||
"RunbookId": runbook.GetID(), | ||
"Name": runbookSnapshotTemplate["NextNameIncrement"], | ||
} | ||
|
||
var packageErrors error = nil | ||
snapshot["SelectedPackages"] = lo.Map(runbookSnapshotTemplate["Packages"].([]any), func(pkg any, index int) any { | ||
snapshotPackage := pkg.(map[string]any) | ||
versions, err := feeds.SearchPackageVersions(myclient, myclient.GetSpaceID(), snapshotPackage["FeedId"].(string), snapshotPackage["PackageId"].(string), "", 1) | ||
|
||
if err != nil { | ||
packageErrors = errors.Join(packageErrors, err) | ||
return nil | ||
} | ||
|
||
return map[string]any{ | ||
"ActionName": snapshotPackage["ActionName"], | ||
"Version": versions.Items[0].Version, | ||
"PackageReferenceName": snapshotPackage["PackageReferenceName"], | ||
} | ||
}) | ||
|
||
if packageErrors != nil { | ||
return packageErrors | ||
} | ||
|
||
snapshotJson, err := json.Marshal(snapshot) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
url = state.Server + "/api/" + state.Space + "/runbookSnapshots?publish=true" | ||
runbookSnapshotRequest, err := http.NewRequest("POST", url+"?publish=true", bytes.NewBuffer(snapshotJson)) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
runbookSnapshotResponse, err := myclient.HttpSession().DoRawRequest(runbookSnapshotRequest) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
runbookSnapshotResponseRaw, err := io.ReadAll(runbookSnapshotResponse.Body) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
runbookSnapshot := map[string]any{} | ||
err = json.Unmarshal(runbookSnapshotResponseRaw, &runbookSnapshot) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
fmt.Println(runbookSnapshot) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package steps | ||
|
||
import ( | ||
"fmt" | ||
"fyne.io/fyne/v2" | ||
"fyne.io/fyne/v2/container" | ||
"fyne.io/fyne/v2/layout" | ||
"fyne.io/fyne/v2/widget" | ||
"github.com/mcasperson/OctoterraWizard/internal/infrastructure" | ||
"github.com/mcasperson/OctoterraWizard/internal/strutil" | ||
"github.com/mcasperson/OctoterraWizard/internal/wizard" | ||
) | ||
|
||
type StartSpaceExportStep struct { | ||
BaseStep | ||
Wizard wizard.Wizard | ||
exportSpace *widget.Button | ||
} | ||
|
||
func (s StartSpaceExportStep) GetContainer(parent fyne.Window) *fyne.Container { | ||
|
||
bottom, previous, next := s.BuildNavigation(func() { | ||
s.Wizard.ShowWizardStep(ProjectExportStep{ | ||
Wizard: s.Wizard, | ||
BaseStep: BaseStep{State: s.State}}) | ||
}, func() { | ||
s.Wizard.ShowWizardStep(FinishStep{ | ||
Wizard: s.Wizard, | ||
BaseStep: BaseStep{State: s.State}}) | ||
}) | ||
next.Disable() | ||
|
||
label1 := widget.NewLabel(strutil.TrimMultilineWhitespace(` | ||
The source space is now ready to begin exporting to the destination space. | ||
We start by serializing the space level resources (feeds, accounts, tenants, certificates, targets etc) using the runbooks in the "Octoterra Space Management" project. | ||
First, we run the "__ 1. Serialize Space" runbook to create the Terraform module. | ||
Then we run the "__ 2. Deploy Space" runbook to apply the Terraform module to the destination space. | ||
Click the "Export Space" button to execute these runbooks. | ||
`)) | ||
result := widget.NewLabel("") | ||
infinite := widget.NewProgressBarInfinite() | ||
infinite.Hide() | ||
infinite.Start() | ||
s.exportSpace = widget.NewButton("Export Space", func() { | ||
s.exportSpace.Disable() | ||
next.Disable() | ||
previous.Disable() | ||
infinite.Show() | ||
defer s.exportSpace.Enable() | ||
defer previous.Enable() | ||
defer infinite.Hide() | ||
|
||
result.SetText("🔵 Running the runbooks.") | ||
|
||
if err := s.Execute(); err != nil { | ||
result.SetText(fmt.Sprintf("🔴 Failed to publish and run the runbooks: %s", err)) | ||
} else { | ||
result.SetText("🟢 Runbooks ran successfully.") | ||
next.Enable() | ||
} | ||
}) | ||
middle := container.New(layout.NewVBoxLayout(), label1, s.exportSpace, infinite, result) | ||
|
||
content := container.NewBorder(nil, bottom, nil, nil, middle) | ||
|
||
return content | ||
} | ||
|
||
func (s StartSpaceExportStep) Execute() (executeError error) { | ||
if err := infrastructure.PublishRunbook(s.State, "__ 1. Serialize Space", "Octoterra Space Management"); err != nil { | ||
return err | ||
} | ||
|
||
if taskId, err := infrastructure.RunRunbook(s.State, "__ 1. Serialize Space", "Octoterra Space Management"); err != nil { | ||
return err | ||
} else { | ||
if err := infrastructure.WaitForTask(s.State, taskId); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.