Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deployment level #1007

Merged
merged 3 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions tooling/templatize/internal/end2end/e2e.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func newE2E(tmpdir string) e2eImpl {
return imp
}

func (e *e2eImpl) UseRandomRG() func() error {
func GenerateRandomRGName() string {
rgSuffx := ""
if jobID := os.Getenv("JOB_ID"); jobID != "" {
rgSuffx = jobID
Expand All @@ -99,8 +99,11 @@ func (e *e2eImpl) UseRandomRG() func() error {
for i := 0; i < 3; i++ {
rgSuffx += string(chars[rand.IntN(len(chars))])
}
return "templatize-e2e-" + rgSuffx
}

e.rgName = "templatize-e2e-" + rgSuffx
func (e *e2eImpl) UseRandomRG() func() error {
e.rgName = GenerateRandomRGName()
e.SetConfig(config.Variables{"defaults": config.Variables{"rg": e.rgName}})

return func() error {
Expand Down
38 changes: 38 additions & 0 deletions tooling/templatize/internal/end2end/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package testutil

import (
"context"
"fmt"
"os"
"path/filepath"
"testing"
Expand All @@ -14,6 +15,7 @@ import (

"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
)

func persistAndRun(t *testing.T, e2eImpl E2E) {
Expand Down Expand Up @@ -285,3 +287,39 @@ param parameterA = 'Hello Bicep'`,
assert.NilError(t, err)
assert.Equal(t, string(io), "Hello Bicep\n")
}

func TestE2EArmDeploySubscriptionScope(t *testing.T) {
if !shouldRunE2E() {
t.Skip("Skipping end-to-end tests")
}

tmpDir := t.TempDir()

e2eImpl := newE2E(tmpDir)
e2eImpl.AddStep(pipeline.NewARMStep("parameterA", "testa.bicep", "testa.bicepparm").WithDeploymentLevel("Subscription"), 0)
rgName := GenerateRandomRGName()
e2eImpl.AddBicepTemplate(fmt.Sprintf(`
targetScope='subscription'

resource newRG 'Microsoft.Resources/resourceGroups@2024-03-01' = {
name: '%s'
location: 'westus3'
}`, rgName),
"testa.bicep",
"using 'testa.bicep'",
"testa.bicepparm")

persistAndRun(t, &e2eImpl)

subsriptionID, err := pipeline.LookupSubscriptionID(context.Background(), "ARO Hosted Control Planes (EA Subscription 1)")
assert.NilError(t, err)

cred, err := azidentity.NewDefaultAzureCredential(nil)
assert.NilError(t, err)

rgClient, err := armresources.NewResourceGroupsClient(subsriptionID, cred, nil)
assert.NilError(t, err)

_, err = rgClient.BeginDelete(context.Background(), rgName, nil)
assert.NilError(t, err)
}
154 changes: 106 additions & 48 deletions tooling/templatize/pkg/pipeline/arm.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/Azure/ARO-HCP/tooling/templatize/pkg/config"

"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
Expand Down Expand Up @@ -59,6 +60,7 @@ func recursivePrint(level int, change *armresources.WhatIfPropertyChange) {
recursivePrint(level, child)
}
}

func printChanges(t armresources.ChangeType, changes []*armresources.WhatIfChange) {
for _, change := range changes {
if *change.ChangeType == t {
Expand All @@ -69,8 +71,49 @@ func printChanges(t armresources.ChangeType, changes []*armresources.WhatIfChang
}
}
}
func doDryRun(ctx context.Context, client *armresources.DeploymentsClient, rgName string, step *ARMStep, vars config.Variables, input map[string]output) (output, error) {

func printChangeReport(changes []*armresources.WhatIfChange) {
fmt.Println("Change report for WhatIf deployment")
fmt.Println("----------")
fmt.Println("Creating")
printChanges(armresources.ChangeTypeCreate, changes)
fmt.Println("----------")
fmt.Println("Deploy")
printChanges(armresources.ChangeTypeDeploy, changes)
fmt.Println("----------")
fmt.Println("Modify")
printChanges(armresources.ChangeTypeModify, changes)
fmt.Println("----------")
fmt.Println("Delete")
printChanges(armresources.ChangeTypeDelete, changes)
fmt.Println("----------")
fmt.Println("Ignoring")
printChanges(armresources.ChangeTypeIgnore, changes)
fmt.Println("----------")
fmt.Println("NoChange")
printChanges(armresources.ChangeTypeNoChange, changes)
fmt.Println("----------")
fmt.Println("Unsupported")
printChanges(armresources.ChangeTypeUnsupported, changes)
}

func pollAndPrint[T any](ctx context.Context, p *runtime.Poller[T]) error {
resp, err := p.PollUntilDone(ctx, nil)
if err != nil {
return fmt.Errorf("failed to wait for deployment completion: %w", err)
}
switch m := any(resp).(type) {
case armresources.DeploymentsClientWhatIfResponse:
printChangeReport(m.Properties.Changes)
case armresources.DeploymentsClientWhatIfAtSubscriptionScopeResponse:
printChangeReport(m.Properties.Changes)
default:
return fmt.Errorf("Unknown type %T", m)
}
return nil
}

func doDryRun(ctx context.Context, client *armresources.DeploymentsClient, rgName string, step *ARMStep, vars config.Variables, input map[string]output) (output, error) {
logger := logr.FromContextOrDiscard(ctx)

inputValues, err := getInputValues(step.Variables, input)
Expand All @@ -88,41 +131,59 @@ func doDryRun(ctx context.Context, client *armresources.DeploymentsClient, rgNam
Properties: deploymentProperties,
}

poller, err := client.BeginWhatIf(ctx, rgName, step.Name, deployment, nil)
if err != nil {
return nil, fmt.Errorf("failed to create WhatIf Deployment: %w", err)
if step.DeploymentLevel == "Subscription" {
// Hardcode until schema is adapted
deployment.Location = to.Ptr("eastus2")
poller, err := client.BeginWhatIfAtSubscriptionScope(ctx, step.Name, deployment, nil)
if err != nil {
return nil, fmt.Errorf("failed to create WhatIf Deployment: %w", err)
}
logger.Info("WhatIf Deployment started", "deployment", step.Name)
err = pollAndPrint(ctx, poller)
if err != nil {
return nil, fmt.Errorf("failed to poll and print: %w", err)
}
} else {
poller, err := client.BeginWhatIf(ctx, rgName, step.Name, deployment, nil)
if err != nil {
return nil, fmt.Errorf("failed to create WhatIf Deployment: %w", err)
}
logger.Info("WhatIf Deployment started", "deployment", step.Name)
err = pollAndPrint(ctx, poller)
if err != nil {
return nil, fmt.Errorf("failed to poll and print: %w", err)
}
}
logger.Info("WhatIf Deployment started", "deployment", step.Name)

resp, err := poller.PollUntilDone(ctx, nil)
return nil, nil
}

func pollAndGetOutput[T any](ctx context.Context, p *runtime.Poller[T]) (armOutput, error) {
respRaw, err := p.PollUntilDone(ctx, nil)
if err != nil {
return nil, fmt.Errorf("failed to wait for deployment completion: %w", err)
}
logger.Info("WhatIf Deployment finished successfully", "deployment", step.Name)

fmt.Println("Change report for WhatIf deployment")
fmt.Println("----------")
fmt.Println("Creating")
printChanges(armresources.ChangeTypeCreate, resp.Properties.Changes)
fmt.Println("----------")
fmt.Println("Deploy")
printChanges(armresources.ChangeTypeDeploy, resp.Properties.Changes)
fmt.Println("----------")
fmt.Println("Modify")
printChanges(armresources.ChangeTypeModify, resp.Properties.Changes)
fmt.Println("----------")
fmt.Println("Delete")
printChanges(armresources.ChangeTypeDelete, resp.Properties.Changes)
fmt.Println("----------")
fmt.Println("Ignoring")
printChanges(armresources.ChangeTypeIgnore, resp.Properties.Changes)
fmt.Println("----------")
fmt.Println("NoChange")
printChanges(armresources.ChangeTypeNoChange, resp.Properties.Changes)
fmt.Println("----------")
fmt.Println("Unsupported")
printChanges(armresources.ChangeTypeUnsupported, resp.Properties.Changes)
var outputs any

switch resp := any(respRaw).(type) {
case armresources.DeploymentsClientCreateOrUpdateResponse:
outputs = resp.Properties.Outputs
case armresources.DeploymentsClientCreateOrUpdateAtSubscriptionScopeResponse:
outputs = resp.Properties.Outputs
default:
return nil, fmt.Errorf("Unknown type %T", resp)
}

if outputs != nil {
if outputMap, ok := outputs.(map[string]any); ok {
returnMap := armOutput{}
for k, v := range outputMap {
returnMap[k] = v
}
return returnMap, nil
}
}
return nil, nil
}

Expand All @@ -144,28 +205,25 @@ func doWaitForDeployment(ctx context.Context, client *armresources.DeploymentsCl
Properties: deploymentProperties,
}

poller, err := client.BeginCreateOrUpdate(ctx, rgName, step.Name, deployment, nil)
if err != nil {
return nil, fmt.Errorf("failed to create deployment: %w", err)
}
logger.Info("Deployment started", "deployment", step.Name)

resp, err := poller.PollUntilDone(ctx, nil)
if err != nil {
return nil, fmt.Errorf("failed to wait for deployment completion: %w", err)
}
logger.Info("Deployment finished successfully", "deployment", step.Name, "responseId", *resp.ID)
if step.DeploymentLevel == "Subscription" {
// Hardcode until schema is adapted
deployment.Location = to.Ptr("eastus2")
poller, err := client.BeginCreateOrUpdateAtSubscriptionScope(ctx, step.Name, deployment, nil)
if err != nil {
return nil, fmt.Errorf("failed to create deployment: %w", err)
}
logger.Info("Deployment started", "deployment", step.Name)

if resp.Properties.Outputs != nil {
if outputMap, ok := resp.Properties.Outputs.(map[string]any); ok {
returnMap := armOutput{}
for k, v := range outputMap {
returnMap[k] = v
}
return returnMap, nil
return pollAndGetOutput(ctx, poller)
} else {
poller, err := client.BeginCreateOrUpdate(ctx, rgName, step.Name, deployment, nil)
if err != nil {
return nil, fmt.Errorf("failed to create deployment: %w", err)
}
logger.Info("Deployment started", "deployment", step.Name)

return pollAndGetOutput(ctx, poller)
}
return nil, nil
}

func (a *armClient) ensureResourceGroupExists(ctx context.Context, rgName string, rgNoPersist bool) error {
Expand Down
Loading