tooling/templatize/internal/end2end/e2e.go (185 lines of code) (raw):
// Copyright 2025 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package testutil
import (
"context"
"fmt"
"os"
"math/rand/v2"
"gopkg.in/yaml.v2"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
"github.com/Azure/ARO-Tools/pkg/config"
"github.com/Azure/ARO-HCP/tooling/templatize/pkg/azauth"
"github.com/Azure/ARO-HCP/tooling/templatize/pkg/pipeline"
)
var defaultRgName = "hcp-templatize"
func shouldRunE2E() bool {
return os.Getenv("RUN_TEMPLATIZE_E2E") == "true"
}
type E2E interface {
SetConfig(updates config.Configuration)
UseRandomRG() func() error
AddBicepTemplate(template, templateFileName, paramfile, paramfileName string)
AddStep(step pipeline.Step, rg int)
SetOSArgs()
EnableDryRun()
Persist() error
}
type bicepTemplate struct {
bicepFile string
bicepFileName string
paramFile string
paramFileName string
}
type e2eImpl struct {
config config.Configuration
makefile string
pipeline pipeline.Pipeline
biceps []bicepTemplate
schema string
tmpdir string
rgName string
}
var _ E2E = &e2eImpl{}
func newE2E(tmpdir string) e2eImpl {
imp := e2eImpl{
tmpdir: tmpdir,
schema: `{"type": "object"}`,
config: config.Configuration{
"$schema": "schema.json",
"defaults": config.Configuration{
"region": "westus3",
"subscription": "ARO Hosted Control Planes (EA Subscription 1)",
"rg": defaultRgName,
},
"clouds": config.Configuration{
"public": config.Configuration{
"defaults": config.Configuration{},
"environments": config.Configuration{
"dev": config.Configuration{
"defaults": config.Configuration{},
},
},
},
},
},
pipeline: pipeline.Pipeline{
ServiceGroup: "Microsoft.Azure.ARO.Test",
RolloutName: "Test Rollout",
ResourceGroups: []*pipeline.ResourceGroup{
{
Name: "{{ .rg }}",
Subscription: "{{ .subscription }}",
},
},
},
rgName: defaultRgName,
biceps: []bicepTemplate{},
}
imp.SetOSArgs()
return imp
}
func GenerateRandomRGName() string {
rgSuffx := ""
if jobID := os.Getenv("JOB_ID"); jobID != "" {
rgSuffx = jobID
}
chars := []rune("abcdefghijklmnopqrstuvwxyz0123456789")
for i := 0; i < 3; i++ {
rgSuffx += string(chars[rand.IntN(len(chars))])
}
return "templatize-e2e-" + rgSuffx
}
func (e *e2eImpl) UseRandomRG() func() error {
e.rgName = GenerateRandomRGName()
e.SetConfig(config.Configuration{"defaults": config.Configuration{"rg": e.rgName}})
return func() error {
subsriptionID, err := pipeline.LookupSubscriptionID(context.Background(), "ARO Hosted Control Planes (EA Subscription 1)")
if err != nil {
return err
}
cred, err := azauth.GetAzureTokenCredentials()
if err != nil {
return err
}
rgClient, err := armresources.NewResourceGroupsClient(subsriptionID, cred, nil)
if err != nil {
return err
}
_, err = rgClient.BeginDelete(context.Background(), e.rgName, nil)
return err
}
}
func (e *e2eImpl) SetOSArgs() {
os.Args = []string{"test",
"--cloud", "public",
"--pipeline-file", e.tmpdir + "/pipeline.yaml",
"--config-file", e.tmpdir + "/config.yaml",
"--deploy-env", "dev",
"--no-persist-tag",
"--region", "westus3",
}
}
func (e *e2eImpl) EnableDryRun() {
os.Args = append(os.Args, "--dry-run")
}
func (e *e2eImpl) AddResourceGroup() {
numRgs := len(e.pipeline.ResourceGroups)
e.pipeline.ResourceGroups = append(e.pipeline.ResourceGroups, &pipeline.ResourceGroup{
Name: fmt.Sprintf("{{ .rg }}-%d", numRgs+1),
Subscription: "{{ .subscription }}",
},
)
}
func (e *e2eImpl) SetAKSName(aksName string) {
e.pipeline.ResourceGroups[0].AKSCluster = aksName
}
func (e *e2eImpl) AddStep(step pipeline.Step, rg int) {
e.pipeline.ResourceGroups[rg].Steps = append(e.pipeline.ResourceGroups[rg].Steps, step)
}
func (e *e2eImpl) SetConfig(updates config.Configuration) {
config.MergeConfiguration(e.config, updates)
}
func (e *e2eImpl) AddBicepTemplate(template, templateFileName, paramfile, paramfileName string) {
e.biceps = append(e.biceps, bicepTemplate{
bicepFile: template,
bicepFileName: templateFileName,
paramFile: paramfile,
paramFileName: paramfileName,
})
}
func (e *e2eImpl) Persist() error {
if len(e.biceps) != 0 {
for _, b := range e.biceps {
err := os.WriteFile(e.tmpdir+"/"+b.bicepFileName, []byte(b.bicepFile), 0644)
if err != nil {
return err
}
err = os.WriteFile(e.tmpdir+"/"+b.paramFileName, []byte(b.paramFile), 0644)
if err != nil {
return err
}
}
}
if e.makefile != "" {
err := os.WriteFile(e.tmpdir+"/Makefile", []byte(e.makefile), 0644)
if err != nil {
return err
}
}
configBytes, err := yaml.Marshal(e.config)
if err != nil {
return fmt.Errorf("failed to marshal config: %w", err)
}
err = os.WriteFile(e.tmpdir+"/config.yaml", configBytes, 0644)
if err != nil {
return err
}
err = os.WriteFile(e.tmpdir+"/schema.json", []byte(e.schema), 0644)
if err != nil {
return err
}
pipelineBytes, err := yaml.Marshal(e.pipeline)
if err != nil {
return fmt.Errorf("failed to marshal pipeline: %w", err)
}
return os.WriteFile(e.tmpdir+"/pipeline.yaml", []byte(pipelineBytes), 0644)
}