tooling/templatize/pkg/ev2/pipeline.go (102 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 ev2 import ( "fmt" "os" "path/filepath" "gopkg.in/yaml.v3" "github.com/Azure/ARO-Tools/pkg/config" "github.com/Azure/ARO-HCP/tooling/templatize/pkg/pipeline" ) const precompiledPrefix = "ev2-precompiled-" func PrecompilePipelineFileForEV2(pipelineFilePath string, cfg config.Configuration) (string, error) { precompiledPipeline, err := PrecompilePipelineForEV2(pipelineFilePath, cfg) if err != nil { return "", err } // store as new file pipelineBytes, err := yaml.Marshal(precompiledPipeline) if err != nil { return "", err } err = os.WriteFile(precompiledPipeline.PipelineFilePath(), pipelineBytes, 0644) if err != nil { return "", err } return precompiledPipeline.PipelineFilePath(), nil } func PrecompilePipelineForEV2(pipelineFilePath string, cfg config.Configuration) (*pipeline.Pipeline, error) { // load the pipeline and referenced files originalPipeline, err := pipeline.NewPipelineFromFile(pipelineFilePath, cfg) if err != nil { return nil, err } referencedFiles, err := readReferencedPipelineFiles(originalPipeline) if err != nil { return nil, fmt.Errorf("failed to read referenced files of pipeline %s: %w", originalPipeline.PipelineFilePath(), err) } // precompile the pipeline and referenced files processedPipeline, processedFiles, err := processPipelineForEV2(originalPipeline, referencedFiles, cfg) if err != nil { return nil, err } // store the processed files to disk relative to the pipeline directory for filePath, content := range processedFiles { absFilePath, err := processedPipeline.AbsoluteFilePath(filePath) if err != nil { return nil, fmt.Errorf("failed to get absolute file path for %q: %w", filePath, err) } err = os.WriteFile(absFilePath, content, 0644) if err != nil { return nil, fmt.Errorf("failed to write precompiled file %q: %w", filePath, err) } } return processedPipeline, nil } func readReferencedPipelineFiles(p *pipeline.Pipeline) (map[string][]byte, error) { referencedFiles := make(map[string][]byte) for _, rg := range p.ResourceGroups { for _, step := range rg.Steps { switch concreteStep := step.(type) { case *pipeline.ARMStep: absFilePath, err := p.AbsoluteFilePath(concreteStep.Parameters) if err != nil { return nil, fmt.Errorf("failed to get absolute file path for %q: %w", concreteStep.Parameters, err) } paramFileContent, err := os.ReadFile(absFilePath) if err != nil { return nil, fmt.Errorf("failed to read parameter file %q: %w", concreteStep.Parameters, err) } referencedFiles[concreteStep.Parameters] = paramFileContent } } } return referencedFiles, nil } func processPipelineForEV2(p *pipeline.Pipeline, referencedFiles map[string][]byte, cfg config.Configuration) (*pipeline.Pipeline, map[string][]byte, error) { processingPipeline, err := p.DeepCopy(buildPrefixedFilePath(p.PipelineFilePath(), precompiledPrefix)) if err != nil { return nil, nil, err } processedFiles := make(map[string][]byte) _, scopeBoundBicepParamVars := EV2Mapping(cfg, NewBicepParamPlaceholders(), []string{}) for _, rg := range processingPipeline.ResourceGroups { for _, step := range rg.Steps { // preprocess the parameters file with scopebinding variables switch concreteStep := step.(type) { case *pipeline.ARMStep: paramFileContent, ok := referencedFiles[concreteStep.Parameters] if !ok { return nil, nil, fmt.Errorf("parameter file %q not found", concreteStep.Parameters) } preprocessedBytes, err := config.PreprocessContent(paramFileContent, scopeBoundBicepParamVars) if err != nil { return nil, nil, err } newParameterFilePath := buildPrefixedFilePath(concreteStep.Parameters, precompiledPrefix) processedFiles[newParameterFilePath] = preprocessedBytes concreteStep.Parameters = newParameterFilePath } } } return processingPipeline, processedFiles, nil } func buildPrefixedFilePath(path, prefix string) string { dir := filepath.Dir(path) base := filepath.Base(path) return filepath.Join(dir, prefix+base) }